diff -r -c --new-file postfix-19990317-pl04-vanilla/conf/sample-tls.cf postfix-19990317-pl04/conf/sample-tls.cf
*** postfix-19990317-pl04-vanilla/conf/sample-tls.cf	Thu Jan  1 01:00:00 1970
--- postfix-19990317-pl04/conf/sample-tls.cf	Fri Apr 30 15:24:51 1999
***************
*** 0 ****
--- 1,173 ----
+ # This file contains example settings of Postfix configuration
+ # parameters that control the behaviour of the TLS extensions.
+ #
+ # We strictly seperate between server side TLS (smtpd_) and client side
+ # TLS (smtp_), as for practical reasons we might choose differently.
+ 
+ # Section with SMTPD specific settings
+ 
+ # To use TLS we do need a certificate and a private key. Both must be in
+ # "pem" format, the private key must not be encrypted, that does mean:
+ # it must be accessable without password. Both parts (certificate and
+ # private key) may be in the same file.
+ #
+ # smtpd_tls_cert_file = /etc/postfix/server.pem
+ # smtpd_tls_key_file = $tls_cert_file
+ 
+ # The certificate was issued by a certification authority (CA), of which
+ # the CA-cert must be available. This file may also contain the the
+ # CA certificates of other trusted CAs. You must use this file for the
+ # list of trusted CAs if you want to use chroot-mode.
+ #
+ # smtpd_tls_CAfile = /etc/postfix/CAcert.pem
+ 
+ # To verify the peer certificate, we need to know the certificates of
+ # certification authorities. These certificates in "pem" format are
+ # collected in a directory. The same CAs are offered to clients for
+ # client verification. Don't forget to create the necessary "hash"
+ # links with $OPENSSL_HOME/bin/c_rehash /etc/postfix/certs. A typical
+ # place for the CA-certs may also be $OPENSSL_HOME/certs, so there is
+ # no default and you explicitly have to set the value here!
+ # This will not work in chroot mode, use the tls_CAfile instead.
+ #
+ smtpd_tls_CApath = /etc/postfix/certs
+ 
+ # To get additional information during the TLS setup and negotiations
+ # you can increase the loglevel from 0..4:
+ # 0: No output about the TLS subsystem
+ # 1: Printout startup and certificate information
+ # 2: 1 + Printout of levels during negotiation
+ # 3: 2 + Hex and ASCII dump of negotiation process
+ # 4: 3 + Hex and ASCII dump of complete transmission after STARTTLS
+ # Use loglevel 3 only in case of problems. Use of loglevel 4 is strongly
+ # discouraged.
+ #
+ # smtpd_tls_loglevel = 0
+ 
+ # By default TLS is disabled, so no difference to plain postfix is visible.
+ # Explicitely switch it on here:
+ #
+ smtpd_use_tls = yes
+ 
+ # You can ENFORCE the use of TLS, so that no commands (except QUIT of course)
+ # are allowed without TLS. According to RFC2487 this MUST NOT be applied
+ # in case of a publicly-referenced SMTP server. So this option is off
+ # by default and should only seldom be used. Using this option implies
+ # smtpd_use_tls = yes
+ #
+ # smtpd_enforce_tls = no
+ 
+ # To receive a client certificate, the server must explicitly ask for one.
+ # Hence netscape will either complain if no certificate is available (for
+ # the list of CAs in /etc/postfix/certs) or will offer you client certificates
+ # to choose from. This might be annoying, so this option is "off" by default.
+ # You will however need the certificate if you want to to e.g. certificate
+ # based relaying.
+ #
+ # smtpd_tls_ask_ccert = no
+ 
+ # You may also decide to REQUIRE a client certificate to allow TLS connections.
+ # I don't think it will be necessary often, it is however included here for
+ # completeness. This option implies smtpd_tls_ask_ccert = yes
+ #
+ # smtpd_tls_req_ccert = no
+ 
+ # The verification depth for client certificates. The default (1) is
+ # sufficient, if the client certificate ist directly issued by a CA
+ # listed under $tls_CApath
+ #
+ # smtpd_tls_ccert_vd = 1
+ 
+ # One additional option has been added for relay control to the UCE rules:
+ # check_relay_ccerts. If this option is added to smtpd_recipient_restrictions
+ # postfix will relay if a valid client certificate is presented and in the
+ # list of client certs (relay_clientcerts).
+ #
+ # smtpd_recipient_restrictions = check_relay_ccerts
+ 
+ # The list of client certificates for which relaying will be allowed.
+ # Unfortunately the routines for lists in postfix use whitespaces as
+ # seperators and choke on special chars. So using the certificate
+ # X509ONELINES is quite impractical. We will use the fingerprints at
+ # this point, as they are difficult to fake but easy to use for lookup.
+ # There is just one small problem: The colon ':' is interpreted as
+ # seperator between database type and filename, so we must avoid it.
+ # We use the underscore "_" instead.
+ # As postmap (when using e.g. db) insists of having a pair of key and value,
+ # but we only need the key, the value can be chosen freely, e.g. the name
+ # of the user or host:
+ # D7_04_2F_A7_0B_8C_A5_21_FA_31_77_E1_41_8A_EE_80 lutzpc.at.home
+ #
+ # relay_clientcerts = hash:/etc/postfix/relay_clientcerts
+ 
+ # Section with SMTP specific settings
+ 
+ # During the startup negotiation we might present a certificate to the server.
+ # Netscape is rather clever here and lets the user select between only those
+ # certs that will match the CAs accepted from the server. As I simply use
+ # the integrated "SSL_connect()" from the OpenSSL package, this is not
+ # possible by now and we have to chose just one cert.
+ # So for now the default is to use _no_ cert and key unless explictly
+ # set here. It is possible to use the same key/cert pair as for the server.
+ # If a cert is to be presented, it must be in "pem" format, the private key
+ # must not be encrypted, that does mean: it must be accessable without
+ # password. Both parts (certificate and # private key) may be in the
+ # same file.
+ #
+ smtp_tls_cert_file = /etc/postfix/server.pem
+ smtp_tls_key_file = $tls_cert_file
+ 
+ # The certificate was issued by a certification authority (CA), of which
+ # the CA-cert must be available. This file may also contain the the
+ # CA certificates of other trusted CAs. You must use this file for the
+ # list of trusted CAs if you want to use chroot-mode.
+ # No default is supplied for this value as of now.
+ #
+ smtp_tls_CAfile = /etc/postfix/CAcert.pem
+ 
+ # To verify the peer certificate, we need to know the certificates of
+ # certification authorities. These certificates in "pem" format are
+ # collected in a directory. Don't forget to create the necessary "hash"
+ # links with $OPENSSL_HOME/bin/c_rehash /etc/postfix/certs. A typical
+ # place for the CA-certs may also be $OPENSSL_HOME/certs, so there is
+ # no default and you explicitly have to set the value here!
+ # This will not work in chroot mode, use the tls_CAfile instead.
+ #
+ smtp_tls_CApath = /etc/postfix/certs
+ 
+ # To get additional information during the TLS setup and negotiations
+ # you can increase the loglevel from 0..4:
+ # 0: No output about the TLS subsystem
+ # 1: Printout startup and certificate information
+ # 2: 1 + Printout of levels during negotiation
+ # 3: 2 + Hex and ASCII dump of negotiation process
+ # 4: 3 + Hex and ASCII dump of complete transmission after STARTTLS
+ # Use loglevel 3 only in case of problems. Use of loglevel 4 is strongly
+ # discouraged.
+ #
+ smtp_tls_loglevel = 0
+ 
+ # By default TLS is disabled, so no difference to plain postfix is visible.
+ # If you enable TLS it will be used when offered by the server.
+ # Explicitely switch it on here:
+ #
+ smtp_use_tls = yes
+ 
+ # You can ENFORCE the use of TLS, so that only connections with TLS will
+ # be accepted.
+ # THIS IS NOT IMPLEMENTED, YET. It is also very likely, that it will never
+ # be realized that way. It is far better to have a list of sites, to which
+ # we will only connect in TLS mode, as we know that they support TLS.
+ #
+ # The second problem is the problem of verifying the certificate. Enforcing
+ # only makes sense when the CN is checked against the actual hostname,
+ # and this requires agreement about accepted CAs. So as of now, all of this
+ # still open.
+ #
+ # smtp_enforce_tls = no
+ 
+ # The verification depth for certificates. The default (1) is sufficient,
+ # if the certificate ist directly issued by a CA listed under $tls_CApath
+ #
+ # smtp_tls_ccert_vd = 1
+ 
diff -r -c --new-file postfix-19990317-pl04-vanilla/global/Makefile.in postfix-19990317-pl04/global/Makefile.in
*** postfix-19990317-pl04-vanilla/global/Makefile.in	Sun Mar 14 23:01:50 1999
--- postfix-19990317-pl04/global/Makefile.in	Thu Apr 15 15:53:37 1999
***************
*** 16,22 ****
  	resolve_clnt.c resolve_local.c rewrite_clnt.c sent.c smtp_stream.c \
  	split_addr.c string_list.c sys_exits.c timed_ipc.c tok822_find.c \
  	tok822_node.c tok822_parse.c tok822_resolve.c tok822_rewrite.c \
! 	tok822_tree.c clnt_stream.c deliver_pass.c
  OBJS	= been_here.o bounce.o canon_addr.o clean_env.o cleanup_strerror.o \
  	config.o config_bool.o config_int.o config_str.o debug_peer.o \
  	debug_process.o defer.o deliver_completed.o deliver_flock.o \
--- 16,22 ----
  	resolve_clnt.c resolve_local.c rewrite_clnt.c sent.c smtp_stream.c \
  	split_addr.c string_list.c sys_exits.c timed_ipc.c tok822_find.c \
  	tok822_node.c tok822_parse.c tok822_resolve.c tok822_rewrite.c \
! 	tok822_tree.c clnt_stream.c deliver_pass.c pfixtls.c
  OBJS	= been_here.o bounce.o canon_addr.o clean_env.o cleanup_strerror.o \
  	config.o config_bool.o config_int.o config_str.o debug_peer.o \
  	debug_process.o defer.o deliver_completed.o deliver_flock.o \
***************
*** 34,40 ****
  	resolve_clnt.o resolve_local.o rewrite_clnt.o sent.o smtp_stream.o \
  	split_addr.o string_list.o sys_exits.o timed_ipc.o tok822_find.o \
  	tok822_node.o tok822_parse.o tok822_resolve.o tok822_rewrite.o \
! 	tok822_tree.o clnt_stream.o deliver_pass.o
  HDRS	= been_here.h bounce.h canon_addr.h clean_env.h cleanup_user.h \
  	config.h debug_peer.h debug_process.h defer.h deliver_completed.h \
  	deliver_flock.h deliver_request.h domain_list.h dot_lockfile.h \
--- 34,40 ----
  	resolve_clnt.o resolve_local.o rewrite_clnt.o sent.o smtp_stream.o \
  	split_addr.o string_list.o sys_exits.o timed_ipc.o tok822_find.o \
  	tok822_node.o tok822_parse.o tok822_resolve.o tok822_rewrite.o \
! 	tok822_tree.o clnt_stream.o deliver_pass.o pfixtls.o
  HDRS	= been_here.h bounce.h canon_addr.h clean_env.h cleanup_user.h \
  	config.h debug_peer.h debug_process.h defer.h deliver_completed.h \
  	deliver_flock.h deliver_request.h domain_list.h dot_lockfile.h \
***************
*** 48,54 ****
  	quote_822_local.h rec_streamlf.h rec_type.h recipient_list.h \
  	record.h resolve_clnt.h resolve_local.h rewrite_clnt.h sent.h \
  	smtp_stream.h split_addr.h string_list.h sys_exits.h timed_ipc.h \
! 	tok822.h clnt_stream.h
  TESTSRC	= rec2stream.c stream2rec.c recdump.c
  WARN	= -W -Wformat -Wimplicit -Wmissing-prototypes \
  	-Wparentheses -Wstrict-prototypes -Wswitch -Wuninitialized \
--- 48,54 ----
  	quote_822_local.h rec_streamlf.h rec_type.h recipient_list.h \
  	record.h resolve_clnt.h resolve_local.h rewrite_clnt.h sent.h \
  	smtp_stream.h split_addr.h string_list.h sys_exits.h timed_ipc.h \
! 	tok822.h clnt_stream.h pfixtls.h
  TESTSRC	= rec2stream.c stream2rec.c recdump.c
  WARN	= -W -Wformat -Wimplicit -Wmissing-prototypes \
  	-Wparentheses -Wstrict-prototypes -Wswitch -Wuninitialized \
***************
*** 802,807 ****
--- 802,808 ----
  smtp_stream.o: ../include/vstring_vstream.h
  smtp_stream.o: ../include/msg.h
  smtp_stream.o: ../include/iostuff.h
+ smtp_stream.o: pfixtls.h
  smtp_stream.o: smtp_stream.h
  split_addr.o: split_addr.c
  split_addr.o: ../include/sys_defs.h
***************
*** 876,878 ****
--- 877,882 ----
  tok822_tree.o: ../include/vbuf.h
  tok822_tree.o: tok822.h
  tok822_tree.o: resolve_clnt.h
+ pfixtls.o: pfixtls.c
+ pfixtls.o: mail_params.h
+ pfixtls.o: pfixtls.h
diff -r -c --new-file postfix-19990317-pl04-vanilla/global/mail_params.c postfix-19990317-pl04/global/mail_params.c
*** postfix-19990317-pl04-vanilla/global/mail_params.c	Sat Mar 20 01:55:18 1999
--- postfix-19990317-pl04/global/mail_params.c	Thu Apr 29 16:35:42 1999
***************
*** 158,163 ****
--- 158,177 ----
  time_t  var_starttime;
  int     var_ownreq_special;
  int     var_daemon_timeout;
