--
Hi,
Due to requirements beyond my control at a site where I'd like to
install Exim I've taken a shot at implementing TURN support in Exim
4.20. I know TURN is by many not considered part of the good SMTP
command family, but since we have SMTP AUTH these days I feel that the
insecurity inherent in TURN is less of an issue than it used to be.
(Yes, I know there's ATRN now. If my patch is acceptable, I suppose
ATRN could be implemented fairly quickly on top of that. Still, ATRN
is mostly just a subset of TURN, functionality-wise, and I'd like the
longest rope in the shop, please. :) )
Our Exchange 2k in the lab seems to be relatively happy with Exim's
behaviour under this patch, and that's my main concern at the moment.
The patch should not affect anyone not setting up the appropriate
acl-s, and should thus be pretty low impact.
I run this with the following configuration bits:
acl_smtp_turn = check_turn
turn_domains = ${tr{${lookup ldapm {ldap:///LDAPBASE?domain?one\
?(turnHost=${quote_ldap:$sender_host_address})}}}{\n}{:}}
hold_domains = ldap;ldap::///LDAPBASE?domain?one\
?(&(turnHost=*)(domain=${quote_ldap:$domain}))
[..]
begin acl
[..]
check_turn:
accept authenticated = *
condition = ${lookup ldap {ldap:///domain=${quote_ldap_dn:$authenticated_id},LDAPBASE\
?domain?base?turnHost=${quote_ldap:$sender_host_address}}{yes}{no}}
deny message = turn not permitted
[..]
begin routers
[..]
turnhosts:
driver = manualroute
transport = remote_smtp
route_data = ${lookup ldap {ldap:///domain=${quote_ldap_dn:$domain},LDAPBASE\
?turnHost?base?}}
As you can see, I have a domain -> ip mapping in LDAP indicating which
domains will run from the queue whenever a given IP successfully
turns. There's also an authenticator of course, but it's a pretty
straightforward one.
Arne.
--
#! /bin/sh -e
## 60_turn.dpatch by Arne Georg Gleditsch
##
## All lines beginning with `## DP:' are a description of the patch.
## DP: Implement SMTP TURN.
if [ $# -ne 1 ]; then
echo >&2 "`basename $0`: script expects -patch|-unpatch as argument"
exit 1
fi
case "$1" in
-patch) patch -f --no-backup-if-mismatch -p1 < $0;;
-unpatch) patch -f --no-backup-if-mismatch -R -p1 < $0;;
*)
echo >&2 "`basename $0`: script expects -patch|-unpatch as argument"
exit 1;;
esac
exit 0
@DPATCH@
diff -ru exim-4.20-upstream-orig/src/acl.c exim-4.20-upstream-turn/src/acl.c
--- exim-4.20-upstream-orig/src/acl.c Mon May 12 15:39:17 2003
+++ exim-4.20-upstream-turn/src/acl.c Mon Jul 21 14:24:25 2003
@@ -106,6 +106,7 @@
(1<<ACL_WHERE_AUTH)|(1<<ACL_WHERE_CONNECT)| /* control */
(1<<ACL_WHERE_HELO)|
(1<<ACL_WHERE_ETRN)|(1<<ACL_WHERE_EXPN)|
+ (1<<ACL_WHERE_TURN)|
(1<<ACL_WHERE_STARTTLS)|(ACL_WHERE_VRFY),
0, /* delay */
@@ -115,6 +116,7 @@
(1<<ACL_WHERE_CONNECT)|(1<<ACL_WHERE_HELO)|
(1<<ACL_WHERE_DATA)|
(1<<ACL_WHERE_ETRN)|(1<<ACL_WHERE_EXPN)|
+ (1<<ACL_WHERE_TURN)|
(1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_STARTTLS)|
(1<<ACL_WHERE_VRFY),
@@ -127,6 +129,7 @@
(1<<ACL_WHERE_CONNECT)|(1<<ACL_WHERE_HELO)|
(1<<ACL_WHERE_DATA)|
(1<<ACL_WHERE_ETRN)|(1<<ACL_WHERE_EXPN)|
+ (1<<ACL_WHERE_TURN)|
(1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_STARTTLS)|
(1<<ACL_WHERE_VRFY),
@@ -137,17 +140,20 @@
(1<<ACL_WHERE_CONNECT)|(1<<ACL_WHERE_HELO)|
(1<<ACL_WHERE_DATA)|
(1<<ACL_WHERE_ETRN)|(1<<ACL_WHERE_EXPN)|
+ (1<<ACL_WHERE_TURN)|
(1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_STARTTLS)|
(1<<ACL_WHERE_VRFY),
(1<<ACL_WHERE_AUTH)|(1<<ACL_WHERE_CONNECT)| /* sender_domains */
(1<<ACL_WHERE_HELO)|
(1<<ACL_WHERE_ETRN)|(1<<ACL_WHERE_EXPN)|
+ (1<<ACL_WHERE_TURN)|
(1<<ACL_WHERE_STARTTLS)|(1<<ACL_WHERE_VRFY),
(1<<ACL_WHERE_AUTH)|(1<<ACL_WHERE_CONNECT)| /* senders */
(1<<ACL_WHERE_HELO)|
(1<<ACL_WHERE_ETRN)|(1<<ACL_WHERE_EXPN)|
+ (1<<ACL_WHERE_TURN)|
(1<<ACL_WHERE_STARTTLS)|(1<<ACL_WHERE_VRFY),
0, /* set */
diff -ru exim-4.20-upstream-orig/src/globals.c exim-4.20-upstream-turn/src/globals.c
--- exim-4.20-upstream-orig/src/globals.c Mon May 12 15:39:19 2003
+++ exim-4.20-upstream-turn/src/globals.c Wed Jul 23 10:41:53 2003
@@ -104,6 +104,8 @@
#endif
+uschar *turn_domains = NULL;
+
/* Input-reading functions for messages, so we can use special ones for
incoming TCP/IP. The defaults use stdin. We never need these for any
stand-alone tests. */
@@ -150,6 +152,7 @@
uschar *acl_smtp_connect = NULL;
uschar *acl_smtp_data = NULL;
uschar *acl_smtp_etrn = NULL;
+uschar *acl_smtp_turn = NULL;
uschar *acl_smtp_expn = NULL;
uschar *acl_smtp_helo = NULL;
uschar *acl_smtp_mail = NULL;
@@ -175,6 +178,7 @@
US"RCPT",
US"STARTTLS",
US"VRFY",
+ US"TURN",
US"non-SMTP" };
int acl_wherecodes[] = { 503, /* AUTH */
@@ -187,6 +191,7 @@
550, /* RCPT */
550, /* STARTTLS */
252, /* VRFY */
+ 502, /* TURN */
0 }; /* not SMTP; not relevant */
BOOL accept_8bitmime = FALSE;
@@ -326,6 +331,9 @@
BOOL continue_more = FALSE;
int continue_sequence = 1;
uschar *continue_transport = NULL;
+BOOL continue_but_turn = FALSE;
+int continue_turn_fd = -1;
+uschar *continue_turn_address = NULL;
BOOL daemon_listen = FALSE;
BOOL daemon_write_pid = TRUE_UNSET;
@@ -551,7 +559,8 @@
{ US"smtp_syntax_error", L_smtp_syntax_error },
{ US"subject", L_subject },
{ US"tls_cipher", L_tls_cipher },
- { US"tls_peerdn", L_tls_peerdn }
+ { US"tls_peerdn", L_tls_peerdn },
+ { US"turn", L_turn }
};
int log_options_count = sizeof(log_options)/sizeof(bit_table);
diff -ru exim-4.20-upstream-orig/src/globals.h exim-4.20-upstream-turn/src/globals.h
--- exim-4.20-upstream-orig/src/globals.h Mon May 12 15:39:19 2003
+++ exim-4.20-upstream-turn/src/globals.h Wed Jul 23 10:42:03 2003
@@ -68,6 +68,8 @@
#endif
+extern uschar *turn_domains; /* Domains fetched by TURN */
+
/* Input-reading functions for messages, so we can use special ones for
incoming TCP/IP. */
@@ -92,6 +94,7 @@
extern uschar *acl_smtp_connect; /* ACL run on SMTP connection */
extern uschar *acl_smtp_data; /* ACL run after DATA */
extern uschar *acl_smtp_etrn; /* ACL run after ETRN */
+extern uschar *acl_smtp_turn; /* ACL run after TURN */
extern uschar *acl_smtp_expn; /* ACL run after EXPN */
extern uschar *acl_smtp_helo; /* ACL run after HELO/EHLO */
extern uschar *acl_smtp_mail; /* ACL run after MAIL */
@@ -167,6 +170,9 @@
extern BOOL continue_more; /* Flag more addresses waiting */
extern int continue_sequence; /* Sequence num for continued delivery */
extern uschar *continue_transport; /* Transport for continued delivery */
+extern BOOL continue_but_turn; /* Use continue_hostname, but renegotiate HELO */
+extern int continue_turn_fd;
+extern uschar *continue_turn_address;
extern BOOL daemon_listen; /* True if listening required */
extern BOOL daemon_write_pid; /* True if a pid file is to be written */
diff -ru exim-4.20-upstream-orig/src/macros.h exim-4.20-upstream-turn/src/macros.h
--- exim-4.20-upstream-orig/src/macros.h Mon May 12 15:39:20 2003
+++ exim-4.20-upstream-turn/src/macros.h Mon Jul 21 12:03:02 2003
@@ -349,6 +349,7 @@
#define L_subject 0x01000000
#define L_tls_cipher 0x02000000
#define L_tls_peerdn 0x04000000
+#define L_turn 0x08000000
#define L_all 0xffffffff
#define L_default (L_connection_reject | \
@@ -362,7 +363,8 @@
L_retry_defer | \
L_size_reject | \
L_skip_delivery | \
- L_tls_cipher)
+ L_tls_cipher | \
+ L_turn)
/* Private error numbers for delivery failures, set negative so as not
to conflict with system errno values. */
@@ -676,7 +678,7 @@
enum { ACL_WHERE_AUTH, ACL_WHERE_CONNECT, ACL_WHERE_DATA, ACL_WHERE_ETRN,
ACL_WHERE_EXPN, ACL_WHERE_HELO, ACL_WHERE_MAIL, ACL_WHERE_RCPT,
- ACL_WHERE_STARTTLS, ACL_WHERE_VRFY, ACL_WHERE_NOTSMTP };
+ ACL_WHERE_STARTTLS, ACL_WHERE_VRFY, ACL_WHERE_TURN, ACL_WHERE_NOTSMTP };
/* Situations for spool_write_header() */
diff -ru exim-4.20-upstream-orig/src/queue.c exim-4.20-upstream-turn/src/queue.c
--- exim-4.20-upstream-orig/src/queue.c Mon May 12 15:39:21 2003
+++ exim-4.20-upstream-turn/src/queue.c Mon Jul 21 12:12:11 2003
@@ -604,7 +604,11 @@
set_process_info("running queue: waiting for children of %d", pid);
(void)read(pfd[pipe_read], buffer, sizeof(buffer));
(void)close(pfd[pipe_read]);
- set_process_info("running queue");
+ if (continue_but_turn == TRUE && queue_2stage == FALSE)
+ f = NULL;
+ else
+ set_process_info("running queue");
+
} /* End loop for list of messages */
store_reset(reset_point1); /* Scavenge list of messages */
diff -ru exim-4.20-upstream-orig/src/readconf.c exim-4.20-upstream-turn/src/readconf.c
--- exim-4.20-upstream-orig/src/readconf.c Mon May 12 15:39:21 2003
+++ exim-4.20-upstream-turn/src/readconf.c Mon Jul 21 12:03:02 2003
@@ -145,6 +145,7 @@
#ifdef SUPPORT_TLS
{ "acl_smtp_starttls", opt_stringptr, &acl_smtp_starttls },
#endif
+ { "acl_smtp_turn", opt_stringptr, &acl_smtp_turn },
{ "acl_smtp_vrfy", opt_stringptr, &acl_smtp_vrfy },
{ "admin_groups", opt_gidlist, &admin_groups },
{ "allow_domain_literals", opt_bool, &allow_domain_literals },
@@ -324,6 +325,7 @@
#endif
{ "trusted_groups", opt_gidlist, &trusted_groups },
{ "trusted_users", opt_uidlist, &trusted_users },
+ { "turn_domains", opt_stringptr, &turn_domains },
{ "unknown_login", opt_stringptr, &unknown_login },
{ "unknown_username", opt_stringptr, &unknown_username },
{ "untrusted_set_sender", opt_stringptr, &untrusted_set_sender },
diff -ru exim-4.20-upstream-orig/src/smtp_in.c exim-4.20-upstream-turn/src/smtp_in.c
--- exim-4.20-upstream-orig/src/smtp_in.c Mon May 12 15:39:22 2003
+++ exim-4.20-upstream-turn/src/smtp_in.c Wed Jul 23 10:41:14 2003
@@ -60,7 +60,7 @@
HELO_CMD, EHLO_CMD, DATA_CMD, /* These are listed in the pipelining */
VRFY_CMD, EXPN_CMD, NOOP_CMD, /* RFC as requiring synchronization */
- ETRN_CMD, /* This by analogy with TURN from the RFC */
+ ETRN_CMD, TURN_CMD, /* This by analogy with TURN from the RFC */
STARTTLS_CMD, /* Required by the STARTTLS RFC */
/* This is a dummy to identify the non-sync commands when pipelining */
@@ -146,6 +146,7 @@
{ "quit", sizeof("quit")-1, QUIT_CMD, FALSE, TRUE },
{ "noop", sizeof("noop")-1, NOOP_CMD, TRUE, FALSE },
{ "etrn", sizeof("etrn")-1, ETRN_CMD, TRUE, FALSE },
+ { "turn", sizeof("turn")-1, TURN_CMD, TRUE, FALSE },
{ "vrfy", sizeof("vrfy")-1, VRFY_CMD, TRUE, FALSE },
{ "expn", sizeof("expn")-1, EXPN_CMD, TRUE, FALSE },
{ "help", sizeof("help")-1, HELP_CMD, TRUE, FALSE }
@@ -949,13 +950,14 @@
break;
- /* The VRFY, EXPN, HELP, ETRN, and NOOP commands are ignored. */
+ /* The VRFY, EXPN, HELP, ETRN, TURN and NOOP commands are ignored. */
case VRFY_CMD:
case EXPN_CMD:
case HELP_CMD:
case NOOP_CMD:
case ETRN_CMD:
+ case TURN_CMD:
bsmtp_transaction_linecount = receive_linecount;
break;
@@ -2219,6 +2221,14 @@
s = string_cat(s, &size, &ptr, US"250-ETRN\r\n", 10);
}
+ /* Advertise TURN if there's an ACL checking whether a host is
+ permitted to issue it; a check is made when any host actually tries. */
+
+ if (acl_smtp_turn != NULL)
+ {
+ s = string_cat(s, &size, &ptr, US"250-TURN\r\n", 10);
+ }
+
/* Advertise EXPN if there's an ACL checking whether a host is
permitted to issue it; a check is made when any host actually tries. */
@@ -2944,6 +2954,7 @@
Ustrcat(buffer, " HELO EHLO MAIL RCPT DATA");
Ustrcat(buffer, " NOOP QUIT RSET HELP");
if (acl_smtp_etrn != NULL) Ustrcat(buffer, " ETRN");
+ if (acl_smtp_turn != NULL) Ustrcat(buffer, " TURN");
if (acl_smtp_expn != NULL) Ustrcat(buffer, " EXPN");
if (acl_smtp_vrfy != NULL) Ustrcat(buffer, " VRFY");
smtp_printf("214%s\r\n", buffer);
@@ -3124,6 +3135,110 @@
break;
+ case TURN_CMD:
+ if (sender_address != NULL)
+ {
+ synprot_error(L_smtp_protocol_error, 503, NULL,
+ US"TURN is not permitted inside a transaction");
+ break;
+ }
+
+ log_write(L_turn, LOG_MAIN, "TURN received from %s",
+ host_and_ident(FALSE));
+
+ rc = acl_check(ACL_WHERE_TURN, smtp_data, acl_smtp_turn, &user_msg,
+ &log_msg);
+ if (rc != OK)
+ {
+ done = smtp_handle_acl_fail(ACL_WHERE_TURN, rc, user_msg, log_msg);
+ break;
+ }
+
+ if (smtp_inptr < smtp_inend)
+ {
+ /* Outstanding input */
+ synprot_error(L_smtp_protocol_error, 503, NULL,
+ US"TURN not properly synchronized");
+ break;
+ }
+
+ smtp_printf("250 OK, You be the server.\r\n");
+ fflush(smtp_out);
+
+ #ifdef SUPPORT_TLS
+ /* Exchange drops out of TLS mode when roles are TURNed. It is
+ not really obvious if this is correct or not, but for the sake of
+ interoperability we follow suit. */
+ tls_active = -1;
+ #endif
+
+ /* No way back. If we fall out, pretend eof and drop connection */
+ done = 1;
+
+ {
+ smtp_inblock inblock;
+ char buf[4000];
+ char *domains;
+ inblock.buffer = smtp_inbuffer;
+ inblock.buffersize = sizeof(smtp_inbuffer);
+ inblock.ptr = smtp_inptr;
+ inblock.ptrend = smtp_inend;
+ inblock.sock = fileno(smtp_in);
+
+ domains = expand_string(turn_domains);
+ deliver_selectstring = (char *)malloc(strlen(domains) + 7);
+ strcpy(deliver_selectstring, ".*@(");
+ strcat(deliver_selectstring, domains);
+ strcat(deliver_selectstring, ")$");
+ while (strchr(deliver_selectstring, ':') != NULL)
+ strchr(deliver_selectstring, ':')[0] = '|';
+ deliver_selectstring_regex = TRUE;
+
+ continue_but_turn = TRUE;
+ continue_turn_fd = fileno(smtp_out);
+ continue_turn_address = string_copy_malloc(sender_host_address);
+ queue_2stage = TRUE;
+ queue_run_force = TRUE;
+ queue_run_in_order = TRUE;
+
+ log_write(L_turn, LOG_MAIN, "TURN accepted, running queue for %s",
+ deliver_selectstring);
+
+ search_tidyup();
+ queue_run_pipe = -1;
+ queue_run(NULL, NULL, FALSE);
+
+ /* If any actual deliveries are made, smtp renegotiation and
+ shutdown will be handled by the transports invoked. If the
+ queue run did not result in any deliveries, we'll do a pro forma
+ job of it here. We check queue_run_pipe to determine which is
+ the case. (This is somewhat magical, and perhaps it would be
+ better if queue_run itself could tell us this by returning an
+ appropriate value. */
+
+ if (queue_run_pipe < 0)
+ {
+ if (!smtp_read_response(&inblock, buf, sizeof(buf), '2', 50))
+ {
+ log_write(0, LOG_MAIN|LOG_REJECT,
+ "Bad SMTP greeting after TURN: \"%s\"", buf);
+ break;
+ }
+ smtp_printf("QUIT\r\n");
+ }
+
+ fflush(smtp_out);
+
+ if (!smtp_read_response(&inblock, buf, sizeof(buf), '2', 50))
+ {
+ log_write(0, LOG_MAIN|LOG_REJECT,
+ "QUIT in TURNed state not accepted: \"%s\"", buf);
+ break;
+ }
+ }
+ break;
+
+
case BADARG_CMD:
synprot_error(L_smtp_syntax_error, 501, NULL, US"unexpected argument data");
break;
diff -ru exim-4.20-upstream-orig/src/transports/smtp.c exim-4.20-upstream-turn/src/transports/smtp.c
--- exim-4.20-upstream-orig/src/transports/smtp.c Mon May 12 15:39:23 2003
+++ exim-4.20-upstream-turn/src/transports/smtp.c Wed Jul 23 10:43:09 2003
@@ -781,9 +781,20 @@
if (continue_hostname == NULL)
{
- inblock.sock = outblock.sock =
- smtp_connect(host, host_af, port, interface, ob->connect_timeout,
- ob->keepalive);
+ if (continue_but_turn == FALSE ||
+ strcmp(continue_turn_address, host->address) != 0)
+ {
+ inblock.sock = outblock.sock =
+ smtp_connect(host, host_af, port, interface, ob->connect_timeout,
+ ob->keepalive);
+ }
+ else
+ {
+ /* The tls_active logic requires these to be identical. */
+ inblock.sock = continue_turn_fd;
+ outblock.sock = continue_turn_fd;
+ }
+
if (inblock.sock < 0)
{
set_errno(addrlist, errno, NULL, DEFER);
--