[Exim] [patch] STARTTLS support for Exim - read blurb!

Startseite
Nachricht löschen
Nachricht beantworten
Autor: Steve Haslam
Datum:  
To: exim-users
Betreff: [Exim] [patch] STARTTLS support for Exim - read blurb!
Following is the work I did to try to get Exim 3.03 to support SSL/TLS
via the STARTTLS command.

Several caveats:

1. Although it worked for simple cases, I think there may have been
cases such as sending several messages down one connection that broke
it. This needs someone who knows more about Exim's internals than me
(not hard) to look at.

2. It's very basic. There ought to be a way of specifying several
levels of checking according to the remote IP (e.g. require a
certifiate for people outside your intranet) and perhaps a switch to
allow relaying if TLS was used to transmit the message. This would
allow you to use certificates instead of SMTP AUTH, I guess.

It adds the following config lines:
tls_certificate = <cert-file>
tls_log_cipher # BOOL
tls_log_peerdn # BOOL
tls_privatekey = <key-file>
tls_rsa_privatekey = <key-file>
tls_verify_dir = <dir-with-certs>
tls_verify_clients # BOOL

3. It was written for Exim 3.03, now several months old.

4. I'd never poked around with Exim before, so I probably did silly
stylistic things, or didn't see odd side-effects of some changes.

So here you go. I wrote it because I thought we might need it for
work, but as it turns out we don't, so I don't have time to look after
it. I hope someone else does.

diff -x util -x build-* -x Local -x *.orig -x *~ -uPr exim-3.03/OS/Makefile-Base exim-3.03+srh/OS/Makefile-Base
--- exim-3.03/OS/Makefile-Base    Mon Aug  2 16:43:01 1999
+++ exim-3.03+srh/OS/Makefile-Base    Tue Oct 26 15:09:41 1999
@@ -197,7 +197,7 @@
         header.o host.o log.o match.o moan.o os.o parse.o queue.o \
         readconf.o retry.o \
         rewrite.o route.o search.o smtp_in.o spool_in.o spool_out.o \
-        store.o string.o tod.o transport.o tree.o verify.o $(EXIM_PERL)
+        store.o string.o tls.o tod.o transport.o tree.o verify.o $(EXIM_PERL)


 exim:   libident/libident.a pcre/libpcre.a lookups/lookups.a \
         directors/directors.a routers/routers.a transports/transports.a \
@@ -210,7 +210,7 @@
       libident/libident.a pcre/libpcre.a directors/directors.a \
       routers/routers.a transports/transports.a lookups/lookups.a \
       $(LIBS) $(IPV6_LIBS) $(EXTRALIBS) $(DBMLIB) $(LIBRESOLV) \
-      $(LOOKUP_LIBS) $(PERL_LIBS)
+      $(LOOKUP_LIBS) $(PERL_LIBS) $(TLS_LIBS)
     $(EXIM_CHMOD)
     @echo " "
     @echo ">>> exim binary built"
@@ -348,6 +348,7 @@
 spool_out.o:     $(HDRS) spool_out.c
 store.o:         $(HDRS) store.c
 string.o:        $(HDRS) string.c
+tls.o:           $(HDRS) tls.c
 tod.o:           $(HDRS) tod.c
 transport.o:     $(HDRS) transport.c
 tree.o:          $(HDRS) tree.c
diff -x util -x build-* -x Local -x *.orig -x *~ -uPr exim-3.03/scripts/MakeLinks exim-3.03+srh/scripts/MakeLinks
--- exim-3.03/scripts/MakeLinks    Mon Aug  2 16:43:03 1999
+++ exim-3.03+srh/scripts/MakeLinks    Tue Oct 26 15:09:41 1999
@@ -176,6 +176,7 @@
 ln -s ../src/spool_out.c       spool_out.c
 ln -s ../src/store.c           store.c
 ln -s ../src/string.c          string.c
+ln -s ../src/tls.c             tls.c
 ln -s ../src/tod.c             tod.c
 ln -s ../src/transport.c       transport.c
 ln -s ../src/tree.c            tree.c
diff -x util -x build-* -x Local -x *.orig -x *~ -uPr exim-3.03/src/accept.c exim-3.03+srh/src/accept.c
--- exim-3.03/src/accept.c    Mon Aug  2 16:43:03 1999
+++ exim-3.03+srh/src/accept.c    Tue Oct 26 15:43:24 1999
@@ -158,9 +158,9 @@
     DEBUG(3)
       debug_printf("421 %s %s - closing connection.\n",
       primary_hostname, msg);
-    fprintf(smtp_out, "421 %s %s - closing connection.\r\n",
+    smtp_fprintf(smtp_out, "421 %s %s - closing connection.\r\n",
       primary_hostname, msg);
-    fflush(smtp_out);
+    smtp_fflush(smtp_out);
     }


/* Control does not return from moan_smtp_batch(). */
@@ -2275,9 +2275,15 @@

accept_call_bombout = TRUE;

+#ifdef SUPPORT_TLS
+#define TLS_FORMATS "%s%s%s%s%s"
+#else
+#defined TLS_FORMATS ""
+#endif
+
   log_write(0, LOG_MAIN | (log_received_recipients? LOG_RECIPIENTS : 0) |
                           (log_received_sender? LOG_SENDER : 0),
-    "<= %s%s%s%s%s%s%s%s%s S=%d%s%s%s",
+    "<= %s%s%s%s%s%s%s%s%s" TLS_FORMATS " S=%d%s%s%s",
     (sender_address[0] == 0)?    "<>" : sender_address,
     (message_reference != NULL)? " R=" : "",
     (message_reference != NULL)? message_reference : "",
@@ -2287,6 +2293,13 @@
     (sender_ident != NULL)?      sender_ident : "",
     (received_protocol != NULL)? " P=" : "",
     (received_protocol != NULL)? received_protocol : "",
+#ifdef SUPPORT_TLS
+    (tls_active && tls_cipher && tls_log_cipher)? " X=" : "",
+    (tls_active && tls_cipher && tls_log_cipher)? tls_cipher : "",
+    (tls_active && tls_peerdn && tls_log_peerdn)? " D=\"" : "",
+    (tls_active && tls_peerdn && tls_log_peerdn)? tls_peerdn : "",
+    (tls_active && tls_peerdn && tls_log_peerdn)? "\"" : "",
+#endif
     msg_size,
     (old_id != NULL)? " id=" : "",
     (old_id != NULL)? old_id : "",
@@ -2340,7 +2353,7 @@
     if (smtp_reply == NULL)
       {
       DEBUG(3) debug_printf("250 OK id=%s\n", message_id);
-      fprintf(fout, "250 OK id=%s\r\n", message_id);
+      smtp_fprintf(fout, "250 OK id=%s\r\n", message_id);
       if (host_checking)
         fprintf(stdout,
           "\n**** SMTP testing: that is not a real message id!\n\n");
@@ -2348,9 +2361,9 @@
     else
       {
       DEBUG(3) debug_printf("%s\n", smtp_reply);
-      fprintf(fout, "%s\r\n", smtp_reply);
+      smtp_fprintf(fout, "%s\r\n", smtp_reply);
       }
-    fflush(fout);
+    smtp_fflush(fout);
     }


   /* For batched SMTP, generate an error message on failure, and do
diff -x util -x build-* -x Local -x *.orig -x *~ -uPr exim-3.03/src/config.h.defaults exim-3.03+srh/src/config.h.defaults
--- exim-3.03/src/config.h.defaults    Mon Aug  2 16:43:03 1999
+++ exim-3.03+srh/src/config.h.defaults    Tue Oct 26 15:09:41 1999
@@ -74,6 +74,8 @@
 #define SUPPORT_MAILSTORE
 #define SUPPORT_MBX


+#define SUPPORT_TLS
+
 #define TRANSPORT_APPENDFILE
 #define TRANSPORT_AUTOREPLY
 #define TRANSPORT_DEBUG
diff -x util -x build-* -x Local -x *.orig -x *~ -uPr exim-3.03/src/daemon.c exim-3.03+srh/src/daemon.c
--- exim-3.03/src/daemon.c    Mon Aug  2 16:43:04 1999
+++ exim-3.03+srh/src/daemon.c    Tue Oct 26 18:25:38 1999
@@ -304,7 +304,7 @@


   if (!smtp_start_session())
     {
-    fflush(smtp_out);
+    smtp_fflush(smtp_out);
     _exit(EXIT_SUCCESS);
     }


@@ -332,7 +332,7 @@
       }
     else
       {
-      fflush(smtp_out);
+      smtp_fflush(smtp_out);
       _exit((rc == 0)? EXIT_SUCCESS : EXIT_FAILURE);
       }


@@ -446,6 +446,11 @@
         log_write(0, LOG_PANIC_DIE, "exec of exim -Mc failed");
         }


+#ifdef SUPPORT_TLS
+      /* Don't ever molest the parent's SSL connection */
+      tls_active = 0;
+      tls_close();
+#endif
       /* No need to re-exec */
       (void)deliver_message(message_id, FALSE, FALSE);
       _exit(EXIT_SUCCESS);
diff -x util -x build-* -x Local -x *.orig -x *~ -uPr exim-3.03/src/deliver.c exim-3.03+srh/src/deliver.c
--- exim-3.03/src/deliver.c    Mon Aug  2 16:43:04 1999
+++ exim-3.03+srh/src/deliver.c    Tue Oct 26 18:41:28 1999
@@ -1559,7 +1559,13 @@
       confirmation = big_buffer;
       }