+ char   *var_smtpd_tls_cert_file;
+ char   *var_smtpd_tls_key_file;
+ char   *var_smtpd_tls_dcert_file;
+ char   *var_smtpd_tls_dkey_file;
+ char   *var_smtpd_tls_CAfile;
+ char   *var_smtpd_tls_CApath;
+ int     var_smtpd_tls_loglevel;
+ char   *var_smtp_tls_cert_file;
+ char   *var_smtp_tls_key_file;
+ char   *var_smtp_tls_dcert_file;
+ char   *var_smtp_tls_dkey_file;
+ char   *var_smtp_tls_CAfile;
+ char   *var_smtp_tls_CApath;
+ int     var_smtp_tls_loglevel;
  
  /* check_myhostname - lookup hostname and validate */
  
***************
*** 250,255 ****
--- 264,281 ----
  	VAR_DB_TYPE, DEF_DB_TYPE, &var_db_type, 1, 0,
  	VAR_HASH_QUEUE_NAMES, DEF_HASH_QUEUE_NAMES, &var_hash_queue_names, 1, 0,
  	VAR_RCPT_DELIM, DEF_RCPT_DELIM, &var_rcpt_delim, 0, 1,
+ 	VAR_SMTPD_TLS_CERT_FILE, DEF_SMTPD_TLS_CERT_FILE, &var_smtpd_tls_cert_file, 1, 0,
+ 	VAR_SMTPD_TLS_KEY_FILE, DEF_SMTPD_TLS_KEY_FILE, &var_smtpd_tls_key_file, 1, 0,
+ 	VAR_SMTPD_TLS_DCERT_FILE, DEF_SMTPD_TLS_DCERT_FILE, &var_smtpd_tls_dcert_file, 1, 0,
+ 	VAR_SMTPD_TLS_DKEY_FILE, DEF_SMTPD_TLS_DKEY_FILE, &var_smtpd_tls_dkey_file, 1, 0,
+ 	VAR_SMTPD_TLS_CA_FILE, DEF_SMTPD_TLS_CA_FILE, &var_smtpd_tls_CAfile, 1, 0,
+ 	VAR_SMTPD_TLS_CA_PATH, DEF_SMTPD_TLS_CA_PATH, &var_smtpd_tls_CApath, 0, 0,
+ 	VAR_SMTP_TLS_CERT_FILE, DEF_SMTP_TLS_CERT_FILE, &var_smtp_tls_cert_file, 0, 0,
+ 	VAR_SMTP_TLS_KEY_FILE, DEF_SMTP_TLS_KEY_FILE, &var_smtp_tls_key_file, 0, 0,
+ 	VAR_SMTP_TLS_DCERT_FILE, DEF_SMTP_TLS_DCERT_FILE, &var_smtp_tls_dcert_file, 0, 0,
+ 	VAR_SMTP_TLS_DKEY_FILE, DEF_SMTP_TLS_DKEY_FILE, &var_smtp_tls_dkey_file, 0, 0,
+ 	VAR_SMTP_TLS_CA_FILE, DEF_SMTP_TLS_CA_FILE, &var_smtp_tls_CAfile, 0, 0,
+ 	VAR_SMTP_TLS_CA_PATH, DEF_SMTP_TLS_CA_PATH, &var_smtp_tls_CApath, 0, 0,
  	0,
      };
      static CONFIG_STR_FN_TABLE function_str_defaults_2[] = {
***************
*** 271,276 ****
--- 297,304 ----
  	VAR_FLOCK_TRIES, DEF_FLOCK_TRIES, &var_flock_tries, 1, 0,
  	VAR_FLOCK_DELAY, DEF_FLOCK_DELAY, &var_flock_delay, 1, 0,
  	VAR_FLOCK_STALE, DEF_FLOCK_STALE, &var_flock_stale, 1, 0,
+ 	VAR_SMTPD_TLS_LOGLEVEL, DEF_SMTPD_TLS_LOGLEVEL, &var_smtpd_tls_loglevel, 0, 0,
+ 	VAR_SMTP_TLS_LOGLEVEL, DEF_SMTP_TLS_LOGLEVEL, &var_smtp_tls_loglevel, 0, 0,
  	VAR_DAEMON_TIMEOUT, DEF_DAEMON_TIMEOUT, &var_daemon_timeout, 1, 0,
  	0,
      };
diff -r -c --new-file postfix-19990317-pl04-vanilla/global/mail_params.h postfix-19990317-pl04/global/mail_params.h
*** postfix-19990317-pl04-vanilla/global/mail_params.h	Sat Mar 20 01:52:38 1999
--- postfix-19990317-pl04/global/mail_params.h	Thu Apr 29 16:30:58 1999
***************
*** 544,549 ****
--- 544,637 ----
  #define DEF_SMTPD_ERR_SLEEP	5
  extern int var_smtpd_err_sleep;
  
