diff -r -c --new-file postfix-19990906-pl07-vanilla/conf/postfix-script-nosgid postfix-19990906-pl07/conf/postfix-script-nosgid
*** postfix-19990906-pl07-vanilla/conf/postfix-script-nosgid	Mon Jul  5 01:08:29 1999
--- postfix-19990906-pl07/conf/postfix-script-nosgid	Mon Nov 15 15:31:04 1999
***************
*** 207,212 ****
--- 207,224 ----
  		chmod 700 private
  		chown $mail_owner private
  	}
+ 	test -d TLSsrvrcache || {
+ 		$WARN creating missing Postfix TLSsrvrcache directory
+ 		mkdir TLSsrvrcache || exit 1
+ 		chmod 700 TLSsrvrcache
+ 		chown $mail_owner TLSsrvrcache
+ 	}
+ 	test -d TLSclntcache || {
+ 		$WARN creating missing Postfix TLSclntcache directory
+ 		mkdir TLSclntcache || exit 1
+ 		chmod 700 TLSclntcache
+ 		chown $mail_owner TLSclntcache
+ 	}
  	find `ls -d $queue_directory/* | \
  	    egrep '/(incoming|active|defer|deferred|bounce|saved|corrupt|public|private)$'` \
  	    ! \( -type p -o -type s \) ! -user $mail_owner \
diff -r -c --new-file postfix-19990906-pl07-vanilla/conf/postfix-script-sgid postfix-19990906-pl07/conf/postfix-script-sgid
*** postfix-19990906-pl07-vanilla/conf/postfix-script-sgid	Mon Jul  5 01:08:23 1999
--- postfix-19990906-pl07/conf/postfix-script-sgid	Mon Nov 15 15:31:04 1999
***************
*** 208,213 ****
--- 208,225 ----
  		chmod 700 private
  		chown $mail_owner private
  	}
+ 	test -d TLSsrvrcache || {
+ 		$WARN creating missing Postfix TLSsrvrcache directory
+ 		mkdir TLSsrvrcache || exit 1
+ 		chmod 700 TLSsrvrcache
+ 		chown $mail_owner TLSsrvrcache
+ 	}
+ 	test -d TLSclntcache || {
+ 		$WARN creating missing Postfix TLSclntcache directory
+ 		mkdir TLSclntcache || exit 1
+ 		chmod 700 TLSclntcache
+ 		chown $mail_owner TLSclntcache
+ 	}
  	find `ls -d $queue_directory/* | \
  	    egrep '/(incoming|active|defer|deferred|bounce|saved|corrupt|public|private)$'` \
  	    ! \( -type p -o -type s \) ! -user $mail_owner \
diff -r -c --new-file postfix-19990906-pl07-vanilla/conf/sample-tls.cf postfix-19990906-pl07/conf/sample-tls.cf
*** postfix-19990906-pl07-vanilla/conf/sample-tls.cf	Thu Jan  1 01:00:00 1970
--- postfix-19990906-pl07/conf/sample-tls.cf	Mon Nov 15 15:32:14 1999
***************
*** 0 ****
--- 1,315 ----
+ # DO NOT EDIT THIS FILE. EDIT THE MAIN.CF FILE INSTEAD. THE STUFF
+ # HERE JUST SERVES AS AN EXAMPLE.
+ #
+ # 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
+ 
+ # To include information about the protocol and cipher used as well as the
+ # client and issuer CommonName into the "Received:" header, set the
+ # smtpd_tls_received_header variable to true. The default is no, as the
+ # information is not necessarily authentic. Only the final destination
+ # is reliable, since the headers might have been changed in between.
+ #
+ #smtpd_tls_received_header = yes
+ 
+ # 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
+ 
+ # The server and client negotiate a session, which takes some computer time
+ # and network bandwidth. To session is cached only in the smtpd process
+ # actually using this session and is lost when the process dies.
+ # To share the session information between the smtpd processes, a disc based
+ # session cache can be used, that is enabled by default.
+ #
+ # smtpd_tls_use_session_cache = yes
+ 
+ # The cached sessions time out after a certain amount of time. For Postfix/TLS
+ # I do not use the OpenSSL default of 300sec, but a longer time of 3600sec
+ # (=1 hour). RFC2246 recommends a maximum of 24 hours.
+ #
+ # smtpd_tls_session_cache_timeout = 3600
+ 
+ # 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
+ 
+ # The server and client negotiate a session, which takes some computer time
+ # and network bandwidth. To session is cached only in the smtp process
+ # actually using this session and is lost when the process dies.
+ # To share the session information between the smtp processes, a disc based
+ # session cache can be used, that is enabled by default.
+ #
+ # smtp_tls_use_session_cache = yes
+ 
+ # The cached sessions time out after a certain amount of time. For Postfix/TLS
+ # I do not use the OpenSSL default of 300sec, but a longer time of 3600sec
+ # (=1 hour). RFC2246 recommends a maximum of 24 hours.
+ #
+ # smtp_tls_session_cache_timeout = 3600
+ 
+ # 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.
+ # WARNING: I didn't have access to other software (except those explicitely
+ # listed) to test the interaction. On corresponding mailing list
+ # there was a discussion going on about MS exchange servers offering
+ # STARTTLS even if it is not configured, so it might be wise to not
+ # use this option on your central mail hub, as you don't know in advance
+ # whether you are going to hit such host. Use the recipient/site specific
+ # options instead.
+ #
+ # In case of failure, a "4xx" code is issued and the mail stays in the queue.
+ #
+ # Explicitely switch it on here, if you really wish it:
+ #
+ smtp_use_tls = yes
+ 
+ # As generally trying TLS can be a bad idea (some hosts offer STARTTLS but
+ # the negotiation will fail leading to unexplainable failures, it is a better
+ # idea to decide based on the recipient or the mailhub to which you are
+ # connecting.
+ #
+ # Recipient: The host/domain part of the recipients email address:
+ # for "joe@dom.ain" the "dom.ain" is used for the 'recipient' matching.
+ #
+ # To try to use TLS whenever email is sent to somebody matching the recipient
+ # domain "dom.ain", add it to the "use_tls_recipients" list. The STARTTLS
+ # option is recognized and used regardless of the MX to which the smtp client
+ # does connect.
+ #
+ smtp_use_tls_recipients = dom.ain, hash:/etc/postfix/use_tls_recipients
+ #
+ # Site: The name of the host the email is actually delivered to.
+ #
+ # To try to use TLS whenever a certain MX is used to deliver an email,
+ # add it to the "use_tls_sites" list:
+ #
+ smtp_use_tls_sites = mailhost.some.where, some.where.else, hash:/etc/postfix/use_tls_sites
+ #
+ # The matching is done as a domain match, such that "some.where.else" will
+ # match all hosts in the domain "some.where.else", e.g.
+ # mailhost.some.where.else. For this reason, this option is named .._sites
+ # and not .._hosts.
+ #
+ # These options will only use STARTTLS if it is offered. If it is not offered,
+ # the mail will be delivered without encryption or authentication!! See also
+ # the "enforce" options below.
+ 
+ # You can ENFORCE the use of TLS, so that only connections with TLS will
+ # be accepted. Additionally, the hostname of the receiving host is matched
+ # against the CommonName in the certificate. Also, the certificate must
+ # be verified "Ok", so that a CA trusted by the client must have issued
+ # the certificate. If the certificate doesn't verify or the hostname doesn't
+ # match, a "4xx" # will be issued and the mail stays in the queue.
+ # The hostname used in the check is beyond question, as it must be the
+ # principle hostname (no CNAME allowed here).
+ #
+ # This option is useful only if you are definitely sure that you will only
+ # connect to servers supporting RFC2487 _and_ with valid certificates.
+ # I use it for my clients which will only send email to one mailhub, which
+ # does offer the necessary STARTTLS support.
+ #
+ # smtp_enforce_tls = no
+ 
+ # For a per_site or per_recipient decision, use the corresponding lists.
+ #
+ # Recipient: The host/domain part of the recipients email address:
+ # for "joe@dom.ain" the "dom.ain" is used for the 'recipient' matching.
+ #
+ # To enforce the use of TLS whenever email is sent to somebody matching the
+ # recipient domain "dom.ain", add it to the "enforce_tls_recipients" list.
+ # This way the email is only sent with encryption and authentication,
+ # regardless of the MX to which the smtp client does connect. This way you
+ # can assure that your email really is delivered to the correct MX; the
+ # correctness of the MX nameserver record cannot be checked from postfix,
+ # however.
+ # Please note, that the authentication process requires that you recognize
+ # the CA that issued the server certificate!
+ #
+ smtp_enforce_tls_recipients = dom.ain, hash:/etc/postfix/enforce_tls_recipients
+ #
+ # Site: The name of the host the email is actually delivered to.
+ #
+ # To enforce the use of TLS whenever a certain MX is used to deliver an email,
+ # add it to the "enforce_tls_sites" list:
+ #
+ smtp_enforce_tls_sites = mailhost.some.where, some.where.else, hash:/etc/postfix/enforce_tls_sites
+ #
+ # The matching is done as a domain match, such that "some.where.else" will
+ # match all hosts in the domain "some.where.else", e.g.
+ # mailhost.some.where.else. For this reason, this option is named .._sites
+ # and not .._hosts.
+ 
+ # In case the client use of TLS is explicitely turned on, a list of
+ # sites/hosts can be given for which no STARTTLS negotiation will be
+ # started, e.g. because it is already known that it might fail for some
+ # reason. It makes no sense for recipients, only receiving mail servers,
+ # so only the "smtp_no_tls_sites" is offered, but not an equivalent
+ # ".._recipients" option.
+ #
+ # HINT: This list is only checked when TLS usage is generally turned on
+ # (usage or enforcement). If the recieving host/recipient is listed by the
+ # specific "use/enforce_tls_sites/recipients" options, this setting has
+ # precedence in any case and STARTTLS will be negotiated. No warning is given.
+ #
+ smtp_no_tls_sites = mailhost.some.where, some.where.else, hash:/etc/postfix/no_tls_sites
+ 
+ # 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
+ 
+ # As we decide on a "per site" basis, wether to use TLS or not, it would be
+ # good to have a list of sites, that offered "STARTTLS'. We can collect it
+ # ourselves with this option.
+ #
+ # If activated and TLS is not already enabled for this host, a line is added
+ # to the logfile:
+ # postfix/smtp[pid]: Host offered STARTTLS: [name.of.host]
+ #
+ smtp_tls_note_starttls_offer = yes
+ 
+ # As the session cache should use "directory forest" for performance reasons,
+ # the default value for "hash_queue_names" has been extended. Donīt forget
+ # to add the new directory names "TLSsrvrcache, TLSclntcache", it you have
+ # changed the default.
+ #
+ # hash_queue_names = defer, TLSsrvrcache, TLSclntcache
diff -r -c --new-file postfix-19990906-pl07-vanilla/global/Makefile.in postfix-19990906-pl07/global/Makefile.in
*** postfix-19990906-pl07-vanilla/global/Makefile.in	Sun Nov  7 18:43:02 1999
--- postfix-19990906-pl07/global/Makefile.in	Mon Nov 15 15:31:05 1999
***************
*** 17,23 ****
  	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 ext_prop.c \
! 	dot_lockfile_as.c
  OBJS	= been_here.o bounce.o canon_addr.o cleanup_strerror.o clnt_stream.o \
  	debug_peer.o debug_process.o defer.o deliver_completed.o \
  	deliver_flock.o deliver_pass.o deliver_request.o domain_list.o \
--- 17,23 ----
  	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 ext_prop.c \
! 	dot_lockfile_as.c pfixtls.c
  OBJS	= been_here.o bounce.o canon_addr.o cleanup_strerror.o clnt_stream.o \
  	debug_peer.o debug_process.o defer.o deliver_completed.o \
  	deliver_flock.o deliver_pass.o deliver_request.o domain_list.o \
***************
*** 36,42 ****
  	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 ext_prop.o \
! 	dot_lockfile_as.o
  HDRS	= been_here.h bounce.h canon_addr.h cleanup_user.h clnt_stream.h \
  	config.h debug_peer.h debug_process.h defer.h deliver_completed.h \
  	deliver_flock.h deliver_pass.h deliver_request.h domain_list.h \
--- 36,42 ----
  	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 ext_prop.o \
! 	dot_lockfile_as.o pfixtls.o
  HDRS	= been_here.h bounce.h canon_addr.h cleanup_user.h clnt_stream.h \
  	config.h debug_peer.h debug_process.h defer.h deliver_completed.h \
  	deliver_flock.h deliver_pass.h deliver_request.h domain_list.h \
***************
*** 50,56 ****
  	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 ext_prop.h dot_lockfile_as.h
  TESTSRC	= rec2stream.c stream2rec.c recdump.c
  WARN	= -W -Wformat -Wimplicit -Wmissing-prototypes \
  	-Wparentheses -Wstrict-prototypes -Wswitch -Wuninitialized \
--- 50,56 ----
  	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 ext_prop.h dot_lockfile_as.h pfixtls.h
  TESTSRC	= rec2stream.c stream2rec.c recdump.c
  WARN	= -W -Wformat -Wimplicit -Wmissing-prototypes \
  	-Wparentheses -Wstrict-prototypes -Wswitch -Wuninitialized \
***************
*** 821,826 ****
--- 821,827 ----
  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
***************
*** 895,897 ****
--- 896,907 ----
  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: mail_queue.h
+ pfixtls.o: pfixtls.h
+ pfixtls.o: ../include/sys_defs.h
+ pfixtls.o: ../include/iostuff.h
+ pfixtls.o: ../include/mymalloc.h
+ pfixtls.o: ../include/vstring.h
+ pfixtls.o: ../include/vstream.h
diff -r -c --new-file postfix-19990906-pl07-vanilla/global/mail_params.c postfix-19990906-pl07/global/mail_params.c
*** postfix-19990906-pl07-vanilla/global/mail_params.c	Sun Sep  5 02:14:36 1999
--- postfix-19990906-pl07/global/mail_params.c	Mon Nov 15 15:31:05 1999
***************
*** 161,166 ****
--- 161,184 ----
  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;
+ int     var_smtpd_tls_use_scache;
+ int     var_smtpd_tls_scache_timeout;
+ 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;
+ int     var_smtp_tls_use_scache;
+ int     var_smtp_tls_scache_timeout;
  
  /* check_myhostname - lookup hostname and validate */
  
***************
*** 261,266 ****
--- 279,296 ----
  	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[] = {
***************
*** 282,287 ****
--- 312,321 ----
  	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_SMTPD_TLS_SCACHTIME, DEF_SMTPD_TLS_SCACHTIME, &var_smtpd_tls_scache_timeout, 0, 0,
+ 	VAR_SMTP_TLS_LOGLEVEL, DEF_SMTP_TLS_LOGLEVEL, &var_smtp_tls_loglevel, 0, 0,
+ 	VAR_SMTP_TLS_SCACHTIME, DEF_SMTP_TLS_SCACHTIME, &var_smtp_tls_scache_timeout, 0, 0,
  	VAR_DAEMON_TIMEOUT, DEF_DAEMON_TIMEOUT, &var_daemon_timeout, 1, 0,
  	0,
      };
***************
*** 290,295 ****
--- 324,331 ----
  	VAR_SOFT_BOUNCE, DEF_SOFT_BOUNCE, &var_soft_bounce,
  	VAR_OWNREQ_SPECIAL, DEF_OWNREQ_SPECIAL, &var_ownreq_special,
  	VAR_MAILTOOL_COMPAT, DEF_MAILTOOL_COMPAT, &var_mailtool_compat,
+ 	VAR_SMTPD_TLS_SCACHE, DEF_SMTPD_TLS_SCACHE, &var_smtpd_tls_use_scache,
+ 	VAR_SMTP_TLS_SCACHE, DEF_SMTP_TLS_SCACHE, &var_smtp_tls_use_scache,
  	0,
      };
  
diff -r -c --new-file postfix-19990906-pl07-vanilla/global/mail_params.h postfix-19990906-pl07/global/mail_params.h
*** postfix-19990906-pl07-vanilla/global/mail_params.h	Sun Sep  5 23:27:00 1999
--- postfix-19990906-pl07/global/mail_params.h	Mon Nov 15 15:31:05 1999
***************
*** 497,503 ****
    * subdirectories, and how deep the forest is.
    */
  #define VAR_HASH_QUEUE_NAMES	"hash_queue_names"
! #define DEF_HASH_QUEUE_NAMES	"defer"
  extern char *var_hash_queue_names;
  
  #define VAR_HASH_QUEUE_DEPTH	"hash_queue_depth"
--- 497,503 ----
    * subdirectories, and how deep the forest is.
    */
  #define VAR_HASH_QUEUE_NAMES	"hash_queue_names"
! #define DEF_HASH_QUEUE_NAMES	"defer, TLSsrvrcache, TLSclntcache"
  extern char *var_hash_queue_names;
  
  #define VAR_HASH_QUEUE_DEPTH	"hash_queue_depth"
***************
*** 587,592 ****
--- 587,724 ----
  #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_SMTPD_TLS_RECHEAD	"smtpd_tls_received_header"