-    log_write(0, LOG_MAIN, "%c> %s%s%s%s%s T=%s H=%s [%s]%s%s",
+#ifdef SUPPORT_TLS
+#define TLS_FORMATS "%s%s%s%s%s"
+#else
+#defined TLS_FORMATS ""
+#endif
+
+    log_write(0, LOG_MAIN, "%c> %s%s%s%s%s T=%s H=%s [%s]%s" TLS_FORMATS "%s",
       logchar,
       log_address,
      (addr->director == NULL)? "" : " D=",
@@ -1570,6 +1576,13 @@
      (addr->transported == NULL)? "" : addr->transported->name,
      (addr->transported == NULL)? "" : addr->transported->address,
      (continue_sequence <= 1)? "" : "*",
+#ifdef SUPPORT_TLS
+    (tls_used && tls_cipher && tls_log_cipher)? " X=" : "",
+    (tls_used && tls_cipher && tls_log_cipher)? tls_cipher : "",
+    (tls_used && tls_peerdn && tls_log_peerdn)? " D=\"" : "",
+    (tls_used && tls_peerdn && tls_log_peerdn)? tls_peerdn : "",
+    (tls_used && tls_peerdn && tls_log_peerdn)? "\"" : "",
+#endif
      confirmation);
     }
   }
diff -x util -x build-* -x Local -x *.orig -x *~ -uPr exim-3.03/src/exim.c exim-3.03+srh/src/exim.c
--- exim-3.03/src/exim.c    Mon Aug  2 16:43:04 1999
+++ exim-3.03+srh/src/exim.c    Wed Oct 27 11:10:36 1999
@@ -389,6 +389,11 @@
 regex_DSN  = regex_must_compile("\\n250[\\s\\-]DSN(\\s|\\n|$)", FALSE, TRUE);
 #endif


+/* TLS too */
+#ifdef SUPPORT_TLS
+regex_STARTTLS = regex_must_compile("\\n250[\\s\\-]STARTTLS(\\s|\\n|$)", FALSE, TRUE);
+#endif
+
 /* If the program is called as "mailq" treat it as equivalent to "exim -bp";
 this seems to be a generally accepted convention, since one finds symbolic
 links called "mailq" in standard OS configurations. */
diff -x util -x build-* -x Local -x *.orig -x *~ -uPr exim-3.03/src/functions.h exim-3.03+srh/src/functions.h
--- exim-3.03/src/functions.h    Mon Aug  2 16:43:05 1999
+++ exim-3.03+srh/src/functions.h    Tue Oct 26 15:37:53 1999
@@ -212,6 +212,21 @@
 extern int   strncmpic(char *, char *, int);
 extern char *strstric(char *, char *, BOOL);


+/* These all work with the current TLS session */
+extern int tls_getc(FILE*);
+extern int tls_ungetc(int, FILE*);
+extern int tls_feof(FILE*);
+extern int tls_ferror(FILE*);
+extern int tls_fwrite(const char*, size_t, size_t, FILE*);
+extern int tls_fprintf(FILE*, const char*, ...);
+extern int tls_fflush(FILE*);
+extern int tls_send(int, const char*, unsigned);
+/* Called by SMTP server */
+extern void start_tls(void);
+/* Called by SMTP transport */
+extern int connect_tls(int sockfd);
+extern void tls_close(void);
+
extern char *tod_stamp(int);
extern BOOL transport_check_serialized(char *, char *);
extern BOOL transport_check_waiting(char *, char *, int, char *, BOOL *);
@@ -241,5 +256,20 @@
extern BOOL verify_sender(int *, char **);
extern BOOL verify_sender_preliminary(int *, char **);
extern void version_init(void);
+
+/* Support for TLS/SSL if configured */
+/* smtp functions use smtp_*() version to write replies
+ and the SSL routines pass to the normal version if SSL
+ not in use */
+#ifdef SUPPORT_TLS
+#define smtp_fwrite(a,b,c,d) tls_fwrite(a,b,c,d)
+#define smtp_fflush(f) tls_fflush(f)
+#define smtp_fprintf(s,f,args...) tls_fprintf(s,f,## args)
+#else
+#define smtp_fwrite(a,b,c,d) fwrite(a,b,c,d)
+#define smtp_fflush(f) fflush(f)
+#define smtp_fprintf(s,f,args...) fprintf(s,f,## args)
+#endif /* defined SUPPORT_TLS */
+

 /* End of functions.h */
diff -x util -x build-* -x Local -x *.orig -x *~ -uPr exim-3.03/src/globals.c exim-3.03+srh/src/globals.c
--- exim-3.03/src/globals.c    Mon Aug  2 16:43:05 1999
+++ exim-3.03+srh/src/globals.c    Wed Oct 27 11:05:43 1999
@@ -587,6 +587,22 @@
 BOOL   strip_excess_angle_brackets = FALSE;
 BOOL   strip_trailing_dot     = FALSE;


+#ifdef SUPPORT_TLS
+/* TLS config settings */
+char *tls_certificate = NULL;
+BOOL tls_log_cipher = TRUE;
+BOOL tls_log_peerdn = FALSE;
+char *tls_privatekey = NULL;
+char *tls_rsa_privatekey = NULL;
+char *tls_verify_dir = NULL;
+BOOL tls_verify_clients = FALSE;
+BOOL tls_active = FALSE;
+BOOL tls_used = FALSE;
+char *tls_cipher = NULL;
+char *tls_peerdn = NULL;
+pcre *regex_STARTTLS = NULL;
+#endif /* defined(SUPPORT_TLS) */
+
transport_instance *transports = NULL;

 transport_instance *transport_message_filter_directory = NULL;
diff -x util -x build-* -x Local -x *.orig -x *~ -uPr exim-3.03/src/globals.h exim-3.03+srh/src/globals.h
--- exim-3.03/src/globals.h    Mon Aug  2 16:43:05 1999
+++ exim-3.03+srh/src/globals.h    Tue Oct 26 18:39:45 1999
@@ -447,6 +447,22 @@
 extern BOOL   strip_excess_angle_brackets; /* Surrounding route-addrs */
 extern BOOL   strip_trailing_dot;     /* Remove dots at ends of domains */


+#ifdef SUPPORT_TLS
+/* TLS config settings */
+extern char *tls_certificate;
+extern BOOL tls_log_cipher;
+extern BOOL tls_log_peerdn;
+extern char *tls_privatekey;
+extern char *tls_rsa_privatekey;
+extern char *tls_verify_dir;
+extern BOOL tls_verify_clients;
+extern BOOL tls_active;
+extern BOOL tls_used;
+extern char *tls_cipher;
+extern char *tls_peerdn;
+extern pcre *regex_STARTTLS;
+#endif /* defined(SUPPORT_TLS) */
+
/* Ditto for system filter */

 extern transport_instance *transport_message_filter_directory;
diff -x util -x build-* -x Local -x *.orig -x *~ -uPr exim-3.03/src/lookups/ldap.c exim-3.03+srh/src/lookups/ldap.c
--- exim-3.03/src/lookups/ldap.c    Mon Aug  2 16:43:06 1999
+++ exim-3.03+srh/src/lookups/ldap.c    Tue Oct 26 15:09:42 1999
@@ -229,7 +229,7 @@


   #if defined LDAP_LIB_SOLARIS7     /* Solaris 7 LDAP */
     *errmsg = string_sprintf("ldap search failed: %s",
-      err2string(ldap_result2error(lcp->ld, result, 0)));
+      ldap_err2string(ldap_result2error(lcp->ld, result, 0)));
   #elif defined LDAP_LIB_NETSCAPE   /* Netscape SDK */
     (void)ldap_get_lderrno(lcp->ld, &matched, &error);
     *errmsg = string_sprintf("ldap search failed: %s (%s)", error, matched);
diff -x util -x build-* -x Local -x *.orig -x *~ -uPr exim-3.03/src/readconf.c exim-3.03+srh/src/readconf.c
--- exim-3.03/src/readconf.c    Mon Aug  2 16:43:08 1999
+++ exim-3.03+srh/src/readconf.c    Tue Oct 26 15:09:42 1999
@@ -214,6 +214,15 @@
   { "spool_directory",          opt_stringptr,   &spool_directory },
   { "strip_excess_angle_brackets", opt_bool,     &strip_excess_angle_brackets },
   { "strip_trailing_dot",       opt_bool,        &strip_trailing_dot },