+ #define VAR_SMTPD_USE_TLS	"smtpd_use_tls"
+ #define DEF_SMTPD_USE_TLS	0
+ extern bool var_smtpd_use_tls;
+ 
+ #define VAR_SMTPD_ENFORCE_TLS	"smtpd_enforce_tls"
+ #define DEF_SMTPD_ENFORCE_TLS	0
+ extern bool var_smtpd_enforce_tls;
+ 
+ #define VAR_SMTPD_TLS_ACERT	"smtpd_tls_ask_ccert"
+ #define DEF_SMTPD_TLS_ACERT	0
+ extern bool var_smtpd_tls_ask_ccert;
+ 
+ #define VAR_SMTPD_TLS_RCERT	"smtpd_tls_req_ccert"
+ #define DEF_SMTPD_TLS_RCERT	0
+ extern bool var_smtpd_tls_req_ccert;
+ 
+ #define VAR_SMTPD_TLS_CCERT_VD	"smtpd_tls_ccert_verifydepth"
+ #define DEF_SMTPD_TLS_CCERT_VD	1
+ extern int var_smtpd_tls_ccert_vd;
+ 
+ #define VAR_SMTPD_TLS_CERT_FILE	"smtpd_tls_cert_file"
+ #define DEF_SMTPD_TLS_CERT_FILE	"/etc/postfix/server.pem"
+ extern char *var_smtpd_tls_cert_file;
+ 
+ #define VAR_SMTPD_TLS_KEY_FILE	"smtpd_tls_key_file"
+ #define DEF_SMTPD_TLS_KEY_FILE	"$smtpd_tls_cert_file"
+ extern char *var_smtpd_tls_key_file;
+ 
+ #define VAR_SMTPD_TLS_DCERT_FILE "smtpd_tls_dcert_file"
+ #define DEF_SMTPD_TLS_DCERT_FILE "/etc/postfix/server.pem"
+ extern char *var_smtpd_tls_dcert_file;
+ 
+ #define VAR_SMTPD_TLS_DKEY_FILE	"smtpd_tls_dkey_file"
+ #define DEF_SMTPD_TLS_DKEY_FILE	"$smtpd_tls_dcert_file"
+ extern char *var_smtpd_tls_dkey_file;
+ 
+ #define VAR_SMTPD_TLS_CA_FILE	"smtpd_tls_CAfile"
+ #define DEF_SMTPD_TLS_CA_FILE	"/etc/postfix/CAcert.pem"
+ extern char *var_smtpd_tls_CAfile;
+ 
+ #define VAR_SMTPD_TLS_CA_PATH	"smtpd_tls_CApath"
+ #define DEF_SMTPD_TLS_CA_PATH	""
+ extern char *var_smtpd_tls_CApath;
+ 
+ #define VAR_SMTPD_TLS_LOGLEVEL	"smtpd_tls_loglevel"
+ #define DEF_SMTPD_TLS_LOGLEVEL	0
+ extern int var_smtpd_tls_loglevel;
+ 
+ #define VAR_SMTP_USE_TLS	"smtp_use_tls"
+ #define DEF_SMTP_USE_TLS	0
+ extern bool var_smtp_use_tls;
+ 
+ #define VAR_SMTP_ENFORCE_TLS	"smtp_enforce_tls"
+ #define DEF_SMTP_ENFORCE_TLS	0
+ extern bool var_smtp_enforce_tls;
+ 
+ #define VAR_SMTP_TLS_SCERT_VD	"smtp_tls_scert_verifydepth"
+ #define DEF_SMTP_TLS_SCERT_VD	1
+ extern int var_smtp_tls_scert_vd;
+ 
+ #define VAR_SMTP_TLS_CERT_FILE	"smtp_tls_cert_file"
+ #define DEF_SMTP_TLS_CERT_FILE	""
+ extern char *var_smtp_tls_cert_file;
+ 
+ #define VAR_SMTP_TLS_KEY_FILE	"smtp_tls_key_file"
+ #define DEF_SMTP_TLS_KEY_FILE	"$smtp_tls_cert_file"
+ extern char *var_smtp_tls_key_file;
+ 
+ #define VAR_SMTP_TLS_DCERT_FILE "smtp_tls_dcert_file"
+ #define DEF_SMTP_TLS_DCERT_FILE ""
+ extern char *var_smtp_tls_dcert_file;
+ 
+ #define VAR_SMTP_TLS_DKEY_FILE	"smtp_tls_dkey_file"
+ #define DEF_SMTP_TLS_DKEY_FILE	"$smtp_tls_dcert_file"
+ extern char *var_smtp_tls_dkey_file;
+ 
+ #define VAR_SMTP_TLS_CA_FILE	"smtp_tls_CAfile"
+ #define DEF_SMTP_TLS_CA_FILE	""
+ extern char *var_smtp_tls_CAfile;
+ 
+ #define VAR_SMTP_TLS_CA_PATH	"smtp_tls_CApath"
+ #define DEF_SMTP_TLS_CA_PATH	""
+ extern char *var_smtp_tls_CApath;
+ 
+ #define VAR_SMTP_TLS_LOGLEVEL	"smtp_tls_loglevel"
+ #define DEF_SMTP_TLS_LOGLEVEL	0
+ extern int var_smtp_tls_loglevel;
+ 
   /*
    * Cleanup service. Header info that exceeds $header_size_limit bytes forces
    * the start of the message body.
***************
*** 643,648 ****
--- 731,740 ----
  #define DEF_RELAY_DOMAINS	"$mydestination, $virtual_maps"
  extern char *var_relay_domains;
  
+ #define VAR_RELAY_CCERTS	"relay_clientcerts"
+ #define DEF_RELAY_CCERTS	""
+ extern char *var_relay_ccerts;
+ 
  #define VAR_CLIENT_CHECKS	"smtpd_client_restrictions"
  #define DEF_CLIENT_CHECKS	""
  extern char *var_client_checks;
***************
*** 710,715 ****
--- 802,808 ----
  extern int var_unk_addr_code;
  
  #define CHECK_RELAY_DOMAINS	"check_relay_domains"
+ #define CHECK_RELAY_CCERTS	"check_relay_ccerts"
  #define VAR_RELAY_CODE		"relay_domains_reject_code"
  #define DEF_RELAY_CODE		550
  extern int var_relay_code;
diff -r -c --new-file postfix-19990317-pl04-vanilla/global/pfixtls.c postfix-19990317-pl04/global/pfixtls.c
*** postfix-19990317-pl04-vanilla/global/pfixtls.c	Thu Jan  1 01:00:00 1970
--- postfix-19990317-pl04/global/pfixtls.c	Thu Apr 29 21:40:03 1999
***************
*** 0 ****
--- 1,790 ----
+ /*++
+ /* NAME
+ /*	pfixtls
+ /* SUMMARY
+ /*	interface to openssl routines
+ /* SYNOPSIS
+ /*	#include <pfixtls.h>
+ /*
+ /* DESCRIPTION
+ /* BUGS
+ /* LICENSE
+ /* AUTHOR(S)
+ /*--*/
+ 
+ #ifdef HAS_SSL
+ 
+ /* System library. */
+ 
+ #include <sys/types.h>
+ #include <unistd.h>
+ #include <stdio.h>
+ #include <string.h>
+ 
+ /* OpenSSL library. */
+ 
+ #include <lhash.h>
+ #include <bn.h>
+ #include <err.h>
+ #include <pem.h>
+ #include <x509.h>
+ #include <ssl.h>
+ 
+ /* Utility library. */
+ 
+ #include <iostuff.h>
+ 
+ /* Application-specific. */
+ 
+ #include "mail_params.h"
+ #include "pfixtls.h"
+ 
+ /* We must keep some of info available */
+ static const char hexcodes[] = "0123456789ABCDEF";
+ 
+ static int pfixtls_fd = -1;
+ static int verify_depth;
+ static int verify_error = X509_V_OK;
+ static int do_dump = 0;
+ static SSL_CTX *ctx = NULL;
+ static SSL *con = NULL;
+ 
+ #define CCERT_BUFSIZ 256
+ static char ccert_subject[CCERT_BUFSIZ];
+ static char ccert_issuer[CCERT_BUFSIZ];
+ static unsigned char md[EVP_MAX_MD_SIZE];
+ static char fingerprint[EVP_MAX_MD_SIZE * 3];
+ 
+ int     pfixtls_serverengine = 0;
+ int     pfixtls_serveractive = 0;	/* available or not */
+ char   *pfixtls_ccert_subject = NULL;
+ char   *pfixtls_ccert_issuer = NULL;
+ char   *pfixtls_ccert_fingerprint = NULL;
+ 
+ int     pfixtls_clientengine = 0;
+ int     pfixtls_clientactive = 0;	/* available or not */
+ 
+  /*
+   * Wrapper routines around read/write and read_wait/write_wait.
+   * If TLS is active, we must use the appropriate SSL function
+   * instead of the direct one.
+   *
+   * We do live quite comfortable here, as smtp_stream can only handle
+   * one connection at a time, so we also only take to care of one
+   * filedescriptor, which is saved as "pfixtls_fd".
+   *
+   * You may note, that pfixtls_read_wait() and pfixtls_write_wait()
+   * seem to be of no real use here. The reason is, that there is no
+   * equivalent to "select()" available for the SSL connection. But I
+   * already have prepared the wrapper functions, so once this equivalent
+   * is available, we can immediately use it.
+   *
+   * We explicitly leave out the select() calls in TLS mode, as there
+   * is buffering included in the SSL_* routines and we don't want to
+   * have it hanging! Consider that there are still bytes in the SSL buffer,
+   * but no new bytes arrive at the interface, then select() on the read
+   * channel would wait erronously.
+   *
+   * The SSL_read() and SSL_write() calls are blocking anyway, so we can
+   * live without select() at this time.
+   */
+ 
+ int     pfixtls_read(int fd, void *buf, size_t count)
+ {
+     int     i;
+     int     ret;
+     char    mybuf[40];
+     char   *mybuf2;
+ 
+     if ((pfixtls_serveractive || pfixtls_clientactive) && (fd == pfixtls_fd)) {
+ 	ret = SSL_read(con, buf, count);
+ 	if ((pfixtls_serveractive && var_smtpd_tls_loglevel >= 4) ||
+ 	    (pfixtls_clientactive && var_smtp_tls_loglevel >= 4)) {
+ 	    mybuf2 = (char *) buf;
+ 	    if (ret > 0) {
+ 		i = 0;
+ 		while ((i < 39) && (i < ret) && (mybuf2[i] != 0)) {
+ 		    mybuf[i] = mybuf2[i];
+ 		    i++;
+ 		}
+ 		mybuf[i] = '\0';
+ 		msg_info("Read %d chars: %s", ret, mybuf);
+ 	    }
+ 	}
+ 	return (ret);
+     } else
+ 	return (read(fd, buf, count));
+ }
+ 
+ int     pfixtls_write(int fd, void *buf, size_t count)
+ {
+     int     i;
+     char    mybuf[40];
+     char   *mybuf2;
+ 
+     if ((pfixtls_serveractive || pfixtls_clientactive) && (fd == pfixtls_fd)) {
+ 	if ((pfixtls_serveractive && var_smtpd_tls_loglevel >= 4) ||
+ 	    (pfixtls_clientactive && var_smtp_tls_loglevel >= 4)) {
+ 	    mybuf2 = (char *) buf;
+ 	    if (count > 0) {
+ 		i = 0;
+ 		while ((i < 39) && (i < count) && (mybuf2[i] != 0)) {
+ 		    mybuf[i] = mybuf2[i];
+ 		    i++;
+ 		}
+ 		mybuf[i] = '\0';
+ 		msg_info("Write %d chars: %s", count, mybuf);
+ 	    }
+ 	}
+ 	return (SSL_write(con, buf, count));
+     } else
+ 	return (write(fd, buf, count));
+ }
+ 
+ 
+ int     pfixtls_read_wait(int fd, int timeout)
+ {
+     if ((pfixtls_serveractive || pfixtls_clientactive) && (fd == pfixtls_fd))
+ 	return (0);
+     else
+ 	return (read_wait(fd, timeout));
+ }
+ 
+ int     pfixtls_write_wait(int fd, int timeout)
+ {
+     if ((pfixtls_serveractive || pfixtls_clientactive) && (fd == pfixtls_fd))
+ 	return (0);
+     else
+ 	return (write_wait(fd, timeout));
+ }
+ 
+  /*
+   * remember the filedescriptor for the open smtp-stream.
+   */
+ void    pfixtls_setfd(int fd)
+ {
+     if (var_smtpd_tls_loglevel > 2)
+ 	msg_info("setting smtp_stream fd[%d]", fd);
+     pfixtls_fd = fd;
+     pfixtls_serveractive = 0;
+ }
+ 
+ /* skeleton taken from OpenSSL crypto/err/err_prn.c */
+ 
+ static void pfixtls_print_errors(void)
+ {
+     unsigned long l;
+     char    buf[256];
+     char   *file;
+     char   *data;
+     int     line;
+     int     flags;
+     unsigned long es;
+ 
+     es = CRYPTO_thread_id();
+     while ((l = ERR_get_error_line_data(&file, &line, &data, &flags)) != 0) {
+ 	if (flags & ERR_TXT_STRING)
+ 	    msg_info("%lu:%s:%s:%d:%s:", es, ERR_error_string(l, buf),
+ 		     file, line, data);
+ 	else
+ 	    msg_info("%lu:%s:%s:%d:", es, ERR_error_string(l, buf),
+ 		     file, line);
+     }
+ }
+ 
+  /*
+   * Set up the cert things on the server side. We do need both the
+   * private key (in key_file) and the cert (in cert_file).
+   * Both files may be identical.
+   *
+   * This function is taken from OpenSSL apps/s_cb.c
+   */
+ 
+ static int set_cert_stuff(SSL_CTX * ctx, char *cert_file, char *key_file)
+ {
+     if (cert_file != NULL) {
+ 	if (SSL_CTX_use_certificate_file(ctx, cert_file,
+ 					 SSL_FILETYPE_PEM) <= 0) {
+ 	    msg_info("unable to get certificate from '%s'", cert_file);
+ 	    pfixtls_print_errors();
+ 	    return (0);
+ 	}
+ 	if (key_file == NULL)
+ 	    key_file = cert_file;
+ 	if (SSL_CTX_use_PrivateKey_file(ctx, key_file,
+ 					SSL_FILETYPE_PEM) <= 0) {
+ 	    msg_info("unable to get private key from '%s'", key_file);
+ 	    pfixtls_print_errors();
+ 	    return (0);
+ 	}
+ 	/* Now we know that a key and cert have been set against
+          * the SSL context */
+ 	if (!SSL_CTX_check_private_key(ctx)) {
+ 	    msg_info("Private key does not match the certificate public key");
+ 	    return (0);
+ 	}
+     }
+     return (1);
+ }
+ 
+ /* taken from OpenSSL apps/s_cb.c */
+ 
+ static RSA *tmp_rsa_cb(SSL * s, int export, int keylength)
+ {
+     static RSA *rsa_tmp = NULL;
+ 
+     if (rsa_tmp == NULL) {
+ 	if (var_smtpd_tls_loglevel >= 2)
+ 	    msg_info("Generating temp (%d bit) RSA key...", keylength);
+ 	rsa_tmp = RSA_generate_key(keylength, RSA_F4, NULL, NULL);
+     }
+     return (rsa_tmp);
+ }
+ 
+ /* taken from OpenSSL apps/s_cb.c */
+ 
+ static int verify_callback(int ok, X509_STORE_CTX * ctx)
+ {
+     char    buf[256];
+     X509   *err_cert;
+     int     err;
+     int     depth;
+ 
+     err_cert = X509_STORE_CTX_get_current_cert(ctx);
+     err = X509_STORE_CTX_get_error(ctx);
+     depth = X509_STORE_CTX_get_error_depth(ctx);
+ 
+     X509_NAME_oneline(X509_get_subject_name(err_cert), buf, 256);
+     if (((pfixtls_serveractive) && (var_smtpd_tls_loglevel >= 1)) ||
+ 	((pfixtls_clientactive) && (var_smtp_tls_loglevel >= 1)))
+ 	msg_info("Client cert verify depth=%d %s", depth, buf);
+     if (!ok) {
+ 	msg_info("verify error:num=%d:%s", err,
+ 		 X509_verify_cert_error_string(err));
+ 	if (verify_depth >= depth) {
+ 	    ok = 1;
+ 	    verify_error = X509_V_OK;
+ 	} else {
+ 	    ok = 0;
+ 	    verify_error = X509_V_ERR_CERT_CHAIN_TOO_LONG;
+ 	}
+     }
+     switch (ctx->error) {
+     case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
+ 	X509_NAME_oneline(X509_get_issuer_name(ctx->current_cert), buf, 256);
+ 	msg_info("issuer= %s", buf);
+ 	break;
+     case X509_V_ERR_CERT_NOT_YET_VALID:
+     case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
+ 	msg_info("cert not yet valid");
+ 	break;
+     case X509_V_ERR_CERT_HAS_EXPIRED:
+     case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
+ 	msg_info("cert has expired");
+ 	break;
+     }
+     if (((pfixtls_serveractive) && (var_smtpd_tls_loglevel >= 1)) ||
+ 	((pfixtls_clientactive) && (var_smtp_tls_loglevel >= 1)))
+ 	msg_info("verify return:%d", ok);
+     return (ok);
+ }
+ 
+ /* taken from OpenSSL apps/s_cb.c */
+ 
+ static void apps_ssl_info_callback(SSL * s, int where, int ret)
+ {
+     char   *str;
+     int     w;
+ 
+     w = where & ~SSL_ST_MASK;
+ 
+     if (w & SSL_ST_CONNECT)
+ 	str = "SSL_connect";
+     else if (w & SSL_ST_ACCEPT)
+ 	str = "SSL_accept";
+     else
+ 	str = "undefined";
+ 
+     if (where & SSL_CB_LOOP) {
+ 	if (var_smtpd_tls_loglevel >= 2)
+ 	    msg_info("%s:%s", str, SSL_state_string_long(s));
+     } else if (where & SSL_CB_ALERT) {
+ 	str = (where & SSL_CB_READ) ? "read" : "write";
+ 	msg_info("SSL3 alert %s:%s:%s", str,
+ 		 SSL_alert_type_string_long(ret),
+ 		 SSL_alert_desc_string_long(ret));
+     } else if (where & SSL_CB_EXIT) {
+ 	if (ret == 0)
+ 	    msg_info("%s:failed in %s",
+ 		     str, SSL_state_string_long(s));
+ 	else if (ret < 0) {
+ 	    msg_info("%s:error in %s",
+ 		     str, SSL_state_string_long(s));
+ 	}
+     }
+ }
+ 
+ /* taken from OpenSSL crypto/bio/b_dump.c */
+ 
+ #define TRUNCATE
+ #define DUMP_WIDTH	16
+ 
+ static int pfixtls_dump(const char *s, int len)
+ {
+     int     ret = 0;
+     char    buf[160 + 1];
+     char    tmp[20];
+     int     i;
+     int     j;
+     int     rows;
+     int     trunc;
+     unsigned char ch;
+ 
+     trunc = 0;
+ 
+ #ifdef TRUNCATE
+     for (; (len > 0) && ((s[len - 1] == ' ') || (s[len - 1] == '\0')); len--)
+ 	trunc++;
+ #endif
+ 
+     rows = (len / DUMP_WIDTH);
+     if ((rows * DUMP_WIDTH) < len)
+ 	rows++;
+     for (i = 0; i < rows; i++) {
+ 	buf[0] = '\0';				/* start with empty string */
+ 	sprintf(tmp, "%04x - ", i * DUMP_WIDTH);
+ 	strcpy(buf, tmp);
+ 	for (j = 0; j < DUMP_WIDTH; j++) {
+ 	    if (((i * DUMP_WIDTH) + j) >= len) {
+ 		strcat(buf, "   ");
+ 	    } else {
+ 		ch = ((unsigned char) *((char *) (s) + i * DUMP_WIDTH + j))
+ 		    & 0xff;
+ 		sprintf(tmp, "%02x%c", ch, j == 7 ? '-' : ' ');
+ 		strcat(buf, tmp);
+ 	    }
+ 	}
+ 	strcat(buf, "  ");
+ 	for (j = 0; j < DUMP_WIDTH; j++) {
+ 	    if (((i * DUMP_WIDTH) + j) >= len)
+ 		break;
+ 	    ch = ((unsigned char) *((char *) (s) + i * DUMP_WIDTH + j)) & 0xff;
+ 	    sprintf(tmp, "%c", ((ch >= ' ') && (ch <= '~')) ? ch : '.');
+ 	    strcat(buf, tmp);
+ 	}
+ 	strcat(buf, "\n");
+ 	/* if this is the last call then update the ddt_dump thing so that
+          * we will move the selection point in the debug window
+          */
+ 	msg_info("%s", buf);
+ 	ret += strlen(buf);
+     }
+ #ifdef TRUNCATE
+     if (trunc > 0) {
+ 	sprintf(buf, "%04x - <SPACES/NULS>\n", len + trunc);
+ 	msg_info("%s", buf);
+ 	ret += strlen(buf);
+     }
+ #endif
+     return (ret);
+ }
+ 
+ 
+ 
+ /* taken from OpenSSL apps/s_cb.c */
+ 
+ static long bio_dump_cb(BIO * bio, int cmd, char *argp, int argi,
+ 			long argl, long ret)
+ {
+     if (!do_dump)
+ 	return (ret);
+ 
+     if (cmd == (BIO_CB_READ | BIO_CB_RETURN)) {
+ 	msg_info("read from %08X [%08lX] (%d bytes => %ld (0x%X))", bio, argp,
+ 		 argi, ret, ret);
+ 	pfixtls_dump(argp, (int) ret);
+ 	return (ret);
+     } else if (cmd == (BIO_CB_WRITE | BIO_CB_RETURN)) {
+ 	msg_info("write to %08X [%08lX] (%d bytes => %ld (0x%X))", bio, argp,
+ 		 argi, ret, ret);
+ 	pfixtls_dump(argp, (int) ret);
+     }
+     return (ret);
+ }
+ 
+ 
+  /*
+   * This is the setup routine for the SSL server. As smtpd might be called
+   * more than once, we only want to do the initialization one time.
+   *
+   * The skeleton of this function is taken from OpenSSL apps/s_server.c.
+   */
+ 
+ int     pfixtls_init_serverengine(int verifydepth, int askcert,
+ 				  int requirecert)
+ {
+     int     off = 0;
+     int     verify_flags = SSL_VERIFY_NONE;
+     char   *CApath;
+     char   *CAfile;
+     char   *s_cert_file;
+     char   *s_key_file;
+ 
+     if (pfixtls_serverengine)
+ 	return (0);				/* already running */
+ 
+     if (var_smtpd_tls_loglevel >= 1)
+ 	msg_info("starting TLS engine");
+ 
+     SSL_load_error_strings();
+     SSLeay_add_ssl_algorithms();
+ 
+     ctx = SSL_CTX_new(SSLv23_server_method());
+     if (ctx == NULL) {
+ 	pfixtls_print_errors();
+ 	return (-1);
+     };
+     SSL_CTX_set_quiet_shutdown(ctx, 1);
+ 
+     SSL_CTX_set_options(ctx, off);
+     SSL_CTX_set_info_callback(ctx, apps_ssl_info_callback);
+     SSL_CTX_sess_set_cache_size(ctx, 128);
+ 
+     if (strlen(var_smtpd_tls_CAfile) == 0)
+ 	CAfile = NULL;
+     else
+ 	CAfile = var_smtpd_tls_CAfile;
+     if (strlen(var_smtpd_tls_CApath) == 0)
+ 	CApath = NULL;
+     else
+ 	CApath = var_smtpd_tls_CApath;
+ 
+     if ((!SSL_CTX_load_verify_locations(ctx, CAfile, CApath)) ||
+ 	(!SSL_CTX_set_default_verify_paths(ctx))) {
+ 	msg_info("TLS engine: cannot load CA data");
+ 	pfixtls_print_errors();
+ 	return (-1);
+     }
+     if (strlen(var_smtpd_tls_cert_file) == 0)
+ 	s_cert_file = NULL;
+     else
+ 	s_cert_file = var_smtpd_tls_cert_file;
+     if (strlen(var_smtpd_tls_key_file) == 0)
+ 	s_key_file = NULL;
+     else
+ 	s_key_file = var_smtpd_tls_key_file;
+ 
+     if (!set_cert_stuff(ctx, s_cert_file, s_key_file)) {
+ 	msg_info("TLS engine: cannot load cert/key data");
+ 	return (-1);
+     }
+     SSL_CTX_set_tmp_rsa_callback(ctx, tmp_rsa_cb);
+ 
+     verify_depth = verifydepth;
+     if (askcert)
+ 	verify_flags |= SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE;
+     if (requirecert)
+ 	verify_flags |= SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT
+ 	    | SSL_VERIFY_CLIENT_ONCE;
+     SSL_CTX_set_verify(ctx, verify_flags, verify_callback);
+ 
+     SSL_CTX_set_client_CA_list(ctx, SSL_load_client_CA_file(CAfile));
+ 
+     pfixtls_serverengine = 1;
+     return (0);
+ }
+ 
+ 
+  /*
+   * This is the actual startup routine for the connection. We expect
+   * that the buffers are flushed and the "220 Ready to start TLS" was
+   * send to the client, so that we can immediately can start the TLS
+   * handshake process.
+   */
+ int     pfixtls_start_servertls(void)
+ {
+     int     sts;
+     int     j;
+     unsigned int n;
+     SSL_SESSION *session;
+     X509   *peer;
+ 
+     if (!pfixtls_serverengine) {		/* should never happen */
+ 	msg_info("tls_engine not running");
+ 	return (-1);
+     }
+     if (var_smtpd_tls_loglevel >= 1)
+ 	msg_info("setting up TLS connection");
+ 
+     if (con == NULL) {
+ 	con = (SSL *) SSL_new(ctx);
+     }
+     if (con == NULL) {
+ 	msg_info("Could not allocate 'con' with SSL_new()");
+ 	return (-1);
+     }
+     SSL_clear(con);
+ 
+     if (!SSL_set_fd(con, pfixtls_fd)) {
+ 	msg_info("SSL_set_fd failed");
+ 	return (-1);
+     }
+     /*
+      * This is the actual handshake routine. It will do all the negotiations
+      * and will check the client cert etc.
+      */
+     SSL_set_accept_state(con);
+ 
+ 
+     /*
+      * We do have an SSL_set_fd() and now suddenly a BIO_ routine is called?
+      * Well there is a BIO below the SSL routines that is automatically
+      * created for us, so we can use it for debugging purposes.
+      */
+     if (var_smtpd_tls_loglevel >= 3)
+ 	BIO_set_callback(SSL_get_rbio(con), bio_dump_cb);
+ 
+     /* Dump the negotiation for loglevels 3 and 4*/
+     if (var_smtpd_tls_loglevel >= 3)
+ 	do_dump = 1;
+     if ((sts = SSL_accept(con)) <= 0) {
+ 	msg_info("SSL_accept error %d", sts);
+ 	session = SSL_get_session(con);
+ 	if (session) {
+ 	    SSL_CTX_remove_session(ctx, session);
+ 	    msg_info("SSL session removed");
+ 	}
+ 	if (con)
+ 	    SSL_free(con);
+ 	con = NULL;
+ 	return (-1);
+     }
+     /* Only loglevel==4 dumps everything */
+     if (var_smtpd_tls_loglevel < 4)
+ 	do_dump = 0;
+ 
+     /*
+      * Lets see, whether a peer certificate is availabe and what is
+      * the actual information. We want to save it for later use.
+      */
+     peer = SSL_get_peer_certificate(con);
+     if (peer != NULL) {
+ 	X509_NAME_oneline(X509_get_subject_name(peer),
+ 			  ccert_subject, CCERT_BUFSIZ);
+ 	if (var_smtpd_tls_loglevel >= 1)
+ 	    msg_info("subject=%s", ccert_subject);
+ 	pfixtls_ccert_subject = ccert_subject;
+ 	X509_NAME_oneline(X509_get_issuer_name(peer),
+ 			  ccert_issuer, CCERT_BUFSIZ);
+ 	if (var_smtpd_tls_loglevel >= 1)
+ 	    msg_info("issuer=%s", ccert_issuer);
+ 	pfixtls_ccert_issuer = ccert_issuer;
+ 	if (X509_digest(peer, EVP_md5(), md, &n)) {
+ 	    for (j = 0; j < (int) n; j++) {
+ 		fingerprint[j * 3] = hexcodes[(md[j] & 0xf0) >> 4];
+ 		fingerprint[(j * 3) + 1] = hexcodes[(md[j] & 0x0f)];
+ 		if (j + 1 != (int) n)
+ 		    fingerprint[(j * 3) + 2] = '_';
+ 		else
+ 		    fingerprint[(j * 3) + 2] = '\0';
+ 	    }
+ 	    if (var_smtpd_tls_loglevel >= 1)
+ 		msg_info("fingerprint=%s", fingerprint);
+ 	    pfixtls_ccert_fingerprint = fingerprint;
+ 	}
+ 	X509_free(peer);
+     }
+     pfixtls_serveractive = 1;
+     msg_info("TLS connection established");
+     return (0);
+ }
+ 
+  /*
+   * Shut down the TLS connection, that does mean: remove all the information
+   * and reset the flags! This is needed if the actual running smtpd is to
+   * be restarted. We do not give back any value, as there is nothing to
+   * be reported.
+   */
+ int     pfixtls_stop_servertls(void)
+ {
+     if (pfixtls_serveractive) {
+ 	SSL_shutdown(con);
+ 	SSL_clear(con);
+     }
+     if (con);
+     SSL_free(con);
+     con = NULL;
+     pfixtls_ccert_subject = NULL;
+     pfixtls_ccert_issuer = NULL;
+     pfixtls_ccert_fingerprint = NULL;
+     pfixtls_serveractive = 0;
+     return (0);
+ }
+ 
+ 
+  /*
+   * This is the setup routine for the SSL client. As smtpd might be called
+   * more than once, we only want to do the initialization one time.
+   *
+   * The skeleton of this function is taken from OpenSSL apps/s_client.c.
+   */
+ 
+ int     pfixtls_init_clientengine(int verifydepth)
+ {
+     int     off = 0;
+     int     verify_flags = SSL_VERIFY_NONE;
+     char   *CApath;
+     char   *CAfile;
+     char   *c_cert_file;
+     char   *c_key_file;
+ 
+ 
+     if (pfixtls_clientengine)
+ 	return (0);				/* already running */
+ 
+     if (var_smtp_tls_loglevel >= 1)
+ 	msg_info("starting TLS engine");
+ 
+     SSL_load_error_strings();
+     SSLeay_add_ssl_algorithms();
+ 
+     ctx = SSL_CTX_new(SSLv23_client_method());
+     if (ctx == NULL) {
+ 	pfixtls_print_errors();
+ 	return (-1);
+     };
+ 
+     SSL_CTX_set_quiet_shutdown(ctx, 1);
+     SSL_CTX_set_options(ctx, off);
+     SSL_CTX_set_info_callback(ctx, apps_ssl_info_callback);
+ 
+     if (strlen(var_smtp_tls_CAfile) == 0)
+ 	CAfile = NULL;
+     else
+ 	CAfile = var_smtp_tls_CAfile;
+     if (strlen(var_smtp_tls_CApath) == 0)
+ 	CApath = NULL;
+     else
+ 	CApath = var_smtp_tls_CApath;
+ 
+     if (CAfile || CApath)
+ 	if ((!SSL_CTX_load_verify_locations(ctx, CAfile, CApath)) ||
+ 	    (!SSL_CTX_set_default_verify_paths(ctx))) {
+ 	    msg_info("TLS engine: cannot load CA data");
+ 	    pfixtls_print_errors();
+ 	    return (-1);
+ 	}
+     if (strlen(var_smtp_tls_cert_file) == 0)
+ 	c_cert_file = NULL;
+     else
+ 	c_cert_file = var_smtp_tls_cert_file;
+     if (strlen(var_smtp_tls_key_file) == 0)
+ 	c_key_file = NULL;
+     else
+ 	c_key_file = var_smtp_tls_key_file;
+ 
+     if (c_cert_file || c_key_file)
+ 	if (!set_cert_stuff(ctx, c_cert_file, c_key_file)) {
+ 	    msg_info("TLS engine: cannot load cert/key data");
+ 	    return (-1);
+ 	}
+     SSL_CTX_set_tmp_rsa_callback(ctx, tmp_rsa_cb);
+ 
+     verify_depth = verifydepth;
+     SSL_CTX_set_verify(ctx, verify_flags, verify_callback);
+ 
+     pfixtls_clientengine = 1;
+     return (0);
+ }
+ 
+ int     pfixtls_start_clienttls(void)
+ {
+     int     sts;
+     int     j;
+     unsigned int n;
+     SSL_SESSION *session;
+     X509   *peer;
+ 
+     if (!pfixtls_clientengine) {		/* should never happen */
+ 	msg_info("tls_engine not running");
+ 	return (-1);
+     }
+     if (var_smtpd_tls_loglevel >= 1)
+ 	msg_info("setting up TLS connection");
+ 
+     if (con == NULL) {
+ 	con = (SSL *) SSL_new(ctx);
+     }
+     if (con == NULL) {
+ 	msg_info("Could not allocate 'con' with SSL_new()");
+ 	return (-1);
+     }
+     SSL_clear(con);
+ 
+     if (!SSL_set_fd(con, pfixtls_fd)) {
+ 	msg_info("SSL_set_fd failed");
+ 	return (-1);
+     }
+     /*
+      * This is the actual handshake routine. It will do all the negotiations
+      * and will check the client cert etc.
+      */
+     SSL_set_connect_state(con);
+ 
+ 
+     /*
+      * We do have an SSL_set_fd() and now suddenly a BIO_ routine is called?
+      * Well there is a BIO below the SSL routines that is automatically
+      * created for us, so we can use it for debugging purposes.
+      */
+     if (var_smtp_tls_loglevel >= 3)
+ 	BIO_set_callback(SSL_get_rbio(con), bio_dump_cb);
+ 
+     /* Dump the negotiation for loglevels 3 and 4 */
+     if (var_smtp_tls_loglevel >= 3)
+ 	do_dump = 1;
+     if ((sts = SSL_connect(con)) <= 0) {
+ 	msg_info("SSL_connect error %d", sts);
+ 	session = SSL_get_session(con);
+ 	if (session) {
+ 	    SSL_CTX_remove_session(ctx, session);
+ 	    msg_info("SSL session removed");
+ 	}
+ 	if (con)
+ 	    SSL_free(con);
+ 	con = NULL;
+ 	return (-1);
+     }
+     /* Only loglevel==4 dumps everything */
+     if (var_smtp_tls_loglevel < 4)
+ 	do_dump = 0;
+ 
+     pfixtls_clientactive = 1;
+     msg_info("TLS connection established");
+     return (0);
+ }
+ 
+  /*
+   * Shut down the TLS connection, that does mean: remove all the information
+   * and reset the flags! This is needed if the actual running smtp is to
+   * be restarted. We do not give back any value, as there is nothing to
+   * be reported.
+   */
+ int     pfixtls_stop_clienttls(void)
+ {
+     if (pfixtls_clientactive) {
+ 	SSL_shutdown(con);
+ 	SSL_clear(con);
+     }
+     if (con);
+     SSL_free(con);
+     con = NULL;
+     pfixtls_ccert_subject = NULL;
+     pfixtls_ccert_issuer = NULL;
+     pfixtls_ccert_fingerprint = NULL;
+     pfixtls_clientactive = 0;
+     return (0);
+ }
+ 
+ 
+ #endif /* HAS_SSL */
diff -r -c --new-file postfix-19990317-pl04-vanilla/global/pfixtls.h postfix-19990317-pl04/global/pfixtls.h
*** postfix-19990317-pl04-vanilla/global/pfixtls.h	Thu Jan  1 01:00:00 1970
--- postfix-19990317-pl04/global/pfixtls.h	Thu Apr 29 21:41:09 1999
***************
*** 0 ****
--- 1,37 ----
+ /*++
+ /* NAME
+ /*      pfixtls 3h
+ /* SUMMARY
+ /*      TLS routines
+ /* SYNOPSIS
+ /*      include "pfixtls.h"
+ /* DESCRIPTION
+ /* .nf
+ /*--*/
+ 
+ #ifdef HAS_SSL
+ int     pfixtls_read(int fd, void *buf, size_t count);
+ int     pfixtls_write(int fd, void *buf, size_t count);
+ int     pfixtls_read_wait(int fd, int timeout);
+ int     pfixtls_write_wait(int fd, int timeout);
+ void    pfixtls_setfd(int fd);
+ 
+ int     pfixtls_init_serverengine(int verifydepth, int askcert,
+ 				  int requirecert);
+ int     pfixtls_start_servertls(void);
+ int     pfixtls_stop_servertls(void);
+ 
+ int     pfixtls_init_clientengine(int verifydepth);
+ int     pfixtls_start_clienttls(void);
+ int     pfixtls_stop_clienttls(void);
+ 
+ extern int pfixtls_serverengine;
+ extern int pfixtls_serveractive;
+ extern char *pfixtls_ccert_subject;
+ extern char *pfixtls_ccert_issuer;
+ extern char *pfixtls_ccert_fingerprint;
+ 
+ extern int pfixtls_clientengine;
+ extern int pfixtls_clientactive;
+ 
+ #endif
diff -r -c --new-file postfix-19990317-pl04-vanilla/global/smtp_stream.c postfix-19990317-pl04/global/smtp_stream.c
*** postfix-19990317-pl04-vanilla/global/smtp_stream.c	Thu Feb 18 21:54:03 1999
--- postfix-19990317-pl04/global/smtp_stream.c	Thu Apr 15 15:53:37 1999
***************
*** 128,133 ****
--- 128,134 ----
  
  /* Application-specific. */
  