+ #define DEF_SMTPD_TLS_RECHEAD	0
+ extern bool var_smtpd_tls_received_header;
+ 
+ #define VAR_SMTPD_TLS_SCACHE	"smtpd_tls_use_session_cache"
+ #define DEF_SMTPD_TLS_SCACHE	1
+ extern bool var_smtpd_tls_use_scache;
+ 
+ #define VAR_SMTPD_TLS_SCACHTIME	"smtpd_tls_session_cache_timeout"
+ #define DEF_SMTPD_TLS_SCACHTIME	3600
+ extern int var_smtpd_tls_scache_timeout;
+ 
+ #define VAR_SMTP_NO_TLS_SITES	"smtp_no_tls_sites"
+ #define DEF_SMTP_NO_TLS_SITES	""
+ extern char *var_smtp_no_tls_sites;
+ 
+ #define VAR_SMTP_USE_TLS	"smtp_use_tls"
+ #define DEF_SMTP_USE_TLS	0
+ extern bool var_smtp_use_tls;
+ 
+ #define VAR_SMTP_USE_TLS_SITES	"smtp_use_tls_sites"
+ #define DEF_SMTP_USE_TLS_SITES	""
+ extern char *var_smtp_use_tls_sites;
+ 
+ #define VAR_SMTP_USE_TLS_RCPTS	"smtp_use_tls_recipients"
+ #define DEF_SMTP_USE_TLS_RCPTS	""
+ extern char *var_smtp_use_tls_recipients;
+ 
+ #define VAR_SMTP_ENFORCE_TLS	"smtp_enforce_tls"
+ #define DEF_SMTP_ENFORCE_TLS	0
+ extern bool var_smtp_enforce_tls;
+ 
+ #define VAR_SMTP_ENF_TLS_SITES	"smtp_enforce_tls_sites"
+ #define DEF_SMTP_ENF_TLS_SITES	""
+ extern char *var_smtp_enforce_tls_sites;
+ 
+ #define VAR_SMTP_ENF_TLS_RCPTS	"smtp_enforce_tls_recipients"
+ #define DEF_SMTP_ENF_TLS_RCPTS	""
+ extern char *var_smtp_enforce_tls_recipients;
+ 
+ #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;
+ 
+ #define VAR_SMTP_TLS_NOTEOFFER	"smtp_tls_note_starttls_offer"
+ #define DEF_SMTP_TLS_NOTEOFFER	0
+ extern bool var_smtp_tls_note_starttls_offer;
+ 
+ #define VAR_SMTP_TLS_SCACHE	"smtp_tls_use_session_cache"
+ #define DEF_SMTP_TLS_SCACHE	1
+ extern bool var_smtp_tls_use_scache;
+ 
+ #define VAR_SMTP_TLS_SCACHTIME	"smtp_tls_session_cache_timeout"
+ #define DEF_SMTP_TLS_SCACHTIME	3600
+ extern int var_smtp_tls_scache_timeout;
+ 
   /*
    * Cleanup service. Header info that exceeds $header_size_limit bytes forces
    * the start of the message body.
***************
*** 694,699 ****
--- 826,835 ----
  #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;
***************
*** 764,769 ****
--- 900,906 ----
  
  #define REJECT_UNAUTH_DEST	"reject_unauth_destination"
  #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		554
  extern int var_relay_code;
diff -r -c --new-file postfix-19990906-pl07-vanilla/global/mail_queue.h postfix-19990906-pl07/global/mail_queue.h
*** postfix-19990906-pl07-vanilla/global/mail_queue.h	Tue Feb 16 15:11:18 1999
--- postfix-19990906-pl07/global/mail_queue.h	Mon Nov 15 15:31:05 1999
***************
*** 27,32 ****
--- 27,34 ----
  #define MAIL_QUEUE_DEFER	"defer"
  #define MAIL_QUEUE_BOUNCE	"bounce"
  #define MAIL_QUEUE_CORRUPT	"corrupt"
+ #define MAIL_TLS_SRVR_CACHE	"TLSsrvrcache"
+ #define MAIL_TLS_CLNT_CACHE	"TLSclntcache"
  
   /*
    * Queue file modes.
diff -r -c --new-file postfix-19990906-pl07-vanilla/global/pfixtls.c postfix-19990906-pl07/global/pfixtls.c
*** postfix-19990906-pl07-vanilla/global/pfixtls.c	Thu Jan  1 01:00:00 1970
--- postfix-19990906-pl07/global/pfixtls.c	Mon Nov 15 15:31:05 1999
***************
*** 0 ****
--- 1,1800 ----
+ /*++
+ /* NAME
+ /*	pfixtls
+ /* SUMMARY
+ /*	interface to openssl routines
+ /* SYNOPSIS
+ /*	#include <pfixtls.h>
+ /*
+ /*	int pfixtls_serverengine;
+ /*	int pfixtls_serveractive;
+ /*	int pfixtls_peer_verified;
+ /*	char *pfixtls_peer_subject;
+ /*	char *pfixtls_peer_issuer;
+ /*	char *pfixtls_peer_fingerprint;
+ /*
+ /*	int pfixtls_clientengine;
+ /*	int pfixtls_clientactive;
+ /*
+ /*	char *pfixtls_peer_CN;
+ /*	char *pfixtls_issuer_CN;
+ /*	char *pfixtls_protocol;
+ /*	const char *pfixtls_cipher_name;
+ /*	int pfixtls_cipher_usebits;
+ /*	int pfixtls_cipher_algbits;
+ /*
+ /*	int pfixtls_read(fd, buf, count)
+ /*	int fd;
+ /*	void *buf;
+ /*	size_t count;
+ /*
+ /*	int pfixtls_write(fd, buf, count);
+ /*	int fd;
+ /*	void *buf;
+ /*	size_t count;
+ /*
+ /*	int pfixtls_read_wait(fd, timeout);
+ /*	int fd, timeout;
+ /*
+ /*	int pfixtls_write_wait(fd, timeout);
+ /*	int fd, timeout;
+ /*
+ /*	void pfixtls_setfd(fd);
+ /*	int fd;
+ /*	
+ /*	int pfixtls_init_serverengine(verifydepth, askcert, requirecert);
+ /*	int verifydepth, askcert, requirecert;
+ /*
+ /*	int pfixtls_start_servertls(void);
+ /*
+ /*	int pfixtls_stop_servertls(void);
+ /*	
+ /*	int pfixtls_init_clientengine(verifydepth);
+ /*	int verifydepth;
+ /*
+ /*	int pfixtls_start_clienttls(void);
+ /*
+ /*	int pfixtls_stop_clienttls(void);
+ /*
+ /* DESCRIPTION
+ /*	This module is the interface between Postfix and the OpenSSL library.
+ /*	As of now only one filedescriptor can be handled, so only one
+ /*	TLS channel can be open at a time. This can be accpted, as smtpd
+ /*	and smtp are seperate services run by seperate programs.
+ /*
+ /*	pfixtls_setfd() sets the filedescriptor to which TLS should be
+ /*	applied later.
+ /*
+ /*	pfixtls_read() reads the requested number of bytes either directly
+ /*	from the input fildescriptor or uses SSL_read(), if TLS is enabled for
+ /*	this descriptor. Unlike read() normally used here, blocking mode
+ /*	is used with TLS enabled.
+ /*	pfixtls_write() is the corresponding write function.
+ /*
+ /*	pfixtls_read_wait() is a wrapper for the read_wait() function.
+ /*	Normally, read_wait() is called. If TLS is enabled for the fd,
+ /*	we are in blocking mode. We must not use select(), as there might
+ /*	be bytes pending in read and write buffers, since a renegotiation
+ /*	(with bidirectional data exchange) might be going on at any time!
+ /*	pfixtls_write_wait() is the corresponding function.
+ /*
+ /*	pfixtls_init_serverengine() is called once when smtpd is started
+ /*	in order to initialize as much of the TLS stuff as possible.
+ /*	The certificate handling is also decided during the setup phase,
+ /*	so that a peer specific handling is not possible.
+ /*
+ /*	pfixtls_init_clientengine() is the corresponding function called
+ /*	in smtp. Here we take the peer's (server's) certificate in any
+ /*	case.
+ /*
+ /*	pfixtls_start_servertls() activates the TLS feature for the
+ /*	filedescriptor selected with pfixtls_setfd() before. We expect
+ /*	that all buffers are flushed and the TLS handshake can begin
+ /*	immediately.
+ /*
+ /*	pfixtls_stop_servertls() sends the "close notify" alert via
+ /*	SSL_shutdown() to the peer and resets all connection specific
+ /*	TLS data. As RFC2487 does not specify a seperate shutdown, it
+ /*	is supposed that the underlying TCP connection is shut down
+ /*	immediately afterwards, so we don't care about additional data
+ /*	coming through the channel.
+ /*
+ /*	pfixtls_start_clienttls() and pfixtls_stop_clienttls() are the
+ /*	corresponding functions for smtp.
+ /*
+ /*	Once the TLS connection is initiated, information about the TLS
+ /*	state is available:
+ /*	pfixtls_protocol holds the protocol name (SSLv2, SSLv3, TLSv1),
+ /*	pfixtls_cipher_name the cipher name (e.g. RC4/MD5),
+ /*	pfixtls_cipher_usebits the number of bits actually used (e.g. 40),
+ /*	pfixtls_cipher_algbits the number of bits the algorithm is based on
+ /*	(e.g. 128).
+ /*	The last two values may be different when talking to a crippled
+ /*	- ahem - export controled peer (e.g. 40/128).
+ /*
+ /*	The status of the peer certificate verification is available in
+ /*	pfixtls_peer_verified. It is set to 1, when the certificate could
+ /*	be verified.
+ /*	If the peer offered a certifcate, part of the certificate data are
+ /*	available as:
+ /*	pfixtls_peer_subject X509v3-oneline with the DN of the peer
+ /*	pfixlts_peer_CN extracted CommonName of the peer
+ /*	pfixtls_peer_issuer  X509v3-oneline with the DN of the issuer
+ /*	pfixlts_peer_CN extracted CommonName of the issuer
+ /*	pfixtls_peer_fingerprint fingerprint of the certificate
+ /*
+ /* DESCRIPTION (SESSION CACHING)
+ /*	In order to achieve high performance when using a lot of connections
+ /*	with TLS, session caching is implemented. It reduces both the CPU load
+ /*	(less cryptograpic operations) and the network load (the amount of
+ /*	certificate data exchanged is reduced).
+ /*	Since postfix uses a setup of independent processes for receiving
+ /*	and sending email, the processes must exchange the session information.
+ /*	Several connections at the same time between the identical peers can
+ /*	occur, so uniqueness and race conditions have to be taken into
+ /*	account.
+ /*	I have checked both Apache-SSL (Ben Laurie), using a seperate "gcache"
+ /*	process and Apache mod_ssl (Ralf S. Engelshall), using shared memory
+ /*	between several identical processes spawned from one parent.
+ /*	They did look quite complicated and seem to require a lot of internal
+ /*	knowledge of the OpenSSL library, but well, both authors are members
+ /*	of the OpenSSL core team. Additionally, the models only take care
+ /*	of the server side.
+ /*	Hence I decided to realize my own disk based model, leaving out the
+ /*	database approach, since that would have complicated things a lot.
+ /*
+ /*	Realization:
+ /*	Both (client and server) session cache are realized by individual
+ /*	cache files for each session. The files are identified by their
+ /*	names consisting of "cleartext" ASCII representation.
+ /*
+ /*	Server side:
+ /*	Session created by OpenSSL have a 32 byte session id, yielding a
+ /*	64 char file name. I consider these sessions to be unique. If they
+ /*	are not, the first session on disk will win, a second session will
+ /*	just not be saved. Remember: everything that is lost is a temporary
+ /*	information and not more than a renegotiation will happen.
+ /*	Originating from the same client host, several sessions can come
+ /*	in (e.g. from several users sending mail with Netscape at the same
+ /*	time), so the session id is the correct identifier; the hostname
+ /*	is of no importance, here.
+ /*
+ /*	Client side:
+ /*	We cannot recall sessions based on their session id, because we would
+ /*	have to check every session on disk for a matching server name, so
+ /*	the lookup has to be done based on the FQDN of the peer (receiving
+ /*	host). Since hostnames can become very long and also the distribution
+ /*	of hostnames might be poorly balanced in the hashed directory forest
+ /*	(see below), since a lot of peers might be called "mail.xxx", I
+ /*	decided to use a fix length hash instead. The MD5 seems to be well
+ /*	suited, especially since it is already part of the OpenSSL library,
+ /*	so just one call is needed :-)
+ /*	With regard to uniqueness, we might experience several open connections
+ /*	to the same server at the same time. This is even very likely to
+ /*	happen, since we might have several mails for the same destination
+ /*	in the queue, when a queue run is started. So several smtpīs might
+ /*	negotiate sessions at the same time. We can however only save one
+ /*	session for one host.
+ /*	Unlike on the server side, here the "last write" wins. The reason is
+ /*	quite simple. If we donīt want to overwrite old sessions, an old
+ /*	session file will just stay in place until it is expired. In the
+ /*	meantime we would lose "fresh" session however. So we will keep the
+ /*	fresh one instead to avoid unnecessary renegotiations.
+ /*
+ /*	Session lifetime:
+ /*	RFC2246 recommends a session lifetime of less than 24 hours. The
+ /*	default is 300 seconds (5 minutes) for OpenSSL and is also used
+ /*	this way in e.g. mod_ssl. The typical usage for emails might be
+ /*	humans typing in emails and sending them, which might take just
+ /*	a while, so I think 3600 seconds (1 hour) is a good compromise.
+ /*	If the environment is save (the cached session contains secret
+ /*	key data), one might even consider using a longer timeout. Anyway,
+ /*	since everlasting sessions must be avoided, the session timeout
+ /*	is done based on the creation date of the session and so each
+ /*	session will timeout eventually.
+ /*
+ /*	Connection failures:
+ /*	RFC2246 requires us to remove sessions if something went wrong.
+ /*	Since the in-memory session cache of other smtp[d] processes cannot
+ /*	be controlled by simple means, we completely rely on the disc
+ /*	based session caching and remove all sessions from memory after
+ /*	connection closure.connection closure.
+ /*
+ /*	Cache cleanup:
+ /*	Since old entries have to be removed from the session cache, a
+ /*	cleanup process is needed that runs through the collected session
+ /*	files on regular basis. This is what qmgr does for the deferred
+ /*	emails, so qmgr is the best fit here. qmgr will go through the
+ /*	files and delete outdated files, based on the "mtime" of the file.
+ /*
+ /* BUGS
+ /*	The memory allocation policy of the OpenSSL library is not well
+ /*	documented, especially when loading sessions from disc. Hence there
+ /*	might be memory leaks.
+ /*
+ /* LICENSE
+ /* AUTHOR(S)
+ /*	Lutz Jaenicke
+ /*	BTU Cottbus
+ /*	Allgemeine Elektrotechnik
+ /*	Universitaetsplatz 3-4
+ /*	D-03044 Cottbus, Germany
+ /*--*/
+ 
+ #ifdef HAS_SSL
+ 
+ /* System library. */
+ 
+ #include <sys_defs.h>
+ #include <sys/types.h>
+ #include <sys/stat.h>
+ #include <unistd.h>
+ #include <stdio.h>
+ #include <string.h>
+ 
+ /* OpenSSL library. */
+ 
+ #include <openssl/lhash.h>
+ #include <openssl/bn.h>
+ #include <openssl/err.h>
+ #include <openssl/pem.h>
+ #include <openssl/x509.h>
+ #include <openssl/ssl.h>
+ #include <openssl/md5.h>
+ 
+ /* Utility library. */
+ 
+ #include <iostuff.h>
+ #include <mymalloc.h>
+ #include <vstring.h>
+ #include <vstream.h>
+ 
+ /* Application-specific. */
+ 
+ #include "mail_params.h"
+ #include "mail_queue.h"
+ #include "pfixtls.h"
+ 
+ #define STR	vstring_str
+ 
+ /* We must keep some of the info available */
+ static const char hexcodes[] = "0123456789ABCDEF";
+ 
+ /*
+  * We saving sessions to disc, we want to make sure, that the lenght of the
+  * filename is somehow limited. When saving client sessions, the hostname
+  * is transformed to an MD5-hash, which is defined by RFC to be 16 bytes long.
+  * The length of the actual session id is however not defined in RFC2246.
+  * OpenSSL defines a SSL_MAX_SSL_SESSION_ID_LENGTH of 32, but nobody
+  * guarantees, that a client might not try to resume a session with a longer
+  * session id. So to make sure, we define an upper bound of 32.
+  */
+ static const int id_maxlength = 32;	/* Max ID length in bytes */
+ 
+ static char server_session_id_context[] = "Postfix/TLS"; /* anything will do */
+ 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 peer_subject[CCERT_BUFSIZ];
+ static char peer_issuer[CCERT_BUFSIZ];
+ static char peer_CN[CCERT_BUFSIZ];
+ static char issuer_CN[CCERT_BUFSIZ];
+ static unsigned char md[EVP_MAX_MD_SIZE];
+ static char fingerprint[EVP_MAX_MD_SIZE * 3];
+ static unsigned char peername_md5[MD5_DIGEST_LENGTH];
+ 
+ int     pfixtls_serverengine = 0;
+ int     pfixtls_serveractive = 0;	/* available or not */
+ int	pfixtls_peer_verified = 0;
+ char   *pfixtls_peer_subject = NULL;
+ char   *pfixtls_peer_issuer = NULL;
+ char   *pfixtls_peer_fingerprint = NULL;
+ 
+ int     pfixtls_clientengine = 0;
+ int     pfixtls_clientactive = 0;	/* available or not */
+ char   *pfixtls_peer_CN = NULL;
+ char   *pfixtls_issuer_CN = NULL;
+ 
+ char   *pfixtls_protocol = NULL;
+ const char   *pfixtls_cipher_name = NULL;
+ int	pfixtls_cipher_usebits = 0;
+ int	pfixtls_cipher_algbits = 0;
+ 
+  /*
+   * 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 ((pfixtls_serverengine && (var_smtpd_tls_loglevel > 2)) ||
+ 	(pfixtls_clientengine && (var_smtp_tls_loglevel > 2)))
+ 	msg_info("setting smtp_stream fd[%d]", fd);
+     pfixtls_fd = fd;
+     pfixtls_serveractive = pfixtls_clientactive = 0;
+ }
+ 
+ /* skeleton taken from OpenSSL crypto/err/err_prn.c */
+ 
+ static void pfixtls_print_errors(void)
+ {
+     unsigned long l;
+     char    buf[256];
+     const char   *file;
+     const 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) {
+ 	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("Peer 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 ((pfixtls_serverengine && (var_smtpd_tls_loglevel >= 2)) ||
+ 	    (pfixtls_clientengine && (var_smtp_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";
+ 	if ((pfixtls_serverengine && (var_smtpd_tls_loglevel >= 2)) ||
+ 	    (pfixtls_clientengine && (var_smtp_tls_loglevel >= 2)) ||
+ 	    ((ret & 0xff) != SSL3_AD_CLOSE_NOTIFY))
+ 	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, modified to save a lot of strcpy
+  * and strcat by Matti Aarnio.
+  */
+ 
+ #define TRUNCATE
+ #define DUMP_WIDTH	16
+ 
+ static int pfixtls_dump(const char *s, int len)
+ {
+     int     ret = 0;
+     char    buf[160 + 1];
+     char    *ss;
+     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 */
+ 	ss = buf;
+ 
+ 	sprintf(ss, "%04x ", i * DUMP_WIDTH);
+ 	ss += strlen(ss);
+ 	for (j = 0; j < DUMP_WIDTH; j++) {
+ 	    if (((i * DUMP_WIDTH) + j) >= len) {
+ 		strcpy(ss, "   ");
+ 	    } else {
+ 		ch = ((unsigned char) *((char *) (s) + i * DUMP_WIDTH + j))
+ 		    & 0xff;
+ 		sprintf(ss, "%02x%c", ch, j == 7 ? '|' : ' ');
+ 		ss += 3;
+ 	    }
+ 	}
+ 	ss += strlen(ss);
+ 	*ss++ = ' ';
+ 	for (j = 0; j < DUMP_WIDTH; j++) {
+ 	    if (((i * DUMP_WIDTH) + j) >= len)
+ 		break;
+ 	    ch = ((unsigned char) *((char *) (s) + i * DUMP_WIDTH + j)) & 0xff;
+ 	    *ss++ = (((ch >= ' ') && (ch <= '~')) ? ch : '.');
+ 	    if (j == 7) *ss++ = ' ';
+ 	}
+ 	*ss = 0;
+ 	/* 
+ 	 * 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, const 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);
+ }
+ 
+ 
+  /*
+   * Callback to retrieve a session from the external session cache.
+   */
+ static SSL_SESSION *get_session_cb(SSL *ssl, unsigned char *SessionID,
+ 				  int length, int *copy)
+ {
+     SSL_SESSION *session;
+     VSTRING *buf;
+     FILE *fp;
+     struct stat st;
+     char *idstring;
+     int n;
+     int uselength;
+     int verify_result;
+ 
+     if (length > id_maxlength)
+ 	uselength = id_maxlength;	/* Limit length of ID */
+     else
+ 	uselength = length;
+ 
+     idstring = (char *)mymalloc(2 * uselength + 1);
+     if (!idstring) {
+ 	msg_info("could not allocate memory for IDstring");
+ 	return (NULL);
+     }
+ 
+     for(n=0 ; n < uselength ; n++)
+ 	sprintf(idstring + 2 * n, "%02X", SessionID[n]);
+     if (var_smtpd_tls_loglevel >= 3)
+ 	msg_info("Trying to reload Session from disc: %s", idstring);
+ 
+     /*
+      * The constant "100" is taken from mail_queue.c and also used there.
+      * It must hold the name the postfix spool directory (if not chrooted)
+      * and the hash directory forest.
+      */
+     buf = vstring_alloc(100 + 2 * uselength + 1);
+     mail_queue_path(buf, MAIL_TLS_SRVR_CACHE, idstring);
+ 
+     /*
+      * Try to read the session from the file. If the file exists, but its
+      * mtime is so old, that the session has already expired, we donīt
+      * waste time anymore, we rather delete the session file immediately.
+      */
+     session = NULL;
+     if (stat(STR(buf), &st) == 0) {
+ 	if (st.st_mtime + var_smtpd_tls_scache_timeout < time(NULL))
+             REMOVE(STR(buf));
+ 	else if ((fp = fopen(STR(buf), "r")) != 0) {
+ 	    if (fscanf(fp, "%d", &verify_result) <= 0)
+ 		verify_result = X509_V_ERR_APPLICATION_VERIFICATION;
+ 	    SSL_set_verify_result(con, verify_result);
+ 	    session = PEM_read_SSL_SESSION(fp, NULL, NULL, NULL);
+ 	    fclose(fp);
+ 	}
+     }
+ 
+     vstring_free(buf);
+     myfree(idstring);
+ 
+     if (session && (var_smtpd_tls_loglevel >= 3))
+ 	msg_info("Successfully reloaded session from disc");
+ 
+     return (session);
+ }
+ 
+ 
+ static SSL_SESSION *load_clnt_session(unsigned char *SessionID, int length,
+ 				      int *verify_result)
+ {
+     SSL_SESSION *session;
+     VSTRING *buf;
+     FILE *fp;
+     struct stat st;
+     char *idstring;
+     int n;
+     int uselength;
+ 
+     if (length > id_maxlength)
+ 	uselength = id_maxlength;	/* Limit length of ID */
+     else
+ 	uselength = length;
+ 
+     idstring = (char *)mymalloc(2 * uselength + 1);
+     if (!idstring) {
+ 	msg_info("could not allocate memory for IDstring");
+ 	return (NULL);
+     }
+ 
+     for(n=0 ; n < uselength ; n++)
+ 	sprintf(idstring+2*n, "%02X", SessionID[n]);
+     if (var_smtp_tls_loglevel >= 3)
+ 	msg_info("Trying to reload Session from disc: %s", idstring);
+ 
+     buf = vstring_alloc(100 + 2 * uselength + 1);
+     mail_queue_path(buf, MAIL_TLS_CLNT_CACHE, idstring);
+ 
+     /*
+      * Try to read the session from the file. If the file exists, but its
+      * mtime is so old, that the session has already expired, we donīt
+      * waste time anymore, we rather delete the session file immediately.
+      *
+      * There is a race condition included. If another process is putting
+      * a new session file for the same HostID in the time during the
+      * "stat()" and the REMOVE, we will delete this new session from the
+      * disc cache. Well, then we have to negotiate a new one.
+      */
+     session = NULL;
+     if (stat(STR(buf), &st) == 0) {
+ 	if (st.st_mtime + var_smtp_tls_scache_timeout < time(NULL))
+             REMOVE(STR(buf));
+ 	else if ((fp = fopen(STR(buf), "r")) != 0) {
+ 	    if (fscanf(fp, "%d", verify_result) <= 0)
+ 		*verify_result = X509_V_ERR_APPLICATION_VERIFICATION;
+ 	    session = PEM_read_SSL_SESSION(fp, NULL, NULL, NULL);
+ 	    fclose(fp);
+ 	}
+     }
+ 
+     vstring_free(buf);
+     myfree(idstring);
+ 
+     if (session && (var_smtp_tls_loglevel >= 3))
+         msg_info("Successfully reloaded session from disc");
+ 
+     return (session);
+ }
+ 
+ 
+ static void remove_srvr_session(unsigned char *SessionID, int length)
+ {
+     VSTRING *buf;
+     char *idstring;
+     int n;
+     int uselength;
+ 
+     if (length > id_maxlength)
+ 	uselength = id_maxlength;	/* Limit length of ID */
+     else
+ 	uselength = length;
+ 
+     idstring = (char *)mymalloc(2 * uselength + 1);
+     if (!idstring) {
+ 	msg_info("could not allocate memory for IDstring");
+ 	return;
+     }
+ 
+     for(n=0 ; n < uselength ; n++)
+ 	sprintf(idstring + 2 * n, "%02X", SessionID[n]);
+     if (var_smtpd_tls_loglevel >= 3)
+ 	msg_info("Trying to remove session from disc: %s", idstring);
+ 
+     /*
+      * The constant "100" is taken from mail_queue.c and also used there.
+      * It must hold the name the postfix spool directory (if not chrooted)
+      * and the hash directory forest.
+      */
+     buf = vstring_alloc(100 + 2 * uselength + 1);
+     mail_queue_path(buf, MAIL_TLS_SRVR_CACHE, idstring);
+ 
+     /*
+      * Try to remove the session from the disc cache. Donīt care for return
+      * values, as either the session file is already gone or there is nothing
+      * we can do anyway.
+      */
+     REMOVE(STR(buf));
+ 
+     vstring_free(buf);
+     myfree(idstring);
+ }
+ 
+ 
+ static void remove_clnt_session(unsigned char *SessionID, int length)
+ {
+     VSTRING *buf;
+     char *idstring;
+     int n;
+     int uselength;
+ 
+     if (length > id_maxlength)
+ 	uselength = id_maxlength;	/* Limit length of ID */
+     else
+ 	uselength = length;
+ 
+     idstring = (char *)mymalloc(2 * uselength + 1);
+     if (!idstring) {
+ 	msg_info("could not allocate memory for IDstring");
+ 	return;
+     }
+ 
+     for(n=0 ; n < uselength ; n++)
+ 	sprintf(idstring + 2 * n, "%02X", SessionID[n]);
+     if (var_smtp_tls_loglevel >= 3)
+ 	msg_info("Trying to remove session from disc: %s", idstring);
+ 
+     /*
+      * The constant "100" is taken from mail_queue.c and also used there.
+      * It must hold the name the postfix spool directory (if not chrooted)
+      * and the hash directory forest.
+      */
+     buf = vstring_alloc(100 + 2 * uselength + 1);
+     mail_queue_path(buf, MAIL_TLS_CLNT_CACHE, idstring);
+ 
+     /*
+      * Try to remove the session from the disc cache. Donīt care for return
+      * values, as either the session file is already gone or there is nothing
+      * we can do anyway.
+      */
+     REMOVE(STR(buf));
+ 
+     vstring_free(buf);
+     myfree(idstring);
+ }
+ 
+ 
+  /*
+   * Save a new session to the external cache
+   */
+ static int new_session_cb(SSL *ssl, SSL_SESSION *session)
+ {
+     VSTRING *buf;
+     FILE *fp;
+     char *myname = "new_session_cb";
+     char *idstring;
+     int n;
+     int uselength;
+     int fd;
+     int success;
+ 
+     if (session->session_id_length > id_maxlength)
+ 	uselength = id_maxlength;	/* Limit length of ID */
+     else
+ 	uselength = session->session_id_length;
+ 
+     idstring = (char *)mymalloc(2 * uselength + 1);
+     if (!idstring) {
+ 	msg_info("could not allocate memory for IDstring");
+ 	return 0;
+     }
+ 
+     for(n=0 ; n < uselength ; n++)
+ 	sprintf(idstring + 2 * n, "%02X", session->session_id[n]);
+ 
+     if (var_smtpd_tls_loglevel >= 3)
+ 	msg_info("Trying to save Session to disc: %s", idstring);
+ 
+     buf = vstring_alloc(100 + 2 * uselength + 1);
+     mail_queue_path(buf, MAIL_TLS_SRVR_CACHE, idstring);
+ 
+     if (mail_queue_mkdirs(STR(buf)) == 0) {
+ 	/*
+ 	 * Now open the session file in exclusive and create mode. If it
+ 	 * already exists, we donīt touch it and silently omit the save.
+ 	 * We cannot use Wietseīs VSTREAM code here, as PEM_write uses
+ 	 * Cīs normal buffered library and we better donīt mix.
+ 	 * The return value of PEM_write_SSL_SESSION is nowhere documented,
+ 	 * but from the source it seems to be something like the number
+ 	 * of lines or bytes written. Anyway, success is positiv and
+ 	 * failure is zero.
+ 	 */
+ 	if ((fd = open(STR(buf), O_WRONLY | O_CREAT | O_EXCL, 0600)) >= 0) {
+ 	    if ((fp = fdopen(fd, "w")) == 0)
+ 		msg_fatal("%s: could not fdopen %s: %s", myname, buf,
+ 			  strerror(errno));
+ 	    fprintf(fp, "%d\n", SSL_get_verify_result(ssl));
+ 	    success = PEM_write_SSL_SESSION(fp, session);
+ 	    fclose(fp);
+ 	    if (success == 0)
+ 		REMOVE(STR(buf));
+ 	    else if (var_smtpd_tls_loglevel >= 3)
+         	msg_info("Successfully saved session to disc");
+ 	}
+     }
+ 
+     vstring_free(buf);
+     myfree(idstring);
+ 
+     return (0);
+ }
+ 
+ 
+  /*
+   * Save the new session to the external cache. As the HostID is given
+   * by the contacted peer, we may have several negotiations going on at
+   * the same time for the same peer. This is not purely hypothetical but
+   * quite likely if several jobs to the same recipient host are in the queue
+   * and a queue run is started. So we have to take care of race conditions.
+   * As I consider the TLS-SessionID to be unique, we will first try to
+   * create a file with the actual SessionID. Once the writing is finished,
+   * the file is closed and moved to its final name. This way we should be
+   * able to deal with race conditions, since rename should be atomic.
+   * If the rename fails for some reason, we will just silently remove
+   * the temporary file and forget about the session.
+   */
+ static void save_clnt_session(SSL_SESSION *session, unsigned char *HostID,
+ 			      int length, int verify_result)
+ {
+     VSTRING *buf;
+     VSTRING *temp;
+     FILE *fp;
+     char *myname = "new_session_cb";
+     char *idstring;
+     int uselength;
+     int n;
+     int fd;
+     int success;
+ 
+     if (length > id_maxlength)
+ 	uselength = id_maxlength;	/* Limit length of ID */
+     else
+ 	uselength = length;
+ 
+     idstring = (char *)mymalloc(2 * id_maxlength + 1);
+     if (!idstring) {
+ 	msg_info("could not allocate memory for IDstring");
+     }
+ 
+     for(n=0 ; n < uselength ; n++)
+ 	sprintf(idstring + 2 * n, "%02X", HostID[n]);
+ 
+     buf = vstring_alloc(100 + 2 * id_maxlength + 1);
+     mail_queue_path(buf, MAIL_TLS_CLNT_CACHE, idstring);
+     if (var_smtp_tls_loglevel >= 3)
+ 	msg_info("Trying to save session for hostID to disc: %s", idstring);
+ 
+     if (session->session_id_length > id_maxlength)
+ 	uselength = id_maxlength;	/* Limit length of ID */
+     else
+ 	uselength = session->session_id_length;
+ 
+     for(n=0 ; n < uselength ; n++)
+         sprintf(idstring + 2 * n, "%02X", session->session_id[n]);
+     if (var_smtp_tls_loglevel >= 3)
+ 	msg_info("Session ID is %s", idstring);
+ 
+     temp = vstring_alloc(100 + 2 * id_maxlength + 1);
+     mail_queue_path(temp, MAIL_TLS_CLNT_CACHE, idstring);
+ 
+     if ((mail_queue_mkdirs(STR(buf)) == 0) &&
+         (mail_queue_mkdirs(STR(temp)) == 0)) {
+ 	/*
+ 	 * Now open the session file in exclusive and create mode. If it
+ 	 * already exists, we donīt touch it and silently omit the save.
+ 	 * We cannot use Wietseīs VSTREAM code here, as PEM_write uses
+ 	 * Cīs normal buffered library and we better donīt mix.
+ 	 * The return value of PEM_write_SSL_SESSION is nowhere documented,
+ 	 * but from the source it seems to be something like the number
+ 	 * of lines or bytes written. Anyway, success is positiv and
+ 	 * failure is zero.
+ 	 */
+ 	if ((fd = open(STR(temp), O_WRONLY | O_CREAT | O_EXCL, 0600)) >= 0) {
+ 	    if ((fp = fdopen(fd, "w")) == 0)
+ 		msg_fatal("%s: could not fdopen %s: %s", myname, temp,
+ 			  strerror(errno));
+ 	    fprintf(fp, "%d\n", verify_result);
+ 	    success = PEM_write_SSL_SESSION(fp, session);
+ 	    fclose(fp);
+ 	    if (success == 0)
+ 		REMOVE(STR(temp));
+ 	    else if (rename(STR(temp), STR(buf)) != 0)
+ 		REMOVE(STR(temp));
+ 	    else if (var_smtp_tls_loglevel >= 3)
+         	msg_info("Successfully saved session to disc");
+ 	}
+     }
+ 
+     vstring_free(temp);
+     vstring_free(buf);
+     myfree(idstring);
+ }
+ 
+ 
+  /*
+   * 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 >= 2)
+ 	msg_info("starting TLS engine");
+ 
+     /*
+      * Initialize the OpenSSL library by the book!
+      * To start with, we must initialize the algorithms.
+      * We want cleartext error messages instead of just error codes, so we
+      * load the error_strings.
+      */
+     SSL_load_error_strings();
+     SSLeay_add_ssl_algorithms();
+ 
+     /*
+      * The SSL/TLS speficications require the client to send a message in
+      * the oldest specification it understands with the highest level it
+      * understands in the message.
+      * Netscape communicator can still communicate with SSLv2 servers, so it
+      * sends out a SSLv2 client hello. To deal with it, our server must be
+      * SSLv2 aware (even if we donīt like SSLv2), so we need to have the
+      * SSLv23 server here. If we want to limit the protocol level, we can
+      * add an option to not use SSLv2/v3/TLSv1 later.
+      */
+     ctx = SSL_CTX_new(SSLv23_server_method());
+     if (ctx == NULL) {
+ 	pfixtls_print_errors();
+ 	return (-1);
+     };
+ 
+     /*
+      * Here we might set SSL_OP_NO_SSLv2, SSL_OP_NO_SSLv3, SSL_OP_NO_TLSv1.
+      * Of course, the last one would not make sense, since RFC2487 is only
+      * defined for TLS, but we also want to accept Netscape communicator
+      * requests, and it only supports SSLv3.
+      */
+     off |= SSL_OP_ALL;		/* Work around all known bugs */
+     SSL_CTX_set_options(ctx, off);
+ 
+     /*
+      * Set the info_callback, that will print out messages during
+      * communication on demand.
+      */
+     SSL_CTX_set_info_callback(ctx, apps_ssl_info_callback);
+ 
+     /*
+      * Initialize the session cache. We only want external caching to
+      * synchronize between server sessions, so we set it to a minimum value
+      * of 1. If the external cache is disabled, we wonīt cache at all.
+      * The recall of old sessions "get" and save to disk of just created
+      * sessions "new" is handled by the appropriate callback functions.
+      *
+      * We must not forget to set a session id context to identify to which
+      * kind of server process the session was related. In our case, the
+      * context is just the name of the patchkit: "Postfix/TLS".
+      */
+     SSL_CTX_sess_set_cache_size(ctx, 1);
+     SSL_CTX_set_timeout(ctx, var_smtpd_tls_scache_timeout);
+     if (var_smtpd_tls_use_scache) {
+ 	SSL_CTX_sess_set_get_cb(ctx, get_session_cb);
+ 	SSL_CTX_sess_set_new_cb(ctx, new_session_cb);
+     } 
+     SSL_CTX_set_session_id_context(ctx, (void*)&server_session_id_context,
+                 sizeof(server_session_id_context));
+ 
+     /*
+      * Now we must add the necessary certificate stuff: A server key, a
+      * server certificate, and the CA certificates for both the server
+      * cert and the verification of client certificates.
+      * As provided by OpenSSL we support two types of CA certificate handling:
+      * One possibility is to add all CA certificates to one large CAfile,
+      * the other possibility is a directory pointed to by CApath, containing
+      * seperate files for each CA pointed on by softlinks named by the hash
+      * values of the certificate.
+      * The first alternative has the advantage, that the file is opened and
+      * read at startup time, so that you donīt have the hassle to maintain
+      * another copy of the CApath directory for chroot-jail. On the other
+      * hand, the file is not really readable.
+      */
+     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);
+     }
+ 
+     /*
+      * Now we load the certificate and key from the files and check,
+      * whether the cert matches the key (internally done by set_cert_stuff().
+      * We cannot run without.
+      */
+     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");
+ 	pfixtls_print_errors();
+ 	return (-1);
+     }
+ 
+     /*
+      * Sometimes a temporary RSA key might be needed by the OpenSSL
+      * library. The OpenSSL doc indicates, that this might happen when
+      * export ciphers are in use. We have to provide one, so well, we
+      * just do it.
+      */
+     SSL_CTX_set_tmp_rsa_callback(ctx, tmp_rsa_cb);
+ 
+     /*
+      * If we want to check client certificates, we have to indicate it
+      * in advance. By now we only allow to decide on a global basis.
+      * If we want to allow certificate based relaying, we must ask the
+      * client to provide one with SSL_VERIFY_PEER. The client now can
+      * decide, whether it provides one or not. We can enforce a failure
+      * of the negotiation with SSL_VERIFY_FAIL_IF_NO_PEER_CERT, if we
+      * do not allow a connection without one.
+      * In the "server hello" following the initialization by the "client hello"
+      * the server must provide a list of CAs it is willing to accept.
+      * Some clever clients will then select one from the list of available
+      * certificates matching these CAs. Netscape Communicator will present
+      * the list of certificates for selecting the one to be sent, or it will
+      * issue a warning, if there is no certificate matching the available
+      * CAs.
+      *
+      * With regard to the purpose of the certificate for relaying, we might
+      * like a later negotiation, maybe relaying would already be allowed
+      * for other reasons, but this would involve severe changes in the
+      * internal postfix logic, so we have to live with it the way it is.
+      */
+     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;
+     SSL_CIPHER *cipher;
+     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 necessary, setup a new SSL structure for a connection. We keep
+      * old ones on closure, so it might not be always necessary. We however
+      * reset the old one, just in case.
+      */
+     if (con != NULL)
+ 	SSL_clear(con);
+     else if ((con = (SSL *) SSL_new(ctx)) == NULL) {
+ 	msg_info("Could not allocate 'con' with SSL_new()");
+ 	pfixtls_print_errors();
+ 	return (-1);
+     }
+ 
+     /*
+      * Now, connect the filedescripter set earlier to the SSL connection
+      */
+     if (!SSL_set_fd(con, pfixtls_fd)) {
+ 	msg_info("SSL_set_fd failed");
+ 	pfixtls_print_errors();
+ 	return (-1);
+     }
+ 
+     /*
+      * Initialize the SSL connection to accept state. This should not be
+      * necessary anymore since 0.9.3, but the call is still in the library
+      * and maintaining compatibility never hurts.
+      */
+     SSL_set_accept_state(con);
+ 
+     /*
+      * If the debug level selected is high enough, all of the data is
+      * dumped: 3 will dump the SSL negotiation, 4 will dump everything.
+      *
+      * 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;
+ 
+     /*
+      * Now we expect the negotiation to begin. This whole process is like a
+      * black box for us. We totally have to rely on the routines build into
+      * the OpenSSL library. The only thing we can do we already have done
+      * by choosing our own callbacks for session caching and certificate
+      * verification.
+      *
+      * Error handling:
+      * If the SSL handhake fails, we print out an error message and remove
+      * everything that might be there. A session has to be removed anyway,
+      * because RFC2246 requires it.
+      */
+     if ((sts = SSL_accept(con)) <= 0) {
+ 	msg_info("SSL_accept error %d", sts);
+ 	pfixtls_print_errors();
+ 	session = SSL_get_session(con);
+ 	if (session) {
+ 	    remove_srvr_session(session->session_id,
+ 			        session->session_id_length);
+ 	    SSL_CTX_remove_session(ctx, session);
+ 	    msg_info("SSL session removed");
+ 	}
+ 	SSL_free(con);
+ 	con = NULL;
+ 	return (-1);
+     }
+ 
+     /* Only loglevel==4 dumps everything */
+     if (var_smtpd_tls_loglevel < 4)
+ 	do_dump = 0;
+ 
+     /*
+      * Check the verification state of a possibly supplied client certificate.
+      */
+     if (SSL_get_verify_result(con) == X509_V_OK)
+ 		pfixtls_peer_verified = 1;
+ 
+     /*
+      * Lets see, whether a peer certificate is available 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),
+ 			  peer_subject, CCERT_BUFSIZ);
+ 	if (var_smtpd_tls_loglevel >= 2)
+ 	    msg_info("subject=%s", peer_subject);
+ 	pfixtls_peer_subject = peer_subject;
+ 	X509_NAME_oneline(X509_get_issuer_name(peer),
+ 			  peer_issuer, CCERT_BUFSIZ);
+ 	if (var_smtpd_tls_loglevel >= 2)
+ 	    msg_info("issuer=%s", peer_issuer);
+ 	pfixtls_peer_issuer = peer_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 >= 2)
+ 		msg_info("fingerprint=%s", fingerprint);
+ 	    pfixtls_peer_fingerprint = fingerprint;
+ 	}
+ 	X509_NAME_get_text_by_NID(X509_get_subject_name(peer),
+ 			  NID_commonName, peer_CN, CCERT_BUFSIZ);
+ 	pfixtls_peer_CN = peer_CN;
+ 	X509_NAME_get_text_by_NID(X509_get_issuer_name(peer),
+ 			  NID_commonName, issuer_CN, CCERT_BUFSIZ);
+ 	if (var_smtp_tls_loglevel >= 3)
+ 	    msg_info("subject_CN=%s, issuer_CN=%s", peer_CN, issuer_CN);
+ 	pfixtls_issuer_CN = issuer_CN;
+ 	X509_free(peer);
+     }
+ 
+     /*
+      * Finally, collect information about protocol and cipher for logging
+      */
+     pfixtls_protocol = SSL_get_version(con);
+     cipher = SSL_get_current_cipher(con);
+     pfixtls_cipher_name = SSL_CIPHER_get_name(cipher);
+     pfixtls_cipher_usebits = SSL_CIPHER_get_bits(cipher,
+ 						 &pfixtls_cipher_algbits);
+ 
+     pfixtls_serveractive = 1;
+     msg_info("TLS connection established: %s with cipher %s (%d/%d bits)",
+ 	     pfixtls_protocol, pfixtls_cipher_name,
+ 	     pfixtls_cipher_usebits, pfixtls_cipher_algbits);
+ 
+     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.
+   * Since our session cache is external, we will remove the session from
+   * memory in any case. The SSL_CTX_flush_sessions might be redundant here,
+   * I however want to make sure nothing is left.
+   * RFC2246 requires us to remove sessions if something went wrong, as
+   * indicated by the "failure" value, so we remove it from the external
+   * cache, too. 
+   */
+ int     pfixtls_stop_servertls(int failure)
+ {
+     SSL_SESSION *session;
+ 
+     if (pfixtls_serveractive) {
+ 	session = SSL_get_session(con);
+ 	SSL_shutdown(con);
+ 	if (session) {
+ 	    if (failure) {
+ 	    remove_srvr_session(session->session_id,
+ 			        session->session_id_length);
+ 	    msg_info("SSL session removed");
+ 	    }
+ 	    SSL_CTX_remove_session(ctx, session);
+ 	}
+ 	SSL_free(con);
+ 	con = NULL;
+ 	SSL_CTX_flush_sessions(ctx,time(NULL));
+ 
+ 	pfixtls_peer_verified = 0;
+ 	pfixtls_peer_subject = NULL;
+ 	pfixtls_peer_issuer = NULL;
+ 	pfixtls_peer_fingerprint = NULL;
+ 	pfixtls_peer_CN = NULL;
+ 	pfixtls_issuer_CN = NULL;
+ 	pfixtls_protocol = NULL;
+ 	pfixtls_cipher_name = NULL;
+ 	pfixtls_cipher_usebits = 0;
+ 	pfixtls_cipher_algbits = 0;
+ 	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 >= 2)
+ 	msg_info("starting TLS engine");
+ 
+     /*
+      * Initialize the OpenSSL library by the book!
+      * To start with, we must initialize the algorithms.
+      * We want cleartext error messages instead of just error codes, so we
+      * load the error_strings.
+      */ 
+     SSL_load_error_strings();
+     SSLeay_add_ssl_algorithms();
+ 
+     /*
+      * The SSL/TLS speficications require the client to send a message in
+      * the oldest specification it understands with the highest level it
+      * understands in the message.
+      * RFC2487 is only specified for TLSv1, but we want to be as compatible
+      * as possible, so we will start off with a SSLv2 greeting allowing
+      * the best we can offer: TLSv1.
+      * We can restrict this with the options setting later, anyhow.
+      */
+     ctx = SSL_CTX_new(SSLv23_client_method());
+     if (ctx == NULL) {
+ 	pfixtls_print_errors();
+ 	return (-1);
+     };
+ 
+     /*
+      * Here we might set SSL_OP_NO_SSLv2, SSL_OP_NO_SSLv3, SSL_OP_NO_TLSv1.
+      * Of course, the last one would not make sense, since RFC2487 is only
+      * defined for TLS, but we donīt know what is out there. So leave things
+      * completely open, as of today.
+      */
+     off |= SSL_OP_ALL;		/* Work around all known bugs */
+     SSL_CTX_set_options(ctx, off);
+ 
+     /*
+      * Set the info_callback, that will print out messages during
+      * communication on demand.
+      */
+     SSL_CTX_set_info_callback(ctx, apps_ssl_info_callback);
+ 
+     /*
+      * Initialize the session cache. We only want external caching to
+      * synchronize between server sessions, so we set it to a minimum value
+      * of 1. If the external cache is disabled, we wonīt cache at all.
+      *
+      * In case of the client, there is no callback used in OpenSSL, so
+      * we must call the session cache functions manually during the process.
+      */
+     SSL_CTX_sess_set_cache_size(ctx, 1);
+     SSL_CTX_set_timeout(ctx, var_smtp_tls_scache_timeout);
+    
+     /*
+      * Now we must add the necessary certificate stuff: A client key, a
+      * client certificate, and the CA certificates for both the client
+      * cert and the verification of server certificates.
+      * In fact, we do not need a client certificate,  so the certificates
+      * are only loaded (and checked), if supplied. A clever client would
+      * handle multiple client certificates and decide based on the list
+      * of acceptable CAs, sent by the server, which certificate to submit.
+      * OpenSSL does however not do this and also has no callback hoods to
+      * easily realize it.
+      *
+      * As provided by OpenSSL we support two types of CA certificate handling:
+      * One possibility is to add all CA certificates to one large CAfile,
+      * the other possibility is a directory pointed to by CApath, containing
+      * seperate files for each CA pointed on by softlinks named by the hash
+      * values of the certificate.
+      * The first alternative has the advantage, that the file is opened and
+      * read at startup time, so that you donīt have the hassle to maintain
+      * another copy of the CApath directory for chroot-jail. On the other
+      * hand, the file is not really readable.
+      */ 
+     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");
+ 	    pfixtls_print_errors();
+ 	    return (-1);
+ 	}
+ 
+     /*
+      * Sometimes a temporary RSA key might be needed by the OpenSSL
+      * library. The OpenSSL doc indicates, that this might happen when
+      * export ciphers are in use. We have to provide one, so well, we
+      * just do it.
+      */
+     SSL_CTX_set_tmp_rsa_callback(ctx, tmp_rsa_cb);
+ 
+     /*
+      * Finally, the setup for the server certificate checking, done
+      * "by the book".
+      */
+     verify_depth = verifydepth;
+     SSL_CTX_set_verify(ctx, verify_flags, verify_callback);
+ 
+ 
+     pfixtls_clientengine = 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
+   * received by us, so that we can immediately can start the TLS
+   * handshake process.
+   */
+ int     pfixtls_start_clienttls(const char *peername)
+ {
+     int     sts;
+     int     j;
+     unsigned int n;
+     SSL_SESSION *session;
+     SSL_CIPHER *cipher;
+     X509   *peer;
+     int     save_session;
+     int	    length;
+     int     verify_result;
+     unsigned char *old_session_id;
+ 
+     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 necessary, setup a new SSL structure for a connection. We keep
+      * old ones on closure, so it might not be always necessary. We however
+      * reset the old one, just in case.
+      */
+     if (con != NULL)
+ 	SSL_clear(con);
+     else if ((con = (SSL *) SSL_new(ctx)) == NULL) {
+ 	msg_info("Could not allocate 'con' with SSL_new()");
+ 	pfixtls_print_errors();
+ 	return (-1);
+     }
+     old_session_id = NULL;	/* make sure no old info is kept */
+ 
+     /*
+      * Now, connect the filedescripter set earlier to the SSL connection
+      */
+     if (!SSL_set_fd(con, pfixtls_fd)) {
+ 	msg_info("SSL_set_fd failed");
+ 	pfixtls_print_errors();
+ 	return (-1);
+     }
+ 
+     /*
+      * Find out the hashed HostID for the client cache and try to
+      * load the session from the cache.
+      * "old_session_id" holds the session ID of the reloaded session, so that
+      * we can later check, whether it is really reused.
+      */
+     if (var_smtp_tls_use_scache) {
+ 	MD5(peername, strlen(peername), peername_md5);
+ 	session = load_clnt_session(peername_md5, MD5_DIGEST_LENGTH,
+ 				    &verify_result);
+ 	if (session) {
+ 	   SSL_CTX_add_session(ctx, session);
+ 	   SSL_set_session(con, session);
+ 	   old_session_id = mymemdup(session->session_id,
+ 				     session->session_id_length);
+ 	   
+ 	}
+     }
+ 
+     /*
+      * Initialize the SSL connection to connect state. This should not be
+      * necessary anymore since 0.9.3, but the call is still in the library
+      * and maintaining compatibility never hurts.
+      */
+     SSL_set_connect_state(con);
+ 
+     /*
+      * If the debug level selected is high enough, all of the data is
+      * dumped: 3 will dump the SSL negotiation, 4 will dump everything.
+      *
+      * 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;
+ 
+     /*
+      * Now we expect the negotiation to begin. This whole process is like a
+      * black box for us. We totally have to rely on the routines build into
+      * the OpenSSL library. The only thing we can do we already have done
+      * by choosing our own callback certificate verification.
+      *
+      * Error handling:
+      * If the SSL handhake fails, we print out an error message and remove
+      * everything that might be there. A session has to be removed anyway,
+      * because RFC2246 requires it. 
+      */
+     if ((sts = SSL_connect(con)) <= 0) {
+ 	msg_info("SSL_connect error %d", sts);
+ 	pfixtls_print_errors();
+ 	session = SSL_get_session(con);
+ 	if (session) {
+ 	    remove_clnt_session(session->session_id,
+ 			        session->session_id_length);
+ 	    SSL_CTX_remove_session(ctx, session);
+ 	    msg_info("SSL session removed");
+ 	}
+ 	SSL_free(con);
+ 	con = NULL;
+ 	return (-1);
+     }
+ 
+     /*
+      * Now we must save the new session to disk, if necessary. If we had
+      * an old session, its ID was saved in "old_session_id" for comparison.
+      */
+     session = SSL_get_session(con);
+     if (var_smtp_tls_use_scache && session) {
+ 	save_session = 1;
+ 	if (old_session_id) {
+ 	    if (memcmp(session->session_id, old_session_id,
+ 		       session->session_id_length) == 0) {
+ 		if (var_smtp_tls_loglevel >= 3)
+ 		    msg_info("Reusing old session");
+ 		save_session = 0;
+ 		SSL_set_verify_result(con, verify_result);
+ 	    }
+ 	    myfree(old_session_id);
+ 	}
+ 	if (save_session)
+ 	    save_clnt_session(session, peername_md5, MD5_DIGEST_LENGTH,
+ 			      SSL_get_verify_result(con));
+     }
+ 
+     /* Only loglevel==4 dumps everything */
+     if (var_smtp_tls_loglevel < 4)
+ 	do_dump = 0;
+ 
+     /*
+      * Check the verification state of the peer certificate.
+      */
+     if (SSL_get_verify_result(con) == X509_V_OK) {
+ 	pfixtls_peer_verified = 1;
+     }
+ 
+     /*
+      * Lets see, whether a peer certificate is available 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_get_text_by_NID(X509_get_subject_name(peer),
+ 			  NID_commonName, peer_CN, CCERT_BUFSIZ);
+ 	pfixtls_peer_CN = peer_CN;
+ 	X509_NAME_get_text_by_NID(X509_get_issuer_name(peer),
+ 			  NID_commonName, issuer_CN, CCERT_BUFSIZ);
+ 	if (var_smtp_tls_loglevel >= 3)
+ 	    msg_info("subject_CN=%s, issuer_CN=%s", peer_CN, issuer_CN);
+ 	pfixtls_issuer_CN = issuer_CN;
+ 	X509_free(peer);
+     }
+ 
+     /*
+      * Finally, collect information about protocol and cipher for logging
+      */ 
+     pfixtls_protocol = SSL_get_version(con);
+     cipher = SSL_get_current_cipher(con);
+     pfixtls_cipher_name = SSL_CIPHER_get_name(cipher);
+     pfixtls_cipher_usebits = SSL_CIPHER_get_bits(cipher,
+ 						 &pfixtls_cipher_algbits);
+ 
+     pfixtls_clientactive = 1;
+     msg_info("TLS connection established: %s with cipher %s (%d/%d bits)",
+ 	     pfixtls_protocol, pfixtls_cipher_name,
+ 	     pfixtls_cipher_usebits, pfixtls_cipher_algbits);
+ 
+     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.
+   * Since our session cache is external, we will remove the session from
+   * memory in any case. The SSL_CTX_flush_sessions might be redundant here,
+   * I however want to make sure nothing is left.
+   * RFC2246 requires us to remove sessions if something went wrong, as
+   * indicated by the "failure" value,so we remove it from the external
+   * cache, too.
+   */
+ int     pfixtls_stop_clienttls(int failure)
+ {
+     SSL_SESSION *session;
+ 
+     if (pfixtls_clientactive) {
+ 	session = SSL_get_session(con);
+ 	SSL_shutdown(con);
+ 	if (session) {
+ 	    if (failure) {
+ 	    remove_clnt_session(peername_md5, MD5_DIGEST_LENGTH);
+ 	    msg_info("SSL session removed");
+ 	    }
+ 	    SSL_CTX_remove_session(ctx, session);
+ 	SSL_free(con);
+         con = NULL;
+ 	}
+ 	SSL_CTX_flush_sessions(ctx,time(NULL));
+ 
+ 	pfixtls_peer_verified = 0;
+ 	pfixtls_peer_subject = NULL;
+ 	pfixtls_peer_issuer = NULL;
+ 	pfixtls_peer_fingerprint = NULL;
+ 	pfixtls_peer_CN = NULL;
+ 	pfixtls_issuer_CN = NULL;
+ 	pfixtls_protocol = NULL;
+ 	pfixtls_cipher_name = NULL;
+ 	pfixtls_cipher_usebits = 0;
+ 	pfixtls_cipher_algbits = 0;
+ 	pfixtls_clientactive = 0;
+     }
+ 
+     return (0);
+ }
+ 
+ 
+ #endif /* HAS_SSL */
diff -r -c --new-file postfix-19990906-pl07-vanilla/global/pfixtls.h postfix-19990906-pl07/global/pfixtls.h
*** postfix-19990906-pl07-vanilla/global/pfixtls.h	Thu Jan  1 01:00:00 1970
--- postfix-19990906-pl07/global/pfixtls.h	Mon Nov 15 15:31:05 1999
***************
*** 0 ****
--- 1,56 ----
+ /*++
+ /* 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(int failure);
+ 
+ int     pfixtls_init_clientengine(int verifydepth);
+ int     pfixtls_start_clienttls(const char *peername);
+ int     pfixtls_stop_clienttls(int failure);
+ 
+ extern int pfixtls_serverengine;
+ extern int pfixtls_serveractive;
+ extern int pfixtls_peer_verified;
+ extern char *pfixtls_peer_subject;
+ extern char *pfixtls_peer_issuer;
+ extern char *pfixtls_peer_fingerprint;
+ 
+ extern int pfixtls_clientengine;
+ extern int pfixtls_clientactive;
+ 
+ extern char *pfixtls_peer_CN;
+ extern char *pfixtls_issuer_CN;
+ extern char *pfixtls_protocol;
+ extern const char *pfixtls_cipher_name;
+ extern int pfixtls_cipher_usebits;
+ extern int pfixtls_cipher_algbits;
+ 
+ #endif
+ 
+ /* LICENSE
+ /* .ad
+ /* .fi
+ /* AUTHOR(S)
+ /*	Lutz Jaenicke
+ /*	BTU Cottbus
+ /*	Allgemeine Elektrotechnik
+ /*	Universitaetsplatz 3-4
+ /*	D-03044 Cottbus, Germany
+ /*--*/
diff -r -c --new-file postfix-19990906-pl07-vanilla/global/smtp_stream.c postfix-19990906-pl07/global/smtp_stream.c
*** postfix-19990906-pl07-vanilla/global/smtp_stream.c	Thu May  6 19:29:34 1999
--- postfix-19990906-pl07/global/smtp_stream.c	Mon Nov 15 15:31:06 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-19990906-pl07-vanilla/qmgr/Makefile.in postfix-19990906-pl07/qmgr/Makefile.in
*** postfix-19990906-pl07-vanilla/qmgr/Makefile.in	Sun Nov  7 18:43:52 1999
--- postfix-19990906-pl07/qmgr/Makefile.in	Mon Nov 15 15:31:06 1999
***************
*** 1,10 ****
  SHELL	= /bin/sh
  SRCS	= qmgr.c qmgr_active.c qmgr_transport.c qmgr_queue.c qmgr_entry.c \
  	qmgr_message.c qmgr_deliver.c qmgr_move.c qmgr_rcpt_list.c \
! 	qmgr_defer.c qmgr_enable.c qmgr_scan.c qmgr_bounce.c
  OBJS	= qmgr.o qmgr_active.o qmgr_transport.o qmgr_queue.o qmgr_entry.o \
  	qmgr_message.o qmgr_deliver.o qmgr_move.o qmgr_rcpt_list.o \
! 	qmgr_defer.o qmgr_enable.o qmgr_scan.o qmgr_bounce.o
  HDRS	= qmgr.h
  TESTSRC	=
  WARN	= -W -Wformat -Wimplicit -Wmissing-prototypes \
--- 1,10 ----
  SHELL	= /bin/sh
  SRCS	= qmgr.c qmgr_active.c qmgr_transport.c qmgr_queue.c qmgr_entry.c \
  	qmgr_message.c qmgr_deliver.c qmgr_move.c qmgr_rcpt_list.c \
! 	qmgr_defer.c qmgr_enable.c qmgr_scan.c qmgr_bounce.c qmgr_tls.c
  OBJS	= qmgr.o qmgr_active.o qmgr_transport.o qmgr_queue.o qmgr_entry.o \
  	qmgr_message.o qmgr_deliver.o qmgr_move.o qmgr_rcpt_list.o \
! 	qmgr_defer.o qmgr_enable.o qmgr_scan.o qmgr_bounce.o qmgr_tls.o
  HDRS	= qmgr.h
  TESTSRC	=
  WARN	= -W -Wformat -Wimplicit -Wmissing-prototypes \
***************
*** 245,247 ****
--- 245,254 ----
  qmgr_transport.o: ../include/scan_dir.h
  qmgr_transport.o: ../include/maps.h
  qmgr_transport.o: ../include/dict.h
+ qmgr_tls.o: qmgr_tls.c
+ qmgr_tls.o: qmgr.h
+ qmgr_tls.o: ../include/sys_defs.h
+ qmgr_tls.o: ../include/iostuff.h
+ qmgr_tls.o: ../include/mail_params.h
+ qmgr_tls.o: ../include/mail_queue.h
+ qmgr_tls.o: ../include/vstring.h
diff -r -c --new-file postfix-19990906-pl07-vanilla/qmgr/qmgr.c postfix-19990906-pl07/qmgr/qmgr.c
*** postfix-19990906-pl07-vanilla/qmgr/qmgr.c	Fri Apr 30 21:45:35 1999
--- postfix-19990906-pl07/qmgr/qmgr.c	Mon Nov 15 15:31:06 1999
***************
*** 269,274 ****
--- 269,276 ----
  
  static QMGR_SCAN *qmgr_incoming;
  static QMGR_SCAN *qmgr_deferred;
+ static QMGR_SCAN *qmgr_srvr_cache;
+ static QMGR_SCAN *qmgr_clnt_cache;
  
  MAPS   *qmgr_relocated;
  MAPS   *qmgr_virtual;
***************
*** 286,291 ****
--- 288,317 ----
      event_request_timer(qmgr_deferred_run_event, dummy, var_queue_run_delay);
  }
  
+ static void qmgr_tls_srvr_cache_run_event(int unused_event, char *dummy)
+ {
+ 
+     /*
+      * This routine runs when it is time for another tls session cache scan.
+      * Make sure this routine gets called again in the future.
+      */
+     qmgr_scan_request(qmgr_srvr_cache, QMGR_SCAN_START);
+     event_request_timer(qmgr_tls_srvr_cache_run_event, dummy,
+ 		 var_smtpd_tls_scache_timeout);
+ }
+ 
+ static void qmgr_tls_clnt_cache_run_event(int unused_event, char *dummy)
+ {
+ 
+     /*
+      * This routine runs when it is time for another tls session cache scan.
+      * Make sure this routine gets called again in the future.
+      */
+     qmgr_scan_request(qmgr_clnt_cache, QMGR_SCAN_START);
+     event_request_timer(qmgr_tls_clnt_cache_run_event, dummy,
+ 		 var_smtp_tls_scache_timeout);
+ }
+ 
  /* qmgr_trigger_event - respond to external trigger(s) */
  
  static void qmgr_trigger_event(char *buf, int len,
***************
*** 293,298 ****
--- 319,326 ----
  {
      int     incoming_flag = 0;
      int     deferred_flag = 0;
+     int     tls_srvr_flag = 0;
+     int     tls_clnt_flag = 0;
      int     i;
  
      /*
***************
*** 322,331 ****
--- 350,363 ----
  	case QMGR_REQ_FLUSH_DEAD:
  	    deferred_flag |= QMGR_FLUSH_DEAD;
  	    incoming_flag |= QMGR_FLUSH_DEAD;
+ 	    tls_srvr_flag |= QMGR_FLUSH_DEAD;
+ 	    tls_clnt_flag |= QMGR_FLUSH_DEAD;
  	    break;
  	case QMGR_REQ_SCAN_ALL:
  	    deferred_flag |= QMGR_SCAN_ALL;
  	    incoming_flag |= QMGR_SCAN_ALL;
+ 	    tls_srvr_flag |= QMGR_SCAN_ALL;
+ 	    tls_clnt_flag |= QMGR_SCAN_ALL;
  	    break;
  	default:
  	    if (msg_verbose)
***************
*** 343,348 ****
--- 375,384 ----
  	qmgr_scan_request(qmgr_incoming, incoming_flag);
      if (deferred_flag != 0)
  	qmgr_scan_request(qmgr_deferred, deferred_flag);
+     if (tls_srvr_flag != 0)
+ 	qmgr_scan_request(qmgr_srvr_cache, tls_srvr_flag);
+     if (tls_clnt_flag != 0)
+ 	qmgr_scan_request(qmgr_clnt_cache, tls_clnt_flag);
  }
  
  /* qmgr_loop - queue manager main loop */
***************
*** 351,356 ****
--- 387,394 ----
  {
      char   *in_path = 0;
      char   *df_path = 0;
+     char   *tls_srvr_cache_path = 0;
+     char   *tls_clnt_cache_path = 0;
  
      /*
       * This routine runs as part of the event handling loop, after the event
***************
*** 383,389 ****
  	&& qmgr_recipient_count < var_qmgr_rcpt_limit)
  	if ((df_path = qmgr_scan_next(qmgr_deferred)) != 0)
  	    qmgr_active_feed(qmgr_deferred, df_path);
!     if (in_path || df_path)
  	return (DONT_WAIT);
      return (WAIT_FOR_EVENT);
  }
--- 421,433 ----
  	&& qmgr_recipient_count < var_qmgr_rcpt_limit)
  	if ((df_path = qmgr_scan_next(qmgr_deferred)) != 0)
  	    qmgr_active_feed(qmgr_deferred, df_path);
!     if ((tls_srvr_cache_path = qmgr_scan_next(qmgr_srvr_cache)) != 0)
! 	qmgr_check_session(MAIL_TLS_SRVR_CACHE, tls_srvr_cache_path,
! 			   var_smtpd_tls_scache_timeout);
!     if ((tls_clnt_cache_path = qmgr_scan_next(qmgr_clnt_cache)) != 0)
! 	qmgr_check_session(MAIL_TLS_CLNT_CACHE, tls_clnt_cache_path,
! 			   var_smtp_tls_scache_timeout);
!     if (in_path || df_path || tls_srvr_cache_path || tls_clnt_cache_path)
  	return (DONT_WAIT);
      return (WAIT_FOR_EVENT);
  }
***************
*** 433,440 ****
--- 477,488 ----
  	      event_time() + var_min_backoff_time);
      qmgr_incoming = qmgr_scan_create(MAIL_QUEUE_INCOMING);
      qmgr_deferred = qmgr_scan_create(MAIL_QUEUE_DEFERRED);
+     qmgr_srvr_cache = qmgr_scan_create(MAIL_TLS_SRVR_CACHE);
+     qmgr_clnt_cache = qmgr_scan_create(MAIL_TLS_CLNT_CACHE);
      qmgr_scan_request(qmgr_incoming, QMGR_SCAN_START);
      qmgr_deferred_run_event(0, (char *) 0);
+     qmgr_tls_srvr_cache_run_event(0, (char *) 0);
+     qmgr_tls_clnt_cache_run_event(0, (char *) 0);
  }
  
  /* main - the main program */
diff -r -c --new-file postfix-19990906-pl07-vanilla/qmgr/qmgr.h postfix-19990906-pl07/qmgr/qmgr.h
*** postfix-19990906-pl07-vanilla/qmgr/qmgr.h	Thu Sep 23 18:47:44 1999
--- postfix-19990906-pl07/qmgr/qmgr.h	Mon Nov 15 15:31:06 1999
***************
*** 308,313 ****
--- 308,318 ----
  extern void qmgr_scan_request(QMGR_SCAN *, int);
  extern char *qmgr_scan_next(QMGR_SCAN *);
  
+  /*
+   * qmgr_tls.c
+   */
+ extern void qmgr_check_session(const char *, const char *, time_t);
+ 
  /* LICENSE
  /* .ad
  /* .fi
diff -r -c --new-file postfix-19990906-pl07-vanilla/qmgr/qmgr_tls.c postfix-19990906-pl07/qmgr/qmgr_tls.c
*** postfix-19990906-pl07-vanilla/qmgr/qmgr_tls.c	Thu Jan  1 01:00:00 1970
--- postfix-19990906-pl07/qmgr/qmgr_tls.c	Mon Nov 15 15:31:06 1999
***************
*** 0 ****
--- 1,80 ----
+ /*++
+ /* NAME
+ /*      qmgr_tls
+ /* SUMMARY
+ /*      TLS session cache cleanup routine
+ /* SYNOPSIS
+ /*	#include "qmgr.h"
+ /*
+ /*	void qmgr_check_session(queue, path, timeout)
+ /*
+ /* DESCRIPTION
+ /*	This module handles the timeout of TLS session cache entries.
+ /*
+ /*	qmgr_check_session() checks the file pointed to by "path" in the
+ /*	queue "queue" for expiry. In order to not clobber qmgr with the
+ /*	TLS/SSL library stuff at all, the decision is based only on the
+ /*	mtime of the file. This might not be totally accurat, but the files
+ /*	are written nearly immediately after the sessions are created.
+ /*	In the worst case a file is left on disc but the session will not
+ /*	used after load from the library, leading to not more than one
+ /*	wasted load operation.
+ /*
+ /* BUGS
+ /*	There is a race condition possible. If a session is overwritten with
+ /*	a new session between taking the "stat()" and actually doing the
+ /*	remove, a valid session file might be deleted. Possible impact: a
+ /*	new session must be negotiated.
+ /*	This can only happen for client sessions, since server sessions are
+ /*	never being. overwritten.
+ /* LICENSE
+ /* AUTHOR(S)
+ /*      Lutz Jaenicke
+ /*      BTU Cottbus
+ /*      Allgemeine Elektrotechnik
+ /*      Universitaetsplatz 3-4
+ /*      D-03044 Cottbus, Germany
+ /*--*/
+ 
+ #include <sys_defs.h>
+ #include <sys/stat.h>
+ #include <stdio.h>
+ #include <string.h>
+ #include <time.h>
+ 
+ #include <iostuff.h>
+ #include <mail_params.h>
+ #include <mail_queue.h>
+ #include <vstring.h>
+ 
+ #include "qmgr.h"
+ 
+ #define STR     vstring_str
+ 
+ void qmgr_check_session(const char *queue, const char *path, time_t timeout)
+ {
+     VSTRING *buf;
+     int length;
+     struct stat st;
+ 
+     /*
+      * Construct the full file name with directory forest
+      */
+     length = strlen(path) + strlen(queue);
+     buf = vstring_alloc(100 + length);
+     mail_queue_path(buf, queue, path);
+ 
+     /*
+      * Letīs stat the file to find the mtime. If it has not been modified
+      * since "timeout", we remove the file. Possible race condition: If the
+      * session file is overwritten between the "stat()" and the REMOVE, we
+      * might remove a valid session, with the harmless consequence of a
+      * new negotiation.
+      */
+     if (stat(STR(buf), &st) == 0) {
+ 	if (st.st_mtime + timeout < time(NULL))
+ 	    REMOVE(STR(buf));
+     }
+ 
+     vstring_free(buf);
+ }
diff -r -c --new-file postfix-19990906-pl07-vanilla/smtp/Makefile.in postfix-19990906-pl07/smtp/Makefile.in
*** postfix-19990906-pl07-vanilla/smtp/Makefile.in	Sun Nov  7 18:43:56 1999
--- postfix-19990906-pl07/smtp/Makefile.in	Mon Nov 15 15:31:06 1999
***************
*** 86,91 ****
--- 86,92 ----
  smtp.o: ../include/mail_proto.h
  smtp.o: ../include/iostuff.h
  smtp.o: ../include/mail_server.h
+ smtp.o: ../include/pfixtls.h
  smtp.o: smtp.h
  smtp.o: ../include/argv.h
  smtp_addr.o: smtp_addr.c
***************
*** 141,146 ****
--- 142,148 ----
  smtp_connect.o: ../include/argv.h
  smtp_connect.o: ../include/deliver_request.h
  smtp_connect.o: ../include/recipient_list.h
+ smtp_connetc.o: ../include/pfixtls.h
  smtp_connect.o: smtp_addr.h
  smtp_proto.o: smtp_proto.c
  smtp_proto.o: ../include/sys_defs.h
***************
*** 164,169 ****
--- 166,172 ----
  smtp_proto.o: ../include/rec_type.h
  smtp_proto.o: ../include/off_cvt.h
  smtp_proto.o: ../include/mark_corrupt.h
+ smtp_proto.o: ../include/pfixtls.h
  smtp_proto.o: smtp.h
  smtp_proto.o: ../include/argv.h
  smtp_proto.o: quote_821_local.h
***************
*** 174,179 ****
--- 177,184 ----
  smtp_session.o: ../include/vbuf.h
  smtp_session.o: ../include/stringops.h
  smtp_session.o: smtp.h
+ smtp_session.o: ../include/mail_params.h
+ smtp_session.o: ../include/pfixtls.h
  smtp_session.o: ../include/vstring.h
  smtp_session.o: ../include/argv.h
  smtp_session.o: ../include/deliver_request.h
diff -r -c --new-file postfix-19990906-pl07-vanilla/smtp/smtp.c postfix-19990906-pl07/smtp/smtp.c
*** postfix-19990906-pl07-vanilla/smtp/smtp.c	Sat Sep  4 23:41:53 1999
--- postfix-19990906-pl07/smtp/smtp.c	Mon Nov 15 15:31:07 1999
***************
*** 173,178 ****
--- 173,179 ----
  #include <debug_peer.h>
  #include <mail_error.h>
  #include <deliver_pass.h>
+ #include <pfixtls.h>
  
  /* Single server skeleton. */
  
***************
*** 204,209 ****
--- 205,219 ----
  char   *var_fallback_relay;
  char   *var_bestmx_transp;
  char   *var_error_rcpt;
+ int     var_smtp_use_tls;
+ char   *var_smtp_use_tls_recipients;
+ char   *var_smtp_use_tls_sites;
+ int     var_smtp_enforce_tls;
+ char   *var_smtp_enforce_tls_recipients;
+ char   *var_smtp_enforce_tls_sites;
+ char   *var_smtp_no_tls_sites;
+ int     var_smtp_tls_scert_vd;
+ int	var_smtp_tls_note_starttls_offer;
  
   /*
    * Global variables. smtp_errno is set by the address lookup routines and by
***************
*** 312,317 ****
--- 322,337 ----
  static void pre_init(char *unused_name, char **unused_argv)
  {
      debug_peer_init();
+     /*
+      * Initialize the TLS data before entering the chroot jail
+      */
+ #ifdef HAS_SSL
+     if (var_smtp_use_tls || var_smtp_enforce_tls ||
+ 	var_smtp_use_tls_sites[0] || var_smtp_use_tls_recipients[0] ||
+ 	var_smtp_enforce_tls_sites[0] || var_smtp_enforce_tls_recipients[0])
+ 	pfixtls_init_clientengine(var_smtp_tls_scert_vd);
+     smtp_tls_list_init();
+ #endif
  }
  
  /* pre_accept - see if tables have changed */
***************
*** 324,329 ****
--- 344,350 ----
      }
  }
  
+ 
  /* main - pass control to the single-threaded skeleton */
  
  int     main(int argc, char **argv)
***************
*** 334,339 ****
--- 355,365 ----
  	VAR_FALLBACK_RELAY, DEF_FALLBACK_RELAY, &var_fallback_relay, 0, 0,
  	VAR_BESTMX_TRANSP, DEF_BESTMX_TRANSP, &var_bestmx_transp, 0, 0,
  	VAR_ERROR_RCPT, DEF_ERROR_RCPT, &var_error_rcpt, 1, 0,
+ 	VAR_SMTP_USE_TLS_SITES, DEF_SMTP_USE_TLS_SITES, &var_smtp_use_tls_sites, 0, 0,
+         VAR_SMTP_USE_TLS_RCPTS, DEF_SMTP_USE_TLS_RCPTS, &var_smtp_use_tls_recipients, 0, 0,
+         VAR_SMTP_ENF_TLS_SITES, DEF_SMTP_ENF_TLS_SITES, &var_smtp_enforce_tls_sites, 0, 0,
+         VAR_SMTP_ENF_TLS_RCPTS, DEF_SMTP_ENF_TLS_RCPTS, &var_smtp_enforce_tls_recipients, 0, 0,
+         VAR_SMTP_NO_TLS_SITES, DEF_SMTP_NO_TLS_SITES, &var_smtp_no_tls_sites, 0, 0,
  	0,
      };
      static CONFIG_INT_TABLE int_table[] = {
***************
*** 346,357 ****
--- 372,387 ----
  	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,
+ 	VAR_SMTP_TLS_NOTEOFFER, DEF_SMTP_TLS_NOTEOFFER, &var_smtp_tls_note_starttls_offer,
  	0,
      };
  
diff -r -c --new-file postfix-19990906-pl07-vanilla/smtp/smtp.h postfix-19990906-pl07/smtp/smtp.h
*** postfix-19990906-pl07-vanilla/smtp/smtp.h	Thu Jun 24 03:14:23 1999
--- postfix-19990906-pl07/smtp/smtp.h	Mon Nov 15 15:31:07 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
***************
*** 56,64 ****
      char   *addr;			/* mail exchanger */
      char   *namaddr;			/* mail exchanger */
      int     best;			/* most preferred host */
  } SMTP_SESSION;
  