+#ifdef SUPPORT_TLS
+  { "tls_certificate",          opt_stringptr,   &tls_certificate },
+  { "tls_log_cipher",           opt_bool,        &tls_log_cipher },
+  { "tls_log_peerdn",           opt_bool,        &tls_log_peerdn },
+  { "tls_privatekey",           opt_stringptr,   &tls_privatekey },
+  { "tls_rsa_privatekey",       opt_stringptr,   &tls_privatekey },
+  { "tls_verify_clients",       opt_bool,        &tls_verify_clients },
+  { "tls_verify_dir",           opt_stringptr,   &tls_verify_dir },
+#endif /* defined(SUPPORT_TLS) */
   { "trusted_groups",           opt_gidlist,     &trusted_groups },
   { "trusted_users",            opt_uidlist,     &trusted_users },
   { "unknown_login",            opt_stringptr,   &unknown_login },
diff -x util -x build-* -x Local -x *.orig -x *~ -uPr exim-3.03/src/smtp_in.c exim-3.03+srh/src/smtp_in.c
--- exim-3.03/src/smtp_in.c    Mon Aug  2 16:43:08 1999
+++ exim-3.03+srh/src/smtp_in.c    Tue Oct 26 15:30:15 1999
@@ -19,7 +19,6 @@
 int deny_severity  = LOG_NOTICE;
 #endif


-
/* Size of buffer for reading SMTP commands */

 #define cmd_buffer_size  512      /* Ref. RFC 821 */
@@ -41,7 +40,7 @@


enum { HELO_CMD, EHLO_CMD, MAIL_CMD, RCPT_CMD, DATA_CMD, VRFY_CMD,
EXPN_CMD, QUIT_CMD, RSET_CMD, NOOP_CMD, DEBUG_CMD, HELP_CMD,
- ETRN_CMD, EOF_CMD, OTHER_CMD, BADARG_CMD };
+ ETRN_CMD, STARTTLS_CMD, EOF_CMD, OTHER_CMD, BADARG_CMD };



@@ -79,6 +78,9 @@
   { "noop",       sizeof("noop")-1,       NOOP_CMD, TRUE },
   { "debug",      sizeof("debug")-1,     DEBUG_CMD, TRUE },
   { "help",       sizeof("help")-1,       HELP_CMD, TRUE },
+#ifdef SUPPORT_TLS
+  { "starttls",   sizeof("starttls")-1, STARTTLS_CMD, TRUE },
+#endif /* defined(SUPPORT_TLS) */
   { "etrn",       sizeof("etrn")-1,       ETRN_CMD, TRUE} };


static smtp_cmd_list *cmd_list_end =
@@ -124,7 +126,7 @@
if (smtp_inptr >= smtp_inend)
{
int rc;
- fflush(smtp_out);
+ smtp_fflush(smtp_out);
alarm(smtp_receive_timeout);
rc = read(fileno(f), smtp_inbuffer, in_buffer_size);
alarm(0);
@@ -234,9 +236,9 @@

DEBUG(3) debug_printf("421 %s: SMTP command timeout - closing connection\n",
primary_hostname);
-fprintf(smtp_out, "421 %s: SMTP command timeout - closing connection\r\n",
+smtp_fprintf(smtp_out, "421 %s: SMTP command timeout - closing connection\r\n",
primary_hostname);
-fflush(smtp_out);
+smtp_fflush(smtp_out);
exit(EXIT_FAILURE);
}

@@ -265,7 +267,7 @@

DEBUG(3) debug_printf("421 %s: Service not available - closing connection\n",
primary_hostname);
-fprintf(smtp_out, "421 %s: Service not available - closing connection\r\n",
+smtp_fprintf(smtp_out, "421 %s: Service not available - closing connection\r\n",
primary_hostname);
exit(EXIT_FAILURE);
}
@@ -353,8 +355,8 @@
if (smtp_reading_data)
(void)accept_read_message_data_smtp(smtp_in, NULL, NULL); /* Swallow */

-fprintf(smtp_out, "421 %s\r\n", message);
-fflush(smtp_out);
+smtp_fprintf(smtp_out, "421 %s\r\n", message);
+smtp_fflush(smtp_out);

for (;;)
{
@@ -366,15 +368,15 @@

     case RSET_CMD:
     DEBUG(3) debug_printf("250 Reset OK\n");
-    fprintf(smtp_out, "250 Reset OK\r\n");
+    smtp_fprintf(smtp_out, "250 Reset OK\r\n");
     break;


     default:
-    fprintf(smtp_out, "421 %s\r\n", message);
+    smtp_fprintf(smtp_out, "421 %s\r\n", message);
     break;
     }


- fflush(smtp_out);
+ smtp_fflush(smtp_out);
}
}

@@ -883,7 +885,7 @@
{
char *ss = s + 1;
while (*ss != 0 && *ss != '\n' && *ss != '|') ss++;
- fprintf(f, "%d-%.*s\r\n", errorcode, ss-s, s);
+ smtp_fprintf(f, "%d-%.*s\r\n", errorcode, ss-s, s);
DEBUG(3) debug_printf("%d-%.*s\r\n", errorcode, ss-s, s);
if (*ss == 0) break;
s = ss + 1;
@@ -1433,8 +1435,8 @@
ss[ptr] = 0; /* string_cat leaves room for this */

DEBUG(3) debug_printf("%s", ss);
-fprintf(smtp_out, "%s", ss);
-fflush(smtp_out);
+smtp_fprintf(smtp_out, "%s", ss);
+smtp_fflush(smtp_out);
return TRUE;
}

@@ -1586,7 +1588,7 @@
       char *s;
       DEBUG(3)
         debug_printf("501 syntactically invalid %s argument(s)\n", hello);