+ #include "pfixtls.h"
  #include "smtp_stream.h"
  
  jmp_buf smtp_timeout_buf;
***************
*** 163,186 ****
--- 164,205 ----
  
  static int smtp_read(int fd, void *buf, unsigned len)
  {
+ #ifdef HAS_SSL
+     if (pfixtls_read_wait(fd, smtp_maxtime) < 0) {
+ 	smtp_timeout_event(fd, SMTP_DIR_READ);
+ 	return (-1);
+     } else {
+ 	return (pfixtls_read(fd, buf, len));
+     }
+ #else
      if (read_wait(fd, smtp_maxtime) < 0) {
  	smtp_timeout_event(fd, SMTP_DIR_READ);
  	return (-1);
      } else {
  	return (read(fd, buf, len));
      }
+ #endif
  }
  
  /* smtp_write - write with timeout */
  
  static int smtp_write(int fd, void *buf, unsigned len)
  {
+ #ifdef HAS_SSL
+     if (pfixtls_write_wait(fd, smtp_maxtime) < 0) {
+ 	smtp_timeout_event(fd, SMTP_DIR_WRITE);
+ 	return (-1);
+     } else {
+ 	return (pfixtls_write(fd, buf, len));
+     }
+ #else
      if (write_wait(fd, smtp_maxtime) < 0) {
  	smtp_timeout_event(fd, SMTP_DIR_WRITE);
  	return (-1);
      } else {
  	return (write(fd, buf, len));
      }
+ #endif
  }
  
  /* smtp_timeout_protect - setup timeout trap for specified stream. */