! extern SMTP_SESSION *smtp_session_alloc(VSTREAM *, char *, char *);
  extern void smtp_session_free(SMTP_SESSION *);
  
   /*
--- 57,68 ----
      char   *addr;			/* mail exchanger */
      char   *namaddr;			/* mail exchanger */
      int     best;			/* most preferred host */
+     int     tls_use_tls;		/* can do TLS */
+     int     tls_enforce_tls;		/* must do TLS */
  } SMTP_SESSION;
  
! extern void smtp_tls_list_init(void);
! extern SMTP_SESSION *smtp_session_alloc(char *, VSTREAM *, char *, char *);
  extern void smtp_session_free(SMTP_SESSION *);
  
   /*
diff -r -c --new-file postfix-19990906-pl07-vanilla/smtp/smtp_connect.c postfix-19990906-pl07/smtp/smtp_connect.c
*** postfix-19990906-pl07-vanilla/smtp/smtp_connect.c	Wed Jul  7 19:44:54 1999
--- postfix-19990906-pl07/smtp/smtp_connect.c	Mon Nov 15 15:31:07 1999
***************
*** 106,111 ****
--- 106,112 ----
  
  #include <mail_params.h>
  #include <own_inet_addr.h>
+ #include <pfixtls.h>
  
  /* DNS library. */
  