-      fprintf(smtp_out, "501 syntactically invalid %s argument(s)\r\n", hello);
+      smtp_fprintf(smtp_out, "501 syntactically invalid %s argument(s)\r\n", hello);
       if (*smtp_data == 0) strcpy(smtp_data, "(no argument given)");
       s = string_printing(smtp_data);
       log_write(0, LOG_MAIN|LOG_REJECT, "rejected %s from %s: syntactically "
@@ -1686,7 +1688,7 @@
           {
           DEBUG(3) debug_printf("550 %s argument does not match calling host\n",
             hello);
-          fprintf(smtp_out, "550 %s argument does not match calling host\n",
+          smtp_fprintf(smtp_out, "550 %s argument does not match calling host\n",
             hello);
           log_write(0, LOG_MAIN|LOG_REJECT, "rejected %s from %s", hello,
             sender_fullhost);
@@ -1781,6 +1783,13 @@


       s = string_cat(s, &size, &ptr, "250-PIPELINING\r\n", 16);


+      /* Advertise TLS (SSL) */
+#ifdef SUPPORT_TLS
+      /* Don't advertise it if already in TLS */
+      if (!tls_active) 
+    s = string_cat(s, &size, &ptr, "250-STARTTLS\r\n", 14);
+#endif /* defined(SUPPORT_TLS) */
+
       /* Finish off the multiline reply with one that is always available. */


       s = string_cat(s, &size, &ptr, "250 HELP\r\n", 10);
@@ -1790,7 +1799,7 @@
     has been seen. */


     s[ptr] = 0;
-    fwrite(s, 1, ptr, smtp_out);
+    smtp_fwrite(s, 1, ptr, smtp_out);
     DEBUG(3) debug_printf("%s", s);
     helo_seen = TRUE;
     break;
@@ -1806,7 +1815,7 @@
     if (helo_required && !helo_seen)
       {
       DEBUG(3) debug_printf("550 HELO or EHLO is required for this host\n");
-      fprintf(smtp_out, "550 HELO or EHLO is required for this host\r\n");
+      smtp_fprintf(smtp_out, "550 HELO or EHLO is required for this host\r\n");
       log_write(0, LOG_MAIN|LOG_REJECT, "rejected MAIL FROM from %s: no "
         "HELO/EHLO given", sender_fullhost);
       break;
@@ -1815,14 +1824,14 @@
     if (sender_address != NULL)
       {
       DEBUG(3) debug_printf("503 Sender already given\n");
-      fprintf(smtp_out, "503 Sender already given\r\n");
+      smtp_fprintf(smtp_out, "503 Sender already given\r\n");
       break;
       }


     if (smtp_data[0] == 0)
       {
       DEBUG(3) debug_printf("501 MAIL FROM must have an address operand\n");
-      fprintf(smtp_out, "501 MAIL FROM must have an address operand\r\n");
+      smtp_fprintf(smtp_out, "501 MAIL FROM must have an address operand\r\n");
       break;
       }


@@ -1858,7 +1867,7 @@
         if (message_size_limit > 0 && size > message_size_limit)
           {
           DEBUG(3) debug_printf("552 Message size exceeds maximum permitted\n");
-          fprintf(smtp_out, "552 Message size exceeds maximum permitted\r\n");
+          smtp_fprintf(smtp_out, "552 Message size exceeds maximum permitted\r\n");
           goto COMMAND_LOOP;
           }


@@ -1873,7 +1882,7 @@
           if (!accept_check_fs(size + 5000))
             {
             DEBUG(3) debug_printf("452 space shortage, please try later\n");
-            fprintf(smtp_out, "452 space shortage, please try later\r\n");
+            smtp_fprintf(smtp_out, "452 space shortage, please try later\r\n");
             goto COMMAND_LOOP;
             }
           size_checked = TRUE;    /* No need to check again below */
@@ -1926,7 +1935,7 @@
     if (!size_checked && !accept_check_fs(0))
       {
       DEBUG(3) debug_printf("452 space shortage, please try later\n");
-      fprintf(smtp_out, "452 space shortage, please try later\r\n");
+      smtp_fprintf(smtp_out, "452 space shortage, please try later\r\n");
       break;
       }


@@ -1947,7 +1956,7 @@
       {
       log_syntax_error(errmess);
       DEBUG(3) debug_printf("501 %s: %s\n", smtp_data, errmess);
-      fprintf(smtp_out, "501 %s: %s\r\n", smtp_data, errmess);
+      smtp_fprintf(smtp_out, "501 %s: %s\r\n", smtp_data, errmess);
       break;
       }


@@ -1974,7 +1983,7 @@
         {
         DEBUG(3) debug_printf("501 %s: sender address must contain a domain\n",
           smtp_data);
-        fprintf(smtp_out, "501 %s: sender address must contain a domain\r\n",
+        smtp_fprintf(smtp_out, "501 %s: sender address must contain a domain\r\n",
           smtp_data);
         log_write(1, LOG_MAIN|LOG_REJECT, "unqualified sender rejected: "
           "<%s>%s%s%s",
@@ -1999,7 +2008,7 @@
             &sender_reject, -1, ':', TRUE))
         {
         smtp_send_prohibition_message(smtp_out, 550, "sender_reject");
-        fprintf(smtp_out, "550 rejected: administrative prohibition%s\r\n",
+        smtp_fprintf(smtp_out, "550 rejected: administrative prohibition%s\r\n",
           host_lookup_msg);
         DEBUG(3) debug_printf("550 rejected: administrative prohibition%s\n",
           host_lookup_msg);
@@ -2044,7 +2053,7 @@
           !verify_sender_preliminary(&errcode, &errmess))
         {
         smtp_send_prohibition_message(smtp_out, errcode, "sender_verify");
-        fprintf(smtp_out, "%d rejected: %s <%s>\r\n", errcode, errmess,
+        smtp_fprintf(smtp_out, "%d rejected: %s <%s>\r\n", errcode, errmess,
           raw_sender);
         DEBUG(3) debug_printf("%d rejected: %s %s\n", errcode,
           errmess, raw_sender);
@@ -2076,7 +2085,7 @@
     if (errmess != NULL)
       {
       DEBUG(3) debug_printf("%d %s <%s>\n", errcode, errmess, raw_sender);
-      fprintf(smtp_out, "%d %s <%s>\r\n", errcode, errmess, raw_sender);
+      smtp_fprintf(smtp_out, "%d %s <%s>\r\n", errcode, errmess, raw_sender);
       log_write(1, LOG_MAIN|LOG_REJECT, "%s <%s>%s%s", errmess, raw_sender,
         (sender_fullhost != NULL)? " H=" : "",
         (sender_fullhost != NULL)? host_and_ident() : "");
@@ -2085,7 +2094,7 @@
       {
       DEBUG(3) debug_printf("250 <%s> is syntactically correct\n",
         raw_sender);
-      fprintf(smtp_out, "250 <%s> is syntactically correct\r\n", raw_sender);
+      smtp_fprintf(smtp_out, "250 <%s> is syntactically correct\r\n", raw_sender);
       }


     /* Note that we haven't yet checked this sender for permission to relay
@@ -2108,14 +2117,14 @@
     if (sender_address == NULL)
       {
       DEBUG(3) debug_printf("503 No sender yet given\n");
-      fprintf(smtp_out, "503 No sender yet given\r\n");
+      smtp_fprintf(smtp_out, "503 No sender yet given\r\n");
       break;
       }


     if (smtp_data[0] == 0)
       {
       DEBUG(3) debug_printf("501 RCPT TO must have an address operand\n");
-      fprintf(smtp_out, "501 RCPT TO must have an address operand\r\n");
+      smtp_fprintf(smtp_out, "501 RCPT TO must have an address operand\r\n");
       break;
       }


@@ -2182,7 +2191,7 @@
       {
       log_syntax_error(errmess);
       DEBUG(3) debug_printf("501 %s: %s\n", smtp_data, errmess);
-      fprintf(smtp_out, "501 %s: %s\r\n", smtp_data, errmess);
+      smtp_fprintf(smtp_out, "501 %s: %s\r\n", smtp_data, errmess);
       break;
       }


@@ -2213,7 +2222,7 @@
         DEBUG(3)
           debug_printf("501 %s: recipient address must contain a domain\n",
             smtp_data);
-        fprintf(smtp_out, "501 %s: recipient address must contain a domain\r\n",
+        smtp_fprintf(smtp_out, "501 %s: recipient address must contain a domain\r\n",
           smtp_data);
         log_write(1, LOG_MAIN|LOG_REJECT, "unqualified recipient rejected: "
           "<%s>%s%s%s",
@@ -2244,7 +2253,7 @@
       {
       DEBUG(3) debug_printf("550 cannot route to sender address <%s>\n",
         sender_address);
-      fprintf(smtp_out, "550 cannot route to sender address <%s>\r\n",
+      smtp_fprintf(smtp_out, "550 cannot route to sender address <%s>\r\n",
         sender_address);
       break;
       }
@@ -2261,7 +2270,7 @@
         if (rbl_msg_buffer != NULL)
           {
           if (prohibition_message == NULL)
-            fprintf(smtp_out, "550-%s\r\n", rbl_msg_buffer);
+            smtp_fprintf(smtp_out, "550-%s\r\n", rbl_msg_buffer);
           else
             smtp_send_prohibition_message(smtp_out, 550, "rbl_reject");
           }
@@ -2275,7 +2284,7 @@


         if (host_refuse_all_rcpts)
           {
-          fprintf(smtp_out, "550 mail from %s rejected: administrative "
+          smtp_fprintf(smtp_out, "550 mail from %s rejected: administrative "
             "prohibition%s\r\n", sender_host_address, host_lookup_msg);
           DEBUG(3) debug_printf("550 mail from %s rejected: "
             "administrative prohibition%s\n", sender_host_address,
@@ -2283,7 +2292,7 @@
           }
         else  /* sender_refuse_all_rcpts */
           {
-          fprintf(smtp_out, "550 rejected: administrative prohibition\r\n");
+          smtp_fprintf(smtp_out, "550 rejected: administrative prohibition\r\n");
           DEBUG(3) debug_printf("550 rejected: administrative prohibition%s\n");
           }


@@ -2308,7 +2317,7 @@
       {
       char *code = recipients_max_reject? "552" : "452";
       DEBUG(3) debug_printf("%s too many recipients\n", code);
-      fprintf(smtp_out, "%s too many recipients\r\n", code);
+      smtp_fprintf(smtp_out, "%s too many recipients\r\n", code);
       toomany = TRUE;
       break;
       }
@@ -2485,7 +2494,7 @@
             relay_need_either? "sender+host_accept_relay" :
               host_allow_relay_anywhere? "sender_relay" : "host_accept_relay");


-          fprintf(smtp_out, msg1, orig_receiver);
+          smtp_fprintf(smtp_out, msg1, orig_receiver);
           DEBUG(3) debug_printf(msg1, orig_receiver);
           log_write(1, LOG_MAIN|LOG_REJECT, "%srefused relay (%s) to <%s> from "
             "<%s>%s%s%s",
@@ -2539,7 +2548,7 @@
         else s = string_sprintf("550 Cannot route to <%s>", orig_receiver);


         DEBUG(3) debug_printf("%s\n", s);
-        fprintf(smtp_out, "%s\r\n", s);
+        smtp_fprintf(smtp_out, "%s\r\n", s);


         log_write(3, LOG_MAIN,
           "verify failed for SMTP recipient %s from <%s>%s%s",
@@ -2566,12 +2575,12 @@
         if (!receiver_try_verify)
           {
           DEBUG(3) debug_printf("451 %s\n", s);
-          fprintf(smtp_out, "451 %s\r\n", s);
+          smtp_fprintf(smtp_out, "451 %s\r\n", s);
           break;   /* End of handling the RCPT TO command */
           }


         DEBUG(3) debug_printf("250 %s accepted unverified\n", s);
-        fprintf(smtp_out, "250 %s accepted unverified\r\n", s);
+        smtp_fprintf(smtp_out, "250 %s accepted unverified\r\n", s);
         }


       /* Verification succeeded */
@@ -2579,7 +2588,7 @@
       else
         {
         DEBUG(3) debug_printf("250 <%s> verified\n", orig_receiver);
-        fprintf(smtp_out, "250 <%s> verified\r\n", orig_receiver);
+        smtp_fprintf(smtp_out, "250 <%s> verified\r\n", orig_receiver);
         }
       }


@@ -2590,7 +2599,7 @@
       {
       DEBUG(3) debug_printf("250 <%s> is syntactically correct\n",
         orig_receiver);
-      fprintf(smtp_out, "250 <%s> is syntactically correct\r\n", orig_receiver);
+      smtp_fprintf(smtp_out, "250 <%s> is syntactically correct\r\n", orig_receiver);
       }


     /* Phew! All the checks have succeeded. Add to the list of receivers */
@@ -2607,7 +2616,7 @@
     if (sender_address == NULL)
       {
       DEBUG(3) debug_printf("503 MAIL FROM command must precede DATA\n");
-      fprintf(smtp_out, "503 MAIL FROM command must precede DATA\r\n");
+      smtp_fprintf(smtp_out, "503 MAIL FROM command must precede DATA\r\n");
       break;
       }


@@ -2618,7 +2627,7 @@
     if (recipients_count <= 0)
       {
       DEBUG(3) debug_printf("503 Valid RCPT TO <recipient> must precede DATA\n");
-      fprintf(smtp_out, "503 Valid RCPT TO <recipient> must precede DATA\r\n");
+      smtp_fprintf(smtp_out, "503 Valid RCPT TO <recipient> must precede DATA\r\n");
       break;
       }


@@ -2627,12 +2636,12 @@
       sender_address = NULL;  /* This will allow a new MAIL without RSET */
       sender_address_rewritten = FALSE;
       DEBUG(3) debug_printf("554 Too many recipients\n");
-      fprintf(smtp_out, "554 Too many recipients\r\n");
+      smtp_fprintf(smtp_out, "554 Too many recipients\r\n");
       break;
       }


     DEBUG(3) debug_printf("354 Enter message, ending with \".\" on a line by itself\n");
-    fprintf(smtp_out, "354 Enter message, ending with \".\" on a line by itself\r\n");
+    smtp_fprintf(smtp_out, "354 Enter message, ending with \".\" on a line by itself\r\n");
     done = 3;
     smtp_reading_data = TRUE;
     break;
@@ -2649,7 +2658,7 @@
     if (!smtp_verify)
       {
       DEBUG(3) debug_printf("252 VRFY not available\n");
-      fprintf(smtp_out, "252 VRFY not available\r\n");
+      smtp_fprintf(smtp_out, "252 VRFY not available\r\n");
       log_write(1, LOG_MAIN|LOG_REJECT, "VRFY rejected%s%s: %s",
         (sender_fullhost == NULL)? "" : " H=",
         (sender_fullhost == NULL)? "" : host_and_ident(),
@@ -2703,7 +2712,7 @@
         }


       DEBUG(3) debug_printf("%s\n", s);
-      fprintf(smtp_out, "%s\r\n", s);
+      smtp_fprintf(smtp_out, "%s\r\n", s);
       }
     break;


@@ -2713,7 +2722,7 @@
     if (!verify_check_host(&smtp_expn_hosts, FALSE))
       {
       DEBUG(3) debug_printf("550 EXPN not available\n");
-      fprintf(smtp_out, "550 EXPN not available\r\n");
+      smtp_fprintf(smtp_out, "550 EXPN not available\r\n");
       log_write(1, LOG_MAIN|LOG_REJECT, "EXPN rejected%s%s%s: %s",
         (sender_fullhost == NULL)? "" : " H=",
         (sender_fullhost == NULL)? "" : host_and_ident(),
@@ -2732,7 +2741,10 @@


     case QUIT_CMD:
     DEBUG(3) debug_printf("221 %s closing connection\n", primary_hostname);
-    fprintf(smtp_out, "221 %s closing connection\r\n", primary_hostname);
+    smtp_fprintf(smtp_out, "221 %s closing connection\r\n", primary_hostname);
+#ifdef SUPPORT_TLS
+    tls_close();
+#endif
     done = 2;
     if (log_smtp_connections)
       log_write(4, LOG_MAIN, "SMTP connection from %s closed by QUIT",
@@ -2745,25 +2757,30 @@
     rcount = 0;
     toomany = FALSE;
     DEBUG(3) debug_printf("250 Reset OK\n");
-    fprintf(smtp_out, "250 Reset OK\r\n");
+    smtp_fprintf(smtp_out, "250 Reset OK\r\n");
     break;



     case NOOP_CMD:
     DEBUG(3) debug_printf("250 OK\n");
-    fprintf(smtp_out, "250 OK\r\n");
+    smtp_fprintf(smtp_out, "250 OK\r\n");
     break;



     case DEBUG_CMD:
     DEBUG(3) debug_printf("500 No way!\n");
-    fprintf(smtp_out, "500 No way!\r\n");
+    smtp_fprintf(smtp_out, "500 No way!\r\n");
     log_write(4, LOG_MAIN, "SMTP \"debug\" command received: %s=%s",
       (sender_fullhost != NULL)? "H" : "U",
       (sender_fullhost != NULL)? host_and_ident() :
         (sender_ident == NULL)? "unknown" : sender_ident);
     break;


+#ifdef SUPPORT_TLS
+    case STARTTLS_CMD:
+      start_tls();
+      break;
+#endif


     /* Show ETRN/EXPN if any hosts are permitted to use them; if actually
     used, a check will be done for permitted hosts. */
@@ -2778,18 +2795,18 @@
       debug_printf("214     NOOP QUIT RSET HELP %s\n",
         smtp_verify? "VRFY" : "");
       }
-    fprintf(smtp_out, "214-Commands supported:\r\n");
-    fprintf(smtp_out, "214-    HELO EHLO MAIL RCPT DATA%s%s\r\n",
+    smtp_fprintf(smtp_out, "214-Commands supported:\r\n");
+    smtp_fprintf(smtp_out, "214-    HELO EHLO MAIL RCPT DATA%s%s\r\n",
         (smtp_etrn_hosts != NULL)? " ETRN" :"",
         (smtp_expn_hosts != NULL)? " EXPN" :"");
-    fprintf(smtp_out, "214     NOOP QUIT RSET HELP %s\r\n",
+    smtp_fprintf(smtp_out, "214     NOOP QUIT RSET HELP %s\r\n",
       smtp_verify? "VRFY" : "");
     break;



     case EOF_CMD:
     DEBUG(3) debug_printf("421 %s lost input connection\n", primary_hostname);
-    fprintf(smtp_out, "421 %s lost input connection\r\n", primary_hostname);
+    smtp_fprintf(smtp_out, "421 %s lost input connection\r\n", primary_hostname);


     /* Don't log by default unless in the middle of a message, as some mailers
     just drop the call rather than sending QUIT, and it clutters up the logs.
@@ -2815,14 +2832,14 @@
     if (smtp_etrn_hosts == NULL)
       {
       DEBUG(3) debug_printf("500 Command unrecognized\n");
-      fprintf(smtp_out, "500 Command unrecognized\r\n");
+      smtp_fprintf(smtp_out, "500 Command unrecognized\r\n");
       break;
       }


     if (sender_address != NULL)
       {
       DEBUG(3) debug_printf("503 ETRN not permitted inside transaction\n");
-      fprintf(smtp_out, "503 ETRN not permitted inside transaction\r\n");
+      smtp_fprintf(smtp_out, "503 ETRN not permitted inside transaction\r\n");
       break;
       }


@@ -2833,7 +2850,7 @@
       log_write(4, LOG_MAIN|LOG_REJECT, "Rejected ETRN %s from %s%s",
         smtp_data, sender_fullhost, host_lookup_msg);
       DEBUG(3) debug_printf("458 Administrative prohibition\n");
-      fprintf(smtp_out, "458 Administrative prohibition\r\n");
+      smtp_fprintf(smtp_out, "458 Administrative prohibition\r\n");
       break;
       }


@@ -2852,7 +2869,7 @@
       if (smtp_data[0] != '#')
         {
         DEBUG(3) debug_printf("501 Syntax error\n");
-        fprintf(smtp_out, "501 Syntax error\r\n");
+        smtp_fprintf(smtp_out, "501 Syntax error\r\n");
         break;
         }
       else smtp_data++;
@@ -2874,7 +2891,7 @@
         {
         log_write(0, LOG_MAIN|LOG_PANIC, "failed to set up ETRN command: %s",
           error);
-        fprintf(smtp_out, "458 Internal failure\r\n");
+        smtp_fprintf(smtp_out, "458 Internal failure\r\n");
         break;
         }
       }
@@ -2919,7 +2936,7 @@
           !transport_check_serialized("etrn-runs", smtp_data))
         {
         DEBUG(3) debug_printf("458 Already processing %s\n", smtp_data);
-        fprintf(smtp_out, "458 Already processing %s\r\n", smtp_data);
+        smtp_fprintf(smtp_out, "458 Already processing %s\r\n", smtp_data);
         break;
         }


@@ -2952,13 +2969,13 @@
     if (pid < 0)
       {
       log_write(0, LOG_MAIN|LOG_PANIC, "fork of process for ETRN failed");
-      fprintf(smtp_out, "458 Unable to fork process\r\n");
+      smtp_fprintf(smtp_out, "458 Unable to fork process\r\n");
       if (smtp_etrn_serialize) transport_end_serialized("etrn-runs", smtp_data);
       }
     else
       {
       DEBUG(3) debug_printf("250 OK\n");
-      fprintf(smtp_out, "250 OK\r\n");
+      smtp_fprintf(smtp_out, "250 OK\r\n");
       }
     break;


@@ -2967,14 +2984,14 @@
     log_syntax_error("unexpected argument data");
     DEBUG(3) debug_printf("501 Unexpected argument data in \"%s\"\n",
       cmd_buffer);
-    fprintf(smtp_out, "501 Unexpected argument data in \"%s\"\r\n", cmd_buffer);
+    smtp_fprintf(smtp_out, "501 Unexpected argument data in \"%s\"\r\n", cmd_buffer);
     break;



     default:
     log_syntax_error("unrecognized command");
     DEBUG(3) debug_printf("500 Command unrecognized\n");
-    fprintf(smtp_out, "500 Command unrecognized\r\n");
+    smtp_fprintf(smtp_out, "500 Command unrecognized\r\n");
     break;
     }


diff -x util -x build-* -x Local -x *.orig -x *~ -uPr exim-3.03/src/tls.c exim-3.03+srh/src/tls.c
--- exim-3.03/src/tls.c    Thu Jan  1 00:00:00 1970
+++ exim-3.03+srh/src/tls.c    Tue Oct 26 18:40:13 1999
@@ -0,0 +1,547 @@
+/*
+ * TLS/SSL support for Exim
+ * by Steve Haslam
+ * adapted from stunnel by Michal Trojnara
+ */
+
+#include "exim.h"
+
+#ifdef SUPPORT_TLS
+
+#include <openssl/lhash.h>
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+
+static char ssl_library_info[128];
+static void tls_error(const char *prefix);
+
+static int session_timeout = 200;
+
+static char *ssl_xfer_buffer = NULL;
+static int ssl_xfer_buffer_size = 4096;
+static int ssl_xfer_buffer_lwm = 0;
+static int ssl_xfer_buffer_hwm = 0;
+static int ssl_xfer_eof = 0;
+static int ssl_xfer_error = 0;
+
+static SSL_CTX *ctx = NULL;
+static SSL *ssl = NULL;
+static DH *dh = NULL;
+
+/* SRH: directly from stunnel! */
+/* Correct callback definitions overriding ssl.h */
+#ifndef NO_RSA
+#ifdef SSL_CTX_set_tmp_rsa_callback
+    #undef SSL_CTX_set_tmp_rsa_callback
+#endif
+#define SSL_CTX_set_tmp_rsa_callback(ctx,cb) \
+    SSL_CTX_ctrl(ctx,SSL_CTRL_SET_TMP_RSA_CB,0,(char *)cb)
+#endif /* NO_RSA */
+
+static RSA *rsa_callback(SSL *s, int export, int keylength);
+static void info_callback(SSL *, int, int);
+static int verify_callback(int, X509_STORE_CTX*);
+
+static const char *sid_ctx = "exim";
+
+#ifndef NO_DH
+static void init_dh(void)
+{
+  BIO *bio;
+  if (tls_certificate) {
+    if ((bio = BIO_new_file(tls_certificate, "r")) == NULL) {
+      log_write(0, LOG_MAIN, "DH: could not read %s: %s\n",
+        tls_certificate, strerror(errno));
+      goto failed;
+    }
+    if ((dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL)) == NULL) {
+      debug_printf("DH: could not load params from %s\n", tls_certificate);
+      goto failed;
+    }
+    SSL_CTX_set_tmp_dh(ctx, dh);
+    DEBUG(2)
+      debug_printf("Diffie-Hellman initialised from %s with %d-bit key\n",
+           tls_certificate, 8*DH_size(dh));
+    BIO_free(bio);
+    DH_free(dh);
+    return;
+
+  failed:
+    debug_printf("Diffie-Hellman intialisation failed\n");
+    if (bio) BIO_free(bio);
+  }
+}
+#endif /* !defined(NO_DH) */
+
+int init_tls(void)
+{
+  SSL_library_init();
+  SSL_load_error_strings();
+
+  if (OPENSSL_VERSION_NUMBER <= 0x0922) {
+    sprintf(ssl_library_info, "OpenSSL/0.%ld.%ld%c", (OPENSSL_VERSION_NUMBER&0x0f00)>>8, (OPENSSL_VERSION_NUMBER&0x00f0)>>4, 'a' + (char)(OPENSSL_VERSION_NUMBER&0x000f));
+  }
+  else {
+    sprintf(ssl_library_info, "OpenSSL/%ld.%ld.%ld", (OPENSSL_VERSION_NUMBER&0xf0000000)>>28, (OPENSSL_VERSION_NUMBER&0x0ff00000)>>20, (OPENSSL_VERSION_NUMBER&0x000ff000)>>12);
+    if (OPENSSL_VERSION_NUMBER&0x100) {
+      /* release */
+      if (OPENSSL_VERSION_NUMBER&0xff) {
+    char s[2];
+    s[0] = 'a' + (OPENSSL_VERSION_NUMBER&0x0ff) - 1;
+    s[1] = '\0';
+    strcat(ssl_library_info, s);
+      }
+    }
+    else {
+      if (OPENSSL_VERSION_NUMBER&0xff) {
+    char s[2];
+    s[0] = '0' + (OPENSSL_VERSION_NUMBER&0xff);
+    s[1] = '\0';
+    strcat(ssl_library_info, "beta");
+      }
+      else {
+    strcat(ssl_library_info, "-dev");
+      }
+    }
+  }
+
+  ctx = SSL_CTX_new(SSLv23_method());
+  if (!ctx) {
+    tls_error("SSL_CTX_new");
+    return 0;
+  }
+
+  if ((ssl_xfer_buffer = malloc(ssl_xfer_buffer_size)) == NULL) {
+    tls_error("malloc");
+    return 0;
+  }
+
+  DEBUG(4) debug_printf("Setting info callback\n");
+  if (!SSL_CTX_set_info_callback(ctx, info_callback)) {
+    tls_error("SSL_CTX_set_info_callback");
+    return 0;
+  }
+
+#ifndef NO_RSA
+  DEBUG(4) debug_printf("Setting tmp RSA callback\n");
+  SSL_CTX_set_tmp_rsa_callback(ctx, rsa_callback);
+#endif
+
+  if (tls_certificate) {
+    DEBUG(4) debug_printf("using certificate %s\n", tls_certificate);
+    if (!SSL_CTX_use_certificate_file(ctx, tls_certificate, SSL_FILETYPE_PEM)) {
+      tls_error("SSL_CTX_use_certificate_file");
+      return 0;
+    }
+  }
+
+#ifndef NO_DH
+  init_dh();
+#endif
+  
+  if (tls_privatekey) {
+    DEBUG(3) debug_printf("using private key %s\n", tls_privatekey);
+    if (!SSL_CTX_use_PrivateKey_file(ctx, tls_privatekey, SSL_FILETYPE_PEM)) {
+      tls_error("SSL_CTX_use_PrivateKey_file");
+      return 0;
+    }
+  }
+
+#ifndef NO_RSA
+  if (tls_rsa_privatekey) {
+    DEBUG(3) debug_printf("using RSA private key %s\n", tls_rsa_privatekey);
+    if (!SSL_CTX_use_RSAPrivateKey_file(ctx, tls_rsa_privatekey, SSL_FILETYPE_PEM)) {
+      tls_error("SSL_CTX_use_RSAPrivateKey_file");
+      return 0;
+    }
+  }
+#endif
+
+  if (tls_verify_clients) {
+    DEBUG(3) debug_printf("Setting default verify paths\n");
+    if (!SSL_CTX_set_default_verify_paths(ctx)) {
+      tls_error("SSL_CTX_set_default_verify_paths");
+      return 0;
+    }
+#if 0
+    if (!SSL_CTX_load_verify_locations(ctx, CLIENT_CA, ssl_clientdir)) {
+      tls_error("SSL_CTX_load_verify_locations");
+      return 0;
+    }
+#endif
+    DEBUG(3) debug_printf("Setting verify level\n");
+    SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, verify_callback);
+#if 0
+    SSL_CTX_set_client_CA_list(ctx, SSL_load_client_CA_file(CLIENT_CA));
+#endif
+  }
+
+#if 0
+  if (ssl_cipher_list) {
+    if (!SSL_CTX_set_cipher_list(ctx, ssl_cipher_list)) {
+      tls_error("SSL_CTX_set_cipher_list");
+      return 0;
+    }
+  }
+#endif
+
+  DEBUG(4) debug_printf("Setting session cache mode\n");
+  SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_BOTH);
+
+  DEBUG(4) debug_printf("Setting session timeout\n");
+  SSL_CTX_set_timeout(ctx, session_timeout);
+
+  debug_printf("Initialised TLS (%s)\n", ssl_library_info);
+
+  return 1;
+}
+
+static void show_cipher(SSL *ssl)
+{
+  static char cipherbuf[256];
+  SSL_CIPHER *c;
+  char *ver;
+  int bits;
+
+  switch (ssl->session->ssl_version) {
+  case SSL2_VERSION:
+    ver="SSLv2";
+    break;
+
+  case SSL3_VERSION:
+    ver="SSLv3";
+    break;
+
+  case TLS1_VERSION:
+    ver="TLSv1";
+    break;
+
+  default:
+    ver="UNKNOWN";
+  }
+  
+  c = SSL_get_current_cipher(ssl);
+  SSL_CIPHER_get_bits(c, &bits);
+
+  sprintf(cipherbuf, "%s:%s:%u", ver, SSL_CIPHER_get_name(c), bits);
+  debug_printf("Negotiated cipher: %s\n", cipherbuf);
+  tls_cipher = cipherbuf;
+}
+
+void start_tls(void)
+{
+  if (tls_active) {
+    /* multiple STARTTLS statements- now what? */
+    log_write(1, LOG_MAIN, "mutliple STARTTLS statements detected");
+    DEBUG(3) debug_printf("454 Already in TLS\n");
+    smtp_fprintf(smtp_out, "454 Already in TLS\r\n");
+    return;
+  }
+  
+  if (!ctx) {
+    if (!init_tls()) {
+    no_tls:
+      DEBUG(3) debug_printf("454 %s currently unavailable\n", ssl_library_info);
+      smtp_fprintf(smtp_out, "454 %s currently unavailable\r\n", ssl_library_info);
+      return;
+    }
+  }
+
+  if ((ssl = SSL_new(ctx)) == NULL) {
+    tls_error("SSL_new");
+    goto no_tls;
+  }
+  SSL_set_session_id_context(ssl, sid_ctx, strlen(sid_ctx));
+
+  DEBUG(3) debug_printf("220 %s go ahead\n", ssl_library_info);
+  fprintf(smtp_out, "220 %s go ahead\r\n", ssl_library_info);
+  fflush(smtp_out);
+
+  SSL_set_fd(ssl, fileno(smtp_out));
+  SSL_set_accept_state(ssl);
+  if (SSL_accept(ssl) <= 0) {
+    tls_error("SSL_accept");
+    return;
+  }
+  show_cipher(ssl);
+
+  ssl_xfer_buffer_lwm = ssl_xfer_buffer_hwm = 0;
+  ssl_xfer_eof = ssl_xfer_error = 0;
+  accept_getc = tls_getc;
+  accept_ungetc = tls_ungetc;
+  accept_feof = tls_feof;
+  accept_ferror = tls_ferror;
+  tls_used = tls_active = 1;
+}
+
+int connect_tls(int fd)
+{
+  if ((ssl = SSL_new(ctx)) == NULL) {
+    tls_error("SSL_new");
+    return 0;
+  }
+  DEBUG(4) debug_printf("SSL_set_session_id_context(%lx, %s, %d)\n",
+            (long)ssl, sid_ctx, strlen(sid_ctx));
+  SSL_set_session_id_context(ssl, sid_ctx, strlen(sid_ctx));
+
+  DEBUG(4) debug_printf("SSL_set_fd(%lx, %d)\n", (long)ssl, fd);
+  SSL_set_fd(ssl, fd);
+  DEBUG(4) debug_printf("SSL_connect_state(%lx)\n", (long)ssl);
+  SSL_set_connect_state(ssl);
+  DEBUG(4) debug_printf("SSL_connect(%lx)\n", (long)ssl);
+  if (SSL_connect(ssl) <= 0) {
+    tls_error("SSL_connect");
+    return 0;
+  }
+  show_cipher(ssl);
+
+  tls_used = tls_active = 1;
+  return 1;
+}
+
+void finish_tls(void)
+{
+  if (ctx) {
+    SSL_CTX_free(ctx);
+    ctx = NULL;
+  }
+}
+
+static RSA *rsa_callback(SSL *s, int export, int keylength)
+{
+  RSA *rsa_key;
+  debug_printf("Generating %d bit RSA key...\n", keylength);
+  rsa_key = RSA_generate_key(keylength, RSA_F4, NULL, NULL);
+  if (!rsa_key) {
+    tls_error("RSA_generate_key");
+    return NULL;
+  }
+  return rsa_key;
+}
+
+static int verify_callback(int state, X509_STORE_CTX *x509ctx)
+{
+  /* SSL libraries do some verification- current yes/no state
+     in 'state' */
+  static char txt[256];
+
+  X509_NAME_oneline(X509_get_subject_name(x509ctx->current_cert),
+            txt, sizeof(txt));
+  if (!state) {
+    log_write(1, LOG_MAIN, "SSL verify error: depth=%d error=%s cert=%s\n",
+         x509ctx->error_depth,
+         X509_verify_cert_error_string(x509ctx->error),
+         txt);
+    return 0;
+  }
+  
+  /*
+    At this point stunnel checks for a certificate in the store(?)
+    that has the same subject name as the client certificate
+    for when -v 3 is specified
+  */
+
+  if (x509ctx->error_depth) {
+    DEBUG(3) debug_printf("SSL verify ok: depth=%d cert=%s\n",
+              x509ctx->error_depth, txt);
+  }
+  else {
+    debug_printf("SSL authenticated peer: %s\n", txt);
+    tls_peerdn = txt;
+  }
+
+  return 1; /* accept */
+}
+
+static void info_callback(SSL *s, int where, int ret)
+{
+  DEBUG(3) debug_printf("SSL info: %s\n", SSL_state_string_long(s));
+}
+
+static void tls_error(const char *prefix)
+{
+  char string[256];
+  ERR_error_string(ERR_get_error(), string);
+  log_write(0, LOG_MAIN, "%s: %s", prefix, string);
+}
+
+/* TLS versions of the SMTP read routines */
+
+inline void checkfileno(FILE *f)
+{
+  //debug_printf("fileno(f)=%d SSL_get_fd(ssl)=%d\n", fileno(f), SSL_get_fd(ssl));
+}
+
+int tls_getc(FILE *f)
+{
+  if (!ssl) return smtp_getc(f);
+  checkfileno(f);
+  if (ssl_xfer_buffer_lwm == ssl_xfer_buffer_hwm) {
+    /* refill */
+    int error;
+    int inbytes;
+    
+    DEBUG(5) debug_printf("Calling SSL_read(%lx, %lx, %u)\n", (long)ssl, (long)ssl_xfer_buffer, ssl_xfer_buffer_size);
+    inbytes = SSL_read(ssl, ssl_xfer_buffer, ssl_xfer_buffer_size);
+    error = SSL_get_error(ssl, inbytes);
+    DEBUG(5) debug_printf("Returned from SSL_read(): inbytes=%d\n", inbytes);
+    if (error == SSL_ERROR_ZERO_RETURN) {
+      DEBUG(5) debug_printf("Got SSL_ERROR_ZERO_RETURN\n");
+      ssl_xfer_eof = 1;
+      return -1;
+    }
+    else if (error != SSL_ERROR_NONE) {
+      ssl_xfer_error = 1;
+      return -1;
+    }
+    ssl_xfer_buffer_hwm = inbytes;
+    ssl_xfer_buffer_lwm = 0;
+  }
+  return ssl_xfer_buffer[ssl_xfer_buffer_lwm++];
+}
+
+int tls_ungetc(int ch, FILE *f)
+{
+  if (!ssl) return smtp_ungetc(ch, f);
+  checkfileno(f);
+  ssl_xfer_buffer[--ssl_xfer_buffer_lwm] = ch;
+  return ch;
+}
+
+int tls_feof(FILE *f)
+{
+  if (!ssl) return tls_feof(f);
+  checkfileno(f);
+  return ssl_xfer_eof;
+}
+
+int tls_ferror(FILE *f)
+{
+  if (!ssl) return tls_ferror(f);
+  checkfileno(f);
+  return ssl_xfer_error;
+}
+
+int tls_do_write(const char *buff, size_t len)
+{
+  int outbytes;
+  int error;
+  int left = len;
+
+  DEBUG(5) debug_printf("tls_do_write(%lx, %d)\n", (long)buff, left);
+  while (left > 0) {
+    DEBUG(5) debug_printf("SSL_write(SSL, %lx, %d)\n", (long)buff, left);
+    outbytes = SSL_write(ssl, buff, left);
+    error = SSL_get_error(ssl, outbytes);
+    DEBUG(6) debug_printf("outbytes=%d error=%d\n", outbytes, error);
+    switch (error) {
+    case SSL_ERROR_SSL:
+      tls_error("SSL_write");
+      exit(1);
+      break;
+    case SSL_ERROR_NONE:
+      left -= outbytes;
+      buff += outbytes;
+      break;
+    case SSL_ERROR_ZERO_RETURN:
+      log_write(0, LOG_MAIN, "SSL channel closed on write");
+      return 0;
+    default:
+      log_write(0, LOG_MAIN, "SSL_write error %d\n", error);
+      abort();
+    }
+  }
+  return 1;
+}
+
+int tls_fwrite(const char *buff, size_t n, size_t m, FILE *f)
+{
+  if (!tls_active) {
+    return fwrite(buff, n, m, f);
+  }
+
+  return tls_do_write(buff, n*m) ? n : 0;
+}
+
+int tls_fprintf(FILE *stream, const char *format, ...)
+{
+  va_list args;
+
+  va_start(args, format);
+  if (!tls_active) {
+    vfprintf(stream, format, args);
+  }
+  else {
+    /* bletch */
+    char buff[2048];
+    vsprintf(buff, format, args);
+    tls_do_write(buff, strlen(buff));
+  }
+  va_end(args);
+  return 1;
+}
+
+int tls_fflush(FILE *stream)
+{
+  if (!tls_active) {
+    fflush(stream);
+  }
+  return 1;
+}
+
+inline BOOL checkfd(int fd)
+{
+  if (tls_active && ssl && fd == SSL_get_fd(ssl)) {
+    DEBUG(5) debug_printf("checkfd(%d)=TRUE: tls_active=%d ssl=%lx ssl_fd=%d\n", fd, tls_active, (long)ssl, SSL_get_fd(ssl));
+    return TRUE;
+  }
+  else {
+    DEBUG(5) debug_printf("checkfd(%d)=FALSE: tls_active=%d ssl=%lx ssl_fd=%d\n", fd, tls_active, (long)ssl, SSL_get_fd(ssl));
+    return FALSE;
+  }
+}
+
+int tls_send(int fd, const char *buffer, unsigned bufsize)
+{
+  if (checkfd(fd)) {
+    DEBUG(4) debug_printf("tls_do_write(%lx, %u)\n", (long)buffer, bufsize);
+    return tls_do_write(buffer, bufsize) ? bufsize : -1;
+  }
+  else {
+    DEBUG(4) debug_printf("!SSL write(%d, %lx, %u)\n", fd, (long)buffer, bufsize);
+    return write(fd, buffer, bufsize);
+  }
+}
+
+int tls_recv(int fd, char *buffer, unsigned bufsize)
+{
+  if (checkfd(fd)) {
+    DEBUG(4) debug_printf("SSL_read(SSL, %lx, %u)\n", (long)buffer, bufsize);
+    return SSL_read(ssl, buffer, bufsize);
+  }
+  else {
+    DEBUG(4) debug_printf("!SSL read(%d, %lx, %u)\n", fd, (long)buffer, bufsize);
+    return read(fd, buffer, bufsize);
+  }
+}
+
+void tls_close(void)
+{
+  if (ssl) {
+    if (tls_active) {
+      DEBUG(4) debug_printf("tls_close(): shutting down SSL\n");
+      SSL_shutdown(ssl);
+      tls_active = 0;
+    }
+    else DEBUG(4) {
+      debug_printf("tls_close(): destroying SSL\n");
+    }
+    SSL_free(ssl);
+    ssl = NULL;
+  }
+  else DEBUG(4) {
+    debug_printf("tls_close(): not shutting down SSL because it isn't active...\n");
+  }
+}
+
+#endif /* defined(SUPPORT_TLS) */
diff -x util -x build-* -x Local -x *.orig -x *~ -uPr exim-3.03/src/transport.c exim-3.03+srh/src/transport.c
--- exim-3.03/src/transport.c    Mon Aug  2 16:43:08 1999
+++ exim-3.03+srh/src/transport.c    Tue Oct 26 15:09:42 1999
@@ -170,7 +170,17 @@
     debug_printf("writing data block size=%d timeout=%d\n",
       len, transport_write_timeout);
   if (transport_write_timeout > 0) alarm(transport_write_timeout);
-  rc = write(fd, block, len);
+#ifdef SUPPORT_TLS
+  if (tls_active) {
+    rc = tls_send(fd, block, len);
+  }
+  else {
+#endif
+    /* = if (!SUPPORT_TLS || !tls_active) */
+    rc = write(fd, block, len);
+#ifdef SUPPORT_TLS
+  }
+#endif
   save_errno = errno;


   /* Cancel the alarm and deal with a timeout */
diff -x util -x build-* -x Local -x *.orig -x *~ -uPr exim-3.03/src/transports/smtp.c exim-3.03+srh/src/transports/smtp.c
--- exim-3.03/src/transports/smtp.c    Mon Aug  2 16:43:09 1999
+++ exim-3.03+srh/src/transports/smtp.c    Tue Oct 26 18:41:04 1999
@@ -121,6 +121,9 @@
 static char *rf_names[] = { "NEVER", "SUCCESS", "FAILURE", "DELAY" };
 #endif


+#ifdef SUPPORT_TLS
+static BOOL smtp_use_tls = FALSE;
+#endif

 /*************************************************
 *             Setup entry point                  *
@@ -357,7 +360,17 @@
     if (FD_ISSET(deliver_socket, &select_inset))
       {
       *readptr = 0;
-      count = recv(deliver_socket, readptr, size-1, 0);
+#ifdef SUPPORT_TLS
+      if (tls_active) {
+    count = tls_recv(deliver_socket, readptr, size-1, 0);
+      }
+      else {
+#endif
+    /* = if (!SUPPORT_TLS || !tls_active) */
+    count = recv(deliver_socket, readptr, size-1, 0);
+#ifdef SUPPORT_TLS
+      }
+#endif
       break;
       }
     }
@@ -525,7 +538,17 @@


/* Note that errno must be preserved after send() when this function exits */

-send_rc = send(deliver_socket, big_buffer, count, 0);
+#ifdef SUPPORT_TLS
+ if (tls_active) {
+   send_rc = tls_send(deliver_socket, big_buffer, count);
+ }
+ else {
+#endif
+   /* = if (!SUPPORT_TLS || !tls_active) */
+   send_rc = send(deliver_socket, big_buffer, count, 0);
+#ifdef SUPPORT_TLS
+ }
+#endif
 big_buffer[count-2] = 0;     /* remove \r\n for debug and error message */
 smtp_command = big_buffer;
 }
@@ -992,6 +1015,9 @@
     {
     if (save_errno == EINTR && sigalrm_seen) save_errno = ETIMEDOUT;
     set_errno(addrlist, save_errno, NULL, DEFER);
+#ifdef SUPPORT_TLS
+    tls_close();
+#endif
     close(deliver_socket);
     DEBUG(1) debug_printf("failed\n");
     return DEFER;
@@ -1069,6 +1095,32 @@
       PCRE_EOPT, NULL, 0) >= 0;
   DEBUG(9) debug_printf("use_dsn=%d\n", smtp_use_dsn);
   #endif
+
+#ifdef SUPPORT_TLS
+  tls_used = FALSE;
+  if (esmtp && pcre_exec(regex_STARTTLS, NULL, buffer, (int)strlen(buffer), 0, PCRE_EOPT, NULL, 0) >= 0) {
+    /* Attempt to negotiate TLS */
+    if (!init_tls()) {
+      goto no_tls;
+    }
+    write_command("STARTTLS\r\n");
+    if (!read_response(buffer, sizeof(buffer), FALSE, FALSE, ob)) {
+      if (buffer[0] == '4') {
+    debug_printf("Failed to even start TLS\n");
+    goto no_tls;
+      }
+      else {
+    goto RESPONSE_FAILED;
+      }
+    }
+    debug_printf("Asked for TLS; remote said \"%s\"\n", buffer);
+    if (!connect_tls(deliver_socket)) {
+      goto RESPONSE_FAILED;
+    }
+  }
+  no_tls:
+#endif
+
   }


/* For continuing deliveries down the same channel, the socket is the standard
@@ -1638,6 +1690,10 @@
remote_max_parallel is forced to 1, so these values will be seen by subsequent
deliveries. */

+#ifdef SUPPORT_TLS
+ tls_close();
+#endif
+
close(deliver_socket);
continue_transport = NULL;
continue_hostname = NULL;
@@ -1673,6 +1729,9 @@
deliver_socket = fileno(stdin);
write_command("QUIT\r\n");
(void) read_response(buffer, sizeof(buffer), FALSE, FALSE, ob);
+#ifdef SUPPORT_TLS
+ tls_close();
+#endif
close(deliver_socket);
}


-- 
Steve Haslam, Production Engineer, Excite UK     steve.haslam@???
                               i sit and stare at the gun pointed at my head
                                       and think about all the possibilities