diff -r -c --new-file postfix-19990317-pl04-vanilla/smtp/smtp.c postfix-19990317-pl04/smtp/smtp.c
*** postfix-19990317-pl04-vanilla/smtp/smtp.c	Tue Mar 16 03:02:27 1999
--- postfix-19990317-pl04/smtp/smtp.c	Fri Apr 30 14:48:53 1999
***************
*** 163,168 ****
--- 163,169 ----
  #include <config.h>
  #include <debug_peer.h>
  #include <mail_error.h>
+ #include <pfixtls.h>
  
  /* Single server skeleton. */
  
***************
*** 192,197 ****
--- 193,201 ----
  int     var_ign_mx_lookup_err;
  int     var_skip_quit_resp;
  char   *var_fallback_relay;
+ int     var_smtp_use_tls;
+ int     var_smtp_enforce_tls;
+ int     var_smtp_tls_scert_vd;
  
   /*
    * Global variables. smtp_errno is set by the address lookup routines and by
***************
*** 306,311 ****
--- 310,329 ----
      }
  }
  
+ /* pre_jail_init - pre-jail initialization */
+ 
+ static void pre_jail_init(void)
+ {
+     /*
+      * Initialize the TLS data before entering the chroot jail
+      */
+     debug_peer_init();
+ #ifdef HAS_SSL
+     if (var_smtp_use_tls || var_smtp_enforce_tls)
+ 	pfixtls_init_clientengine(var_smtp_tls_scert_vd);
+ #endif
+ }
+ 
  /* main - pass control to the single-threaded skeleton */
  
  int     main(int argc, char **argv)
***************
*** 326,337 ****
--- 344,358 ----
  	VAR_SMTP_DATA2_TMOUT, DEF_SMTP_DATA2_TMOUT, &var_smtp_data2_tmout, 1, 0,
  	VAR_SMTP_QUIT_TMOUT, DEF_SMTP_QUIT_TMOUT, &var_smtp_quit_tmout, 1, 0,
  	VAR_DEBUG_PEER_LEVEL, DEF_DEBUG_PEER_LEVEL, &var_debug_peer_level, 1, 0,
+ 	VAR_SMTP_TLS_SCERT_VD, DEF_SMTP_TLS_SCERT_VD, &var_smtp_tls_scert_vd, 0, 0,
  	0,
      };
      static CONFIG_BOOL_TABLE bool_table[] = {
  	VAR_SMTP_SKIP_4XX, DEF_SMTP_SKIP_4XX, &var_smtp_skip_4xx_greeting,
  	VAR_IGN_MX_LOOKUP_ERR, DEF_IGN_MX_LOOKUP_ERR, &var_ign_mx_lookup_err,
  	VAR_SKIP_QUIT_RESP, DEF_SKIP_QUIT_RESP, &var_skip_quit_resp,
+ 	VAR_SMTP_USE_TLS, DEF_SMTP_USE_TLS, &var_smtp_use_tls,
+ 	VAR_SMTP_ENFORCE_TLS, DEF_SMTP_ENFORCE_TLS, &var_smtp_enforce_tls,
  	0,
      };
  
***************
*** 339,344 ****
  		       MAIL_SERVER_INT_TABLE, int_table,
  		       MAIL_SERVER_STR_TABLE, str_table,
  		       MAIL_SERVER_BOOL_TABLE, bool_table,
! 		       MAIL_SERVER_PRE_INIT, debug_peer_init,
  		       0);
  }
--- 360,365 ----
  		       MAIL_SERVER_INT_TABLE, int_table,
  		       MAIL_SERVER_STR_TABLE, str_table,
  		       MAIL_SERVER_BOOL_TABLE, bool_table,
! 		       MAIL_SERVER_PRE_INIT, pre_jail_init,
  		       0);
  }
diff -r -c --new-file postfix-19990317-pl04-vanilla/smtp/smtp.h postfix-19990317-pl04/smtp/smtp.h
*** postfix-19990317-pl04-vanilla/smtp/smtp.h	Mon Jan 18 21:11:49 1999
--- postfix-19990317-pl04/smtp/smtp.h	Thu Apr 29 15:59:43 1999
***************
*** 41,46 ****
--- 41,47 ----
  #define SMTP_FEATURE_8BITMIME	(1<<1)
  #define SMTP_FEATURE_PIPELINING	(1<<2)
  #define SMTP_FEATURE_SIZE	(1<<3)
+ #define SMTP_FEATURE_STARTTLS	(1<<4)
  
   /*
    * smtp.c
diff -r -c --new-file postfix-19990317-pl04-vanilla/smtp/smtp_connect.c postfix-19990317-pl04/smtp/smtp_connect.c
*** postfix-19990317-pl04-vanilla/smtp/smtp_connect.c	Thu Feb 25 17:23:34 1999
--- postfix-19990317-pl04/smtp/smtp_connect.c	Fri Apr 30 14:49:50 1999
***************
*** 105,110 ****
--- 105,111 ----
  
  #include <mail_params.h>
  #include <own_inet_addr.h>
+ #include <pfixtls.h>
  
  /* DNS library. */
  
***************
*** 224,229 ****
--- 225,231 ----
  	vstream_fclose(stream);
  	return (0);
      }
+     pfixtls_setfd(sock);
      vstream_ungetc(stream, ch);
      return (smtp_session_alloc(stream, addr->name, inet_ntoa(sin.sin_addr)));
  }
diff -r -c --new-file postfix-19990317-pl04-vanilla/smtp/smtp_proto.c postfix-19990317-pl04/smtp/smtp_proto.c
*** postfix-19990317-pl04-vanilla/smtp/smtp_proto.c	Fri Mar  5 02:13:34 1999
--- postfix-19990317-pl04/smtp/smtp_proto.c	Fri Apr 30 14:56:39 1999
***************
*** 98,103 ****
--- 98,104 ----
  #include <rec_type.h>
  #include <off_cvt.h>
  #include <mark_corrupt.h>
+ #include <pfixtls.h>
  
  /* Application-specific. */
  
***************
*** 152,157 ****
--- 153,160 ----
      char   *words;
      char   *word;
      int     n;
+     int     oldfeatures;
+     int     rval;
  
      /*
       * Prepare for disaster.
***************
*** 204,210 ****
  				   session->host,
  				   translit(resp->str, "\n", " ")));
      }
- 
      /*
       * Pick up some useful features offered by the SMTP server. XXX Until we
       * have a portable routine to convert from string to off_t with proper
--- 207,212 ----
***************
*** 214,219 ****
--- 216,222 ----
       */
      lines = resp->str;
      (void) mystrtok(&lines, "\n");
+     oldfeatures = state->features;		/* remember */
      while ((words = mystrtok(&lines, "\n")) != 0) {
  	if (mystrtok(&words, "- ") && (word = mystrtok(&words, " \t")) != 0) {
  	    if (strcasecmp(word, "8BITMIME") == 0)
***************
*** 222,231 ****
--- 225,292 ----
  		state->features |= SMTP_FEATURE_PIPELINING;
  	    else if (strcasecmp(word, "SIZE") == 0)
  		state->features |= SMTP_FEATURE_SIZE;
+ 	    else if (strcasecmp(word, "STARTTLS") == 0)
+ 		state->features |= SMTP_FEATURE_STARTTLS;
  	}
      }
      if (msg_verbose)
  	msg_info("server features: 0x%x", state->features);
+     if ((state->features & SMTP_FEATURE_STARTTLS) && 
+ 	(var_smtp_use_tls || var_smtp_enforce_tls)) {
+ 	/*
+          * Try to use the TLS feature
+          */
+ 	if (state->features & SMTP_FEATURE_STARTTLS) {
+ 	    smtp_chat_cmd(state, "STARTTLS");
+ 	    if ((resp = smtp_chat_resp(state))->code / 100 != 2) {
+ 		state->features &= ~SMTP_FEATURE_STARTTLS;
+ 		/*
+ 	         * At this point a political decision is necessary. If we
+ 	         * enforce usage of tls, we have to close the connection
+ 	         * now.
+ 	         */
+ 		if (var_smtp_enforce_tls)
+ 		    return (smtp_site_fail(state, resp->code,
+ 					 "host %s refused to start TLS: %s",
+ 					   session->host,
+ 					   translit(resp->str, "\n", " ")));
+ 	    } else {
+ 		if (rval = pfixtls_start_clienttls())
+ 		    return (smtp_site_fail(state, 450, "Could not start TLS"));
+ 		/*
+ 	         * At this point we have to re-negotiate the "EHLO" to reget
+ 	         * the feature-list
+ 	         */
+ 		state->features = oldfeatures;
+ 		if (state->features & SMTP_FEATURE_ESMTP) {
+ 		    smtp_chat_cmd(state, "EHLO %s", var_myhostname);
+ 		    if ((resp = smtp_chat_resp(state))->code / 100 != 2)
+ 			state->features &= ~SMTP_FEATURE_ESMTP;
+ 		}
+ 		lines = resp->str;
+ 		(void) mystrtok(&lines, "\n");
+ 		while ((words = mystrtok(&lines, "\n")) != 0) {
+ 		    if (mystrtok(&words, "- ") &&
+ 			(word = mystrtok(&words, " \t")) != 0) {
+ 			if (strcasecmp(word, "8BITMIME") == 0)
+ 			    state->features |= SMTP_FEATURE_8BITMIME;
+ 			else if (strcasecmp(word, "PIPELINING") == 0)
+ 			    state->features |= SMTP_FEATURE_PIPELINING;
+ 			else if (strcasecmp(word, "SIZE") == 0)
+ 			    state->features |= SMTP_FEATURE_SIZE;
+ 			else if (strcasecmp(word, "STARTTLS") == 0)
+ 			    state->features |= SMTP_FEATURE_STARTTLS;
+ 		    }
+ 		}
+ 		/*
+ 	         * Actually, at this point STARTTLS should not be offered
+ 		 * anymore, so we could check for a protocol violation, but
+ 		 * what should we do then?
+ 	         */
+ 
+ 	    }
+ 	}
+     }
      return (0);
  }
  