***************
*** 118,124 ****
  
  /* smtp_connect_addr - connect to explicit address */
  
! static SMTP_SESSION *smtp_connect_addr(DNS_RR *addr, unsigned port,
  				               VSTRING *why)
  {
      char   *myname = "smtp_connect_addr";
--- 119,125 ----
  
  /* smtp_connect_addr - connect to explicit address */
  
! static SMTP_SESSION *smtp_connect_addr(char *dest, DNS_RR *addr, unsigned port,
  				               VSTRING *why)
  {
      char   *myname = "smtp_connect_addr";
***************
*** 225,232 ****
  	vstream_fclose(stream);
  	return (0);
      }
      vstream_ungetc(stream, ch);
!     return (smtp_session_alloc(stream, addr->name, inet_ntoa(sin.sin_addr)));
  }
  
  /* smtp_connect_host - direct connection to host */
--- 226,236 ----
  	vstream_fclose(stream);
  	return (0);
      }
+ #ifdef HAS_SSL
+     pfixtls_setfd(sock);
+ #endif
      vstream_ungetc(stream, ch);
!     return (smtp_session_alloc(dest, stream, addr->name, inet_ntoa(sin.sin_addr)));
  }
  
  /* smtp_connect_host - direct connection to host */
***************
*** 244,250 ****
       */
      addr_list = smtp_host_addr(host, why);
      for (addr = addr_list; addr; addr = addr->next) {
! 	if ((session = smtp_connect_addr(addr, port, why)) != 0) {
  	    session->best = 1;
  	    break;
  	}
--- 248,254 ----
       */
      addr_list = smtp_host_addr(host, why);
      for (addr = addr_list; addr; addr = addr->next) {
! 	if ((session = smtp_connect_addr(host, addr, port, why)) != 0) {
  	    session->best = 1;
  	    break;
  	}
***************
*** 271,277 ****
       */
      addr_list = smtp_domain_addr(name, why);
      for (addr = addr_list; addr; addr = addr->next) {
! 	if ((session = smtp_connect_addr(addr, port, why)) != 0) {
  	    session->best = (addr->pref == addr_list->pref);
  	    break;
  	}
--- 275,281 ----
       */
      addr_list = smtp_domain_addr(name, why);
      for (addr = addr_list; addr; addr = addr->next) {
! 	if ((session = smtp_connect_addr(name, addr, port, why)) != 0) {
  	    session->best = (addr->pref == addr_list->pref);
  	    break;
  	}
diff -r -c --new-file postfix-19990906-pl07-vanilla/smtp/smtp_proto.c postfix-19990906-pl07/smtp/smtp_proto.c
*** postfix-19990906-pl07-vanilla/smtp/smtp_proto.c	Mon Jul  5 23:25:08 1999
--- postfix-19990906-pl07/smtp/smtp_proto.c	Mon Nov 15 15:31:07 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.
***************
*** 201,207 ****
  				   session->namaddr,
  				   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
--- 204,209 ----
***************
*** 210,215 ****
--- 212,218 ----
       * advertises a really huge message size limit.
       */
      lines = resp->str;
+     oldfeatures = state->features;		/* remember */
      while ((words = mystrtok(&lines, "\n")) != 0) {
  	if (mystrtok(&words, "- ") && (word = mystrtok(&words, " \t")) != 0) {
  	    if (strcasecmp(word, "8BITMIME") == 0)
***************
*** 218,223 ****
--- 221,228 ----
  		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;
  	    else if (strcasecmp(word, var_myhostname) == 0) {
  		msg_warn("host %s replied to HELO/EHLO with my own hostname %s",
  			 session->namaddr, var_myhostname);
***************
*** 229,234 ****
--- 234,339 ----
      }
      if (msg_verbose)
  	msg_info("server features: 0x%x", state->features);
+ #ifdef HAS_SSL
+     if ((state->features & SMTP_FEATURE_STARTTLS) &&
+ 	(var_smtp_tls_note_starttls_offer) &&
+ 	(!(session->tls_enforce_tls || session->tls_use_tls)))
+  	msg_info("Host offered STARTTLS: [%s]", session->host);
+     if ((session->tls_enforce_tls) &&
+ 	!(state->features & SMTP_FEATURE_STARTTLS))
+     {
+ 	/*
+ 	 * We are enforced to use TLS but it is not offered, so we will give
+ 	 * up on this host. We won't even try STARTTLS, because we could
+ 	 * receive a "500 command unrecognized" which would bounce the
+ 	 * message. We instead want to delay until STARTTLS becomes
+ 	 * available.
+ 	 */
+ 	return (smtp_site_fail(state, 450, "Could not start TLS"));
+ 
+     }
+     if ((state->features & SMTP_FEATURE_STARTTLS) &&
+ 	((session->tls_use_tls) || (session->tls_enforce_tls))) {
+ 	/*
+          * Try to use the TLS feature
+          */
+ 	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 (session->tls_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(session->host))
+ 		return (smtp_site_fail(state, 450, "Could not start TLS"));
+ 
+ 	    /*
+ 	     * Now the connection is established and maybe we do have a
+ 	     * validated cert with a CommonName in it. For logging, we
+ 	     * will check the CommonName against the name of the host.
+ 	     * In a later development stage, we may decide to immediately
+ 	     * close the connection, but this must be based on a "per
+ 	     * site" policy.
+ 	     */
+ 	    if (pfixtls_peer_CN != NULL) {
+ 		if (!pfixtls_peer_verified) {
+ 		    msg_info("Peer certficate could not be verified");
+ 		    if (session->tls_enforce_tls)
+ 			return(smtp_site_fail(state, 450, "TLS-failure: Could not verify certificate"));
+ 		}
+ 		if (strcasecmp(pfixtls_peer_CN, session->host)) {
+ 		    msg_info("Hostname/Certificate mismatch: %s != %s",
+ 			session->host, pfixtls_peer_CN);
+ 		    if (session->tls_enforce_tls)
+ 			return (smtp_site_fail(state, 450, "TLS-failure: Hostname/Certificate mismatch"));
+ 		} else if (msg_verbose) {
+ 		    msg_info("Match: %s == %s", pfixtls_peer_CN,
+ 			 session->host);
+ 		}
+ 	    } else if (session->tls_enforce_tls)
+ 		return (smtp_site_fail(state, 450, "TLS-failure: Cannot verify hostname"));
+ 
+ 	    /*
+ 	     * 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?
+ 	     */
+ 
+ 	}
+     }
+ #endif
      return (0);
  }
  
diff -r -c --new-file postfix-19990906-pl07-vanilla/smtp/smtp_session.c postfix-19990906-pl07/smtp/smtp_session.c
*** postfix-19990906-pl07-vanilla/smtp/smtp_session.c	Thu Jun 24 03:14:23 1999
--- postfix-19990906-pl07/smtp/smtp_session.c	Mon Nov 15 15:31:07 1999
***************
*** 42,54 ****
  #include <vstream.h>
  #include <stringops.h>
  
  /* Application-specific. */
  
  #include "smtp.h"
  
  /* smtp_session_alloc - allocate and initialize SMTP_SESSION structure */
  
! SMTP_SESSION *smtp_session_alloc(VSTREAM *stream, char *host, char *addr)
  {
      SMTP_SESSION *session;
  
--- 42,79 ----
  #include <vstream.h>
  #include <stringops.h>
  
+ #include <mail_params.h>
+ #include <domain_list.h>
+ #include <pfixtls.h>
+ 
  /* Application-specific. */
  
  #include "smtp.h"
  
+ #ifdef HAS_SSL
+ /* static lists */
+ static DOMAIN_LIST *tls_no_tls_sites;
+ static DOMAIN_LIST *tls_use_tls_sites;
+ static DOMAIN_LIST *tls_use_tls_recipients;
+ static DOMAIN_LIST *tls_enforce_tls_sites;
+ static DOMAIN_LIST *tls_enforce_tls_recipients;
+ 
+ /* smtp_tls_list_init - initialize lists */
+ 
+ void smtp_tls_list_init(void)
+ {
+     tls_no_tls_sites = domain_list_init(var_smtp_no_tls_sites);
+     tls_use_tls_sites = domain_list_init(var_smtp_use_tls_sites);
+     tls_use_tls_recipients = domain_list_init(var_smtp_use_tls_recipients);
+     tls_enforce_tls_sites = domain_list_init(var_smtp_enforce_tls_sites);
+     tls_enforce_tls_recipients = 
+ 	domain_list_init(var_smtp_enforce_tls_recipients);
+ }
+ #endif
+ 
  /* smtp_session_alloc - allocate and initialize SMTP_SESSION structure */
  
! SMTP_SESSION *smtp_session_alloc(char *dest, VSTREAM *stream, char *host, char *addr)
  {
      SMTP_SESSION *session;
  
***************
*** 58,63 ****
--- 83,103 ----
      session->addr = mystrdup(addr);
      session->namaddr = concatenate(host, "[", addr, "]", (char *) 0);
      session->best = 1;
+     session->tls_use_tls = session->tls_enforce_tls = 0;
+ #ifdef HAS_SSL
+     if ((var_smtp_enforce_tls &&
+ 	 !domain_list_match(tls_no_tls_sites, session->host)) ||
+ 	domain_list_match(tls_enforce_tls_sites, session->host) ||
+ 	domain_list_match(tls_enforce_tls_recipients, session->host) ||
+ 	domain_list_match(tls_enforce_tls_recipients, dest))
+       session->tls_enforce_tls = session->tls_use_tls = 1;
+     else if ((var_smtp_use_tls &&
+          !domain_list_match(tls_no_tls_sites, session->host)) ||
+ 	domain_list_match(tls_use_tls_sites, session->host) ||
+ 	domain_list_match(tls_use_tls_recipients, session->host) ||
+ 	domain_list_match(tls_use_tls_recipients, dest))
+       session->tls_use_tls = 1;
+ #endif
      return (session);
  }
  
***************
*** 65,70 ****
--- 105,114 ----
  
  void    smtp_session_free(SMTP_SESSION *session)
  {
+ #ifdef HAS_SSL
+     vstream_fflush(session->stream);
+     pfixtls_stop_clienttls(0);
+ #endif
      vstream_fclose(session->stream);
      myfree(session->host);
      myfree(session->addr);
diff -r -c --new-file postfix-19990906-pl07-vanilla/smtpd/Makefile.in postfix-19990906-pl07/smtpd/Makefile.in
*** postfix-19990906-pl07-vanilla/smtpd/Makefile.in	Sun Nov  7 18:43:36 1999
--- postfix-19990906-pl07/smtpd/Makefile.in	Mon Nov 15 15:31:07 1999
***************
*** 112,117 ****
--- 112,118 ----
  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-19990906-pl07-vanilla/smtpd/smtpd.c postfix-19990906-pl07/smtpd/smtpd.c
*** postfix-19990906-pl07-vanilla/smtpd/smtpd.c	Thu Nov  4 15:47:32 1999
--- postfix-19990906-pl07/smtpd/smtpd.c	Mon Nov 15 15:31:08 1999
***************
*** 228,233 ****
--- 228,234 ----
  #include <mail_flush.h>
  #include <mail_stream.h>
  #include <mail_queue.h>
+ #include <pfixtls.h>
  
  /* Single-threaded server skeleton. */
  
***************
*** 251,256 ****
--- 252,258 ----
  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 */
***************
*** 278,283 ****
--- 280,291 ----
  char   *var_always_bcc;
  char   *var_error_rcpt;
  int     var_smtpd_delay_reject;
+ 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;
+ int	var_smtpd_tls_received_header;
  
   /*
    * Global state, for stand-alone mode queue file cleanup. When this is
***************
*** 360,365 ****
--- 368,377 ----
      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);
  }
***************
*** 619,624 ****
--- 631,658 ----
  		"Received: from %s (%s [%s])",
  		state->helo_name ? state->helo_name : state->name,
  		state->name, state->addr);
+     if (var_smtpd_tls_received_header && state->tls_active) {
+ 	rec_fprintf(state->cleanup, REC_TYPE_NORM,
+ 		    "\tusing %s with cipher %s (%d/%d bits)",
+ 		    state->tls_protocol, state->tls_cipher_name,
+ 		    state->tls_usebits, state->tls_algbits);
+ 	if (state->tls_client_CN) {
+ 	    if (state->tls_peer_verified)
+ 		rec_fprintf(state->cleanup, REC_TYPE_NORM,
+ 			"\tClient CN \"%s\", Issuer CN \"%s\" (verified OK)",
+ 			state->tls_client_CN, state->tls_issuer_CN);
+ 	    else
+ 		rec_fprintf(state->cleanup, REC_TYPE_NORM,
+ 			"\tClient CN \"%s\", Issuer CN \"%s\" (not verified)",
+ 			state->tls_client_CN, state->tls_issuer_CN);
+ 	}
+ 	else if (var_smtpd_tls_ask_ccert)
+ 	    rec_fprintf(state->cleanup, REC_TYPE_NORM,
+ 			"\tClient did not present a certificate");
+ 	else
+ 	    rec_fprintf(state->cleanup, REC_TYPE_NORM,
+ 			"\tNo client certificate requested");
+     }
      if (state->rcpt_count == 1 && state->recipient) {
  	rec_fprintf(state->cleanup, REC_TYPE_NORM,
  		    "\tby %s (%s) with %s id %s",
***************
*** 882,887 ****
--- 916,1001 ----
      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_peer_verified = pfixtls_peer_verified;
+     state->tls_peer_subject = pfixtls_peer_subject;
+     state->tls_peer_issuer = pfixtls_peer_issuer;
+     state->tls_peer_fingerprint = pfixtls_peer_fingerprint;
+     state->tls_client_CN = pfixtls_peer_CN;
+     state->tls_issuer_CN = pfixtls_issuer_CN;
+     state->tls_protocol = pfixtls_protocol;
+     state->tls_cipher_name = pfixtls_cipher_name;
+     state->tls_usebits = pfixtls_cipher_usebits;
+     state->tls_algbits = pfixtls_cipher_algbits;
+     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)
+ {
+     int failure = 0;
+ 
+     if (state->reason && state->where && strcmp(state->where, SMTPD_AFTER_DOT))
+ 	failure = 1;
+ #ifdef HAS_SSL
+     vstream_fflush(state->client);
+     if (state->tls_active)
+ 	pfixtls_stop_servertls(failure);
+ #endif
+     state->tls_active = 0;
+     state->tls_peer_subject = NULL;
+     state->tls_peer_issuer = NULL;
+     state->tls_peer_fingerprint = NULL;
+     state->tls_client_CN = NULL;
+     state->tls_issuer_CN = NULL;
+     state->tls_protocol = NULL;
+     state->tls_cipher_name = NULL;
+     state->tls_usebits = 0;
+     state->tls_algbits = 0;
+ }
+ 
   /*
    * The table of all SMTP commands that we know.
    */
***************
*** 893,898 ****
--- 1007,1013 ----
  static SMTPD_CMD smtpd_cmd_table[] = {
      "HELO", helo_cmd,
      "EHLO", ehlo_cmd,
+     "STARTTLS", starttls_cmd,
      "MAIL", mail_cmd,
      "RCPT", rcpt_cmd,
      "DATA", data_cmd,
***************
*** 977,985 ****
  		state->error_count++;
  		continue;
  	    }
  	    state->where = cmdp->name;
! 	    if (cmdp->action(state, argc, argv) != 0)
  		state->error_count++;
  
  	    if (cmdp->action == quit_cmd)
  		break;
--- 1092,1119 ----
  		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++;
+ 		/*
+ 		 * Die after TLS negotiation failure, as there is no
+ 		 * stable way to recover from a possible mixture of
+ 		 * TLS and SMTP protocol from the client.
+ 		 */
+ 		if (cmdp->action == starttls_cmd)
+ 		    break;
+ 	    }
  
  	    if (cmdp->action == quit_cmd)
  		break;
***************
*** 1013,1018 ****
--- 1147,1153 ----
       * Cleanup whatever information the client gave us during the SMTP
       * dialog.
       */
+     tls_reset(state);
      helo_reset(state);
      mail_reset(state);
      rcpt_reset(state);
***************
*** 1042,1047 ****
--- 1177,1185 ----
       * 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);
  
      /*
***************
*** 1060,1065 ****
--- 1198,1210 ----
  	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.
***************
*** 1138,1143 ****
--- 1283,1293 ----
      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 */
***************
*** 1161,1175 ****
--- 1311,1332 ----
  	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_DELAY_REJECT, DEF_SMTPD_DELAY_REJECT, &var_smtpd_delay_reject,
+ 	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,
+ 	VAR_SMTPD_TLS_RECHEAD, DEF_SMTPD_TLS_RECHEAD, &var_smtpd_tls_received_header,
  	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,
***************
*** 1196,1198 ****
--- 1353,1356 ----
  		       MAIL_SERVER_PRE_ACCEPT, pre_accept,
  		       0);
  }
+ 
diff -r -c --new-file postfix-19990906-pl07-vanilla/smtpd/smtpd.c.orig postfix-19990906-pl07/smtpd/smtpd.c.orig
*** postfix-19990906-pl07-vanilla/smtpd/smtpd.c.orig	Thu Jan  1 01:00:00 1970
--- postfix-19990906-pl07/smtpd/smtpd.c.orig	Thu Nov  4 15:47:32 1999
***************
*** 0 ****
--- 1,1198 ----
+ /*++
+ /* NAME
+ /*	smtpd 8
+ /* SUMMARY
+ /*	Postfix SMTP server
+ /* SYNOPSIS
+ /*	\fBsmtpd\fR [generic Postfix daemon options]
+ /* DESCRIPTION
+ /*	The SMTP server accepts network connection requests
+ /*	and performs zero or more SMTP transactions per connection.
+ /*	Each received message is piped through the \fBcleanup\fR(8)
+ /*	daemon, and is placed into the \fBincoming\fR queue as one
+ /*	single queue file.  For this mode of operation, the program
+ /*	expects to be run from the \fBmaster\fR(8) process manager.
+ /*
+ /*	Alternatively, the SMTP server takes an established
+ /*	connection on standard input and deposits messages directly
+ /*	into the \fBmaildrop\fR queue. In this so-called stand-alone
+ /*	mode, the SMTP server can accept mail even while the mail
+ /*	system is not running.
+ /*
+ /*	The SMTP server implements a variety of policies for connection
+ /*	requests, and for parameters given to \fBHELO, MAIL FROM, VRFY\fR
+ /*	and \fBRCPT TO\fR commands. They are detailed below and in the
+ /*	\fBmain.cf\fR configuration file.
+ /* SECURITY
+ /* .ad
+ /* .fi
+ /*	The SMTP server is moderately security-sensitive. It talks to SMTP
+ /*	clients and to DNS servers on the network. The SMTP server can be
+ /*	run chrooted at fixed low privilege.
+ /* STANDARDS
+ /*	RFC 821 (SMTP protocol)
+ /*	RFC 1123 (Host requirements)
+ /*	RFC 1651 (SMTP service extensions)
+ /*	RFC 1652 (8bit-MIME transport)
+ /*	RFC 1854 (SMTP Pipelining)
+ /*	RFC 1870 (Message Size Declaration)
+ /*	RFC 1985 (ETRN command) (partial)
+ /* DIAGNOSTICS
+ /*	Problems and transactions are logged to \fBsyslogd\fR(8).
+ /*
+ /*	Depending on the setting of the \fBnotify_classes\fR parameter,
+ /*	the postmaster is notified of bounces, protocol problems,
+ /*	policy violations, and of other trouble.
+ /* BUGS
+ /*	RFC 1985 is implemented by forcing delivery of all deferred mail.
+ /* CONFIGURATION PARAMETERS
+ /* .ad
+ /* .fi
+ /*	The following \fBmain.cf\fR parameters are especially relevant to
+ /*	this program. See the Postfix \fBmain.cf\fR file for syntax details
+ /*	and for default values. Use the \fBpostfix reload\fR command after
+ /*	a configuration change.
+ /* .SH Miscellaneous
+ /* .ad
+ /* .fi
+ /* .IP \fBalways_bcc\fR
+ /*	Address to send a copy of each message that enters the system.
+ /* .IP \fBcommand_directory\fR
+ /*	Location of Postfix support commands (default:
+ /*	\fB$program_directory\fR).
+ /* .IP \fBdebug_peer_level\fR
+ /*	Increment in verbose logging level when a remote host matches a
+ /*	pattern in the \fBdebug_peer_list\fR parameter.
+ /* .IP \fBdebug_peer_list\fR
+ /*	List of domain or network patterns. When a remote host matches
+ /*	a pattern, increase the verbose logging level by the amount
+ /*	specified in the \fBdebug_peer_level\fR parameter.
+ /* .IP \fBerror_notice_recipient\fR
+ /*	Recipient of protocol/policy/resource/software error notices.
+ /* .IP \fBhopcount_limit\fR
+ /*	Limit the number of \fBReceived:\fR message headers.
+ /* .IP \fBnotify_classes\fR
+ /*	List of error classes. Of special interest are:
+ /* .RS
+ /* .IP \fBpolicy\fR
+ /*	When a client violates any policy, mail a transcript of the
+ /*	entire SMTP session to the postmaster.
+ /* .IP \fBprotocol\fR
+ /*	When a client violates the SMTP protocol or issues an unimplemented
+ /*	command, mail a transcript of the entire SMTP session to the
+ /*	postmaster.
+ /* .RE
+ /* .IP \fBsmtpd_banner\fR
+ /*	Text that follows the \fB220\fR status code in the SMTP greeting banner.
+ /* .IP \fBsmtpd_recipient_limit\fR
+ /*	Restrict the number of recipients that the SMTP server accepts
+ /*	per message delivery.
+ /* .IP \fBsmtpd_timeout\fR
+ /*	Limit the time to send a server response and to receive a client
+ /*	request.
+ /* .SH "Resource controls"
+ /* .ad
+ /* .fi
+ /* .IP \fBline_length_limit\fR
+ /*	Limit the amount of memory in bytes used for the handling of
+ /*	partial input lines.
+ /* .IP \fBmessage_size_limit\fR
+ /*	Limit the total size in bytes of a message, including on-disk
+ /*	storage for envelope information.
+ /* .IP \fBqueue_minfree\fR
+ /*	Minimal amount of free space in bytes in the queue file system
+ /*	for the SMTP server to accept any mail at all.
+ /* .SH Tarpitting
+ /* .ad
+ /* .fi
+ /* .IP \fBsmtpd_error_sleep_time\fR
+ /*	Time to wait in seconds before sending a 4xx or 5xx server error
+ /*	response.
+ /* .IP \fBsmtpd_soft_error_limit\fR
+ /*	When an SMTP client has made this number of errors, wait
+ /*	\fIerror_count\fR seconds before responding to any client request.
+ /* .IP \fBsmtpd_hard_error_limit\fR
+ /*	Disconnect after a client has made this number of errors.
+ /* .SH "UCE control restrictions"
+ /* .ad
+ /* .fi
+ /* .IP \fBsmtpd_client_restrictions\fR
+ /*	Restrict what clients may connect to this mail system.
+ /* .IP \fBsmtpd_helo_required\fR
+ /*	Require that clients introduce themselves at the beginning
+ /*	of an SMTP session.
+ /* .IP \fBsmtpd_helo_restrictions\fR
+ /*	Restrict what client hostnames are allowed in \fBHELO\fR and
+ /*	\fBEHLO\fR commands.
+ /* .IP \fBsmtpd_sender_restrictions\fR
+ /*	Restrict what sender addresses are allowed in \fBMAIL FROM\fR commands.
+ /* .IP \fBsmtpd_recipient_restrictions\fR
+ /*	Restrict what recipient addresses are allowed in \fBRCPT TO\fR commands.
+ /* .IP \fBsmtpd_etrn_restrictions\fR
+ /*	Restrict what domain names can be used in \fBETRN\fR commands,
+ /*	and what clients may issue \fBETRN\fR commands.
+ /* .IP \fBmaps_rbl_domains\fR
+ /*	List of DNS domains that publish the addresses of blacklisted
+ /*	hosts.
+ /* .IP \fBrelay_domains\fR
+ /*	Restrict what domains or networks this mail system will relay
+ /*	mail from or to.
+ /* .SH "UCE control responses"
+ /* .ad
+ /* .fi
+ /* .IP \fBaccess_map_reject_code\fR
+ /*	Server response when a client violates an access database restriction.
+ /* .IP \fBinvalid_hostname_reject_code\fR
+ /*	Server response when a client violates the \fBreject_invalid_hostname\fR
+ /*	restriction.
+ /* .IP \fBmaps_rbl_reject_code\fR
+ /*	Server response when a client violates the \fBmaps_rbl_domains\fR
+ /*	restriction.
+ /* .IP \fBreject_code\fR
+ /*	Response code when the client matches a \fBreject\fR restriction.
+ /* .IP \fBrelay_domains_reject_code\fR
+ /*	Server response when a client attempts to violate the mail relay
+ /*	policy.
+ /* .IP \fBunknown_address_reject_code\fR
+ /*	Server response when a client violates the \fBreject_unknown_address\fR
+ /*	restriction.
+ /* .IP \fBunknown_client_reject_code\fR
+ /*	Server response when a client without address to name mapping
+ /*	violates the \fBreject_unknown_clients\fR restriction.
+ /* .IP \fBunknown_hostname_reject_code\fR
+ /*	Server response when a client violates the \fBreject_unknown_hostname\fR
+ /*	restriction.
+ /* SEE ALSO
+ /*	cleanup(8) message canonicalization
+ /*	master(8) process manager
+ /*	syslogd(8) system logging
+ /* 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
+ /*--*/
+ 
+ /* System library. */
+ 
+ #include <sys_defs.h>
+ #include <sys/socket.h>
+ #include <sys/stat.h>
+ #include <netinet/in.h>
+ #include <arpa/inet.h>
+ #include <netdb.h>
+ #include <string.h>
+ #include <stdio.h>			/* remove() */
+ #include <setjmp.h>
+ #include <unistd.h>
+ #include <stdlib.h>
+ #include <time.h>
+ #include <errno.h>
+ #include <ctype.h>
+ #include <signal.h>
+ 
+ #ifdef STRCASECMP_IN_STRINGS_H
+ #include <strings.h>
+ #endif
+ 
+ /* Utility library. */
+ 
+ #include <msg.h>
+ #include <mymalloc.h>
+ #include <vstring.h>
+ #include <vstream.h>
+ #include <vstring_vstream.h>
+ #include <stringops.h>
+ #include <events.h>
+ #include <smtp_stream.h>
+ #include <valid_hostname.h>
+ #include <dict.h>
+ 
+ /* Global library. */
+ 
+ #include <peer_name.h>
+ #include <mail_params.h>
+ #include <record.h>
+ #include <rec_type.h>
+ #include <mail_proto.h>
+ #include <cleanup_user.h>
+ #include <mail_date.h>
+ #include <mail_conf.h>
+ #include <off_cvt.h>
+ #include <debug_peer.h>
+ #include <mail_error.h>
+ #include <mail_flush.h>
+ #include <mail_stream.h>
+ #include <mail_queue.h>
+ 
+ /* Single-threaded server skeleton. */
+ 
+ #include <mail_server.h>
+ 
+ /* Application-specific */
+ 
+ #include "smtpd_token.h"
+ #include "smtpd.h"
+ #include "smtpd_check.h"
+ #include "smtpd_chat.h"
+ 
+  /*
+   * Tunable parameters. Make sure that there is some bound on the length of
+   * an SMTP command, so that the mail system stays in control even when a
+   * malicious client sends commands of unreasonable length (qmail-dos-1).
+   * Make sure there is some bound on the number of recipients, so that the
+   * mail system stays in control even when a malicious client sends an
+   * unreasonable number of recipients (qmail-dos-2).
+   */
+ int     var_smtpd_rcpt_limit;
+ int     var_smtpd_tmout;
+ char   *var_relay_domains;
+ int     var_smtpd_soft_erlim;
+ int     var_smtpd_hard_erlim;
+ int     var_queue_minfree;		/* XXX use off_t */
+ char   *var_smtpd_banner;
+ char   *var_debug_peer_list;
+ int     var_debug_peer_level;
+ char   *var_notify_classes;
+ char   *var_client_checks;
+ char   *var_helo_checks;
+ char   *var_mail_checks;
+ char   *var_rcpt_checks;
+ char   *var_etrn_checks;
+ int     var_unk_client_code;
+ int     var_bad_name_code;
+ int     var_unk_name_code;
+ int     var_unk_addr_code;
+ int     var_relay_code;
+ int     var_maps_rbl_code;
+ int     var_access_map_code;
+ char   *var_maps_rbl_domains;
+ int     var_helo_required;
+ int     var_reject_code;
+ int     var_smtpd_err_sleep;
+ int     var_non_fqdn_code;
+ char   *var_always_bcc;
+ char   *var_error_rcpt;
+ int     var_smtpd_delay_reject;
+ 
+  /*
+   * Global state, for stand-alone mode queue file cleanup. When this is
+   * non-null at cleanup time, the named file is removed.
+   */
+ char   *smtpd_path;
+ 
+ /* collapse_args - put arguments together again */
+ 
+ static void collapse_args(int argc, SMTPD_TOKEN *argv)
+ {
+     int     i;
+ 
+     for (i = 2; i < argc; i++) {
+ 	vstring_strcat(argv[1].vstrval, " ");
+ 	vstring_strcat(argv[1].vstrval, argv[i].strval);
+     }
+     argv[1].strval = vstring_str(argv[1].vstrval);
+ }
+ 
+ /* helo_cmd - process HELO command */
+ 
+ static int helo_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
+ {
+     char   *err;
+ 
+     if (argc < 2) {
+ 	state->error_mask |= MAIL_ERROR_PROTOCOL;
+ 	smtpd_chat_reply(state, "501 Syntax: HELO hostname");
+ 	return (-1);
+     }
+     if (state->helo_name != 0) {
+ 	state->error_mask |= MAIL_ERROR_PROTOCOL;
+ 	smtpd_chat_reply(state, "503 Duplicate HELO/EHLO");
+ 	return (-1);
+     }
+     collapse_args(argc, argv);
+     if (SMTPD_STAND_ALONE(state) == 0
+ 	&& var_smtpd_delay_reject == 0
+ 	&& (err = smtpd_check_helo(state, argv[1].strval)) != 0) {
+ 	smtpd_chat_reply(state, "%s", err);
+ 	return (-1);
+     }
+     state->helo_name = mystrdup(printable(argv[1].strval, '?'));
+     state->protocol = "SMTP";
+     smtpd_chat_reply(state, "250 %s", var_myhostname);
+     return (0);
+ }
+ 
+ /* ehlo_cmd - process EHLO command */
+ 
+ static int ehlo_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
+ {
+     char   *err;
+ 
+     if (argc < 2) {
+ 	state->error_mask |= MAIL_ERROR_PROTOCOL;
+ 	smtpd_chat_reply(state, "501 Syntax: EHLO hostname");
+ 	return (-1);
+     }
+     if (state->helo_name != 0) {
+ 	state->error_mask |= MAIL_ERROR_PROTOCOL;
+ 	smtpd_chat_reply(state, "503 Error: duplicate HELO/EHLO");
+ 	return (-1);
+     }
+     collapse_args(argc, argv);
+     if (SMTPD_STAND_ALONE(state) == 0
+ 	&& var_smtpd_delay_reject == 0
+ 	&& (err = smtpd_check_helo(state, argv[1].strval)) != 0) {
+ 	smtpd_chat_reply(state, "%s", err);
+ 	return (-1);
+     }
+     state->helo_name = mystrdup(printable(argv[1].strval, '?'));
+     state->protocol = "ESMTP";
+     smtpd_chat_reply(state, "250-%s", var_myhostname);
+     smtpd_chat_reply(state, "250-PIPELINING");
+     if (var_message_limit)
+ 	smtpd_chat_reply(state, "250-SIZE %lu",
+ 			 (unsigned long) var_message_limit);	/* XXX */
+     else
+ 	smtpd_chat_reply(state, "250-SIZE");
+     smtpd_chat_reply(state, "250-ETRN");
+     smtpd_chat_reply(state, "250 8BITMIME");
+     return (0);
+ }
+ 
+ /* helo_reset - reset HELO/EHLO command stuff */
+ 
+ static void helo_reset(SMTPD_STATE *state)
+ {
+     if (state->helo_name)
+ 	myfree(state->helo_name);
+     state->helo_name = 0;
+ }
+ 
+ /* mail_open_stream - open mail destination */
+ 
+ static void mail_open_stream(SMTPD_STATE *state)
+ {
+     char   *postdrop_command;
+ 
+     /*
+      * If running from the master or from inetd, connect to the cleanup
+      * service.
+      */
+     if (SMTPD_STAND_ALONE(state) == 0) {
+ 	state->dest = mail_stream_service(MAIL_CLASS_PRIVATE,
+ 					  MAIL_SERVICE_CLEANUP);
+ 	if (state->dest == 0
+ 	 || mail_print(state->dest->stream, "%d", CLEANUP_FLAG_FILTER) != 0)
+ 	    msg_fatal("unable to connect to the %s %s service",
+ 		      MAIL_CLASS_PRIVATE, MAIL_SERVICE_CLEANUP);
+     }
+ 
+     /*
+      * Otherwise, if the maildrop is writable, create a maildrop file.
+      * Arrange for pickup service notification. Make a copy of the pathname
+      * so that the file can be deleted in case of a fatal run-time error.
+      */
+     else if (access(MAIL_QUEUE_MAILDROP, W_OK) == 0) {
+ 	state->dest = mail_stream_file(MAIL_QUEUE_MAILDROP,
+ 				    MAIL_CLASS_PUBLIC, MAIL_SERVICE_PICKUP);
+ 	smtpd_path = mystrdup(VSTREAM_PATH(state->dest->stream));
+     }
+ 
+     /*
+      * Otherwise, pipe the message through the privileged postdrop helper.
+      * XXX Make postdrop a manifest constant.
+      */
+     else {
+ 	postdrop_command = concatenate(var_command_dir, "/postdrop",
+ 			      msg_verbose ? " -v" : (char *) 0, (char *) 0);
+ 	state->dest = mail_stream_command(postdrop_command);
+ 	if (state->dest == 0)
+ 	    msg_fatal("unable to execute %s", postdrop_command);
+ 	myfree(postdrop_command);
+     }
+     state->cleanup = state->dest->stream;
+     state->queue_id = mystrdup(state->dest->id);
+ }
+ 
+ /* mail_cmd - process MAIL command */
+ 
+ static int mail_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
+ {
+     char   *err;
+     int     narg;
+     off_t   size = 0;
+     char   *arg;
+ 
+     /*
+      * Sanity checks. XXX Ignore bad SIZE= values until we can reliably and
+      * portably detect overflows while converting from string to off_t.
+      */
+     if (var_helo_required && state->helo_name == 0) {
+ 	state->error_mask |= MAIL_ERROR_POLICY;
+ 	smtpd_chat_reply(state, "503 Error: send HELO/EHLO first");
+ 	return (-1);
+     }
+     if (state->cleanup != 0) {
+ 	state->error_mask |= MAIL_ERROR_PROTOCOL;
+ 	smtpd_chat_reply(state, "503 Error: nested MAIL command");
+ 	return (-1);
+     }
+     if (argc < 4
+ 	|| strcasecmp(argv[1].strval, "from") != 0
+ 	|| strcmp(argv[2].strval, ":") != 0) {
+ 	state->error_mask |= MAIL_ERROR_PROTOCOL;
+ 	smtpd_chat_reply(state, "501 Syntax: MAIL FROM: <address>");
+ 	return (-1);
+     }
+     for (narg = 4; narg < argc; narg++) {
+ 	arg = argv[narg].strval;
+ 	if (strcasecmp(arg, "BODY=8BITMIME") == 0
+ 	    || strcasecmp(arg, "BODY=7BIT") == 0) {
+ 	     /* void */ ;
+ 	} else if (strncasecmp(arg, "SIZE=", 5) == 0) {
+ 	    if ((size = off_cvt_string(arg + 5)) < 0)
+ 		size = 0;
+ 	} else {
+ 	    state->error_mask |= MAIL_ERROR_PROTOCOL;
+ 	    smtpd_chat_reply(state, "555 Unsupported option: %s", arg);
+ 	    return (-1);
+ 	}
+     }
+     state->time = time((time_t *) 0);
+     if (SMTPD_STAND_ALONE(state) == 0
+ 	&& var_smtpd_delay_reject == 0
+ 	&& (err = smtpd_check_mail(state, argv[3].strval)) != 0) {
+ 	smtpd_chat_reply(state, "%s", err);
+ 	return (-1);
+     }
+     if ((err = smtpd_check_size(state, size)) != 0) {
+ 	smtpd_chat_reply(state, "%s", err);
+ 	return (-1);
+     }
+ 
+     /*
+      * Open queue file or IPC stream.
+      */
+     mail_open_stream(state);
+     msg_info("%s: client=%s[%s]", state->queue_id, state->name, state->addr);
+ 
+     /*
+      * Record the time of arrival and the sender envelope address.
+      */
+     rec_fprintf(state->cleanup, REC_TYPE_TIME, "%ld",
+ 		(long) time((time_t *) 0));
+     rec_fputs(state->cleanup, REC_TYPE_FROM, argv[3].strval);
+     state->sender = mystrdup(argv[3].strval);
+     smtpd_chat_reply(state, "250 Ok");
+     return (0);
+ }
+ 
+ /* mail_reset - reset MAIL command stuff */
+ 
+ static void mail_reset(SMTPD_STATE *state)
+ {
+ 
+     /*
+      * Unceremoniously close the pipe to the cleanup service. The cleanup
+      * service will delete the queue file when it detects a premature
+      * end-of-file condition on input.
+      */
+     if (state->cleanup != 0) {
+ 	mail_stream_cleanup(state->dest);
+ 	state->dest = 0;
+ 	state->cleanup = 0;
+     }
+     state->err = 0;
+     if (state->queue_id != 0) {
+ 	myfree(state->queue_id);
+ 	state->queue_id = 0;
+     }
+     if (smtpd_path) {
+ 	if (remove(smtpd_path))
+ 	    msg_warn("remove %s: %m", smtpd_path);
+ 	else if (msg_verbose)
+ 	    msg_info("remove %s", smtpd_path);
+ 	myfree(smtpd_path);
+ 	smtpd_path = 0;
+     }
+     if (state->sender) {
+ 	myfree(state->sender);
+ 	state->sender = 0;
+     }
+ }
+ 
+ /* rcpt_cmd - process RCPT TO command */
+ 
+ static int rcpt_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
+ {
+     char   *err;
+ 
+     /*
+      * Sanity checks.
+      */
+     if (state->cleanup == 0) {
+ 	state->error_mask |= MAIL_ERROR_PROTOCOL;
+ 	smtpd_chat_reply(state, "503 Error: need MAIL command");
+ 	return (-1);
+     }
+     if (argc != 4
+ 	|| strcasecmp(argv[1].strval, "to") != 0
+ 	|| strcmp(argv[2].strval, ":") != 0) {
+ 	state->error_mask |= MAIL_ERROR_PROTOCOL;
+ 	smtpd_chat_reply(state, "501 Syntax: RCPT TO: <address>");
+ 	return (-1);
+     }
+     if (var_smtpd_rcpt_limit && state->rcpt_count >= var_smtpd_rcpt_limit) {
+ 	state->error_mask |= MAIL_ERROR_POLICY;
+ 	smtpd_chat_reply(state, "452 Error: too many recipients");
+ 	return (-1);
+     }
+     if (SMTPD_STAND_ALONE(state) == 0
+ 	&& (err = smtpd_check_rcpt(state, argv[3].strval)) != 0) {
+ 	smtpd_chat_reply(state, "%s", err);
+ 	return (-1);
+     }
+ 
+     /*
+      * Store the recipient. Remember the first one.
+      */
+     state->rcpt_count++;
+     if (state->recipient == 0)
+ 	state->recipient = mystrdup(argv[3].strval);
+     rec_fputs(state->cleanup, REC_TYPE_RCPT, argv[3].strval);
+     smtpd_chat_reply(state, "250 Ok");
+     return (0);
+ }
+ 
+ /* rcpt_reset - reset RCPT stuff */
+ 
+ static void rcpt_reset(SMTPD_STATE *state)
+ {
+     if (state->recipient) {
+ 	myfree(state->recipient);
+ 	state->recipient = 0;
+     }
+     state->rcpt_count = 0;
+ }
+ 
+ /* data_cmd - process DATA command */
+ 
+ static int data_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *unused_argv)
+ {
+     char   *start;
+     int     len;
+     int     curr_rec_type;
+     int     prev_rec_type;
+     int     first = 1;
+ 
+     /*
+      * Sanity checks. With ESMTP command pipelining the client can send DATA
+      * before all recipients are rejected, so don't report that as a protocol
+      * error.
+      */
+     if (state->rcpt_count == 0) {
+ 	if (state->cleanup == 0)
+ 	    state->error_mask |= MAIL_ERROR_PROTOCOL;
+ 	smtpd_chat_reply(state, "503 Error: need RCPT command");
+ 	return (-1);
+     }
+     if (argc != 1) {
+ 	state->error_mask |= MAIL_ERROR_PROTOCOL;
+ 	smtpd_chat_reply(state, "501 Syntax: DATA");
+ 	return (-1);
+     }
+ 
+     /*
+      * Terminate the message envelope segment. Start the message content
+      * segment, and prepend our own Received: header. If there is only one
+      * recipient, list the recipient address.
+      */
+     if (*var_always_bcc)
+ 	rec_fputs(state->cleanup, REC_TYPE_RCPT, var_always_bcc);
+     rec_fputs(state->cleanup, REC_TYPE_MESG, "");
+     rec_fprintf(state->cleanup, REC_TYPE_NORM,
+ 		"Received: from %s (%s [%s])",
+ 		state->helo_name ? state->helo_name : state->name,
+ 		state->name, state->addr);
+     if (state->rcpt_count == 1 && state->recipient) {
+ 	rec_fprintf(state->cleanup, REC_TYPE_NORM,
+ 		    "\tby %s (%s) with %s id %s",
+ 		    var_myhostname, var_mail_name,
+ 		    state->protocol, state->queue_id);
+ 	rec_fprintf(state->cleanup, REC_TYPE_NORM,
+ 		"\tfor <%s>; %s", state->recipient, mail_date(state->time));
+     } else {
+ 	rec_fprintf(state->cleanup, REC_TYPE_NORM,
+ 		    "\tby %s (%s) with %s",
+ 		    var_myhostname, var_mail_name, state->protocol);
+ 	rec_fprintf(state->cleanup, REC_TYPE_NORM,
+ 		    "\tid %s; %s", state->queue_id, mail_date(state->time));
+     }
+     smtpd_chat_reply(state, "354 End data with <CR><LF>.<CR><LF>");
+ 
+     /*
+      * Copy the message content. If the cleanup process has a problem, keep
+      * reading until the remote stops sending, then complain. Read typed
+      * records from the SMTP stream so we can handle data that spans buffers.
+      * 
+      * XXX Force an empty record when the queue file content begins with
+      * whitespace, so that it won't be considered as being part of our own
+      * Received: header. What an ugly Kluge.
+      */
+     if (vstream_fflush(state->cleanup))
+ 	state->err = CLEANUP_STAT_WRITE;
+ 
+     for (prev_rec_type = 0; /* void */ ; prev_rec_type = curr_rec_type) {
+ 	if (smtp_get(state->buffer, state->client, var_line_limit) == '\n')
+ 	    curr_rec_type = REC_TYPE_NORM;
+ 	else
+ 	    curr_rec_type = REC_TYPE_CONT;
+ 	start = vstring_str(state->buffer);
+ 	len = VSTRING_LEN(state->buffer);
+ 	if (first) {
+ 	    first = 0;
+ 	    if (len > 0 && ISSPACE(start[0]))
+ 		rec_put(state->cleanup, REC_TYPE_NORM, "", 0);
+ 	}
+ 	if (prev_rec_type != REC_TYPE_CONT
+ 	    && *start == '.' && (++start, --len) == 0)
+ 	    break;
+ 	if (state->err == CLEANUP_STAT_OK
+ 	    && rec_put(state->cleanup, curr_rec_type, start, len) < 0)
+ 	    state->err = CLEANUP_STAT_WRITE;
+     }
+ 
+     /*
+      * Send the end-of-segment markers.
+      */
+     if (state->err == CLEANUP_STAT_OK)
+ 	if (rec_fputs(state->cleanup, REC_TYPE_XTRA, "") < 0
+ 	    || rec_fputs(state->cleanup, REC_TYPE_END, "") < 0
+ 	    || vstream_fflush(state->cleanup))
+ 	    state->err = CLEANUP_STAT_WRITE;
+ 
+     /*
+      * Finish the queue file or finish the cleanup conversation.
+      */
+     if (state->err == 0)
+ 	state->err |= mail_stream_finish(state->dest);
+     else
+ 	mail_stream_cleanup(state->dest);
+     state->dest = 0;
+     state->cleanup = 0;
+ 
+     /*
+      * Delete the queue file or disable delete on fatal error or interrupt.
+      */
+     if (smtpd_path) {
+ 	if (state->err != 0) {
+ 	    if (remove(smtpd_path))
+ 		msg_warn("remove %s: %m", smtpd_path);
+ 	    else if (msg_verbose)
+ 		msg_info("remove %s", smtpd_path);
+ 	}
+ 	myfree(smtpd_path);
+ 	smtpd_path = 0;
+     }
+ 
+     /*
+      * Handle any errors. One message may suffer from multiple errors, so
+      * complain only about the most severe error. Forgive any previous client
+      * errors when a message was received successfully.
+      */
+     if (state->err == CLEANUP_STAT_OK) {
+ 	state->error_count = 0;
+ 	state->error_mask = 0;
+ 	smtpd_chat_reply(state, "250 Ok: queued as %s", state->queue_id);
+     } else if ((state->err & CLEANUP_STAT_BAD) != 0) {
+ 	state->error_mask |= MAIL_ERROR_SOFTWARE;
+ 	smtpd_chat_reply(state, "451 Error: internal error %d", state->err);
+     } else if ((state->err & CLEANUP_STAT_RCPT) != 0) {
+ 	state->error_mask |= MAIL_ERROR_SOFTWARE;
+ 	smtpd_chat_reply(state, "451 Error: internal error %d", state->err);
+     } else if ((state->err & CLEANUP_STAT_SIZE) != 0) {
+ 	state->error_mask |= MAIL_ERROR_BOUNCE;
+ 	smtpd_chat_reply(state, "552 Error: message too large");
+     } else if ((state->err & CLEANUP_STAT_HOPS) != 0) {
+ 	state->error_mask |= MAIL_ERROR_BOUNCE;
+ 	smtpd_chat_reply(state, "554 Error: too many hops");
+     } else if ((state->err & CLEANUP_STAT_CONT) != 0) {
+ 	state->error_mask |= MAIL_ERROR_BOUNCE;
+ 	smtpd_chat_reply(state, "552 Error: content rejected");
+     } else if ((state->err & CLEANUP_STAT_WRITE) != 0) {
+ 	state->error_mask |= MAIL_ERROR_RESOURCE;
+ 	smtpd_chat_reply(state, "451 Error: queue file write error");
+     } else {
+ 	msg_panic("data_cmd: unknown status %d", state->err);
+     }
+ 
+     /*
+      * Disconnect after transmission must not be treated as "lost connection
+      * after DATA".
+      */
+     state->where = SMTPD_AFTER_DOT;
+ 
+     /*
+      * Cleanup. The client may send another MAIL command.
+      */
+     mail_reset(state);
+     rcpt_reset(state);
+     return (state->err);
+ }
+ 
+ /* rset_cmd - process RSET */
+ 
+ static int rset_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *unused_argv)
+ {
+ 
+     /*
+      * Sanity checks.
+      */
+     if (argc != 1) {
+ 	state->error_mask |= MAIL_ERROR_PROTOCOL;
+ 	smtpd_chat_reply(state, "501 Syntax: RSET");
+ 	return (-1);
+     }
+ 
+     /*
+      * Restore state to right after HELO/EHLO command.
+      */
+     mail_reset(state);
+     rcpt_reset(state);
+     smtpd_chat_reply(state, "250 Ok");
+     return (0);
+ }
+ 
+ /* noop_cmd - process NOOP */
+ 
+ static int noop_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *unused_argv)
+ {
+ 
+     /*
+      * Sanity checks.
+      */
+     if (argc != 1) {
+ 	state->error_mask |= MAIL_ERROR_PROTOCOL;
+ 	smtpd_chat_reply(state, "501 Syntax: NOOP");
+ 	return (-1);
+     }
+     smtpd_chat_reply(state, "250 Ok");
+     return (0);
+ }
+ 
+ /* vrfy_cmd - process VRFY */
+ 
+ static int vrfy_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
+ {
+     char   *err = 0;
+ 
+     /*
+      * The SMTP standard (RFC 821) disallows unquoted special characters in
+      * the VRFY argument. Common practice violates the standard, however.
+      * Postfix accomodates common practice where it violates the standard.
+      */
+     if (argc < 2) {
+ 	state->error_mask |= MAIL_ERROR_PROTOCOL;
+ 	smtpd_chat_reply(state, "501 Syntax: VRFY address");
+ 	return (-1);
+     }
+     collapse_args(argc, argv);
+     if (SMTPD_STAND_ALONE(state) == 0)
+ 	err = smtpd_check_rcpt(state, argv[1].strval);
+ 
+     /*
+      * End untokenize.
+      */
+     if (err != 0) {
+ 	smtpd_chat_reply(state, "%s", err);
+ 	return (-1);
+     }
+     smtpd_chat_reply(state, "252 Send mail to find out");
+     return (0);
+ }
+ 
+ /* etrn_cmd - process ETRN command */
+ 
+ static int etrn_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
+ {
+     char   *err;
+ 
+     /*
+      * Sanity checks.
+      */
+     if (var_helo_required && state->helo_name == 0) {
+ 	state->error_mask |= MAIL_ERROR_POLICY;
+ 	smtpd_chat_reply(state, "503 Error: send HELO/EHLO first");
+ 	return (-1);
+     }
+     if (state->cleanup != 0) {
+ 	state->error_mask |= MAIL_ERROR_PROTOCOL;
+ 	smtpd_chat_reply(state, "503 Error: MAIL transaction in progress");
+ 	return (-1);
+     }
+     if (argc != 2) {
+ 	state->error_mask |= MAIL_ERROR_PROTOCOL;
+ 	smtpd_chat_reply(state, "500 Syntax: ETRN domain");
+ 	return (-1);
+     }
+     if (!ISALNUM(argv[1].strval[0]))
+ 	argv[1].strval++;
+     if (!valid_hostname(argv[1].strval)) {
+ 	state->error_mask |= MAIL_ERROR_PROTOCOL;
+ 	smtpd_chat_reply(state, "501 Error: invalid parameter syntax");
+ 	return (-1);
+     }
+ 
+     /*
+      * XXX The implementation borrows heavily from the code that implements
+      * UCE restrictions. These typically return 450 or 550 when a request is
+      * rejected. RFC 1985 requires that 459 be sent when the server refuses
+      * to perform the request.
+      */
+     if (SMTPD_STAND_ALONE(state) == 0
+ 	&& (err = smtpd_check_etrn(state, argv[1].strval)) != 0) {
+ 	smtpd_chat_reply(state, "%s", err);
+ 	return (-1);
+     }
+ 
+     /*
+      * XXX The preliminary implementation causes a full deferred queue scan.
+      */
+     if (mail_flush_site(argv[1].strval) < 0)
+ 	smtpd_chat_reply(state, "458 Unable to queue messages");
+     else
+ 	smtpd_chat_reply(state, "250 Queuing started");
+     return (0);
+ }
+ 
+ /* quit_cmd - process QUIT command */
+ 
+ static int quit_cmd(SMTPD_STATE *state, int unused_argc, SMTPD_TOKEN *unused_argv)
+ {
+ 
+     /*
+      * Don't bother checking the syntax.
+      */
+     smtpd_chat_reply(state, "221 Bye");
+     return (0);
+ }
+ 
+  /*
+   * The table of all SMTP commands that we know.
+   */
+ typedef struct SMTPD_CMD {
+     char   *name;
+     int     (*action) (SMTPD_STATE *, int, SMTPD_TOKEN *);
+ }       SMTPD_CMD;
+ 
+ static SMTPD_CMD smtpd_cmd_table[] = {
+     "HELO", helo_cmd,
+     "EHLO", ehlo_cmd,
+     "MAIL", mail_cmd,
+     "RCPT", rcpt_cmd,
+     "DATA", data_cmd,
+     "RSET", rset_cmd,
+     "NOOP", noop_cmd,
+     "VRFY", vrfy_cmd,
+     "ETRN", etrn_cmd,
+     "QUIT", quit_cmd,
+     0,
+ };
+ 
+ /* smtpd_proto - talk the SMTP protocol */
+ 
+ static void smtpd_proto(SMTPD_STATE *state)
+ {
+     int     argc;
+     SMTPD_TOKEN *argv;
+     SMTPD_CMD *cmdp;
+ 
+     /*
+      * Print a greeting banner and run the state machine. Read SMTP commands
+      * one line at a time. According to the standard, a sender or recipient
+      * address could contain an escaped newline. I think this is perverse,
+      * and anyone depending on this is really asking for trouble.
+      * 
+      * In case of mail protocol trouble, the program jumps back to this place,
+      * so that it can perform the necessary cleanup before talking to the
+      * next client. The setjmp/longjmp primitives are like a sharp tool: use
+      * with care. I would certainly recommend against the use of
+      * setjmp/longjmp in programs that change privilege levels.
+      * 
+      * In case of file system trouble the program terminates after logging the
+      * error and after informing the client. In all other cases (out of
+      * memory, panic) the error is logged, and the msg_cleanup() exit handler
+      * cleans up, but no attempt is made to inform the client of the nature
+      * of the problem.
+      */
+     smtp_timeout_setup(state->client, var_smtpd_tmout);
+ 
+     switch (setjmp(smtp_timeout_buf)) {
+ 
+     default:
+ 	msg_panic("smtpd_proto: unknown error reading from %s[%s]",
+ 		  state->name, state->addr);
+ 	break;
+ 
+     case SMTP_ERR_TIME:
+ 	state->reason = "timeout";
+ 	smtpd_chat_reply(state, "421 Error: timeout exceeded");
+ 	break;
+ 
+     case SMTP_ERR_EOF:
+ 	state->reason = "lost connection";
+ 	break;
+ 
+     case 0:
+ 	for (;;) {
+ 	    if (state->error_count > var_smtpd_hard_erlim) {
+ 		state->reason = "too many errors";
+ 		state->error_mask |= MAIL_ERROR_PROTOCOL;
+ 		smtpd_chat_reply(state, "421 Error: too many errors");
+ 		break;
+ 	    }
+ 	    smtpd_chat_query(state);
+ 	    if ((argc = smtpd_token(vstring_str(state->buffer), &argv)) == 0) {
+ 		state->error_mask |= MAIL_ERROR_PROTOCOL;
+ 		smtpd_chat_reply(state, "500 Error: bad syntax");
+ 		state->error_count++;
+ 		continue;
+ 	    }
+ 	    for (cmdp = smtpd_cmd_table; cmdp->name != 0; cmdp++)
+ 		if (strcasecmp(argv[0].strval, cmdp->name) == 0)
+ 		    break;
+ 	    if (cmdp->name == 0) {
+ 		smtpd_chat_reply(state, "502 Error: command not implemented");
+ 		state->error_mask |= MAIL_ERROR_PROTOCOL;
+ 		state->error_count++;
+ 		continue;
+ 	    }
+ 	    if (state->access_denied && cmdp->action != quit_cmd) {
+ 		smtpd_chat_reply(state, "%s", state->access_denied);
+ 		state->error_count++;
+ 		continue;
+ 	    }
+ 	    state->where = cmdp->name;
+ 	    if (cmdp->action(state, argc, argv) != 0)
+ 		state->error_count++;
+ 
+ 	    if (cmdp->action == quit_cmd)
+ 		break;
+ 	}
+ 	break;
+     }
+ 
+     /*
+      * Log abnormal session termination, in case postmaster notification has
+      * been turned off. In the log, indicate the last recognized state before
+      * things went wrong. Don't complain about clients that go away without
+      * sending QUIT.
+      */
+     if (state->reason && state->where && strcmp(state->where, SMTPD_AFTER_DOT))
+ 	msg_info("%s after %s from %s[%s]",
+ 		 state->reason, state->where, state->name, state->addr);
+ 
+     /*
+      * Notify the postmaster if there were errors but no message was
+      * collected. This usually indicates a client configuration problem, or
+      * that someone is trying nasty things. Either is significant enough to
+      * bother the postmaster. XXX Can't report problems when running in
+      * stand-alone mode: postmaster notices require availability of the
+      * cleanup service.
+      */
+     if (state->history != 0 && state->client != VSTREAM_IN
+ 	&& (state->error_mask & state->notify_mask))
+ 	smtpd_chat_notify(state);
+ 
+     /*
+      * Cleanup whatever information the client gave us during the SMTP
+      * dialog.
+      */
+     helo_reset(state);
+     mail_reset(state);
+     rcpt_reset(state);
+     smtpd_chat_reset(state);
+ }
+ 
+ /* smtpd_service - service one client */
+ 
+ static void smtpd_service(VSTREAM *stream, char *unused_service, char **argv)
+ {
+     SMTPD_STATE state;
+     PEER_NAME *peer;
+ 
+     /*
+      * Sanity check. This service takes no command-line arguments.
+      */
+     if (argv[0])
+ 	msg_fatal("unexpected command-line argument: %s", argv[0]);
+ 
+     /*
+      * This routine runs when a client has connected to our network port, or
+      * when the smtp server is run in stand-alone mode (input from pipe).
+      * 
+      * Look up and sanitize the peer name, then initialize some connection-
+      * specific state. When the name service is hosed, hostname lookup will
+      * take a while. This is why I always run a local name server on critical
+      * machines.
+      */
+     peer = peer_name(vstream_fileno(stream));
+     smtpd_state_init(&state, stream, peer->name, peer->addr);
+ 
+     /*
+      * See if we need to turn on verbose logging for this client.
+      */
+     debug_peer_check(state.name, state.addr);
+ 
+     /*
+      * See if we want to talk to this client at all. Then, log the connection
+      * event.
+      */
+     if (var_smtpd_delay_reject == 0
+ 	&& (state.access_denied = smtpd_check_client(&state)) != 0) {
+ 	smtpd_chat_reply(&state, "%s", state.access_denied);
+     } else {
+ 	smtpd_chat_reply(&state, "220 %s", var_smtpd_banner);
+ 	msg_info("connect from %s[%s]", state.name, state.addr);
+     }
+ 
+     /*
+      * Provide the SMTP service.
+      */
+     smtpd_proto(&state);
+ 
+     /*
+      * After the client has gone away, clean up whatever we have set up at
+      * connection time.
+      */
+     msg_info("disconnect from %s[%s]", state.name, state.addr);
+     smtpd_state_reset(&state);
+     debug_peer_restore();
+ }
+ 
+ /* smtpd_cleanup - stand-alone mode queue file cleanup */
+ 
+ static void smtpd_cleanup(void)
+ {
+     char   *myname = "smtpd_cleanup";
+ 
+     /*
+      * This routine is called by the run-time error handler, right before
+      * program exit.
+      */
+     if (smtpd_path) {
+ 	if (remove(smtpd_path))
+ 	    msg_warn("%s: remove %s: %m", myname, smtpd_path);
+ 	else if (msg_verbose)
+ 	    msg_info("%s: remove %s", myname, smtpd_path);
+ 	smtpd_path = 0;
+     }
+ }
+ 
+ /* smtpd_sig - signal handler */
+ 
+ static void smtpd_sig(int sig)
+ {
+     smtpd_cleanup();
+     exit(sig);
+ }
+ 
+ /* pre_accept - see if tables have changed */
+ 
+ static void pre_accept(char *unused_name, char **unused_argv)
+ {
+     if (dict_changed()) {
+ 	msg_info("lookup table has changed -- exiting");
+ 	exit(0);
+     }
+ }
+ 
+ /* post_jail_init - post-jail initialization */
+ 
+ static void post_jail_init(char *unused_name, char **unused_argv)
+ {
+ 
+     /*
+      * Set up signal handlers so that we clean up in stand-alone mode.
+      */
+     signal(SIGHUP, smtpd_sig);
+     signal(SIGINT, smtpd_sig);
+     signal(SIGQUIT, smtpd_sig);
+     signal(SIGTERM, smtpd_sig);
+ }
+ 
+ /* pre_jail_init - pre-jail initialization */
+ 
+ static void pre_jail_init(char *unused_name, char **unused_argv)
+ {
+ 
+     /*
+      * Initialize blacklist/etc. patterns before entering the chroot jail, in
+      * case they specify a filename pattern.
+      */
+     smtpd_check_init();
+     debug_peer_init();
+     msg_cleanup(smtpd_cleanup);
+ }
+ 
+ /* main - the main program */
+ 
+ int     main(int argc, char **argv)
+ {
+     static CONFIG_INT_TABLE int_table[] = {
+ 	VAR_SMTPD_RCPT_LIMIT, DEF_SMTPD_RCPT_LIMIT, &var_smtpd_rcpt_limit, 1, 0,
+ 	VAR_SMTPD_TMOUT, DEF_SMTPD_TMOUT, &var_smtpd_tmout, 1, 0,
+ 	VAR_SMTPD_SOFT_ERLIM, DEF_SMTPD_SOFT_ERLIM, &var_smtpd_soft_erlim, 1, 0,
+ 	VAR_SMTPD_HARD_ERLIM, DEF_SMTPD_HARD_ERLIM, &var_smtpd_hard_erlim, 1, 0,
+ 	VAR_QUEUE_MINFREE, DEF_QUEUE_MINFREE, &var_queue_minfree, 0, 0,
+ 	VAR_DEBUG_PEER_LEVEL, DEF_DEBUG_PEER_LEVEL, &var_debug_peer_level, 1, 0,
+ 	VAR_UNK_CLIENT_CODE, DEF_UNK_CLIENT_CODE, &var_unk_client_code, 0, 0,
+ 	VAR_BAD_NAME_CODE, DEF_BAD_NAME_CODE, &var_bad_name_code, 0, 0,
+ 	VAR_UNK_NAME_CODE, DEF_UNK_NAME_CODE, &var_unk_name_code, 0, 0,
+ 	VAR_UNK_ADDR_CODE, DEF_UNK_ADDR_CODE, &var_unk_addr_code, 0, 0,
+ 	VAR_RELAY_CODE, DEF_RELAY_CODE, &var_relay_code, 0, 0,
+ 	VAR_MAPS_RBL_CODE, DEF_MAPS_RBL_CODE, &var_maps_rbl_code, 0, 0,
+ 	VAR_ACCESS_MAP_CODE, DEF_ACCESS_MAP_CODE, &var_access_map_code, 0, 0,
+ 	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,
+ 	0,
+     };
+     static CONFIG_BOOL_TABLE bool_table[] = {
+ 	VAR_HELO_REQUIRED, DEF_HELO_REQUIRED, &var_helo_required,
+ 	VAR_SMTPD_DELAY_REJECT, DEF_SMTPD_DELAY_REJECT, &var_smtpd_delay_reject,
+ 	0,
+     };
+     static CONFIG_STR_TABLE str_table[] = {
+ 	VAR_RELAY_DOMAINS, DEF_RELAY_DOMAINS, &var_relay_domains, 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,
+ 	VAR_CLIENT_CHECKS, DEF_CLIENT_CHECKS, &var_client_checks, 0, 0,
+ 	VAR_HELO_CHECKS, DEF_HELO_CHECKS, &var_helo_checks, 0, 0,
+ 	VAR_MAIL_CHECKS, DEF_MAIL_CHECKS, &var_mail_checks, 0, 0,
+ 	VAR_RCPT_CHECKS, DEF_RCPT_CHECKS, &var_rcpt_checks, 0, 0,
+ 	VAR_ETRN_CHECKS, DEF_ETRN_CHECKS, &var_etrn_checks, 0, 0,
+ 	VAR_MAPS_RBL_DOMAINS, DEF_MAPS_RBL_DOMAINS, &var_maps_rbl_domains, 0, 0,
+ 	VAR_ALWAYS_BCC, DEF_ALWAYS_BCC, &var_always_bcc, 0, 0,
+ 	VAR_ERROR_RCPT, DEF_ERROR_RCPT, &var_error_rcpt, 1, 0,
+ 	0,
+     };
+ 
+     /*
+      * Pass control to the single-threaded service skeleton.
+      */
+     single_server_main(argc, argv, smtpd_service,
+ 		       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,
+ 		       MAIL_SERVER_POST_INIT, post_jail_init,
+ 		       MAIL_SERVER_PRE_ACCEPT, pre_accept,
+ 		       0);
+ }
diff -r -c --new-file postfix-19990906-pl07-vanilla/smtpd/smtpd.h postfix-19990906-pl07/smtpd/smtpd.h
*** postfix-19990906-pl07-vanilla/smtpd/smtpd.h	Mon May 10 02:51:21 1999
--- postfix-19990906-pl07/smtpd/smtpd.h	Mon Nov 15 15:31:08 1999
***************
*** 49,54 ****
--- 49,67 ----
      char   *recipient;
      char   *protocol;
      char   *where;