diff -r -c --new-file postfix-19990317-pl04-vanilla/smtp/smtp_proto.c.gut postfix-19990317-pl04/smtp/smtp_proto.c.gut
*** postfix-19990317-pl04-vanilla/smtp/smtp_proto.c.gut	Thu Jan  1 01:00:00 1970
--- postfix-19990317-pl04/smtp/smtp_proto.c.gut	Thu Apr 29 16:57:27 1999
***************
*** 0 ****
--- 1,683 ----
+ /*++
+ /* NAME
+ /*	smtp_proto 3
+ /* SUMMARY
+ /*	client SMTP protocol
+ /* SYNOPSIS
+ /*	#include "smtp.h"
+ /*
+ /*	int	smtp_helo(state)
+ /*	SMTP_STATE *state;
+ /*
+ /*	int	smtp_xfer(state)
+ /*	SMTP_STATE *state;
+ /* DESCRIPTION
+ /*	This module implements the client side of the SMTP protocol.
+ /*
+ /*	smtp_helo() performs the initial handshake with the SMTP server.
+ /*
+ /*	smtp_xfer() sends message envelope information followed by the
+ /*	message data, and finishes the SMTP conversation. These operations
+ /*	are combined in one function, in order to implement SMTP pipelining.
+ /*	Recipients are marked as "done" in the mail queue file when
+ /*	bounced or delivered. The message delivery status is updated
+ /*	accordingly.
+ /* DIAGNOSTICS
+ /*	smtp_helo() and smtp_xfer() return 0 in case of success, -1 in case
+ /*	of failure. For smtp_xfer(), success means the ability to perform
+ /*	an SMTP conversation, not necessarily the ability to deliver mail.
+ /*
+ /*	Warnings: corrupt message file. A corrupt message is marked
+ /*	as "corrupt" by changing its queue file permissions.
+ /* BUGS
+ /*	Some SMTP servers will abort when the number of recipients
+ /*	for one message exceeds their capacity. This behavior violates
+ /*	the SMTP protocol.
+ /*	The only way around this is to limit the number of recipients
+ /*	per transaction to an artificially-low value.
+ /* SEE ALSO
+ /*	smtp(3h) internal data structures
+ /*	smtp_chat(3) query/reply SMTP support
+ /*	smtp_trouble(3) error handlers
+ /* LICENSE
+ /* .ad
+ /* .fi
+ /*	The Secure Mailer license must be distributed with this software.
+ /* AUTHOR(S)
+ /*	Wietse Venema
+ /*	IBM T.J. Watson Research
+ /*	P.O. Box 704
+ /*	Yorktown Heights, NY 10598, USA
+ /*
+ /*	Pipelining code in cooperation with:
+ /*	Jon Ribbens
+ /*	Oaktree Internet Solutions Ltd.,
+ /*	Internet House,
+ /*	Canal Basin,
+ /*	Coventry,
+ /*	CV1 4LY, United Kingdom.
+ /*
+ /*--*/
+ 
+ /* System library. */
+ 
+ #include <sys_defs.h>
+ #include <sys/stat.h>
+ #include <sys/socket.h>			/* shutdown(2) */
+ #include <setjmp.h>
+ #include <string.h>
+ #include <unistd.h>
+ #include <stdlib.h>			/* 44BSD stdarg.h uses abort() */
+ #include <stdarg.h>
+ 
+ #ifdef STRCASECMP_IN_STRINGS_H
+ #include <strings.h>
+ #endif
+ 
+ /* Utility library. */
+ 
+ #include <msg.h>
+ #include <vstring.h>
+ #include <vstream.h>
+ #include <vstring_vstream.h>
+ #include <stringops.h>
+ #include <mymalloc.h>
+ 
+ /* Global library. */
+ 
+ #include <mail_params.h>
+ #include <smtp_stream.h>
+ #include <mail_queue.h>
+ #include <recipient_list.h>
+ #include <deliver_request.h>
+ #include <deliver_completed.h>
+ #include <defer.h>
+ #include <bounce.h>
+ #include <sent.h>
+ #include <record.h>
+ #include <rec_type.h>
+ #include <off_cvt.h>
+ #include <mark_corrupt.h>
+ #include <pfixtls.h>
+ 
+ /* Application-specific. */
+ 
+ #include "smtp.h"
+ #include "quote_821_local.h"
+ 
+  /*
+   * Sender and receiver state. A session does not necessarily go through a
+   * linear progression, but states are guaranteed to not jump backwards.
+   * Normal sessions go from MAIL->RCPT->DATA->DOT->QUIT->LAST. The states
+   * MAIL, RCPT, and DATA may also be followed by ABORT->QUIT->LAST.
+   * 
+   * By default, the receiver skips the QUIT response. Some SMTP servers
+   * disconnect after responding to ".", and some SMTP servers wait before
+   * responding to QUIT.
+   */
+ #define SMTP_STATE_MAIL		0
+ #define SMTP_STATE_RCPT		1
+ #define SMTP_STATE_DATA		2
+ #define SMTP_STATE_DOT		3
+ #define SMTP_STATE_ABORT	4
+ #define SMTP_STATE_QUIT		5
+ #define SMTP_STATE_LAST		6
+ 
+ int    *xfer_timeouts[SMTP_STATE_LAST] = {
+     &var_smtp_mail_tmout,
+     &var_smtp_rcpt_tmout,
+     &var_smtp_data0_tmout,
+     &var_smtp_data2_tmout,
+     &var_smtp_quit_tmout,
+     &var_smtp_quit_tmout,
+ };
+ 
+ char   *xfer_states[SMTP_STATE_LAST] = {
+     "sending MAIL FROM",
+     "sending RCPT TO",
+     "sending DATA command",
+     "sending end of data -- message may be sent more than once",
+     "sending final RSET",
+     "sending QUIT",
+ };
+ 
+ /* smtp_helo - perform initial handshake with SMTP server */
+ 
+ int     smtp_helo(SMTP_STATE *state)
+ {
+     SMTP_SESSION *session = state->session;
+     DELIVER_REQUEST *request = state->request;
+     SMTP_RESP *resp;
+     int     except;
+     char   *lines;
+     char   *words;
+     char   *word;
+     int     n;
+     int oldfeatures, rval;
+ 
+     /*
+      * Prepare for disaster.
+      */
+     smtp_timeout_setup(state->session->stream, var_smtp_helo_tmout);
+     if ((except = setjmp(smtp_timeout_buf)) != 0)
+ 	return (smtp_stream_except(state, except, "sending HELO"));
+ 
+     /*
+      * Read and parse the server's SMTP greeting banner.
+      */
+     if (((resp = smtp_chat_resp(state))->code / 100) != 2)
+ 	return (smtp_site_fail(state, resp->code,
+ 			       "host %s refused to talk to me: %s",
+ 			    session->host, translit(resp->str, "\n", " ")));
+ 
+     /*
+      * See if we are talking to ourself. This should not be possible with the
+      * way we implement DNS lookups. However, people are known to sometimes
+      * screw up the naming service. And, mailer loops are still possible when
+      * our own mailer routing tables are mis-configured.
+      */
+     words = resp->str;
+     (void) mystrtok(&words, "- \t\n");
+     for (n = 0; (word = mystrtok(&words, " \t\n")) != 0; n++) {
+ 	if (n == 0 && strcasecmp(word, var_myhostname) == 0) {
+ 	    msg_warn("host %s greeted me with my own hostname %s",
+ 		     session->host, var_myhostname);
+ 	    return (smtp_site_fail(state, session->best ? 550 : 450,
+ 				   "mail for %s loops back to myself",
+ 				   request->nexthop));
+ 	} else if (strcasecmp(word, "ESMTP") == 0)
+ 	    state->features |= SMTP_FEATURE_ESMTP;
+     }
+ 
+     /*
+      * Return the compliment. Fall back to SMTP if our ESMTP recognition
+      * heuristic failed.
+      */
+     if (state->features & SMTP_FEATURE_ESMTP) {
+ 	smtp_chat_cmd(state, "EHLO %s", var_myhostname);
+ 	if ((resp = smtp_chat_resp(state))->code / 100 != 2)
+ 	    state->features &= ~SMTP_FEATURE_ESMTP;
+     }
+     if ((state->features & SMTP_FEATURE_ESMTP) == 0) {
+ 	smtp_chat_cmd(state, "HELO %s", var_myhostname);
+ 	if ((resp = smtp_chat_resp(state))->code / 100 != 2)
+ 	    return (smtp_site_fail(state, resp->code,
+ 				   "host %s refused to talk to me: %s",
+ 				   session->host,
+ 				   translit(resp->str, "\n", " ")));
+     }
+ 
+     /*
+      * Pick up some useful features offered by the SMTP server. XXX Until we
+      * have a portable routine to convert from string to off_t with proper
+      * overflow detection, ignore the message size limit advertised by the
+      * SMTP server. Otherwise, we might do the wrong thing when the server
+      * advertises a really huge message size limit.
+      */
+     lines = resp->str;
+     (void) mystrtok(&lines, "\n");
+     oldfeatures = state->features;	/* remember */
+     while ((words = mystrtok(&lines, "\n")) != 0) {
+ 	if (mystrtok(&words, "- ") && (word = mystrtok(&words, " \t")) != 0) {
+ 	    if (strcasecmp(word, "8BITMIME") == 0)
+ 		state->features |= SMTP_FEATURE_8BITMIME;
+ 	    else if (strcasecmp(word, "PIPELINING") == 0)
+ 		state->features |= SMTP_FEATURE_PIPELINING;
+ 	    else if (strcasecmp(word, "SIZE") == 0)
+ 		state->features |= SMTP_FEATURE_SIZE;
+ 	    else if (strcasecmp(word, "STARTTLS") == 0)
+ 		state->features |= SMTP_FEATURE_STARTTLS;
+ 	}
+     }
+     if (msg_verbose)
+ 	msg_info("server features: 0x%x", state->features);
+     if ((state->features & SMTP_FEATURE_STARTTLS) && var_smtp_use_tls)
+     {
+       /*
+        * Try to use the TLS feature
+        */
+     if (state->features & SMTP_FEATURE_STARTTLS) {
+ 	smtp_chat_cmd(state, "STARTTLS");
+ 	if ((resp = smtp_chat_resp(state))->code / 100 != 2)
+ 	{
+ 	    state->features &= ~SMTP_FEATURE_STARTTLS;
+ 	    /*
+ 	     * At this point a political decision is necessary. If we
+ 	     * enforce usage of tls, we have to close the connection
+ 	     * now.
+ 	     */
+ 	    if (var_smtp_enforce_tls)
+ 	      return (smtp_site_fail(state, resp->code,
+ 				   "host %s refused to start TLS: %s",
+ 				   session->host,
+ 				   translit(resp->str, "\n", " ")));
+         }
+ 	else
+ 	{
+ 	  if (rval=pfixtls_start_clienttls())
+ 	    return(smtp_site_fail(state, 450, "Could not start TLS"));
+           /*
+ 	   * At this point we have to re-negotiate the "EHLO" to reget
+ 	   * the feature-list
+ 	   */
+ 	   state->features = oldfeatures;
+     if (state->features & SMTP_FEATURE_ESMTP) {
+ 	smtp_chat_cmd(state, "EHLO %s", var_myhostname);
+ 	if ((resp = smtp_chat_resp(state))->code / 100 != 2)
+ 	    state->features &= ~SMTP_FEATURE_ESMTP;
+     }
+     lines = resp->str;
+     (void) mystrtok(&lines, "\n");
+     while ((words = mystrtok(&lines, "\n")) != 0) {
+ 	if (mystrtok(&words, "- ") && (word = mystrtok(&words, " \t")) != 0) {
+ 	    if (strcasecmp(word, "8BITMIME") == 0)
+ 		state->features |= SMTP_FEATURE_8BITMIME;
+ 	    else if (strcasecmp(word, "PIPELINING") == 0)
+ 		state->features |= SMTP_FEATURE_PIPELINING;
+ 	    else if (strcasecmp(word, "SIZE") == 0)
+ 		state->features |= SMTP_FEATURE_SIZE;
+ 	    else if (strcasecmp(word, "STARTTLS") == 0)
+ 		state->features |= SMTP_FEATURE_STARTTLS;
+ 	}
+     }
+     /*
+      * Actually, at this point STARTTLS should not be offered anymore,
+      * so we could check for a protocol violation, but what should
+      * we do then?
+      */
+ 
+ 	}
+     }
+     }
+     return (0);
+ }
+ 
+ /* smtp_xfer - send a batch of envelope information and the message data */
+ 
+ int     smtp_xfer(SMTP_STATE *state)
+ {
+     char   *myname = "smtp_xfer";
+     DELIVER_REQUEST *request = state->request;
+     SMTP_SESSION *session = state->session;
+     SMTP_RESP *resp;
+     RECIPIENT *rcpt;
+     VSTRING *next_command = vstring_alloc(100);
+     int     next_state;
+     int     next_rcpt;
+     int     send_state;
+     int     recv_state;
+     int     send_rcpt;
+     int     recv_rcpt;
+     int     nrcpt;
+     int     except;
+     int     rec_type;
+     int     prev_type = 0;
+     int     sndbufsize;
+     int     sndbuffree;
+     SOCKOPT_SIZE optlen = sizeof(sndbufsize);
+     int     mail_from_rejected;
+ 
+     /*
+      * Macros for readability.
+      */
+ #define REWRITE_ADDRESS(addr) do { \
+ 	if (*(addr)) { \
+ 	    quote_821_local(state->scratch, addr); \
+ 	    smtp_unalias_addr(state->scratch2, vstring_str(state->scratch)); \
+ 	    myfree(addr); \
+ 	    addr = mystrdup(vstring_str(state->scratch2)); \
+ 	} \
+     } while (0)
+ 
+ #define RETURN(x) do { vstring_free(next_command); return (x); } while (0)
+ 
+ #define SENDER_IS_AHEAD \
+ 	(recv_state < send_state || recv_rcpt != send_rcpt)
+ 
+ #define SENDER_IN_WAIT_STATE \
+ 	(send_state == SMTP_STATE_DOT || send_state == SMTP_STATE_LAST)
+ 
+     /*
+      * We use SMTP command pipelining if the server said it supported it.
+      * Since we use blocking I/O, RFC 2197 says that we should inspect the
+      * TCP window size and not send more than this amount of information.
+      * Unfortunately this information is not available using the sockets
+      * interface. However, we *can* get the TCP send buffer size on the local
+      * TCP/IP stack. We should be able to fill this buffer without being
+      * blocked, and then the kernel will effectively do non-blocking I/O for
+      * us by automatically writing out the contents of its send buffer while
+      * we are reading in the responses. In addition to TCP buffering we have
+      * to be aware of application-level buffering by the vstream module,
+      * which is limited to a couple kbytes.
+      */
+     if (state->features & SMTP_FEATURE_PIPELINING) {
+ 	if (getsockopt(vstream_fileno(state->session->stream), SOL_SOCKET,
+ 		       SO_SNDBUF, (char *) &sndbufsize, &optlen) < 0)
+ 	    msg_fatal("%s: getsockopt: %m", myname);
+ 	if (msg_verbose)
+ 	    msg_info("Using ESMTP PIPELINING, TCP send buffer size is %d",
+ 		     sndbufsize);
+     } else {
+ 	sndbufsize = 0;
+     }
+     sndbuffree = sndbufsize;
+ 
+     /*
+      * Pipelining support requires two loops: one loop for sending and one
+      * for receiving. Each loop has its own independent state. Most of the
+      * time the sender can run ahead of the receiver by as much as the TCP
+      * send buffer permits. There are only two places where the sender must
+      * wait for status information from the receiver: once after sending DATA
+      * and once after sending QUIT.
+      * 
+      * The sender state advances until the TCP send buffer would overflow, or
+      * until the sender needs status information from the receiver. At that
+      * point the receiver starts processing responses. Once the receiver has
+      * caught up with the sender, the sender resumes sending commands. If the
+      * receiver detects a serious problem (MAIL FROM rejected, all RCPT TO
+      * commands rejected, DATA rejected) it forces the sender to abort the
+      * SMTP dialog with RSET and QUIT.
+      */
+     nrcpt = 0;
+     recv_state = send_state = SMTP_STATE_MAIL;
+     next_rcpt = send_rcpt = recv_rcpt = 0;
+     mail_from_rejected = 0;
+ 
+     while (recv_state != SMTP_STATE_LAST) {
+ 
+ 	/*
+ 	 * Build the next command.
+ 	 */
+ 	switch (send_state) {
+ 
+ 	    /*
+ 	     * Sanity check.
+ 	     */
+ 	default:
+ 	    msg_panic("%s: bad sender state %d", myname, send_state);
+ 
+ 	    /*
+ 	     * Build the MAIL FROM command.
+ 	     */
+ 	case SMTP_STATE_MAIL:
+ 	    if (*request->sender)
+ 		if (var_disable_dns == 0)
+ 		    REWRITE_ADDRESS(request->sender);
+ 	    vstring_sprintf(next_command, "MAIL FROM:<%s>", request->sender);
+ 	    if (state->features & SMTP_FEATURE_SIZE)
+ 		vstring_sprintf_append(next_command, " SIZE=%lu",
+ 				       request->data_size);
+ 	    next_state = SMTP_STATE_RCPT;
+ 	    break;
+ 
+ 	    /*
+ 	     * Build one RCPT TO command before we have seen the MAIL FROM
+ 	     * response.
+ 	     */
+ 	case SMTP_STATE_RCPT:
+ 	    rcpt = request->rcpt_list.info + send_rcpt;
+ 	    if (var_disable_dns == 0)
+ 		REWRITE_ADDRESS(rcpt->address);
+ 	    vstring_sprintf(next_command, "RCPT TO:<%s>", rcpt->address);
+ 	    if ((next_rcpt = send_rcpt + 1) == request->rcpt_list.len)
+ 		next_state = SMTP_STATE_DATA;
+ 	    break;
+ 
+ 	    /*
+ 	     * Build the DATA command before we have seen all the RCPT TO
+ 	     * responses.
+ 	     */
+ 	case SMTP_STATE_DATA:
+ 	    vstring_strcpy(next_command, "DATA");
+ 	    next_state = SMTP_STATE_DOT;
+ 	    break;
+ 
+ 	    /*
+ 	     * Build the "." command before we have seen the DATA response.
+ 	     */
+ 	case SMTP_STATE_DOT:
+ 	    vstring_strcpy(next_command, ".");
+ 	    next_state = SMTP_STATE_QUIT;
+ 	    break;
+ 
+ 	    /*
+ 	     * Can't happen. The SMTP_STATE_ABORT sender state is entered by
+ 	     * the receiver and is left before the bottom of the main loop.
+ 	     */
+ 	case SMTP_STATE_ABORT:
+ 	    msg_panic("%s: sender abort state", myname);
+ 
+ 	    /*
+ 	     * Build the QUIT command before we have seen the "." or RSET
+ 	     * response.
+ 	     */
+ 	case SMTP_STATE_QUIT:
+ 	    vstring_strcpy(next_command, "QUIT");
+ 	    next_state = SMTP_STATE_LAST;
+ 	    break;
+ 
+ 	    /*
+ 	     * The final sender state has no action associated with it.
+ 	     */
+ 	case SMTP_STATE_LAST:
+ 	    break;
+ 	}
+ 	VSTRING_TERMINATE(next_command);
+ 
+ 	/*
+ 	 * Process responses until the receiver has caught up. Vstreams
+ 	 * automatically flush buffered output when reading new data.
+ 	 */
+ 	if (SENDER_IN_WAIT_STATE
+ 	|| (SENDER_IS_AHEAD && VSTRING_LEN(next_command) + 2 > sndbuffree)) {
+ 	    while (SENDER_IS_AHEAD) {
+ 
+ 		/*
+ 		 * Sanity check.
+ 		 */
+ 		if (recv_state < SMTP_STATE_MAIL
+ 		    || recv_state > SMTP_STATE_QUIT)
+ 		    msg_panic("%s: bad receiver state %d (sender state %d)",
+ 			      myname, recv_state, send_state);
+ 
+ 		/*
+ 		 * Receive the next server response. Use the proper timeout,
+ 		 * and log the proper client state in case of trouble.
+ 		 */
+ 		smtp_timeout_setup(state->session->stream,
+ 				   *xfer_timeouts[recv_state]);
+ 		if ((except = setjmp(smtp_timeout_buf)) != 0)
+ 		    RETURN(smtp_stream_except(state, except,
+ 					      xfer_states[recv_state]));
+ 		resp = smtp_chat_resp(state);
+ 
+ 		/*
+ 		 * Process the response.
+ 		 */
+ 		switch (recv_state) {
+ 
+ 		    /*
+ 		     * Process the MAIL FROM response. When the server
+ 		     * rejects the sender, set the mail_from_rejected flag so
+ 		     * that the receiver may apply a course correction.
+ 		     */
+ 		case SMTP_STATE_MAIL:
+ 		    if (resp->code / 100 != 2) {
+ 			smtp_mesg_fail(state, resp->code,
+ 				       "host %s said: %s", session->host,
+ 				       translit(resp->str, "\n", " "));
+ 			mail_from_rejected = 1;
+ 		    }
+ 		    recv_state = SMTP_STATE_RCPT;
+ 		    break;
+ 
+ 		    /*
+ 		     * Process one RCPT TO response. If MAIL FROM was
+ 		     * rejected, ignore RCPT TO responses: all recipients are
+ 		     * dead already. When all recipients are rejected the
+ 		     * receiver may apply a course correction.
+ 		     */
+ 		case SMTP_STATE_RCPT:
+ 		    if (!mail_from_rejected) {
+ 			if (resp->code / 100 == 2) {
+ 			    ++nrcpt;
+ 			} else {
+ 			    rcpt = request->rcpt_list.info + recv_rcpt;
+ 			    smtp_rcpt_fail(state, resp->code, rcpt,
+ 					   "host %s said: %s", session->host,
+ 					   translit(resp->str, "\n", " "));
+ 			    rcpt->offset = 0;	/* in case deferred */
+ 			}
+ 		    }
+ 		    if (++recv_rcpt == request->rcpt_list.len)
+ 			recv_state = SMTP_STATE_DATA;
+ 		    break;
+ 
+ 		    /*
+ 		     * Process the DATA response. When the server rejects
+ 		     * DATA, set nrcpt to a negative value so that the
+ 		     * receiver can apply a course correction.
+ 		     */
+ 		case SMTP_STATE_DATA:
+ 		    if (resp->code / 100 != 3) {
+ 			if (nrcpt > 0)
+ 			    smtp_mesg_fail(state, resp->code,
+ 					   "host %s said: %s", session->host,
+ 					   translit(resp->str, "\n", " "));
+ 			nrcpt = -1;
+ 		    }
+ 		    recv_state = SMTP_STATE_DOT;
+ 		    break;
+ 
+ 		    /*
+ 		     * Process the end of message response. Ignore the
+ 		     * response when no recipient was accepted: all
+ 		     * recipients are dead already, and the next receiver
+ 		     * state is SMTP_STATE_QUIT regardless. Otherwise, if the
+ 		     * message transfer fails, bounce all remaining
+ 		     * recipients, else cross off the recipients that were
+ 		     * delivered.
+ 		     */
+ 		case SMTP_STATE_DOT:
+ 		    if (nrcpt > 0) {
+ 			if (resp->code / 100 != 2) {
+ 			    smtp_mesg_fail(state, resp->code,
+ 					   "host %s said: %s",
+ 					   session->host,
+ 					   translit(resp->str, "\n", " "));
+ 			} else {
+ 			    for (nrcpt = 0; nrcpt < recv_rcpt; nrcpt++) {
+ 				rcpt = request->rcpt_list.info + nrcpt;
+ 				if (rcpt->offset) {
+ 				    sent(request->queue_id, rcpt->address,
+ 					 session->host, request->arrival_time, "%s",
+ 					 resp->str);
+ 				    deliver_completed(state->src, rcpt->offset);
+ 				    rcpt->offset = 0;
+ 				}
+ 			    }
+ 			}
+ 		    }
+ 		    recv_state = (var_skip_quit_resp ?
+ 				  SMTP_STATE_LAST : SMTP_STATE_QUIT);
+ 		    break;
+ 
+ 		    /*
+ 		     * Ignore the RSET response.
+ 		     */
+ 		case SMTP_STATE_ABORT:
+ 		    recv_state = (var_skip_quit_resp ?
+ 				  SMTP_STATE_LAST : SMTP_STATE_QUIT);
+ 		    break;
+ 
+ 		    /*
+ 		     * Ignore the QUIT response.
+ 		     */
+ 		case SMTP_STATE_QUIT:
+ 		    recv_state = SMTP_STATE_LAST;
+ 		    break;
+ 		}
+ 	    }
+ 
+ 	    /*
+ 	     * At this point, the sender and receiver are fully synchronized,
+ 	     * so that the entire TCP send buffer becomes available again.
+ 	     */
+ 	    sndbuffree = sndbufsize;
+ 
+ 	    /*
+ 	     * We know the server response to every command that was sent.
+ 	     * Apply a course correction if necessary: the sender wants to
+ 	     * send RCPT TO but MAIL FROM was rejected; the sender wants to
+ 	     * send DATA but all recipients were rejected; the sender wants
+ 	     * to deliver the message but DATA was rejected.
+ 	     */
+ 	    if ((send_state == SMTP_STATE_RCPT && mail_from_rejected)
+ 		|| (send_state == SMTP_STATE_DATA && nrcpt == 0)
+ 		|| (send_state == SMTP_STATE_DOT && nrcpt < 0)) {
+ 		send_state = recv_state = SMTP_STATE_ABORT;
+ 		send_rcpt = recv_rcpt = 0;
+ 		vstring_strcpy(next_command, "RSET");
+ 		next_state = SMTP_STATE_QUIT;
+ 		next_rcpt = 0;
+ 	    }
+ 	}
+ 
+ 	/*
+ 	 * Make the next sender state the current sender state.
+ 	 */
+ 	if (send_state == SMTP_STATE_LAST)
+ 	    continue;
+ 
+ 	/*
+ 	 * Special case if the server accepted the DATA command. If the
+ 	 * server accepted at least one recipient send the entire message.
+ 	 * Otherwise, just send "." as per RFC 2197.
+ 	 */
+ 	if (send_state == SMTP_STATE_DOT && nrcpt > 0) {
+ 	    smtp_timeout_setup(state->session->stream,
+ 			       var_smtp_data1_tmout);
+ 	    if ((except = setjmp(smtp_timeout_buf)) != 0)
+ 		RETURN(smtp_stream_except(state, except,
+ 					  "sending message body"));
+ 
+ 	    if (vstream_fseek(state->src, request->data_offset, SEEK_SET) < 0)
+ 		msg_fatal("seek queue file: %m");
+ 
+ 	    while ((rec_type = rec_get(state->src, state->scratch, 0)) > 0) {
+ 		if (rec_type != REC_TYPE_NORM && rec_type != REC_TYPE_CONT)
+ 		    break;
+ 		if (prev_type != REC_TYPE_CONT)
+ 		    if (vstring_str(state->scratch)[0] == '.')
+ 			smtp_fputc('.', session->stream);
+ 		if (rec_type == REC_TYPE_CONT)
+ 		    smtp_fwrite(vstring_str(state->scratch),
+ 				VSTRING_LEN(state->scratch),
+ 				session->stream);
+ 		else
+ 		    smtp_fputs(vstring_str(state->scratch),
+ 			       VSTRING_LEN(state->scratch),
+ 			       session->stream);
+ 		prev_type = rec_type;
+ 	    }
+ 
+ 	    if (prev_type == REC_TYPE_CONT)	/* missing newline at end */
+ 		smtp_fputs("", 0, session->stream);
+ 	    if (vstream_ferror(state->src))
+ 		msg_fatal("queue file read error");
+ 	    if (rec_type != REC_TYPE_XTRA)
+ 		RETURN(mark_corrupt(state->src));
+ 	}
+ 
+ 	/*
+ 	 * Copy the next command to the buffer and update the sender state.
+ 	 */
+ 	if (sndbuffree > 0)
+ 	    sndbuffree -= VSTRING_LEN(next_command) + 2;
+ 	smtp_chat_cmd(state, "%s", vstring_str(next_command));
+ 	send_state = next_state;
+ 	send_rcpt = next_rcpt;
+     }
+ 
+     RETURN(0);
+ }
diff -r -c --new-file postfix-19990317-pl04-vanilla/smtp/smtp_session.c postfix-19990317-pl04/smtp/smtp_session.c
*** postfix-19990317-pl04-vanilla/smtp/smtp_session.c	Mon Jan 18 21:11:48 1999
--- postfix-19990317-pl04/smtp/smtp_session.c	Thu Apr 29 17:13:10 1999
***************
*** 41,46 ****
--- 41,48 ----
  #include <mymalloc.h>
  #include <vstream.h>
  
+ #include <pfixtls.h>
+ 
  /* Application-specific. */
  
  #include "smtp.h"
***************
*** 63,68 ****
--- 65,73 ----
  
  void    smtp_session_free(SMTP_SESSION *session)
  {
+ #ifdef HAS_SSL
+     pfixtls_stop_clienttls();
+ #endif
      vstream_fclose(session->stream);
      myfree(session->host);
      myfree(session->addr);
diff -r -c --new-file postfix-19990317-pl04-vanilla/smtpd/Makefile.in postfix-19990317-pl04/smtpd/Makefile.in
*** postfix-19990317-pl04-vanilla/smtpd/Makefile.in	Mon Mar 15 18:49:59 1999
--- postfix-19990317-pl04/smtpd/Makefile.in	Thu Apr 15 15:53:37 1999
***************
*** 111,116 ****
--- 111,117 ----
  smtpd.o: ../include/mail_stream.h
  smtpd.o: ../include/mail_queue.h
  smtpd.o: ../include/mail_server.h
+ smtpd.o: ../include/pfixtls.h
  smtpd.o: smtpd_token.h
  smtpd.o: smtpd.h
  smtpd.o: ../include/argv.h
diff -r -c --new-file postfix-19990317-pl04-vanilla/smtpd/smtpd.c postfix-19990317-pl04/smtpd/smtpd.c
*** postfix-19990317-pl04-vanilla/smtpd/smtpd.c	Sat Apr 10 04:17:18 1999
--- postfix-19990317-pl04/smtpd/smtpd.c	Thu Apr 29 21:03:51 1999
***************
*** 225,230 ****
--- 225,231 ----
  #include <mail_flush.h>
  #include <mail_stream.h>
  #include <mail_queue.h>
+ #include <pfixtls.h>
  
  /* Single-threaded server skeleton. */
  
***************
*** 248,253 ****
--- 249,255 ----
  int     var_smtpd_rcpt_limit;
  int     var_smtpd_tmout;
  char   *var_relay_domains;
+ char   *var_relay_ccerts;
  int     var_smtpd_soft_erlim;
  int     var_smtpd_hard_erlim;
  int     var_queue_minfree;		/* XXX use off_t */
***************
*** 273,278 ****
--- 275,285 ----
  int     var_smtpd_err_sleep;
  int     var_non_fqdn_code;
  char   *var_always_bcc;
+ int     var_smtpd_use_tls;
+ int     var_smtpd_enforce_tls;
+ int     var_smtpd_tls_ask_ccert;
+ int     var_smtpd_tls_req_ccert;
+ int     var_smtpd_tls_ccert_vd;
  
   /*
    * Global state, for stand-alone mode queue file cleanup. When this is
***************
*** 338,343 ****
--- 345,354 ----
      else
  	smtpd_chat_reply(state, "250-SIZE");
      smtpd_chat_reply(state, "250-ETRN");
+ #ifdef HAS_SSL
+     if ((state->tls_use_tls || state->tls_enforce_tls) && (!state->tls_active))
+ 	smtpd_chat_reply(state, "250-STARTTLS");
+ #endif
      smtpd_chat_reply(state, "250 8BITMIME");
      return (0);
  }
***************
*** 864,869 ****
--- 875,943 ----
      return (0);
  }
  
+ static int starttls_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
+ {
+     char   *err;
+ 
+ #ifdef HAS_SSL
+     if (argc != 1) {
+ 	state->error_mask |= MAIL_ERROR_PROTOCOL;
+ 	smtpd_chat_reply(state, "501 Syntax: STARTTLS");
+ 	return (-1);
+     }
+     if (state->tls_active != 0) {
+ 	state->error_mask |= MAIL_ERROR_PROTOCOL;
+ 	smtpd_chat_reply(state, "554 Error: TLS already active");
+ 	return (-1);
+     }
+     if (state->tls_use_tls == 0) {
+ 	state->error_mask |= MAIL_ERROR_PROTOCOL;
+ 	smtpd_chat_reply(state, "502 Error: command not implemented");
+ 	return (-1);
+     }
+     if (!pfixtls_serverengine) {
+ 	smtpd_chat_reply(state, "454 TLS not available due to temporary reason");
+ 	state->error_mask |= MAIL_ERROR_SOFTWARE;
+ 	return (-1);
+     }
+     smtpd_chat_reply(state, "220 Ready to start TLS");
+     vstream_fflush(state->client);
+     if (pfixtls_start_servertls()){
+ 	/*
+          * typically the connection is hanging at this point, so
+          * we should try to shut it down by force! Unfortunately this
+          * problem is not addressed in postfix!
+          */
+ 	return (-1);
+     }
+     state->tls_ccert_subject = pfixtls_ccert_subject;
+     state->tls_ccert_issuer = pfixtls_ccert_issuer;
+     state->tls_ccert_fingerprint = pfixtls_ccert_fingerprint;
+     state->tls_active = 1;
+     helo_reset(state);
+     mail_reset(state);
+     rcpt_reset(state);
+     return (0);
+ #else
+     state->error_mask |= MAIL_ERROR_PROTOCOL;
+     smtpd_chat_reply(state, "502 Error: command not implemented");
+     return (-1);
+ #endif
+ }
+ 
+ static void tls_reset(SMTPD_STATE *state)
+ {
+ #ifdef HAS_SSL
+     vstream_fflush(state->client);
+     if (state->tls_active)
+ 	pfixtls_stop_servertls();
+ #endif
+     state->tls_active = 0;
+     state->tls_ccert_subject = NULL;
+     state->tls_ccert_issuer = NULL;
+     state->tls_ccert_fingerprint = NULL;
+ }
+ 
   /*
    * The table of all SMTP commands that we know.
    */