+     int     tls_active;
+     int     tls_use_tls;
+     int     tls_enforce_tls;
+     int	    tls_peer_verified;
+     char   *tls_peer_subject;
+     char   *tls_peer_issuer;
+     char   *tls_peer_fingerprint;
+     char   *tls_client_CN;
+     char   *tls_issuer_CN;
+     char   *tls_protocol;
+     char   *tls_cipher_name;
+     int     tls_usebits;
+     int     tls_algbits;
  } SMTPD_STATE;
  
  extern void smtpd_state_init(SMTPD_STATE *, VSTREAM *,
diff -r -c --new-file postfix-19990906-pl07-vanilla/smtpd/smtpd_check.c postfix-19990906-pl07/smtpd/smtpd_check.c
*** postfix-19990906-pl07-vanilla/smtpd/smtpd_check.c	Sat Nov  6 15:42:33 1999
--- postfix-19990906-pl07/smtpd/smtpd_check.c	Mon Nov 15 15:31:08 1999
***************
*** 255,260 ****
--- 255,261 ----
  
  #include <namadr_list.h>
  #include <domain_list.h>
+ #include <string_list.h>
  #include <mail_params.h>
  #include <canon_addr.h>
  #include <resolve_clnt.h>
***************
*** 292,297 ****
--- 293,301 ----
    */
  static DOMAIN_LIST *relay_domains;
  static NAMADR_LIST *mynetworks;
+ #ifdef HAS_SSL
+ static STRING_LIST *relay_ccerts;
+ #endif
  
   /*
    * Pre-parsed restriction lists.
***************
*** 354,359 ****
--- 358,366 ----
       */
      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
***************
*** 629,634 ****
--- 636,659 ----
      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_peer_verified && state->tls_peer_fingerprint) {
+ 	if (string_list_match(relay_ccerts, state->tls_peer_fingerprint)) {
+ 	    if (msg_verbose)
+ 		msg_info("Relaying allowed for certified client");
+ 	    return (SMTPD_CHECK_OK);
+ 	} else if (msg_verbose)
+ 	    msg_info("relay_clientcerts: No match for fingerprint '%s'",
+ 		     state->tls_peer_fingerprint);
+     }
+     return (SMTPD_CHECK_DUNNO);
+ }
+ #endif
+ 
  /* check_relay_domains - OK/FAIL for message relaying */
  
  static int check_relay_domains(SMTPD_STATE *state, char *recipient,
***************
*** 1581,1586 ****
--- 1606,1615 ----
  	} else if (strcasecmp(name, REJECT_UNKNOWN_RCPTDOM) == 0) {
  	    status = reject_unknown_address(state, recipient,
  					    recipient, SMTPD_NAME_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,
  					   recipient, SMTPD_NAME_RECIPIENT);
***************
*** 1704,1709 ****
--- 1733,1739 ----
  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-19990906-pl07-vanilla/smtpd/smtpd_state.c postfix-19990906-pl07/smtpd/smtpd_state.c
*** postfix-19990906-pl07-vanilla/smtpd/smtpd_state.c	Mon May 10 02:55:30 1999
--- postfix-19990906-pl07/smtpd/smtpd_state.c	Mon Nov 15 15:31:08 1999
***************
*** 97,102 ****
--- 97,108 ----
      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_peer_subject = NULL;
+     state->tls_peer_issuer = NULL;
+     state->tls_peer_fingerprint = NULL;
  
      /*
       * Initialize the conversation history.