***************
*** 875,880 ****
--- 949,955 ----
  static SMTPD_CMD smtpd_cmd_table[] = {
      "HELO", helo_cmd,
      "EHLO", ehlo_cmd,
+     "STARTTLS", starttls_cmd,
      "MAIL", mail_cmd,
      "RCPT", rcpt_cmd,
      "DATA", data_cmd,
***************
*** 959,964 ****
--- 1034,1050 ----
  		state->error_count++;
  		continue;
  	    }
+ 	    if (state->tls_enforce_tls &&
+ 		!state->tls_active &&
+ 		cmdp->action != starttls_cmd &&
+ 		cmdp->action != noop_cmd &&
+ 		cmdp->action != ehlo_cmd &&
+ 		cmdp->action != quit_cmd) {
+ 		smtpd_chat_reply(state,
+ 				 "530 Must issue a STARTTLS command first");
+ 		state->error_count++;
+ 		continue;
+ 	    }
  	    state->where = cmdp->name;
  	    if (cmdp->action(state, argc, argv) != 0)
  		state->error_count++;
***************
*** 995,1000 ****
--- 1081,1087 ----
       * Cleanup whatever information the client gave us during the SMTP
       * dialog.
       */
+     tls_reset(state);
      helo_reset(state);
      mail_reset(state);
      rcpt_reset(state);
***************
*** 1024,1029 ****
--- 1111,1119 ----
       * machines.
       */
      peer = peer_name(vstream_fileno(stream));
+ #ifdef HAS_SSL
+     pfixtls_setfd(vstream_fileno(stream));
+ #endif
      smtpd_state_init(&state, stream, peer->name, peer->addr);
  
      /*
***************
*** 1041,1046 ****
--- 1131,1143 ----
  	smtpd_chat_reply(&state, "220 %s", var_smtpd_banner);
  	msg_info("connect from %s[%s]", state.name, state.addr);
      }
+ #ifdef HAS_SSL
+     state.tls_use_tls = var_smtpd_use_tls | var_smtpd_enforce_tls;
+     state.tls_enforce_tls = var_smtpd_enforce_tls;
+ #else
+     state.tls_use_tls = 0;
+     state.tls_enforce_tls = 0;
+ #endif
  
      /*
       * Provide the SMTP service.
***************
*** 1109,1114 ****
--- 1206,1216 ----
      smtpd_check_init();
      debug_peer_init();
      msg_cleanup(smtpd_cleanup);
+ #ifdef HAS_SSL
+     if (var_smtpd_use_tls || var_smtpd_enforce_tls)
+ 	pfixtls_init_serverengine(var_smtpd_tls_ccert_vd,
+ 			  var_smtpd_tls_ask_ccert, var_smtpd_tls_req_ccert);
+ #endif
  }
  
  /* main - the main program */
***************
*** 1132,1145 ****
--- 1234,1253 ----
  	VAR_REJECT_CODE, DEF_REJECT_CODE, &var_reject_code, 0, 0,
  	VAR_SMTPD_ERR_SLEEP, DEF_SMTPD_ERR_SLEEP, &var_smtpd_err_sleep, 0, 0,
  	VAR_NON_FQDN_CODE, DEF_NON_FQDN_CODE, &var_non_fqdn_code, 0, 0,
+ 	VAR_SMTPD_TLS_CCERT_VD, DEF_SMTPD_TLS_CCERT_VD, &var_smtpd_tls_ccert_vd, 0, 0,
  	0,
      };
      static CONFIG_BOOL_TABLE bool_table[] = {
  	VAR_HELO_REQUIRED, DEF_HELO_REQUIRED, &var_helo_required,
+ 	VAR_SMTPD_USE_TLS, DEF_SMTPD_USE_TLS, &var_smtpd_use_tls,
+ 	VAR_SMTPD_ENFORCE_TLS, DEF_SMTPD_ENFORCE_TLS, &var_smtpd_enforce_tls,
+ 	VAR_SMTPD_TLS_ACERT, DEF_SMTPD_TLS_ACERT, &var_smtpd_tls_ask_ccert,
+ 	VAR_SMTPD_TLS_RCERT, DEF_SMTPD_TLS_RCERT, &var_smtpd_tls_req_ccert,
  	0,
      };
      static CONFIG_STR_TABLE str_table[] = {
  	VAR_RELAY_DOMAINS, DEF_RELAY_DOMAINS, &var_relay_domains, 0, 0,
+ 	VAR_RELAY_CCERTS, DEF_RELAY_CCERTS, &var_relay_ccerts, 0, 0,
  	VAR_SMTPD_BANNER, DEF_SMTPD_BANNER, &var_smtpd_banner, 1, 0,
  	VAR_DEBUG_PEER_LIST, DEF_DEBUG_PEER_LIST, &var_debug_peer_list, 0, 0,
  	VAR_NOTIFY_CLASSES, DEF_NOTIFY_CLASSES, &var_notify_classes, 0, 0,
***************
*** 1164,1166 ****
--- 1272,1275 ----
  		       MAIL_SERVER_POST_INIT, post_jail_init,
  		       0);
  }
+ 
diff -r -c --new-file postfix-19990317-pl04-vanilla/smtpd/smtpd.h postfix-19990317-pl04/smtpd/smtpd.h
*** postfix-19990317-pl04-vanilla/smtpd/smtpd.h	Mon Jan 18 19:13:22 1999
--- postfix-19990317-pl04/smtpd/smtpd.h	Thu Apr 29 21:12:33 1999
***************
*** 48,53 ****
--- 48,59 ----
      char   *recipient;
      char   *protocol;
      char   *where;
+     int     tls_active;
+     int     tls_use_tls;
+     int     tls_enforce_tls;
+     char   *tls_ccert_subject;
+     char   *tls_ccert_issuer;
+     char   *tls_ccert_fingerprint;
  } SMTPD_STATE;
  
  extern void smtpd_state_init(SMTPD_STATE *, VSTREAM *,
diff -r -c --new-file postfix-19990317-pl04-vanilla/smtpd/smtpd_check.c postfix-19990317-pl04/smtpd/smtpd_check.c
*** postfix-19990317-pl04-vanilla/smtpd/smtpd_check.c	Tue Mar 16 21:51:38 1999
--- postfix-19990317-pl04/smtpd/smtpd_check.c	Thu Apr 29 21:21:23 1999
***************
*** 241,246 ****
--- 241,247 ----
  
  #include <namadr_list.h>
  #include <domain_list.h>
+ #include <string_list.h>
  #include <mail_params.h>
  #include <canon_addr.h>
  #include <resolve_clnt.h>
***************
*** 278,283 ****
--- 279,287 ----
    */
  static DOMAIN_LIST *relay_domains;
  static NAMADR_LIST *mynetworks;
+ #ifdef HAS_SSL
+ static STRING_LIST *relay_ccerts;
+ #endif
  
   /*
    * Pre-parsed restriction lists.
***************
*** 328,333 ****
--- 332,340 ----
       */
      mynetworks = namadr_list_init(var_mynetworks);
      relay_domains = domain_list_init(var_relay_domains);
+ #ifdef HAS_SSL
+     relay_ccerts = string_list_init(var_relay_ccerts);
+ #endif
  
      /*
       * Reply is used as a cache for resolved addresses, and error_text is
***************
*** 581,586 ****
--- 588,610 ----
      return (SMTPD_CHECK_DUNNO);
  }
  
+ /* check_relay_ccerts - OK/DUNNO for message relaying */
+ 
+ #ifdef HAS_SSL
+ static int check_relay_ccerts(SMTPD_STATE *state)
+ {
+     if (state->tls_ccert_fingerprint) {
+ 	if (string_list_match(relay_ccerts, state->tls_ccert_fingerprint)) {
+ 	    msg_info("Relaying allowed for certified client");
+ 	    return (SMTPD_CHECK_OK);
+ 	} else
+ 	    msg_info("relay_clientcerts: No match for fingerprint '%s'",
+ 		     state->tls_ccert_fingerprint);
+     }
+     return (SMTPD_CHECK_DUNNO);
+ }
+ #endif
+ 
  /* check_relay_domains - OK/FAIL for message relaying */
  
  static int check_relay_domains(SMTPD_STATE *state, char *recipient)
***************
*** 1336,1341 ****
--- 1360,1369 ----
  	    status = permit_mx_backup(state, recipient);
  	} else if (strcasecmp(name, CHECK_RELAY_DOMAINS) == 0) {
  	    status = check_relay_domains(state, recipient);
+ #ifdef HAS_SSL
+ 	} else if (strcasecmp(name, CHECK_RELAY_CCERTS) == 0) {
+ 	    status = check_relay_ccerts(state);
+ #endif
  	} else if (strcasecmp(name, REJECT_NON_FQDN_RCPT) == 0) {
  	    status = reject_non_fqdn_address(state, recipient);
  	} else if (generic_checks(state, name, &cpp, &status, recipient) == 0) {
***************
*** 1444,1449 ****
--- 1472,1478 ----
  char   *var_rcpt_checks = "";
  char   *var_etrn_checks = "";
  char   *var_relay_domains = "";
+ char   *var_relay_ccerts = "";
  char   *var_mynetworks = "";
  char   *var_notify_classes = "";
  
diff -r -c --new-file postfix-19990317-pl04-vanilla/smtpd/smtpd_state.c postfix-19990317-pl04/smtpd/smtpd_state.c
*** postfix-19990317-pl04-vanilla/smtpd/smtpd_state.c	Tue Dec 29 17:51:27 1998
--- postfix-19990317-pl04/smtpd/smtpd_state.c	Thu Apr 15 15:53:37 1999
***************
*** 96,101 ****
--- 96,107 ----
      state->recipient = 0;
      state->protocol = "SMTP";
      state->where = SMTPD_AFTER_CONNECT;
+     state->tls_active = 0;
+     state->tls_use_tls = 0;
+     state->tls_enforce_tls = 0;
+     state->tls_ccert_subject = NULL;
+     state->tls_ccert_issuer = NULL;
+     state->tls_ccert_fingerprint = NULL;
  
      /*
       * Initialize the conversation history.
