diff options
author | Nick Hadaway <raker@gentoo.org> | 2002-07-22 23:09:48 +0000 |
---|---|---|
committer | Nick Hadaway <raker@gentoo.org> | 2002-07-22 23:09:48 +0000 |
commit | 05dc154ca1075d73dc18b1d7e129e5519e3d707b (patch) | |
tree | 68036c5dd1b8603abcb20467554b56f65b76169d /net-mail/postfix | |
parent | add mask for new postfix build which supports ipv6,tls, and saslv2 (diff) | |
download | historical-05dc154ca1075d73dc18b1d7e129e5519e3d707b.tar.gz historical-05dc154ca1075d73dc18b1d7e129e5519e3d707b.tar.bz2 historical-05dc154ca1075d73dc18b1d7e129e5519e3d707b.zip |
New ebuild with tls and ipv6 support. Also added support for saslv2.
Thanks to Philipp Morger for a start on the ebuilds and patches.
Diffstat (limited to 'net-mail/postfix')
-rw-r--r-- | net-mail/postfix/ChangeLog | 11 | ||||
-rw-r--r-- | net-mail/postfix/files/digest-postfix-1.1.11.20020613 | 1 | ||||
-rw-r--r-- | net-mail/postfix/files/saslv2.diff | 42 | ||||
-rw-r--r-- | net-mail/postfix/files/tls+ipv6.diff | 10732 | ||||
-rw-r--r-- | net-mail/postfix/postfix-1.1.11.20020613.ebuild | 138 |
5 files changed, 10923 insertions, 1 deletions
diff --git a/net-mail/postfix/ChangeLog b/net-mail/postfix/ChangeLog index f41472e7f731..27b5433bc47d 100644 --- a/net-mail/postfix/ChangeLog +++ b/net-mail/postfix/ChangeLog @@ -1,6 +1,15 @@ # ChangeLog for net-mail/postfix # Copyright 2002 Gentoo Technologies, Inc.; Distributed under the GPL -# $Header: /var/cvsroot/gentoo-x86/net-mail/postfix/ChangeLog,v 1.22 2002/07/21 17:16:28 blizzy Exp $ +# $Header: /var/cvsroot/gentoo-x86/net-mail/postfix/ChangeLog,v 1.23 2002/07/22 23:09:48 raker Exp $ + +*postfix-1.1.11.20020613 (22 Jul 2002) + + 22 Jul 2002; Nick Hadaway <raker@gentoo.org> + postfix-1.1.11.20020613.ebuild, files/digest-postfix-1.1.11.20020613, + files/tls+ipv6.diff, files/saslv2.diff + + New ebuild with tls and ipv6 support. Also added support for saslv2. + Thanks to Philipp Morger for a start on the ebuilds and patches. 21 Jul 2002; Maik Schreiber <blizzy@gentoo.org> postfix-1.1.11-r4.ebuild, ChangeLog: diff --git a/net-mail/postfix/files/digest-postfix-1.1.11.20020613 b/net-mail/postfix/files/digest-postfix-1.1.11.20020613 new file mode 100644 index 000000000000..d3bf64ede9a6 --- /dev/null +++ b/net-mail/postfix/files/digest-postfix-1.1.11.20020613 @@ -0,0 +1 @@ +MD5 7c7f2f27463539c00c6bfe8bf50bc0d9 postfix-1.1.11-20020613.tar.gz 1249496 diff --git a/net-mail/postfix/files/saslv2.diff b/net-mail/postfix/files/saslv2.diff new file mode 100644 index 000000000000..879b7640aabe --- /dev/null +++ b/net-mail/postfix/files/saslv2.diff @@ -0,0 +1,42 @@ +diff -urN postfix-1.1.11-20020613/src/lmtp/lmtp.h postfix-1.1.11-20020613-modified/src/lmtp/lmtp.h +--- postfix-1.1.11-20020613/src/lmtp/lmtp.h Tue May 22 13:37:14 2001 ++++ postfix-1.1.11-20020613-modified/src/lmtp/lmtp.h Mon Jul 22 17:04:54 2002 +@@ -12,8 +12,8 @@ + * SASL library. + */ + #ifdef USE_SASL_AUTH +-#include <sasl.h> +-#include <saslutil.h> ++#include <sasl/sasl.h> ++#include <sasl/saslutil.h> + #endif + + /* +diff -urN postfix-1.1.11-20020613/src/smtp/smtp.h postfix-1.1.11-20020613-modified/src/smtp/smtp.h +--- postfix-1.1.11-20020613/src/smtp/smtp.h Thu May 23 14:18:02 2002 ++++ postfix-1.1.11-20020613-modified/src/smtp/smtp.h Mon Jul 22 17:04:41 2002 +@@ -12,8 +12,8 @@ + * SASL library. + */ + #ifdef USE_SASL_AUTH +-#include <sasl.h> +-#include <saslutil.h> ++#include <sasl/sasl.h> ++#include <sasl/saslutil.h> + #endif + + /* +diff -urN postfix-1.1.11-20020613/src/smtpd/smtpd.h postfix-1.1.11-20020613-modified/src/smtpd/smtpd.h +--- postfix-1.1.11-20020613/src/smtpd/smtpd.h Fri Mar 29 15:10:13 2002 ++++ postfix-1.1.11-20020613-modified/src/smtpd/smtpd.h Mon Jul 22 17:04:22 2002 +@@ -17,8 +17,8 @@ + * SASL library. + */ + #ifdef USE_SASL_AUTH +-#include <sasl.h> +-#include <saslutil.h> ++#include <sasl/sasl.h> ++#include <sasl/saslutil.h> + #endif + + /* diff --git a/net-mail/postfix/files/tls+ipv6.diff b/net-mail/postfix/files/tls+ipv6.diff new file mode 100644 index 000000000000..c85d32017337 --- /dev/null +++ b/net-mail/postfix/files/tls+ipv6.diff @@ -0,0 +1,10732 @@ +diff -Pur postfix-1.1.11.20020613-orig/Makefile.in postfix-1.1.11-20020613/Makefile.in +--- postfix-1.1.11.20020613-orig/Makefile.in Sat Jun 8 20:49:42 2002 ++++ postfix-1.1.11.20020613/Makefile.in Wed Jun 26 15:26:47 2002 +@@ -6,7 +6,8 @@ + src/lmtp src/trivial-rewrite src/qmgr src/smtp src/bounce src/pipe \ + src/showq src/postalias src/postcat src/postconf src/postdrop \ + src/postkick src/postlock src/postlog src/postmap src/postqueue \ +- src/postsuper src/nqmgr src/qmqpd src/spawn src/flush src/virtual ++ src/postsuper src/nqmgr src/qmqpd src/spawn src/flush src/virtual \ ++ src/tlsmgr + MANDIRS = proto man html + + default: update +diff -Pur postfix-1.1.11.20020613-orig/conf/master.cf postfix-1.1.11-20020613/conf/master.cf +--- postfix-1.1.11.20020613-orig/conf/master.cf Sat Jun 8 20:21:38 2002 ++++ postfix-1.1.11.20020613/conf/master.cf Wed Jun 26 15:26:47 2002 +@@ -70,11 +70,16 @@ + # (yes) (yes) (yes) (never) (50) + # ========================================================================== + smtp inet n - n - - smtpd ++#smtps inet n - n - - smtpd ++# -o smtpd_tls_wrappermode=yes -o smtpd_sasl_auth_enable=yes ++#submission inet n - n - - smtpd ++# -o smtpd_enforce_tls=yes -o smtpd_sasl_auth_enable=yes + #628 inet n - n - - qmqpd + pickup fifo n - n 60 1 pickup + cleanup unix n - n - 0 cleanup + qmgr fifo n - n 300 1 qmgr + #qmgr fifo n - n 300 1 nqmgr ++#tlsmgr fifo - - n 300 1 tlsmgr + rewrite unix - - n - - trivial-rewrite + bounce unix - - n - 0 bounce + defer unix - - n - 0 bounce +diff -Pur postfix-1.1.11.20020613-orig/conf/postfix-files postfix-1.1.11-20020613/conf/postfix-files +--- postfix-1.1.11.20020613-orig/conf/postfix-files Sun May 26 23:27:31 2002 ++++ postfix-1.1.11.20020613/conf/postfix-files Wed Jun 26 15:26:47 2002 +@@ -69,6 +69,7 @@ + $daemon_directory/smtp:f:root:-:755 + $daemon_directory/smtpd:f:root:-:755 + $daemon_directory/spawn:f:root:-:755 ++$daemon_directory/tlsmgr:f:root:-:755 + $daemon_directory/trivial-rewrite:f:root:-:755 + $daemon_directory/virtual:f:root:-:755 + $command_directory/postalias:f:root:-:755 +@@ -139,6 +140,7 @@ + $manpage_directory/man8/smtp.8:f:root:-:644 + $manpage_directory/man8/smtpd.8:f:root:-:644 + $manpage_directory/man8/spawn.8:f:root:-:644 ++$manpage_directory/man8/tlsmgr.8:f:root:-:644 + $manpage_directory/man8/trivial-rewrite.8:f:root:-:644 + $manpage_directory/man8/virtual.8:f:root:-:644 + $sample_directory/sample-aliases.cf:f:root:-:644 +@@ -166,6 +168,7 @@ + $sample_directory/sample-rewrite.cf:f:root:-:644 + $sample_directory/sample-smtp.cf:f:root:-:644 + $sample_directory/sample-smtpd.cf:f:root:-:644 ++$sample_directory/sample-tls.cf:f:root:-:644 + $sample_directory/sample-transport.cf:f:root:-:644 + $sample_directory/sample-virtual.cf:f:root:-:644 + $readme_directory/DB_README:f:root:-:644 +diff -Pur postfix-1.1.11.20020613-orig/conf/sample-auth.cf postfix-1.1.11-20020613/conf/sample-auth.cf +--- postfix-1.1.11.20020613-orig/conf/sample-auth.cf Fri Mar 29 22:36:53 2002 ++++ postfix-1.1.11.20020613/conf/sample-auth.cf Wed Jun 26 15:26:47 2002 +@@ -117,3 +117,15 @@ + # + #smtp_sasl_security_options = + smtp_sasl_security_options = noplaintext ++ ++# Sending AUTH data over an unencrypted channel poses a security risk. When ++# smtpd_tls_enforce_tls is set, AUTH will only be announced and accepted, ++# once the TLS layer has been activated via the STARTTLS protocol. If ++# TLS layer encryption is optional, it may however still be useful to only ++# offer AUTH, when TLS is active. To not break compatiblity with unpatched ++# postfix versions, the default is to accept AUTH without encryption. In ++# order to change this behaviour, set smtpd_tls_auth_only = yes. ++# THIS OPTION ONLY WORKS WITH SSL/TLS SUPPORT COMPILED IN. ++# ++# smtpd_tls_auth_only = no ++ +diff -Pur postfix-1.1.11.20020613-orig/conf/sample-smtp.cf postfix-1.1.11-20020613/conf/sample-smtp.cf +--- postfix-1.1.11.20020613-orig/conf/sample-smtp.cf Tue Mar 26 22:46:30 2002 ++++ postfix-1.1.11.20020613/conf/sample-smtp.cf Wed Jun 26 15:26:47 2002 +@@ -188,6 +188,14 @@ + # + smtp_helo_timeout = 300s + ++# The smtp_starttls_timeout parameter limits the time in seconds to write and ++# read operations during TLS start and stop handhake procedures. ++# ++# In case of problems the client does NOT try the next address on ++# the mail exchanger list. ++# ++# smtp_starttls_timeout = 300s ++ + # The smtp_mail_timeout parameter specifies the SMTP client timeout + # for sending the SMTP MAIL FROM command, and for receiving the server + # response. +diff -Pur postfix-1.1.11.20020613-orig/conf/sample-smtpd.cf postfix-1.1.11-20020613/conf/sample-smtpd.cf +--- postfix-1.1.11.20020613-orig/conf/sample-smtpd.cf Sat Jun 8 20:21:38 2002 ++++ postfix-1.1.11.20020613/conf/sample-smtpd.cf Wed Jun 26 15:26:47 2002 +@@ -116,6 +116,11 @@ + # + strict_rfc821_envelopes = no + ++# The smtpd_starttls_timeout parameter limits the time in seconds to write and ++# read operations during TLS start and stop handhake procedures. ++# ++# smtpd_starttls_timeout = 300s ++ + # + # TARPIT CONTROLS + # +diff -Pur postfix-1.1.11.20020613-orig/conf/sample-tls.cf postfix-1.1.11-20020613/conf/sample-tls.cf +--- postfix-1.1.11.20020613-orig/conf/sample-tls.cf Thu Jan 1 01:00:00 1970 ++++ postfix-1.1.11.20020613/conf/sample-tls.cf Wed Jun 26 15:26:47 2002 +@@ -0,0 +1,497 @@ ++# 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. ++# ++# Both RSA and DSA are certificates are supported. Typically you will only ++# have RSA certificates issued by a commercial CA, also the tools supplied ++# with OpenSSL will by default issue RSA certificates. ++# You can have both at the same time, in this case the cipher used decides, ++# which certificate is presented. For Netscape and OpenSSL clients without ++# special cipher choices, the RSA certificate is preferred. ++# ++# In order to check the certificates, the CA-certificate (in case of a ++# certificate chain, all CA-certificates) must be available. ++# You should add these certificates to the server certificate, the server ++# certificate first, then the issuing CA(s). ++# ++# Example: the certificate for "server.dom.ain" was issued by "intermediate CA" ++# which itself has a certificate of "root CA". Create the server.pem file by ++# 'cat server_cert.pem intemediate_CA.pem root_CA.pem > server.pem' ++# ++# If you want to accept certificates issued by these CAs yourself, you can ++# also add the CA-certificates to the smtpd_tls_CAfile, in which case it is ++# not necessary to have them in the smtpd_tls_[d]cert_file. ++# ++# A certificate supplied here must be useable as SSL server certificate and ++# hence pass the "openssl verify -purpose sslserver ..." test. ++# ++smtpd_tls_cert_file = /etc/postfix/server.pem ++smtpd_tls_key_file = $smtpd_tls_cert_file ++# ++# Its DSA counterparts: ++smtpd_tls_dcert_file = /etc/postfix/server-dsa.pem ++smtpd_tls_dkey_file = $smtpd_tls_dcert_file ++ ++# The certificate was issued by a certification authority (CA), the CA-cert ++# of which must be available, if not in the certificate file. ++# 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. ++# ++# 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! ++# ++# To use this option in chroot mode, this directory itself or a copy of it ++# must be inside the chroot jail. Please note also, that the CAs in this ++# directory are not listed to the client, so that e.g. Netscape might not ++# offer certificates issued by them. ++# ++# I therefore discourage the use of this option. ++# ++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 using "smtpd_use_tls". (Note: when invoked ++# via "sendmail -bs", STARTTLS is never offered due to insufficient ++# privileges to access the private key. This is intended behaviour.) ++# ++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. (Note: when invoked via "sendmail -bs", STARTTLS ++# is never offered due to insufficient privileges to access the private key. ++# This is intended behaviour.) ++# ++# smtpd_enforce_tls = no ++ ++# Besides RFC2487 some clients, namely Outlook [Express] prefer to run the ++# non-standard "wrapper" mode, not the STARTTLS enhancement to SMTP. ++# This is true for OE (Win32 < 5.0 and Win32 >=5.0 when run on a port!=25 ++# and OE (5.01 Mac on all ports). ++# It is strictly discouraged to use this mode from main.cf. If you want to ++# support this service, enable a special port in master.cf. Port 465 (smtps) ++# was once chosen for this feature. ++# ++# smtpd_tls_wrappermode = 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 ++# ++# Please be aware, that this will inhibit TLS connections without a proper ++# certificate and only makes sense, when normal submission is disabled and ++# TLS is enforced (smtpd_enforce_tls). Otherwise clients may bypass by simply ++# not using STARTTLS at all. When TLS is not enforced, the connection will be ++# handled, as if only smtpd_tls_ask_ccert = yes would be set and an information ++# is logged. ++# ++# smtpd_tls_req_ccert = no ++ ++# The verification depth for client certificates. A depth of 1 is sufficient, ++# if the certificate ist directly issued by a CA listed in the CA locations. ++# The default value (5) should also suffice for longer chains (root CA issues ++# special CA which then issues the actual certificate...) ++# ++# smtpd_tls_ccert_verifydepth = 5 ++ ++# Sending AUTH data over an unencrypted channel poses a security risk. When ++# smtpd_tls_enforce_tls is set, AUTH will only be announced and accepted, ++# once the TLS layer has been activated via the STARTTLS protocol. If ++# TLS layer encryption is optional, it may however still be useful to only ++# offer AUTH, when TLS is active. To not break compatiblity with unpatched ++# postfix versions, the default is to accept AUTH without encryption. In ++# order to change this behaviour, set smtpd_tls_auth_only = yes. ++# ++# smtpd_tls_auth_only = no ++ ++# The server and client negotiate a session, which takes some computer time ++# and network bandwidth. The 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 based on the SDBM databases (routines included ++# in Postfix/TLS). Since concurrent writing must be supported, only SDBM ++# can be used. ++# ++smtpd_tls_session_cache_database = sdbm:/etc/postfix/smtpd_scache ++ ++# 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 = 3600s ++ ++# Two additional options has been added for relay control to the UCE rules: ++# permit_tls_clientcerts (a) ++# and ++# permit_tls_all_clientcerts. (b) ++# ++# If one of these options is added to ++# smtpd_recipient_restrictions, ++# postfix will relay if ++# (a) a valid (it passed the verification) client certificate is presented ++# and its fingerprint is listed in the list of client certs ++# (relay_clientcerts), ++# (b) any valid (it passed the verification) client certificate is presented. ++# ++# Option (b) must only be used, if a special CA issues the certificates and ++# only this CA is listed as trusted CA. If other CAs are trusted, any owner ++# of a valid (SSL client)-certificate can relay. Option (b) can be practical ++# for a specically created email relay. It is however recommended to stay with ++# option (a) and list all certificates, as (b) does not permit any control ++# when a certificate must no longer be used (e.g. an employee leaving). ++# ++# smtpd_recipient_restrictions = ... permit_tls_clientcerts ... ++ ++# 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. ++# 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 ++ ++# To influence the cipher selection scheme, you can give cipherlist-string. ++# A detailed description would go to far here, please refer to the openssl ++# documentation. ++# If you don't know what to do with it, simply don't touch it and leave the ++# (openssl-)compiled in default! ++# ++# DO NOT USE " to enclose the string, just the string!!! ++# ++# smtpd_tls_cipherlist = DEFAULT ++ ++# If you want to take advantage of ciphers with EDH, DH parameters are needed. ++# There are built in DH parameters for both 1025bit and 512bit available. It ++# is however better to have "own" parameters, since otherwise it would "pay" ++# for a possible attacker to start a brute force attack against these ++# parameters commonly used by everybody. For this reason, the parameters ++# chosen are already different from those distributed with other TLS packages. ++# ++# To generate your own set of parameters, use ++# openssl gendh -out /etc/postfix/dh_1024.pem -2 -rand /var/run/egd-pool 1024 ++# openssl gendh -out /etc/postfix/dh_512.pem -2 -rand /var/run/egd-pool 512 ++# (your source for "entropy" might vary; on Linux there is /dev/random, on ++# other system, you might consider the "Entropy Gathering Daemon EGD", ++# available at http://www.lothar.com/tech/crypto/. ++# ++smtpd_tls_dh1024_param_file = /etc/postfix/dh_1024.pem ++smtpd_tls_dh512_param_file = /etc/postfix/dh_512.pem ++ ++# The smtpd_starttls_timeout parameter limits the time in seconds to write and ++# read operations during TLS start and stop handhake procedures. ++# ++# smtpd_starttls_timeout = 300s ++ ++# 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. ++# ++# In order to check the certificates, the CA-certificate (in case of a ++# certificate chain, all CA-certificates) must be available. ++# You should add these certificates to the server certificate, the server ++# certificate first, then the issuing CA(s). ++# ++# Example: the certificate for "client.dom.ain" was issued by "intermediate CA" ++# which itself has a certificate of "root CA". Create the client.pem file by ++# 'cat client_cert.pem intemediate_CA.pem root_CA.pem > client.pem' ++# ++# If you want to accept certificates issued by these CAs yourself, you can ++# also add the CA-certificates to the smtp_tls_CAfile, in which case it is ++# not necessary to have them in the smtp_tls_[d]cert_file. ++# ++# A certificate supplied here must be useable as SSL client certificate and ++# hence pass the "openssl verify -purpose sslclient ..." test. ++# ++smtp_tls_cert_file = /etc/postfix/client.pem ++smtp_tls_key_file = $smtp_tls_cert_file ++ ++# The certificate was issued by a certification authority (CA), the CA-cert ++# of which must be available, if not in the certificate file. ++# 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! ++# ++# To use this option in chroot mode, this directory itself or a copy of it ++# must be inside the chroot jail. ++# ++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. The 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 smtp processes, a disc based ++# session cache can be used based on the SDBM databases (routines included ++# in Postfix/TLS). Since concurrent writing must be supported, only SDBM ++# can be used. ++# ++smtp_tls_session_cache_database = sdbm:/etc/postfix/smtp_scache ++ ++# 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 = 3600s ++ ++# 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. ++# HINT: I have it switched on on my mailservers and did experience one ++# single failure since client side TLS is implemented. (There was one ++# misconfired MS Exchange server; I contacted ths admin.) Hence, I am happy ++# with it running all the time, but I am interested in testing anyway. ++# You have been warned, however :-) ++# ++# In case of failure, a "4xx" code is issued and the mail stays in the queue. ++# ++# Explicitely switch it on here, if you want it. ++# ++smtp_use_tls = yes ++ ++# 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). ++# The behaviour may be changed with the smtp_tls_enforce_peername option ++# ++# 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 ++ ++# As of RFC2487 the requirements for hostname checking for MTA clients are ++# not set. When in smtp_enforce_tls mode, the option smtp_tls_enforce_peername ++# can be set to "no" to disable strict peername checking. In this case, the ++# mail delivery will be continued, if a TLS connection was established ++# _and_ the peer certificate passed verification _but_ regardless of the ++# CommonName listed in the certificate. This option only applies to the ++# default setting smtp_enforce_tls_mode, special settings in the ++# smtp_tls_per_site table override smtp_tls_enforce_peername. ++# ++# This can make sense in closed environment where special CAs are created. ++# If not used carefully, this option opens the danger of a "man-in-the-middle" ++# attack (the CommonName of this attacker is logged). ++# ++# smtp_tls_enforce_peername = yes ++ ++# As generally trying TLS can be a bad idea (some hosts offer STARTTLS but ++# the negotiation will fail leading to unexplainable failures, it may be ++# a good idea to decide based on the recipient or the mailhub to which you are ++# connecting. ++# ++# Deciding per recipient may be difficult, since a singe email can have ++# several recipients. We use the "nexthop" mechanism inside postfix. ++# When an email is to be delivered, the "nexthop" is obtained. If it matches ++# an entry in the smtp_tls_per_site list, appropriate action is taken. ++# Since entries in the transport table or the use of a relay_host override ++# the nexthop setting, in these cases the relay_host etc must be listed ++# in the table. In any case, the hostname of the peer to be contacted is ++# looked up (that is: the MX or the name of the host, if no MX is given). ++# ++# Special hint for enforcement mode: ++# Since there is no secure mechanism for DNS lookups available, the ++# recommended setup is: put the sensible domains with their mailhost ++# into the transport table (since you can asure security of this table ++# unlike DNS), then set MUST mode for this mailhost. ++# ++# Format of the table: ++# The keys entries are on the left hand side, no wildcards allowed. On the ++# right hand side the keywords NONE (don't use TLS at all), MAY (try to use ++# STARTTLS if offered, no problem if not), MUST (enforce usage of STARTTLS, ++# check server certificate CommonName against server FQDN), MUST_NOPEERMATCH ++# (enforce usage of STARTTLS and verify certificate, but ignore differences ++# between CommonName and server FQDN). ++# dom.ain NONE ++# host.dom.ain MAY ++# important.host MUST ++# some.host.dom.ain MUST_NOPEERMATCH ++# ++# If an entry is not matched, the default policy is applied; if the default ++# policy is "enforce", NONE explicitely switches it off, otherwise the ++# "enforce" mode is used even for MAY entries. ++# ++smtp_tls_per_site = hash:/etc/postfix/tls_per_site ++ ++# The verification depth for server certificates. A depth of 1 is sufficient, ++# if the certificate ist directly issued by a CA listed in the CA locations. ++# The default value (5) should also suffice for longer chains (root CA issues ++# special CA which then issues the actual certificate...) ++# ++# smtp_tls_scert_verifydepth = 5 ++ ++# 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 ++ ++# To influence the cipher selection scheme, you can give cipherlist-string. ++# A detailed description would go to far here, please refer to the openssl ++# documentation. ++# If you don't know what to do with it, simply don't touch it and leave the ++# (openssl-)compiled in default! ++# ++# DO NOT USE " to enclose the string, just the string!!! ++# ++# smtp_tls_cipherlist = DEFAULT ++ ++# The smtp_starttls_timeout parameter limits the time in seconds to write and ++# read operations during TLS start and stop handhake procedures. ++# ++# In case of problems the client does NOT try the next address on ++# the mail exchanger list. ++# ++# smtp_starttls_timeout = 300s ++ ++# In order to seed the PRNG Pseude Random Number Generator, random data is ++# needed. The PRNG pool is maintained by the "tlsmgr" daemon and is used ++# (read) by the smtp[d] processes after adding some more entropy by stirring ++# in time and process id. ++# The file, which is from time to time rewritten by the tlsmgr, is created ++# if not existant. A default value is given; the default should probably ++# be on the /var partition but _not_ inside chroot jail. ++# ++# tls_random_exchange_name = /etc/postfix/prng_exch ++ ++# To feed the PRNG pool, entropy is being read from an external source, ++# both at startup and during run. ++# Specify a good entropy source here, like EGD or /dev/urandom; make sure ++# to only use non-blocking sources. ++# In both cases, 32 bytes are read at each re-seeding event (which is an ++# amount of 256bits and hence good enough for 128bit symmetric keys). ++# You must specify the type of source: "dev:" for a device special file ++# or "egd:" for a source with EGD compatible socket interface. A maximum ++# 255 bytes is read from these sources in each step. ++# If you specify a normal file, a larger amount of data can be read. ++# ++# The entropy source is queried again after a certain amount of time. The ++# time is calculated using the PRNG, it is between 0 and the time specified, ++# default is a maximum of 1 hour. ++# ++# tls_random_source = dev:/dev/urandom ++tls_random_source = egd:/var/run/egd-pool ++# tls_random_bytes = 32 ++# tls_random_reseed_period = 3600s ++ ++# The PRNG pool inside tlsmgr is used to re-generate the 1024 byte file ++# being read by smtp[d]. The time, after which the exchange file is ++# rewritten is calculated using the PRNG, it is between 0 and the time ++# specified, default is a maximum of 60 seconds. ++# ++# tls_random_upd_period = 60s ++ ++# If you have a entropy source available, that is not easily drained (like ++# /dev/urandom), the daemons can also load additional entropy on startup from ++# the source specified. By default an amount of 32 bytes is read, the ++# equivalent to 256 bits. This is more than enough to generate a 128bit ++# (or 168bit) session key, but we may have to generate more than one. ++# Usage of this option may drain EGD (consider the case of 50 smtp starting ++# up with a full queue and "postfix start", which will request 1600bytes ++# of entropy). This is however not fatal, as long as "entropy" data could ++# be read from the exchange file. ++# ++# tls_daemon_random_source = dev:/dev/urandom ++tls_daemon_random_source = egd:/var/run/egd-pool ++# tls_daemon_random_bytes = 32 ++ +diff -Pur postfix-1.1.11.20020613-orig/makedefs postfix-1.1.11-20020613/makedefs +--- postfix-1.1.11.20020613-orig/makedefs Sat May 4 15:36:19 2002 ++++ postfix-1.1.11.20020613/makedefs Wed Jun 26 15:26:47 2002 +@@ -52,6 +52,21 @@ + SYSTEM=`(uname -s) 2>/dev/null` + RELEASE=`(uname -r) 2>/dev/null` + VERSION=`(uname -v) 2>/dev/null` ++if test -f /usr/include/netinet6/in6.h; then ++ grep __KAME__ /usr/include/netinet6/in6.h 2>&1 >/dev/null ++ if [ $? = 1 ]; then ++ INET6= ++ else ++ if [ -f /usr/local/v6/lib/libinet6.a ]; then ++ INET6=kame ++ else ++ INET6=kame-merged ++ fi ++ fi ++fi ++if [ -z "$INET6" -a -f /usr/include/netinet/ip6.h -a -f /usr/include/linux/icmpv6.h ]; then ++ INET6=linux ++fi + + case "$VERSION" in + dcosx*) SYSTEM=$VERSION;; +@@ -295,6 +310,26 @@ + esac + + : ${CC='gcc $(WARN)'} ${OPT='-O'} ${DEBUG='-g'} ${AWK=awk} ++ ++case "$INET6" in ++kame) ++ CCARGS="$CCARGS -DINET6 -D__ss_family=ss_family -D__ss_len=ss_len" ++ if test -f /usr/local/v6/lib/libinet6.a; then ++ SYSLIBS="$SYSLIBS -L/usr/local/v6/lib -linet6" ++ fi ++ ;; ++kame-merged) ++ CCARGS="$CCARGS -DINET6 -D__ss_family=ss_family -D__ss_len=ss_len" ++ ;; ++linux) ++ CCARGS="$CCARGS -DINET6 -D__ss_family=ss_family" ++ if test -f /usr/include/libinet6/netinet/ip6.h -a \ ++ -f /usr/lib/libinet6.a; then ++ CCARGS="$CCARGS -I/usr/include/libinet6 -DUSAGI_LIBINET6" ++ SYSLIBS="$SYSLIBS -linet6" ++ fi ++ ;; ++esac + + export SYSTYPE AR ARFL RANLIB SYSLIBS CC OPT DEBUG AWK OPTS + +diff -Pur postfix-1.1.11.20020613-orig/man/man8/tlsmgr.8 postfix-1.1.11-20020613/man/man8/tlsmgr.8 +--- postfix-1.1.11.20020613-orig/man/man8/tlsmgr.8 Thu Jan 1 01:00:00 1970 ++++ postfix-1.1.11.20020613/man/man8/tlsmgr.8 Wed Jun 26 15:26:47 2002 +@@ -0,0 +1,130 @@ ++.TH TLSMGR 8 ++.ad ++.fi ++.SH NAME ++tlsmgr ++\- ++Postfix TLS session cache and PRNG handling manager ++.SH SYNOPSIS ++.na ++.nf ++\fBtlsmgr\fR [generic Postfix daemon options] ++.SH DESCRIPTION ++.ad ++.fi ++The tlsmgr process does housekeeping on the session cache database ++files. It runs through the databases and removes expired entries ++and entries written by older (incompatible) versions. ++ ++The tlsmgr is responsible for the PRNG handling. The used internal ++OpenSSL PRNG has a pool size of 8192 bits (= 1024 bytes). The pool ++is initially seeded at startup from an external source (EGD or ++/dev/urandom) and additional seed is obtained later during program ++run at a configurable period. The exact time of seed query is ++using random information and is equally distributed in the range of ++[0-\fBtls_random_reseed_period\fR] with a \fBtls_random_reseed_period\fR ++having a default of 1 hour. ++ ++Tlsmgr can be run chrooted and with dropped privileges, as it will ++connect to the entropy source at startup. ++ ++The PRNG is additionally seeded internally by the data found in the ++session cache and timevalues. ++ ++Tlsmgr reads the old value of the exchange file at startup to keep ++entropy already collected during previous runs. ++ ++From the PRNG random pool a cryptographically strong 1024 byte random ++sequence is written into the PRNG exchange file. The file is updated ++periodically with the time changing randomly from ++[0-\fBtls_random_prng_update_period\fR]. ++.SH STANDARDS ++.na ++.nf ++.SH SECURITY ++.na ++.nf ++.ad ++.fi ++Tlsmgr is not security-sensitive. It only deals with external data ++to be fed into the PRNG, the contents is never trusted. The session ++cache housekeeping will only remove entries if expired and will never ++touch the contents of the cached data. ++.SH DIAGNOSTICS ++.ad ++.fi ++Problems and transactions are logged to the syslog daemon. ++.SH BUGS ++.ad ++.fi ++There is no automatic means to limit the number of entries in the ++session caches and/or the size of the session cache files. ++.SH CONFIGURATION PARAMETERS ++.na ++.nf ++.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 Session Cache ++.ad ++.fi ++.IP \fBsmtpd_tls_session_cache_database\fR ++Name of the SDBM file (type sdbm:) containing the SMTP server session ++cache. If the file does not exist, it is created. ++.IP \fBsmtpd_tls_session_cache_timeout\fR ++Expiry time of SMTP server session cache entries in seconds. Entries ++older than this are removed from the session cache. A cleanup-run is ++performed periodically every \fBsmtpd_tls_session_cache_timeout\fR ++seconds. Default is 3600 (= 1 hour). ++.IP \fBsmtp_tls_session_cache_database\fR ++Name of the SDBM file (type sdbm:) containing the SMTP client session ++cache. If the file does not exist, it is created. ++.IP \fBsmtp_tls_session_cache_timeout\fR ++Expiry time of SMTP client session cache entries in seconds. Entries ++older than this are removed from the session cache. A cleanup-run is ++performed periodically every \fBsmtp_tls_session_cache_timeout\fR ++seconds. Default is 3600 (= 1 hour). ++.SH Pseudo Random Number Generator ++.ad ++.fi ++.IP \fBtls_random_source\fR ++Name of the EGD socket or device or regular file to obtain entropy ++from. The type of entropy source must be specified by preceding the ++name with the appropriate type: egd:/path/to/egd_socket, ++dev:/path/to/devicefile, or /path/to/regular/file. ++tlsmgr opens \fBtls_random_source\fR and tries to read ++\fBtls_random_bytes\fR from it. ++.IP \fBtls_random_bytes\fR ++Number of bytes to be read from \fBtls_random_source\fR. ++Default value is 32 bytes. If using EGD, a maximum of 255 bytes is read. ++.IP \fBtls_random_exchange_name\fR ++Name of the file written by tlsmgr and read by smtp and smtpd at ++startup. The length is 1024 bytes. Default value is ++/etc/postfix/prng_exch. ++.IP \fBtls_random_reseed_period\fR ++Time in seconds until the next reseed from external sources is due. ++This is the maximum value. The actual point in time is calculated ++with a random factor equally distributed between 0 and this maximum ++value. Default is 3600 (= 60 minutes). ++.IP \fBtls_random_prng_update_period\fR ++Time in seconds until the PRNG exchange file is updated with new ++pseude random values. This is the maximum value. The actual point ++in time is calculated with a random factor equally distributed ++between 0 and this maximum value. Default is 60 (= 1 minute). ++.SH SEE ALSO ++.na ++.nf ++smtp(8) SMTP client ++smtpd(8) SMTP server ++.SH LICENSE ++.na ++.nf ++.ad ++.fi ++The Secure Mailer license must be distributed with this software. ++.SH AUTHOR(S) ++.na ++.nf +diff -Pur postfix-1.1.11.20020613-orig/src/dns/dns_lookup.c postfix-1.1.11-20020613/src/dns/dns_lookup.c +--- postfix-1.1.11.20020613-orig/src/dns/dns_lookup.c Sun Feb 4 19:16:20 2001 ++++ postfix-1.1.11.20020613/src/dns/dns_lookup.c Wed Jun 26 15:26:47 2002 +@@ -132,6 +132,9 @@ + } DNS_REPLY; + + #define INET_ADDR_LEN 4 /* XXX */ ++#ifdef INET6 ++#define INET6_ADDR_LEN 16 ++#endif + + /* dns_query - query name server and pre-parse the reply */ + +@@ -337,6 +340,19 @@ + memcpy(temp, pos, fixed->length); + data_len = fixed->length; + break; ++#ifdef INET6 ++ case T_AAAA: ++ if (fixed->length != INET6_ADDR_LEN) { ++ msg_warn("extract_answer: bad IPv6 address length: %d", fixed->length); ++ return (0); ++ } ++ if (fixed->length > sizeof(temp)) ++ msg_panic("dns_get_rr: length %d > DNS_NAME_LEN", ++ fixed->length); ++ memcpy(temp, pos, fixed->length); ++ data_len = fixed->length; ++ break; ++#endif + case T_TXT: + data_len = MIN2(pos[0] + 1, MIN2(fixed->length + 1, sizeof(temp))); + for (src = pos + 1, dst = (unsigned char *) (temp); +diff -Pur postfix-1.1.11.20020613-orig/src/global/Makefile.in postfix-1.1.11-20020613/src/global/Makefile.in +--- postfix-1.1.11.20020613-orig/src/global/Makefile.in Tue Jun 11 03:13:13 2002 ++++ postfix-1.1.11.20020613/src/global/Makefile.in Wed Jun 26 15:26:47 2002 +@@ -20,7 +20,7 @@ + tok822_resolve.c tok822_rewrite.c tok822_tree.c xtext.c bounce_log.c \ + flush_clnt.c mail_conf_time.c mbox_conf.c mbox_open.c abounce.c \ + verp_sender.c match_parent_style.c mime_state.c header_token.c \ +- strip_addr.c ++ strip_addr.c pfixtls.c wildcard_inet_addr.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 \ +@@ -42,7 +42,7 @@ + tok822_resolve.o tok822_rewrite.o tok822_tree.o xtext.o bounce_log.o \ + flush_clnt.o mail_conf_time.o mbox_conf.o mbox_open.o abounce.o \ + verp_sender.o match_parent_style.o mime_state.o header_token.o \ +- strip_addr.o ++ strip_addr.o pfixtls.o wildcard_inet_addr.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 \ +@@ -60,7 +60,7 @@ + sys_exits.h timed_ipc.h tok822.h xtext.h bounce_log.h flush_clnt.h \ + mbox_conf.h mbox_open.h abounce.h qmqp_proto.h verp_sender.h \ + match_parent_style.h quote_flags.h mime_state.h header_token.h \ +- lex_822.h strip_addr.h ++ lex_822.h strip_addr.h pfixtls.h wildcard_inet_addr.h + TESTSRC = rec2stream.c stream2rec.c recdump.c + WARN = -W -Wformat -Wimplicit -Wmissing-prototypes \ + -Wparentheses -Wstrict-prototypes -Wswitch -Wuninitialized \ +@@ -1160,3 +1160,16 @@ + xtext.o: ../../include/vbuf.h + xtext.o: ../../include/vstring.h + xtext.o: xtext.h ++pfixtls.o: pfixtls.c ++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 ++pfixtls.o: ../../include/dict.h ++pfixtls.o: ../../include/myflock.h ++pfixtls.o: ../../include/stringops.h ++pfixtls.o: ../../include/msg.h ++pfixtls.o: ../../include/connect.h ++pfixtls.o: mail_params.h ++pfixtls.o: pfixtls.h +diff -Pur postfix-1.1.11.20020613-orig/src/global/mail_params.c postfix-1.1.11-20020613/src/global/mail_params.c +--- postfix-1.1.11.20020613-orig/src/global/mail_params.c Sat Jun 8 20:21:40 2002 ++++ postfix-1.1.11.20020613/src/global/mail_params.c Wed Jun 26 15:26:47 2002 +@@ -223,6 +223,31 @@ + int var_in_flow_delay; + char *var_par_dom_match; + char *var_config_dirs; ++char *var_tls_rand_exch_name; ++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; ++char *var_smtpd_tls_cipherlist; ++char *var_smtpd_tls_dh512_param_file; ++char *var_smtpd_tls_dh1024_param_file; ++int var_smtpd_tls_loglevel; ++char *var_smtpd_tls_scache_db; ++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; ++char *var_smtp_tls_cipherlist; ++int var_smtp_tls_loglevel; ++char *var_smtp_tls_scache_db; ++int var_smtp_tls_scache_timeout; ++char *var_tls_daemon_rand_source; ++int var_tls_daemon_rand_bytes; + + char *var_import_environ; + char *var_export_environ; +@@ -467,6 +492,26 @@ + VAR_SHOWQ_SERVICE, DEF_SHOWQ_SERVICE, &var_showq_service, 1, 0, + VAR_ERROR_SERVICE, DEF_ERROR_SERVICE, &var_error_service, 1, 0, + VAR_FLUSH_SERVICE, DEF_FLUSH_SERVICE, &var_flush_service, 1, 0, ++ VAR_TLS_RAND_EXCH_NAME, DEF_TLS_RAND_EXCH_NAME, &var_tls_rand_exch_name, 0, 0, ++ VAR_SMTPD_TLS_CERT_FILE, DEF_SMTPD_TLS_CERT_FILE, &var_smtpd_tls_cert_file, 0, 0, ++ VAR_SMTPD_TLS_KEY_FILE, DEF_SMTPD_TLS_KEY_FILE, &var_smtpd_tls_key_file, 0, 0, ++ VAR_SMTPD_TLS_DCERT_FILE, DEF_SMTPD_TLS_DCERT_FILE, &var_smtpd_tls_dcert_file, 0, 0, ++ VAR_SMTPD_TLS_DKEY_FILE, DEF_SMTPD_TLS_DKEY_FILE, &var_smtpd_tls_dkey_file, 0, 0, ++ VAR_SMTPD_TLS_CA_FILE, DEF_SMTPD_TLS_CA_FILE, &var_smtpd_tls_CAfile, 0, 0, ++ VAR_SMTPD_TLS_CA_PATH, DEF_SMTPD_TLS_CA_PATH, &var_smtpd_tls_CApath, 0, 0, ++ VAR_SMTPD_TLS_CLIST, DEF_SMTPD_TLS_CLIST, &var_smtpd_tls_cipherlist, 0, 0, ++ VAR_SMTPD_TLS_512_FILE, DEF_SMTPD_TLS_512_FILE, &var_smtpd_tls_dh512_param_file, 0, 0, ++ VAR_SMTPD_TLS_1024_FILE, DEF_SMTPD_TLS_1024_FILE, &var_smtpd_tls_dh1024_param_file, 0, 0, ++ VAR_SMTPD_TLS_SCACHE_DB, DEF_SMTPD_TLS_SCACHE_DB, &var_smtpd_tls_scache_db, 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, ++ VAR_SMTP_TLS_CLIST, DEF_SMTP_TLS_CLIST, &var_smtp_tls_cipherlist, 0, 0, ++ VAR_SMTP_TLS_SCACHE_DB, DEF_SMTP_TLS_SCACHE_DB, &var_smtp_tls_scache_db, 0, 0, ++ VAR_TLS_DAEMON_RAND_SOURCE, DEF_TLS_DAEMON_RAND_SOURCE, &var_tls_daemon_rand_source, 0, 0, + 0, + }; + static CONFIG_STR_FN_TABLE function_str_defaults_2[] = { +@@ -489,6 +534,9 @@ + VAR_TOKEN_LIMIT, DEF_TOKEN_LIMIT, &var_token_limit, 1, 0, + VAR_MIME_MAXDEPTH, DEF_MIME_MAXDEPTH, &var_mime_maxdepth, 1, 0, + VAR_MIME_BOUND_LEN, DEF_MIME_BOUND_LEN, &var_mime_bound_len, 1, 0, ++ VAR_SMTPD_TLS_LOGLEVEL, DEF_SMTPD_TLS_LOGLEVEL, &var_smtpd_tls_loglevel, 0, 0, ++ VAR_SMTP_TLS_LOGLEVEL, DEF_SMTP_TLS_LOGLEVEL, &var_smtp_tls_loglevel, 0, 0, ++ VAR_TLS_DAEMON_RAND_BYTES, DEF_TLS_DAEMON_RAND_BYTES, &var_tls_daemon_rand_bytes, 0, 0, + 0, + }; + static CONFIG_TIME_TABLE time_defaults[] = { +@@ -499,6 +547,8 @@ + VAR_FORK_DELAY, DEF_FORK_DELAY, &var_fork_delay, 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_SCACHTIME, DEF_SMTPD_TLS_SCACHTIME, &var_smtpd_tls_scache_timeout, 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, + VAR_IN_FLOW_DELAY, DEF_IN_FLOW_DELAY, &var_in_flow_delay, 0, 10, + 0, +diff -Pur postfix-1.1.11.20020613-orig/src/global/mail_params.h postfix-1.1.11-20020613/src/global/mail_params.h +--- postfix-1.1.11.20020613-orig/src/global/mail_params.h Mon Jun 10 19:49:56 2002 ++++ postfix-1.1.11.20020613/src/global/mail_params.h Wed Jun 26 15:26:47 2002 +@@ -458,6 +458,34 @@ + #define DEF_DUP_FILTER_LIMIT 1000 + extern int var_dup_filter_limit; + ++#define VAR_TLS_RAND_EXCH_NAME "tls_random_exchange_name" ++#define DEF_TLS_RAND_EXCH_NAME "${config_directory}/prng_exch" ++extern char *var_tls_rand_exch_name; ++ ++#define VAR_TLS_RAND_SOURCE "tls_random_source" ++#define DEF_TLS_RAND_SOURCE "" ++extern char *var_tls_rand_source; ++ ++#define VAR_TLS_RAND_BYTES "tls_random_bytes" ++#define DEF_TLS_RAND_BYTES 32 ++extern int var_tls_rand_bytes; ++ ++#define VAR_TLS_DAEMON_RAND_SOURCE "tls_daemon_random_source" ++#define DEF_TLS_DAEMON_RAND_SOURCE "" ++extern char *var_tls_daemon_rand_source; ++ ++#define VAR_TLS_DAEMON_RAND_BYTES "tls_daemon_random_bytes" ++#define DEF_TLS_DAEMON_RAND_BYTES 32 ++extern int var_tls_daemon_rand_bytes; ++ ++#define VAR_TLS_RESEED_PERIOD "tls_random_reseed_period" ++#define DEF_TLS_RESEED_PERIOD "3600s" ++extern int var_tls_reseed_period; ++ ++#define VAR_TLS_PRNG_UPD_PERIOD "tls_random_prng_update_period" ++#define DEF_TLS_PRNG_UPD_PERIOD "60s" ++extern int var_tls_prng_upd_period; ++ + /* + * Queue manager: relocated databases. + */ +@@ -678,6 +706,10 @@ + #define DEF_SMTP_HELO_TMOUT "300s" + extern int var_smtp_helo_tmout; + ++#define VAR_SMTP_STARTTLS_TMOUT "smtp_starttls_timeout" ++#define DEF_SMTP_STARTTLS_TMOUT "300s" ++extern int var_smtp_starttls_tmout; ++ + #define VAR_SMTP_MAIL_TMOUT "smtp_mail_timeout" + #define DEF_SMTP_MAIL_TMOUT "300s" + extern int var_smtp_mail_tmout; +@@ -734,6 +766,12 @@ + #define DEF_SMTP_BIND_ADDR "" + extern char *var_smtp_bind_addr; + ++#ifdef INET6 ++#define VAR_SMTP_BIND_ADDR6 "smtp_bind_address6" ++#define DEF_SMTP_BIND_ADDR6 "" ++extern char *var_smtp_bind_addr6; ++#endif ++ + #define VAR_SMTP_HELO_NAME "smtp_helo_name" + #define DEF_SMTP_HELO_NAME "$myhostname" + extern char *var_smtp_helo_name; +@@ -767,6 +805,10 @@ + #define DEF_SMTPD_TMOUT "300s" + extern int var_smtpd_tmout; + ++#define VAR_SMTPD_STARTTLS_TMOUT "smtpd_starttls_timeout" ++#define DEF_SMTPD_STARTTLS_TMOUT "300s" ++extern int var_smtpd_starttls_tmout; ++ + #define VAR_SMTPD_RCPT_LIMIT "smtpd_recipient_limit" + #define DEF_SMTPD_RCPT_LIMIT 1000 + extern int var_smtpd_rcpt_limit; +@@ -795,6 +837,150 @@ + #define DEF_SMTPD_NOOP_CMDS "" + extern char *var_smtpd_noop_cmds; + ++#define VAR_SMTPD_TLS_WRAPPER "smtpd_tls_wrappermode" ++#define DEF_SMTPD_TLS_WRAPPER 0 ++extern bool var_smtpd_tls_wrappermode; ++ ++#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_AUTH_ONLY "smtpd_tls_auth_only" ++#define DEF_SMTPD_TLS_AUTH_ONLY 0 ++extern bool var_smtpd_tls_auth_only; ++ ++#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 5 ++extern int var_smtpd_tls_ccert_vd; ++ ++#define VAR_SMTPD_TLS_CERT_FILE "smtpd_tls_cert_file" ++#define DEF_SMTPD_TLS_CERT_FILE "" ++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 "" ++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 "" ++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_CLIST "smtpd_tls_cipherlist" ++#define DEF_SMTPD_TLS_CLIST "" ++extern char *var_smtpd_tls_cipherlist; ++ ++#define VAR_SMTPD_TLS_512_FILE "smtpd_tls_dh512_param_file" ++#define DEF_SMTPD_TLS_512_FILE "" ++extern char *var_smtpd_tls_dh512_param_file; ++ ++#define VAR_SMTPD_TLS_1024_FILE "smtpd_tls_dh1024_param_file" ++#define DEF_SMTPD_TLS_1024_FILE "" ++extern char *var_smtpd_tls_dh1024_param_file; ++ ++#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_DB "smtpd_tls_session_cache_database" ++#define DEF_SMTPD_TLS_SCACHE_DB "" ++extern char *var_smtpd_tls_scache_db; ++ ++#define VAR_SMTPD_TLS_SCACHTIME "smtpd_tls_session_cache_timeout" ++#define DEF_SMTPD_TLS_SCACHTIME "3600s" ++extern int var_smtpd_tls_scache_timeout; ++ ++#define VAR_SMTP_TLS_PER_SITE "smtp_tls_per_site" ++#define DEF_SMTP_TLS_PER_SITE "" ++extern char *var_smtp_tls_per_site; ++ ++#define VAR_SMTP_USE_TLS "smtp_use_tls" ++#define DEF_SMTP_USE_TLS 0 ++extern bool var_smtp_use_tls; ++ ++#define VAR_SMTP_ENFORCE_TLS "smtp_enforce_tls" ++#define DEF_SMTP_ENFORCE_TLS 0 ++extern bool var_smtp_enforce_tls; ++ ++#define VAR_SMTP_TLS_ENFORCE_PN "smtp_tls_enforce_peername" ++#define DEF_SMTP_TLS_ENFORCE_PN 1 ++extern bool var_smtp_tls_enforce_peername; ++ ++#define VAR_SMTP_TLS_SCERT_VD "smtp_tls_scert_verifydepth" ++#define DEF_SMTP_TLS_SCERT_VD 5 ++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_CLIST "smtp_tls_cipherlist" ++#define DEF_SMTP_TLS_CLIST "" ++extern char *var_smtp_tls_cipherlist; ++ ++#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_DB "smtp_tls_session_cache_database" ++#define DEF_SMTP_TLS_SCACHE_DB "" ++extern char *var_smtp_tls_scache_db; ++ ++#define VAR_SMTP_TLS_SCACHTIME "smtp_tls_session_cache_timeout" ++#define DEF_SMTP_TLS_SCACHTIME "3600s" ++extern int var_smtp_tls_scache_timeout; ++ + /* + * SASL authentication support, SMTP server side. + */ +@@ -953,6 +1139,16 @@ + #define DEF_LMTP_QUIT_TMOUT "300s" + extern int var_lmtp_quit_tmout; + ++#define VAR_LMTP_BIND_ADDR "lmtp_bind_address" ++#define DEF_LMTP_BIND_ADDR "" ++extern char *var_lmtp_bind_addr; ++ ++#ifdef INET6 ++#define VAR_LMTP_BIND_ADDR6 "lmtp_bind_address6" ++#define DEF_LMTP_BIND_ADDR6 "" ++extern char *var_lmtp_bind_addr6; ++#endif ++ + /* + * Cleanup service. Header info that exceeds $header_size_limit bytes forces + * the start of the message body. +@@ -1088,6 +1284,10 @@ + #define DEF_RELAY_DOMAINS "$mydestination" + 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; +@@ -1167,6 +1367,8 @@ + #define PERMIT_AUTH_DEST "permit_auth_destination" + #define REJECT_UNAUTH_DEST "reject_unauth_destination" + #define CHECK_RELAY_DOMAINS "check_relay_domains" ++#define PERMIT_TLS_CLIENTCERTS "permit_tls_clientcerts" ++#define PERMIT_TLS_ALL_CLIENTCERTS "permit_tls_all_clientcerts" + #define VAR_RELAY_CODE "relay_domains_reject_code" + #define DEF_RELAY_CODE 554 + extern int var_relay_code; +diff -Pur postfix-1.1.11.20020613-orig/src/global/mail_proto.h postfix-1.1.11-20020613/src/global/mail_proto.h +--- postfix-1.1.11.20020613-orig/src/global/mail_proto.h Sun May 12 16:46:59 2002 ++++ postfix-1.1.11.20020613/src/global/mail_proto.h Wed Jun 26 15:26:47 2002 +@@ -34,6 +34,7 @@ + #define MAIL_SERVICE_LOCAL "local" + #define MAIL_SERVICE_PICKUP "pickup" + #define MAIL_SERVICE_QUEUE "qmgr" ++#define MAIL_SERVICE_TLSMGR "tlsmgr" + #define MAIL_SERVICE_RESOLVE "resolve" + #define MAIL_SERVICE_REWRITE "rewrite" + #define MAIL_SERVICE_VIRTUAL "virtual" +diff -Pur postfix-1.1.11.20020613-orig/src/global/mynetworks.c postfix-1.1.11-20020613/src/global/mynetworks.c +--- postfix-1.1.11.20020613-orig/src/global/mynetworks.c Sun Feb 25 02:46:07 2001 ++++ postfix-1.1.11.20020613/src/global/mynetworks.c Wed Jun 26 15:26:47 2002 +@@ -50,6 +50,11 @@ + #include <vstring.h> + #include <inet_addr_list.h> + #include <name_mask.h> ++#ifdef INET6 ++#include <sys/socket.h> ++#include <netinet/in.h> ++#include <netdb.h> ++#endif + + /* Global library. */ + +@@ -75,6 +80,9 @@ + const char *mynetworks(void) + { + static VSTRING *result; ++#ifdef INET6 ++ char hbuf[NI_MAXHOST]; ++#endif + + if (result == 0) { + char *myname = "mynetworks"; +@@ -87,6 +95,9 @@ + int junk; + int i; + int mask_style; ++#ifdef INET6 ++ struct sockaddr *sa; ++#endif + + mask_style = name_mask("mynetworks mask style", mask_styles, + var_mynetworks_style); +@@ -96,8 +107,19 @@ + my_mask_list = own_inet_mask_list(); + + for (i = 0; i < my_addr_list->used; i++) { ++#ifdef INET6 ++ sa = (struct sockaddr *)&my_addr_list->addrs[i]; ++ if (sa->sa_family != AF_INET) { ++ if (sa->sa_family == AF_INET6) ++ vstring_sprintf_append(result, "XAATODOmynetworks "); ++ continue; ++ } ++ addr = ntohl(((struct sockaddr_in *)sa)->sin_addr.s_addr); ++ mask = ntohl(((struct sockaddr_in *)&my_mask_list->addrs[i])->sin_addr.s_addr); ++#else + addr = ntohl(my_addr_list->addrs[i].s_addr); + mask = ntohl(my_mask_list->addrs[i].s_addr); ++#endif + + switch (mask_style) { + +@@ -119,8 +141,15 @@ + mask = IN_CLASSD_NET; + shift = IN_CLASSD_NSHIFT; + } else { ++#ifdef INET6 ++ if (getnameinfo(sa, SA_LEN(sa), hbuf, sizeof(hbuf), NULL, 0, ++ NI_NUMERICHOST)) ++ strncpy(hbuf, "???", sizeof(hbuf)); ++ msg_fatal("%s: bad address class: %s", myname, hbuf); ++#else + msg_fatal("%s: bad address class: %s", + myname, inet_ntoa(my_addr_list->addrs[i])); ++#endif + } + break; + +diff -Pur postfix-1.1.11.20020613-orig/src/global/own_inet_addr.c postfix-1.1.11-20020613/src/global/own_inet_addr.c +--- postfix-1.1.11.20020613-orig/src/global/own_inet_addr.c Tue Jul 31 20:38:29 2001 ++++ postfix-1.1.11.20020613/src/global/own_inet_addr.c Wed Jun 26 15:26:47 2002 +@@ -39,6 +39,10 @@ + #include <netinet/in.h> + #include <arpa/inet.h> + #include <string.h> ++#ifdef INET6 ++#include <sys/socket.h> ++#include <netdb.h> ++#endif + + #ifdef STRCASECMP_IN_STRINGS_H + #include <strings.h> +@@ -101,10 +105,11 @@ + */ + else { + bufp = hosts = mystrdup(var_inet_interfaces); +- while ((host = mystrtok(&bufp, sep)) != 0) ++ while ((host = mystrtok(&bufp, sep)) != 0) { + if (inet_addr_host(addr_list, host) == 0) + msg_fatal("config variable %s: host not found: %s", + VAR_INET_INTERFACES, host); ++ } + myfree(hosts); + + /* +@@ -121,15 +126,39 @@ + msg_fatal("could not find any active network interfaces"); + for (nvirtual = 0; nvirtual < addr_list->used; nvirtual++) { + for (nlocal = 0; /* see below */ ; nlocal++) { +- if (nlocal >= local_addrs.used) ++ if (nlocal >= local_addrs.used) { ++#ifdef INET6 ++ char hbuf[NI_MAXHOST]; ++ if (getnameinfo((struct sockaddr *)&addr_list->addrs[nvirtual], ++ SS_LEN(addr_list->addrs[nvirtual]), hbuf, ++ sizeof(hbuf), NULL, 0, NI_NUMERICHOST) != 0) ++ strncpy(hbuf, "???", sizeof(hbuf)); ++ msg_fatal("parameter %s: no local interface found for %s", ++ VAR_INET_INTERFACES, hbuf); ++#else + msg_fatal("parameter %s: no local interface found for %s", + VAR_INET_INTERFACES, + inet_ntoa(addr_list->addrs[nvirtual])); ++#endif ++ } ++#ifdef INET6 ++ if (addr_list->addrs[nvirtual].ss_family == ++ local_addrs.addrs[nlocal].ss_family && ++ SS_LEN(addr_list->addrs[nvirtual]) == ++ SS_LEN(local_addrs.addrs[nlocal]) && ++ memcmp(&addr_list->addrs[nvirtual], ++ &local_addrs.addrs[nlocal], ++ SS_LEN(local_addrs.addrs[nlocal])) == 0) { ++ inet_addr_list_append(mask_list, (struct sockaddr *)&local_masks.addrs[nlocal]); ++ break; ++ } ++#else + if (addr_list->addrs[nvirtual].s_addr + == local_addrs.addrs[nlocal].s_addr) { + inet_addr_list_append(mask_list, &local_masks.addrs[nlocal]); + break; + } ++#endif + } + } + inet_addr_list_free(&local_addrs); +@@ -139,6 +168,42 @@ + + /* own_inet_addr - is this my own internet address */ + ++#ifdef INET6 ++int own_inet_addr(struct sockaddr * addr) ++{ ++ int i; ++ char *p, *q; ++ int l; ++ struct sockaddr *sa; ++ ++ if (addr_list.used == 0) ++ own_inet_addr_init(&addr_list, &mask_list); ++ ++ for (i = 0; i < addr_list.used; i++) { ++ sa = (struct sockaddr *)&addr_list.addrs[i]; ++ if (addr->sa_family != sa->sa_family) ++ continue; ++ switch (addr->sa_family) { ++ case AF_INET: ++ p = (char *)&((struct sockaddr_in *)addr)->sin_addr; ++ q = (char *)&((struct sockaddr_in *)&addr_list.addrs[i])->sin_addr; ++ l = sizeof(struct in_addr); ++ break; ++ case AF_INET6: ++ /* XXX scope */ ++ p = (char *)&((struct sockaddr_in6 *)addr)->sin6_addr; ++ q = (char *)&((struct sockaddr_in6 *)&addr_list.addrs[i])->sin6_addr; ++ l = sizeof(struct in6_addr); ++ break; ++ default: ++ continue; ++ } ++ if (memcmp(p, q, l) == 0) ++ return (1); ++ } ++ return (0); ++} ++#else + int own_inet_addr(struct in_addr * addr) + { + int i; +@@ -149,8 +214,8 @@ + for (i = 0; i < addr_list.used; i++) + if (addr->s_addr == addr_list.addrs[i].s_addr) + return (1); +- return (0); + } ++#endif + + /* own_inet_addr_list - return list of addresses */ + +diff -Pur postfix-1.1.11.20020613-orig/src/global/own_inet_addr.h postfix-1.1.11-20020613/src/global/own_inet_addr.h +--- postfix-1.1.11.20020613-orig/src/global/own_inet_addr.h Sat Feb 24 02:25:32 2001 ++++ postfix-1.1.11.20020613/src/global/own_inet_addr.h Wed Jun 26 15:26:48 2002 +@@ -15,11 +15,18 @@ + * System library. + */ + #include <netinet/in.h> ++#ifdef INET6 ++#include <sys/socket.h> ++#endif + + /* + * External interface. + */ ++#ifdef INET6 ++extern int own_inet_addr(struct sockaddr *); ++#else + extern int own_inet_addr(struct in_addr *); ++#endif + extern struct INET_ADDR_LIST *own_inet_addr_list(void); + extern struct INET_ADDR_LIST *own_inet_mask_list(void); + +diff -Pur postfix-1.1.11.20020613-orig/src/global/peer_name.c postfix-1.1.11-20020613/src/global/peer_name.c +--- postfix-1.1.11.20020613-orig/src/global/peer_name.c Sun Jan 28 16:23:02 2001 ++++ postfix-1.1.11.20020613/src/global/peer_name.c Wed Jun 26 15:26:48 2002 +@@ -69,12 +69,32 @@ + PEER_NAME *peer_name(int sock) + { + static PEER_NAME peer; +- struct sockaddr_in sin; +- SOCKADDR_SIZE len = sizeof(sin); ++ union sockunion { ++ struct { ++ u_char si_len; ++ u_char si_family; ++ u_short si_port; ++ } su_si; ++ struct sockaddr peer_un; ++ struct sockaddr_in peer_un4; ++#ifdef INET6 ++ struct sockaddr_in6 peer_un6; ++#endif ++ } p_un; ++#define sun p_un.peer_un ++#define sin p_un.peer_un4 ++#ifdef INET6 ++#define sin6 p_un.peer_un6 ++ static char hbuf[NI_MAXHOST]; ++ static char abuf[NI_MAXHOST]; ++#else + struct hostent *hp; ++#endif ++ SOCKADDR_SIZE len = sizeof(p_un); + +- if (getpeername(sock, (struct sockaddr *) & sin, &len) == 0) { +- switch (sin.sin_family) { ++ if (getpeername(sock, (struct sockaddr *)&p_un, &len) == 0) { ++ switch (p_un.peer_un.sa_family) { ++#ifndef INET6 + case AF_INET: + peer.type = PEER_TYPE_INET; + hp = gethostbyaddr((char *) &(sin.sin_addr), +@@ -83,6 +103,24 @@ + hp->h_name : "unknown"); + peer.addr = inet_ntoa(sin.sin_addr); + return (&peer); ++#else ++ case AF_INET: ++ peer.type = PEER_TYPE_INET; ++ if (getnameinfo(&sun, len, hbuf, sizeof(hbuf), NULL, 0, NI_NAMEREQD) != 0) ++ peer.name = "unknown"; ++ else ++ peer.name = hbuf; ++ peer.addr = abuf; ++ return (&peer); ++ case AF_INET6: ++ peer.type = PEER_TYPE_INET6; ++ if (getnameinfo(&sun, len, hbuf, sizeof(hbuf), NULL, 0, NI_NAMEREQD) != 0) ++ peer.name = "unknown"; ++ else ++ peer.name = hbuf; ++ peer.addr = abuf; ++ return (&peer); ++#endif + case AF_UNSPEC: + case AF_UNIX: + peer.type = PEER_TYPE_LOCAL; +diff -Pur postfix-1.1.11.20020613-orig/src/global/peer_name.h postfix-1.1.11-20020613/src/global/peer_name.h +--- postfix-1.1.11.20020613-orig/src/global/peer_name.h Fri Dec 11 19:55:32 1998 ++++ postfix-1.1.11.20020613/src/global/peer_name.h Wed Jun 26 15:26:48 2002 +@@ -22,6 +22,9 @@ + #define PEER_TYPE_UNKNOWN 0 + #define PEER_TYPE_INET 1 + #define PEER_TYPE_LOCAL 2 ++#ifdef INET6 ++#define PEER_TYPE_INET6 3 ++#endif + + extern PEER_NAME *peer_name(int); + +diff -Pur postfix-1.1.11.20020613-orig/src/global/pfixtls.c postfix-1.1.11-20020613/src/global/pfixtls.c +--- postfix-1.1.11.20020613-orig/src/global/pfixtls.c Thu Jan 1 01:00:00 1970 ++++ postfix-1.1.11.20020613/src/global/pfixtls.c Wed Jun 26 15:26:48 2002 +@@ -0,0 +1,2742 @@ ++/*++ ++/* NAME ++/* pfixtls ++/* SUMMARY ++/* interface to openssl routines ++/* SYNOPSIS ++/* #include <pfixtls.h> ++/* ++/* const long scache_db_version; ++/* const long openssl_version; ++/* ++/* int pfixtls_serverengine; ++/* ++/* int pfixtls_clientengine; ++/* ++/* int pfixtls_timed_read(fd, buf, len, timeout, unused_context) ++/* int fd; ++/* void *buf; ++/* unsigned len; ++/* int timeout; ++/* void *context; ++/* ++/* int pfixtls_timed_write(fd, buf, len, timeout, unused_context); ++/* int fd; ++/* void *buf; ++/* unsigned len; ++/* int timeout; ++/* void *context; ++/* ++/* int pfixtls_init_serverengine(verifydepth, askcert); ++/* int verifydepth; ++/* int askcert; ++/* ++/* int pfixtls_start_servertls(stream, timeout, peername, peeraddr, ++/* tls_info, requirecert); ++/* VSTREAM *stream; ++/* int timeout; ++/* const char *peername; ++/* const char *peeraddr; ++/* tls_info_t *tls_info; ++/* int requirecert; ++/* ++/* int pfixtls_stop_servertls(stream, failure, tls_info); ++/* VSTREAM *stream; ++/* int failure; ++/* tls_info_t *tls_info; ++/* ++/* int pfixtls_init_clientengine(verifydepth); ++/* int verifydepth; ++/* ++/* int pfixtls_start_clienttls(stream, timeout, peername, peeraddr, ++/* tls_info); ++/* VSTREAM *stream; ++/* int timeout; ++/* const char *peername; ++/* const char *peeraddr; ++/* tls_info_t *tls_info; ++/* ++/* int pfixtls_stop_clienttls(stream, failure, tls_info); ++/* VSTREAM *stream; ++/* int failure; ++/* tls_info_t *tls_info; ++/* ++/* DESCRIPTION ++/* This module is the interface between Postfix and the OpenSSL library. ++/* ++/* pfixtls_timed_read() reads the requested number of bytes calling ++/* SSL_read(). pfixtls_time_read() will only be called indirect ++/* as a VSTREAM_FN function. ++/* pfixtls_timed_write() is the corresponding write 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 VSTREAM ++/* passed as argument. We expect that all buffers are flushed and the ++/* TLS handshake can begin immediately. Information about the peer ++/* is stored into the tls_info structure passed as argument. ++/* ++/* 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. ++/* If the failure flag is set, the session is cleared from the cache. ++/* ++/* 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 via the tls_info structure: ++/* protocol holds the protocol name (SSLv2, SSLv3, TLSv1), ++/* tls_info->cipher_name the cipher name (e.g. RC4/MD5), ++/* tls_info->cipher_usebits the number of bits actually used (e.g. 40), ++/* tls_info->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: ++/* tls_info->peer_subject X509v3-oneline with the DN of the peer ++/* tls_info->peer_CN extracted CommonName of the peer ++/* tls_info->peer_issuer X509v3-oneline with the DN of the issuer ++/* tls_info->peer_CN extracted CommonName of the issuer ++/* tls_info->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. ++/* ++/* Postfix/TLS uses a database approach based on the internal "dict" ++/* interface. Since the session cache information is approximately ++/* 1300 bytes binary data, it will not fit into the dbm/ndbm model. ++/* It also needs write access to the database, ruling out most other ++/* interface, leaving Berkeley DB, which however cannot handle concurrent ++/* access by several processes. Hence a modified SDBM (public domain DBM) ++/* with enhanced buffer size is used and concurrent write capability ++/* is used. SDBM is part of Postfix/TLS. ++/* ++/* Realization: ++/* Both (client and server) session cache are realized by individual ++/* cache databases. A common database would not make sense, since the ++/* key criteria are different (session ID for server, peername for ++/* client). ++/* ++/* 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 last session will win, overwriting the older one in ++/* the database. 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). ++/* 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. ++/* Like on the server side, 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. ++/* ++/* 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. The task is performed by tlsmgr based on ++/* the timestamp created by pfixtls and included in the saved session, ++/* so that tlsmgr has not to care about the SSL_SESSION internal data. ++/* ++/* 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 ++/*--*/ ++ ++/* System library. */ ++ ++#include <sys_defs.h> ++#include <sys/types.h> ++#include <sys/stat.h> ++#include <sys/time.h> /* gettimeofday, not in POSIX */ ++#include <unistd.h> ++#include <stdio.h> ++#include <string.h> ++#include <errno.h> ++#include <ctype.h> ++ ++/* Utility library. */ ++ ++#include <iostuff.h> ++#include <mymalloc.h> ++#include <vstring.h> ++#include <vstream.h> ++#include <dict.h> ++#include <myflock.h> ++#include <stringops.h> ++#include <msg.h> ++#include <connect.h> ++ ++/* Application-specific. */ ++ ++#include "mail_params.h" ++#include "pfixtls.h" ++ ++#define STR vstring_str ++ ++const tls_info_t tls_info_zero = { ++ 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0 ++}; ++ ++#ifdef HAS_SSL ++ ++/* OpenSSL library. */ ++ ++#include <openssl/lhash.h> ++#include <openssl/bn.h> ++#include <openssl/err.h> ++#include <openssl/pem.h> ++#include <openssl/x509.h> ++#include <openssl/rand.h> ++#include <openssl/ssl.h> ++ ++/* We must keep some of the info available */ ++static const char hexcodes[] = "0123456789ABCDEF"; ++ ++/* ++ * When saving sessions, we want to make sure, that the lenght of the key ++ * is somehow limited. When saving client sessions, the hostname is used ++ * as key. According to HP-UX 10.20, MAXHOSTNAMELEN=64. Maybe new standards ++ * will increase this value, but as this will break compatiblity with existing ++ * implementations, we won't see this for long. We therefore choose a limit ++ * of 64 bytes. ++ * The length of the (TLS) session id can be up to 32 bytes according to ++ * RFC2246, so it fits well into the 64bytes limit. ++ */ ++#define ID_MAXLENGTH 64 /* Max ID length in bytes */ ++ ++/* ++ * The session_id_context is set, such that the client knows which services ++ * on a host share the same session information (on the postfix host may ++ * as well run a TLS-enabled webserver. ++ */ ++static char server_session_id_context[] = "Postfix/TLS"; /* anything will do */ ++static int TLScontext_index = -1; ++static int TLSpeername_index = -1; ++static int do_dump = 0; ++static DH *dh_512 = NULL, *dh_1024 = NULL; ++static SSL_CTX *ctx = NULL; ++ ++static int rand_exch_fd = -1; ++ ++static DICT *scache_db = NULL; ++const long scache_db_version = 0x00000003L; ++const long openssl_version = OPENSSL_VERSION_NUMBER; ++ ++ ++int pfixtls_serverengine = 0; ++static int pfixtls_serveractive = 0; /* available or not */ ++ ++int pfixtls_clientengine = 0; ++static int pfixtls_clientactive = 0; /* available or not */ ++ ++/* ++ * Define a maxlength for certificate onelines. The length is checked by ++ * all routines when copying. ++ */ ++#define CCERT_BUFSIZ 256 ++ ++typedef struct { ++ SSL *con; ++ BIO *internal_bio; /* postfix/TLS side of pair */ ++ BIO *network_bio; /* netsork side of pair */ ++ char peer_subject[CCERT_BUFSIZ]; ++ char peer_issuer[CCERT_BUFSIZ]; ++ char peer_CN[CCERT_BUFSIZ]; ++ char issuer_CN[CCERT_BUFSIZ]; ++ unsigned char md[EVP_MAX_MD_SIZE]; ++ char fingerprint[EVP_MAX_MD_SIZE * 3]; ++ char peername_save[129]; ++ int enforce_verify_errors; ++ int enforce_CN; ++} TLScontext_t; ++ ++typedef struct { ++ int pid; ++ struct timeval tv; ++} randseed_t; ++ ++static randseed_t randseed; ++ ++/* ++ * Finally some "backup" DH-Parameters to be loaded, if no parameters are ++ * explicitely loaded from file. ++ */ ++static unsigned char dh512_p[] = { ++ 0x88, 0x3F, 0x00, 0xAF, 0xFC, 0x0C, 0x8A, 0xB8, 0x35, 0xCD, 0xE5, 0xC2, ++ 0x0F, 0x55, 0xDF, 0x06, 0x3F, 0x16, 0x07, 0xBF, 0xCE, 0x13, 0x35, 0xE4, ++ 0x1C, 0x1E, 0x03, 0xF3, 0xAB, 0x17, 0xF6, 0x63, 0x50, 0x63, 0x67, 0x3E, ++ 0x10, 0xD7, 0x3E, 0xB4, 0xEB, 0x46, 0x8C, 0x40, 0x50, 0xE6, 0x91, 0xA5, ++ 0x6E, 0x01, 0x45, 0xDE, 0xC9, 0xB1, 0x1F, 0x64, 0x54, 0xFA, 0xD9, 0xAB, ++ 0x4F, 0x70, 0xBA, 0x5B, ++}; ++ ++static unsigned char dh512_g[] = { ++ 0x02, ++}; ++ ++static unsigned char dh1024_p[] = { ++ 0xB0, 0xFE, 0xB4, 0xCF, 0xD4, 0x55, 0x07, 0xE7, 0xCC, 0x88, 0x59, 0x0D, ++ 0x17, 0x26, 0xC5, 0x0C, 0xA5, 0x4A, 0x92, 0x23, 0x81, 0x78, 0xDA, 0x88, ++ 0xAA, 0x4C, 0x13, 0x06, 0xBF, 0x5D, 0x2F, 0x9E, 0xBC, 0x96, 0xB8, 0x51, ++ 0x00, 0x9D, 0x0C, 0x0D, 0x75, 0xAD, 0xFD, 0x3B, 0xB1, 0x7E, 0x71, 0x4F, ++ 0x3F, 0x91, 0x54, 0x14, 0x44, 0xB8, 0x30, 0x25, 0x1C, 0xEB, 0xDF, 0x72, ++ 0x9C, 0x4C, 0xF1, 0x89, 0x0D, 0x68, 0x3F, 0x94, 0x8E, 0xA4, 0xFB, 0x76, ++ 0x89, 0x18, 0xB2, 0x91, 0x16, 0x90, 0x01, 0x99, 0x66, 0x8C, 0x53, 0x81, ++ 0x4E, 0x27, 0x3D, 0x99, 0xE7, 0x5A, 0x7A, 0xAF, 0xD5, 0xEC, 0xE2, 0x7E, ++ 0xFA, 0xED, 0x01, 0x18, 0xC2, 0x78, 0x25, 0x59, 0x06, 0x5C, 0x39, 0xF6, ++ 0xCD, 0x49, 0x54, 0xAF, 0xC1, 0xB1, 0xEA, 0x4A, 0xF9, 0x53, 0xD0, 0xDF, ++ 0x6D, 0xAF, 0xD4, 0x93, 0xE7, 0xBA, 0xAE, 0x9B, ++}; ++ ++static unsigned char dh1024_g[] = { ++ 0x02, ++}; ++ ++/* ++ * DESCRIPTION: Keeping control of the network interface using BIO-pairs. ++ * ++ * When the TLS layer is active, all input/output must be filtered through ++ * it. On the other hand to handle timeout conditions, full control over ++ * the network socket must be kept. This rules out the "normal way" of ++ * connecting the TLS layer directly to the socket. ++ * The TLS layer is realized with a BIO-pair: ++ * ++ * postfix | TLS-engine ++ * | | ++ * +--------> SSL_operations() ++ * | /\ || ++ * | || \/ ++ * | BIO-pair (internal_bio) ++ * +--------< BIO-pair (network_bio) ++ * | | ++ * socket | ++ * ++ * The normal postfix operations connect to the SSL operations to send ++ * and retrieve (cleartext) data. Inside the TLS-engine the data are converted ++ * to/from TLS protocol. The TLS functionality itself is only connected to ++ * the internal_bio and hence only has status information about this internal ++ * interface. ++ * Thus, if the SSL_operations() return successfully (SSL_ERROR_NONE) or want ++ * to read (SSL_ERROR_WANT_READ) there may as well be data inside the buffering ++ * BIO-pair. So whenever an SSL_operation() returns without a fatal error, ++ * the BIO-pair internal buffer must be flushed to the network. ++ * NOTE: This is especially true in the SSL_ERROR_WANT_READ case: the TLS-layer ++ * might want to read handshake data, that will never come since its own ++ * written data will only reach the peer after flushing the buffer! ++ * ++ * The BIO-pair buffer size has been set to 8192 bytes, this is an arbitrary ++ * value that can hold more data than the typical PMTU, so that it does ++ * not force the generation of packets smaller than necessary. ++ * It is also larger than the default VSTREAM_BUFSIZE (4096, see vstream.h), ++ * so that large write operations could be handled within one call. ++ * The internal buffer in the network/network_bio handling layer has been ++ * set to the same value, since this seems to be reasonable. The code is ++ * however able to handle arbitrary values smaller or larger than the ++ * buffer size in the BIO-pair. ++ */ ++ ++const ssize_t BIO_bufsiz = 8192; ++ ++/* ++ * The interface layer between network and BIO-pair. The BIO-pair buffers ++ * the data to/from the TLS layer. Hence, at any time, there may be data ++ * in the buffer that must be written to the network. This writing has ++ * highest priority because the handshake might fail otherwise. ++ * Only then a read_request can be satisfied. ++ */ ++static int network_biopair_interop(int fd, int timeout, BIO *network_bio) ++{ ++ int want_write; ++ int num_write; ++ int write_pos; ++ int from_bio; ++ int want_read; ++ int num_read; ++ int to_bio; ++#define NETLAYER_BUFFERSIZE 8192 ++ char buffer[8192]; ++ ++ while ((want_write = BIO_ctrl_pending(network_bio)) > 0) { ++ if (want_write > NETLAYER_BUFFERSIZE) ++ want_write = NETLAYER_BUFFERSIZE; ++ from_bio = BIO_read(network_bio, buffer, want_write); ++ ++ /* ++ * Write the complete contents of the buffer. Since TLS performs ++ * underlying handshaking, we cannot afford to leave the buffer ++ * unflushed, as we could run into a deadlock trap (the peer ++ * waiting for a final byte and we already waiting for his reply ++ * in read position). ++ */ ++ write_pos = 0; ++ do { ++ if (timeout > 0 && write_wait(fd, timeout) < 0) ++ return (-1); ++ num_write = write(fd, buffer + write_pos, from_bio - write_pos); ++ if (num_write <= 0) ++ return (-1); /* something happened to the socket */ ++ write_pos += num_write; ++ } while (write_pos < from_bio); ++ } ++ ++ while ((want_read = BIO_ctrl_get_read_request(network_bio)) > 0) { ++ if (want_read > NETLAYER_BUFFERSIZE) ++ want_read = NETLAYER_BUFFERSIZE; ++ if (timeout > 0 && read_wait(fd, timeout) < 0) ++ return (-1); ++ num_read = read(fd, buffer, want_read); ++ if (num_read <= 0) ++ return (-1); /* something happened to the socket */ ++ to_bio = BIO_write(network_bio, buffer, num_read); ++ if (to_bio != num_read) ++ msg_fatal("to_bio != num_read"); ++ } ++ ++ return (0); ++} ++ ++static void pfixtls_print_errors(void); ++ ++ /* ++ * Function to perform the handshake for SSL_accept(), SSL_connect(), ++ * and SSL_shutdown() and perform the SSL_read(), SSL_write() operations. ++ * Call the underlying network_biopair_interop-layer to make sure the ++ * write buffer is flushed after every operation (that did not fail with ++ * a fatal error). ++ */ ++static int do_tls_operation(int fd, int timeout, TLScontext_t *TLScontext, ++ int (*hsfunc)(SSL *), ++ int (*rfunc)(SSL *, void *, int), ++ int (*wfunc)(SSL *, const void *, int), ++ char *buf, int num) ++{ ++ int status; ++ int err; ++ int retval = 0; ++ int biop_retval; ++ int done = 0; ++ ++ while (!done) { ++ if (hsfunc) ++ status = hsfunc(TLScontext->con); ++ else if (rfunc) ++ status = rfunc(TLScontext->con, buf, num); ++ else ++ status = wfunc(TLScontext->con, (const char *)buf, num); ++ err = SSL_get_error(TLScontext->con, status); ++ ++#if (OPENSSL_VERSION_NUMBER <= 0x0090581fL) ++ /* ++ * There is a bug up to and including OpenSSL-0.9.5a: if an error ++ * occurs while checking the peers certificate due to some certificate ++ * error (e.g. as happend with a RSA-padding error), the error is put ++ * onto the error stack. If verification is not enforced, this error ++ * should be ignored, but the error-queue is not cleared, so we ++ * can find this error here. The bug has been fixed on May 28, 2000. ++ * ++ * This bug so far has only manifested as ++ * 4800:error:0407006A:rsa routines:RSA_padding_check_PKCS1_type_1:block type is not 01:rsa_pk1.c:100: ++ * 4800:error:04067072:rsa routines:RSA_EAY_PUBLIC_DECRYPT:padding check failed:rsa_eay.c:396: ++ * 4800:error:0D079006:asn1 encoding routines:ASN1_verify:bad get asn1 object call:a_verify.c:109: ++ * so that we specifically test for this error. We print the errors ++ * to the logfile and automatically clear the error queue. Then we ++ * retry to get another error code. We cannot do better, since we ++ * can only retrieve the last entry of the error-queue without ++ * actually cleaning it on the way. ++ * ++ * This workaround is secure, as verify_result is set to "failed" ++ * anyway. ++ */ ++ if (err == SSL_ERROR_SSL) { ++ if (ERR_peek_error() == 0x0407006AL) { ++ pfixtls_print_errors(); /* Keep information for the logfile */ ++ msg_info("OpenSSL <= 0.9.5a workaround called: certificate errors ignored"); ++ err = SSL_get_error(TLScontext->con, status); ++ } ++ } ++#endif ++ ++ switch (err) { ++ case SSL_ERROR_NONE: /* success */ ++ retval = status; ++ done = 1; /* no break, flush buffer before */ ++ /* leaving */ ++ case SSL_ERROR_WANT_WRITE: ++ case SSL_ERROR_WANT_READ: ++ biop_retval = network_biopair_interop(fd, timeout, ++ TLScontext->network_bio); ++ if (biop_retval < 0) ++ return (-1); /* fatal network error */ ++ break; ++ case SSL_ERROR_ZERO_RETURN: /* connection was closed cleanly */ ++ case SSL_ERROR_SYSCALL: ++ case SSL_ERROR_SSL: ++ default: ++ retval = status; ++ done = 1; ++ ; ++ } ++ }; ++ return retval; ++} ++ ++int pfixtls_timed_read(int fd, void *buf, unsigned buf_len, int timeout, ++ void *context) ++{ ++ int i; ++ int ret; ++ char mybuf[40]; ++ char *mybuf2; ++ TLScontext_t *TLScontext; ++ ++ TLScontext = (TLScontext_t *)context; ++ if (!TLScontext) ++ msg_fatal("Called tls_timed_read() without TLS-context"); ++ ++ ret = do_tls_operation(fd, timeout, TLScontext, NULL, SSL_read, NULL, ++ (char *)buf, buf_len); ++ 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); ++} ++ ++int pfixtls_timed_write(int fd, void *buf, unsigned len, int timeout, ++ void *context) ++{ ++ int i; ++ char mybuf[40]; ++ char *mybuf2; ++ TLScontext_t *TLScontext; ++ ++ TLScontext = (TLScontext_t *)context; ++ if (!TLScontext) ++ msg_fatal("Called tls_timed_write() without TLS-context"); ++ ++ if ((pfixtls_serveractive && var_smtpd_tls_loglevel >= 4) || ++ (pfixtls_clientactive && var_smtp_tls_loglevel >= 4)) { ++ mybuf2 = (char *) buf; ++ if (len > 0) { ++ i = 0; ++ while ((i < 39) && (i < len) && (mybuf2[i] != 0)) { ++ mybuf[i] = mybuf2[i]; ++ i++; ++ } ++ mybuf[i] = '\0'; ++ msg_info("Write %d chars: %s", len, mybuf); ++ } ++ } ++ return (do_tls_operation(fd, timeout, TLScontext, NULL, NULL, SSL_write, ++ buf, len)); ++} ++ ++/* Add some more entropy to the pool by adding the actual time */ ++ ++static void pfixtls_stir_seed(void) ++{ ++ GETTIMEOFDAY(&randseed.tv); ++ RAND_seed(&randseed, sizeof(randseed_t)); ++} ++ ++/* ++ * Skeleton taken from OpenSSL crypto/err/err_prn.c. ++ * Query the error stack and print the error string into the logging facility. ++ * Clear the error stack on the way. ++ */ ++ ++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_chain_file(ctx, cert_file) <= 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); ++} ++ ++ ++static DH *get_dh512(void) ++{ ++ DH *dh; ++ ++ if (dh_512 == NULL) { ++ /* No parameter file loaded, use the compiled in parameters */ ++ if ((dh = DH_new()) == NULL) return(NULL); ++ dh->p = BN_bin2bn(dh512_p, sizeof(dh512_p), NULL); ++ dh->g = BN_bin2bn(dh512_g, sizeof(dh512_g), NULL); ++ if ((dh->p == NULL) || (dh->g == NULL)) ++ return(NULL); ++ else ++ dh_512 = dh; ++ } ++ return (dh_512); ++} ++ ++static DH *get_dh1024(void) ++{ ++ DH *dh; ++ ++ if (dh_1024 == NULL) { ++ /* No parameter file loaded, use the compiled in parameters */ ++ if ((dh = DH_new()) == NULL) return(NULL); ++ dh->p = BN_bin2bn(dh1024_p, sizeof(dh1024_p), NULL); ++ dh->g = BN_bin2bn(dh1024_g, sizeof(dh1024_g), NULL); ++ if ((dh->p == NULL) || (dh->g == NULL)) ++ return(NULL); ++ else ++ dh_1024 = dh; ++ } ++ return (dh_1024); ++} ++ ++/* partly inspired by mod_ssl */ ++ ++static DH *tmp_dh_cb(SSL *s, int export, int keylength) ++{ ++ DH *dh_tmp = NULL; ++ ++ if (export) { ++ if (keylength == 512) ++ dh_tmp = get_dh512(); /* export cipher */ ++ else if (keylength == 1024) ++ dh_tmp = get_dh1024(); /* normal */ ++ else ++ dh_tmp = get_dh1024(); /* not on-the-fly (too expensive) */ ++ /* so use the 1024bit instead */ ++ } ++ else { ++ dh_tmp = get_dh1024(); /* sign-only certificate */ ++ } ++ return (dh_tmp); ++} ++ ++ ++/* ++ * Skeleton taken from OpenSSL apps/s_cb.c ++ * ++ * The verify_callback is called several times (directly or indirectly) from ++ * crypto/x509/x509_vfy.c. It is called as a last check for several issues, ++ * so this verify_callback() has the famous "last word". If it does return "0", ++ * the handshake is immediately shut down and the connection fails. ++ * ++ * Postfix/TLS has two modes, the "use" mode and the "enforce" mode: ++ * ++ * In the "use" mode we never want the connection to fail just because there is ++ * something wrong with the certificate (as we would have sent happily without ++ * TLS). Therefore the return value is always "1". ++ * ++ * In the "enforce" mode we can shut down the connection as soon as possible. ++ * In server mode TLS itself may be enforced (e.g. to protect passwords), ++ * but certificates are optional. In this case the handshake must not fail ++ * if we are unhappy with the certificate and return "1" in any case. ++ * Only if a certificate is required the certificate must pass the verification ++ * and failure to do so will result in immediate termination (return 0). ++ * In the client mode the decision is made with respect to the peername ++ * enforcement. If we strictly enforce the matching of the expected peername ++ * the verification must fail immediatly on verification errors. We can also ++ * immediatly check the expected peername, as it is the CommonName at level 0. ++ * In all other cases, the problem is logged, so the SSL_get_verify_result() ++ * will inform about the verification failure, but the handshake (and SMTP ++ * connection will continue). ++ * ++ * The only error condition not handled inside the OpenSSL-Library is the ++ * case of a too-long certificate chain, so we check inside verify_callback(). ++ * We only take care of this problem, if "ok = 1", because otherwise the ++ * verification already failed because of another problem and we don't want ++ * to overwrite the other error message. And if the verification failed, ++ * there is no such thing as "more failed", "most failed"... :-) ++ */ ++ ++static int verify_callback(int ok, X509_STORE_CTX * ctx) ++{ ++ char buf[256]; ++ char *CN_lowercase; ++ char *peername_left; ++ X509 *err_cert; ++ int err; ++ int depth; ++ int verify_depth; ++ int hostname_matched; ++ SSL *con; ++ TLScontext_t *TLScontext; ++ ++ err_cert = X509_STORE_CTX_get_current_cert(ctx); ++ err = X509_STORE_CTX_get_error(ctx); ++ depth = X509_STORE_CTX_get_error_depth(ctx); ++ ++ con = X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx()); ++ TLScontext = SSL_get_ex_data(con, TLScontext_index); ++ ++ X509_NAME_oneline(X509_get_subject_name(err_cert), buf, 256); ++ if (((pfixtls_serverengine) && (var_smtpd_tls_loglevel >= 2)) || ++ ((pfixtls_clientengine) && (var_smtp_tls_loglevel >= 2))) ++ msg_info("Peer cert verify depth=%d %s", depth, buf); ++ ++ verify_depth = SSL_get_verify_depth(con); ++ if (ok && (verify_depth >= 0) && (depth > verify_depth)) { ++ ok = 0; ++ err = X509_V_ERR_CERT_CHAIN_TOO_LONG; ++ X509_STORE_CTX_set_error(ctx, err); ++ } ++ if (!ok) { ++ msg_info("verify error:num=%d:%s", err, ++ X509_verify_cert_error_string(err)); ++ } ++ ++ if (ok && (depth == 0) && pfixtls_clientengine) { ++ /* ++ * Check out the name certified against the hostname expected. ++ * In case it does not match, print an information about the result. ++ * If a matching is enforced, bump out with a verification error ++ * immediately. ++ */ ++ buf[0] = '\0'; ++ if (!X509_NAME_get_text_by_NID(X509_get_subject_name(err_cert), ++ NID_commonName, buf, 256)) { ++ msg_info("Could not parse server's subject CN"); ++ pfixtls_print_errors(); ++ } ++ CN_lowercase = lowercase(buf); ++ hostname_matched = 0; ++ if (!strcmp(TLScontext->peername_save, CN_lowercase)) ++ hostname_matched = 1; ++ else if ((strlen(CN_lowercase) > 2) && ++ (CN_lowercase[0] == '*') && (CN_lowercase[1] == '.')) { ++ /* ++ * Allow wildcard certificate matching. The proposed rules in ++ * RFCs (2818: HTTP/TLS, 2830: LDAP/TLS) are different, RFC2874 ++ * does not specify a rule, so here the strict rule is applied. ++ * An asterisk '*' is allowed as the leftmost component and may ++ * replace the left most part of the hostname. Matching is done ++ * by removing '*.' from the wildcard name and the `name.` from ++ * the peername and compare what is left. ++ */ ++ peername_left = strchr(TLScontext->peername_save, '.'); ++ if (peername_left) { ++ if (!strcmp(peername_left + 1, CN_lowercase + 2)) ++ hostname_matched = 1; ++ } ++ } ++ ++ if (!hostname_matched) { ++ msg_info("Peer verification: CommonName in certificate does not match: %s != %s", CN_lowercase, TLScontext->peername_save); ++ if (TLScontext->enforce_verify_errors && TLScontext->enforce_CN) { ++ err = X509_V_ERR_CERT_REJECTED; ++ X509_STORE_CTX_set_error(ctx, err); ++ msg_info("Verify failure: Hostname mismatch"); ++ ok = 0; ++ } ++ } ++ } ++ ++ 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_serverengine) && (var_smtpd_tls_loglevel >= 2)) || ++ ((pfixtls_clientengine) && (var_smtp_tls_loglevel >= 2))) ++ msg_info("verify return:%d", ok); ++ ++ if (TLScontext->enforce_verify_errors) ++ return (ok); ++ else ++ return (1); ++} ++ ++/* 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) { ++ msg_info("%s:%s", str, SSL_state_string_long(s)); ++ } else if (where & SSL_CB_ALERT) { ++ str = (where & SSL_CB_READ) ? "read" : "write"; ++ if ((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))", ++ (unsigned int)bio, (unsigned long)argp, argi, ++ ret, (unsigned int)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))", ++ (unsigned int)bio, (unsigned long)argp, argi, ++ ret, (unsigned int)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; ++ char idstring[2 * ID_MAXLENGTH + 1]; ++ int n; ++ int uselength; ++ int hex_length; ++ const char *session_hex; ++ pfixtls_scache_info_t scache_info; ++ unsigned char nibble, *data, *sess_data; ++ ++ if (length > ID_MAXLENGTH) ++ uselength = ID_MAXLENGTH; /* Limit length of ID */ ++ else ++ uselength = length; ++ ++ 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); ++ ++ session = NULL; ++ ++ session_hex = dict_get(scache_db, idstring); ++ if (session_hex) { ++ hex_length = strlen(session_hex); ++ data = (unsigned char *)mymalloc(hex_length / 2); ++ if (!data) { ++ msg_info("could not allocate memory for session reload"); ++ return(NULL); ++ } ++ ++ memset(data, 0, hex_length / 2); ++ for (n = 0; n < hex_length; n++) { ++ if ((session_hex[n] >= '0') && (session_hex[n] <= '9')) ++ nibble = session_hex[n] - '0'; ++ else ++ nibble = session_hex[n] - 'A' + 10; ++ if (n % 2) ++ data[n / 2] |= nibble; ++ else ++ data[n / 2] |= (nibble << 4); ++ } ++ ++ /* ++ * First check the version numbers, since wrong session data might ++ * hit us hard (SEGFAULT). We also have to check for expiry. ++ */ ++ memcpy(&scache_info, data, sizeof(pfixtls_scache_info_t)); ++ if ((scache_info.scache_db_version != scache_db_version) || ++ (scache_info.openssl_version != openssl_version) || ++ (scache_info.timestamp + var_smtpd_tls_scache_timeout < time(NULL))) ++ dict_del(scache_db, idstring); ++ else { ++ sess_data = data + sizeof(pfixtls_scache_info_t); ++ session = d2i_SSL_SESSION(NULL, &sess_data, ++ hex_length / 2 - sizeof(pfixtls_scache_info_t)); ++ if (!session) ++ pfixtls_print_errors(); ++ } ++ myfree((char *)data); ++ } ++ ++ if (session && (var_smtpd_tls_loglevel >= 3)) ++ msg_info("Successfully reloaded session from disc"); ++ ++ return (session); ++} ++ ++ ++static SSL_SESSION *load_clnt_session(const char *hostname, ++ int enforce_peername) ++{ ++ SSL_SESSION *session = NULL; ++ char idstring[ID_MAXLENGTH + 1]; ++ int n; ++ int uselength; ++ int length; ++ int hex_length; ++ const char *session_hex; ++ pfixtls_scache_info_t scache_info; ++ unsigned char nibble, *data, *sess_data; ++ ++ length = strlen(hostname); ++ if (length > ID_MAXLENGTH) ++ uselength = ID_MAXLENGTH; /* Limit length of ID */ ++ else ++ uselength = length; ++ ++ for(n=0 ; n < uselength ; n++) ++ idstring[n] = tolower(hostname[n]); ++ idstring[uselength] = '\0'; ++ if (var_smtp_tls_loglevel >= 3) ++ msg_info("Trying to reload Session from disc: %s", idstring); ++ ++ session_hex = dict_get(scache_db, idstring); ++ if (session_hex) { ++ hex_length = strlen(session_hex); ++ data = (unsigned char *)mymalloc(hex_length / 2); ++ if (!data) { ++ msg_info("could not allocate memory for session reload"); ++ return(NULL); ++ } ++ ++ memset(data, 0, hex_length / 2); ++ for (n = 0; n < hex_length; n++) { ++ if ((session_hex[n] >= '0') && (session_hex[n] <= '9')) ++ nibble = session_hex[n] - '0'; ++ else ++ nibble = session_hex[n] - 'A' + 10; ++ if (n % 2) ++ data[n / 2] |= nibble; ++ else ++ data[n / 2] |= (nibble << 4); ++ } ++ ++ /* ++ * First check the version numbers, since wrong session data might ++ * hit us hard (SEGFAULT). We also have to check for expiry. ++ * When we enforce_peername, we may find an old session, that was ++ * saved when enforcement was not set. In this case the session will ++ * be removed and a fresh session will be negotiated. ++ */ ++ memcpy(&scache_info, data, sizeof(pfixtls_scache_info_t)); ++ if ((scache_info.scache_db_version != scache_db_version) || ++ (scache_info.openssl_version != openssl_version) || ++ (scache_info.timestamp + var_smtpd_tls_scache_timeout < time(NULL))) ++ dict_del(scache_db, idstring); ++ else if (enforce_peername && (!scache_info.enforce_peername)) ++ dict_del(scache_db, idstring); ++ else { ++ sess_data = data + sizeof(pfixtls_scache_info_t); ++ session = d2i_SSL_SESSION(NULL, &sess_data, ++ hex_length / 2 - sizeof(time_t)); ++ strncpy(SSL_SESSION_get_ex_data(session, TLSpeername_index), ++ idstring, ID_MAXLENGTH + 1); ++ if (!session) ++ pfixtls_print_errors(); ++ } ++ myfree((char *)data); ++ } ++ ++ if (session && (var_smtp_tls_loglevel >= 3)) ++ msg_info("Successfully reloaded session from disc"); ++ ++ return (session); ++} ++ ++ ++static void create_client_lookup_id(char *idstring, char *hostname) ++{ ++ int n, len, uselength; ++ ++ len = strlen(hostname); ++ if (len > ID_MAXLENGTH) ++ uselength = ID_MAXLENGTH; /* Limit length of ID */ ++ else ++ uselength = len; ++ ++ for (n = 0 ; n < uselength ; n++) ++ idstring[n] = tolower(hostname[n]); ++ idstring[uselength] = '\0'; ++} ++ ++ ++static void create_server_lookup_id(char *idstring, SSL_SESSION *session) ++{ ++ int n, uselength; ++ ++ 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]); ++} ++ ++ ++static void remove_session_cb(SSL_CTX *ctx, SSL_SESSION *session) ++{ ++ char idstring[2 * ID_MAXLENGTH + 1]; ++ char *hostname; ++ ++ if (pfixtls_clientengine) { ++ hostname = SSL_SESSION_get_ex_data(session, TLSpeername_index); ++ create_client_lookup_id(idstring, hostname); ++ if (var_smtp_tls_loglevel >= 3) ++ msg_info("Trying to remove session from disc: %s", idstring); ++ } ++ else { ++ create_server_lookup_id(idstring, session); ++ if (var_smtpd_tls_loglevel >= 3) ++ msg_info("Trying to remove session from disc: %s", idstring); ++ } ++ ++ if (scache_db) ++ dict_del(scache_db, idstring); ++} ++ ++ ++/* ++ * We need space to save the peername into the SSL_SESSION, as we must ++ * look up the external database for client sessions by peername, not ++ * by session id. We therefore allocate place for the peername string, ++ * when a new SSL_SESSION is generated. It is filled later. ++ */ ++static int new_peername_func(void *parent, void *ptr, CRYPTO_EX_DATA *ad, ++ int idx, long argl, void *argp) ++{ ++ char *peername; ++ ++ peername = (char *)mymalloc(ID_MAXLENGTH + 1); ++ if (!peername) ++ return 0; ++ peername[0] = '\0'; /* initialize */ ++ return CRYPTO_set_ex_data(ad, idx, peername); ++} ++ ++/* ++ * When the SSL_SESSION is removed again, we must free the memory to avoid ++ * leaks. ++ */ ++static void free_peername_func(void *parent, void *ptr, CRYPTO_EX_DATA *ad, ++ int idx, long argl, void *argp) ++{ ++ myfree(CRYPTO_get_ex_data(ad, idx)); ++} ++ ++/* ++ * Duplicate application data, when a SSL_SESSION is duplicated ++ */ ++static int dup_peername_func(CRYPTO_EX_DATA *to, CRYPTO_EX_DATA *from, ++ void *from_d, int idx, long argl, void *argp) ++{ ++ char *peername_old, *peername_new; ++ ++ peername_old = CRYPTO_get_ex_data(from, idx); ++ peername_new = CRYPTO_get_ex_data(to, idx); ++ if (!peername_old || !peername_new) ++ return 0; ++ memcpy(peername_new, peername_old, ID_MAXLENGTH + 1); ++ return 1; ++} ++ ++ ++ /* ++ * Save a new session to the external cache ++ */ ++static int new_session_cb(SSL *ssl, SSL_SESSION *session) ++{ ++ char idstring[2 * ID_MAXLENGTH + 1]; ++ int n; ++ int dsize; ++ int len; ++ unsigned char *data, *sess_data; ++ pfixtls_scache_info_t scache_info; ++ char *hexdata, *hostname; ++ TLScontext_t *TLScontext; ++ ++ if (pfixtls_clientengine) { ++ TLScontext = SSL_get_ex_data(ssl, TLScontext_index); ++ hostname = TLScontext->peername_save; ++ create_client_lookup_id(idstring, hostname); ++ strncpy(SSL_SESSION_get_ex_data(session, TLSpeername_index), ++ hostname, ID_MAXLENGTH + 1); ++ /* ++ * Remember, whether peername matching was enforced when the session ++ * was created. If later enforce mode is enabled, we do not want to ++ * reuse a session that was not sufficiently checked. ++ */ ++ scache_info.enforce_peername = ++ (TLScontext->enforce_verify_errors && TLScontext->enforce_CN); ++ ++ if (var_smtp_tls_loglevel >= 3) ++ msg_info("Trying to save session for hostID to disc: %s", idstring); ++ ++#if (OPENSSL_VERSION_NUMBER < 0x00906011L) || (OPENSSL_VERSION_NUMBER == 0x00907000L) ++ /* ++ * Ugly Hack: OpenSSL before 0.9.6a does not store the verify ++ * result in sessions for the client side. ++ * We modify the session directly which is version specific, ++ * but this bug is version specific, too. ++ * ++ * READ: 0-09-06-01-1 = 0-9-6-a-beta1: all versions before ++ * beta1 have this bug, it has been fixed during development ++ * of 0.9.6a. The development version of 0.9.7 can have this ++ * bug, too. It has been fixed on 2000/11/29. ++ */ ++ session->verify_result = SSL_get_verify_result(TLScontext->con); ++#endif ++ ++ } ++ else { ++ create_server_lookup_id(idstring, session); ++ if (var_smtpd_tls_loglevel >= 3) ++ msg_info("Trying to save Session to disc: %s", idstring); ++ } ++ ++ ++ /* ++ * Get the session and convert it into some "database" useable form. ++ * First, get the length of the session to allocate the memory. ++ */ ++ dsize = i2d_SSL_SESSION(session, NULL); ++ if (dsize < 0) { ++ msg_info("Could not access session"); ++ return 0; ++ } ++ data = (unsigned char *)mymalloc(dsize + sizeof(pfixtls_scache_info_t)); ++ if (!data) { ++ msg_info("could not allocate memory for SSL session"); ++ return 0; ++ } ++ ++ /* ++ * OpenSSL is not robust against wrong session data (might SEGFAULT), ++ * so we secure it against version ids (session cache structure as well ++ * as OpenSSL version). ++ */ ++ scache_info.scache_db_version = scache_db_version; ++ scache_info.openssl_version = openssl_version; ++ ++ /* ++ * Put a timestamp, so that expiration can be checked without ++ * analyzing the session data itself. (We would need OpenSSL funtions, ++ * since the SSL_SESSION is a private structure.) ++ */ ++ scache_info.timestamp = time(NULL); ++ ++ memcpy(data, &scache_info, sizeof(pfixtls_scache_info_t)); ++ sess_data = data + sizeof(pfixtls_scache_info_t); ++ ++ /* ++ * Now, obtain the session. Unfortunately, it is binary and dict_update ++ * cannot handle binary data (it could contain '\0' in it) directly. ++ * To save memory we could use base64 encoding. To make handling easier, ++ * we simply use hex format. ++ */ ++ len = i2d_SSL_SESSION(session, &sess_data); ++ len += sizeof(pfixtls_scache_info_t); ++ ++ hexdata = (char *)mymalloc(2 * len + 1); ++ ++ if (!hexdata) { ++ msg_info("could not allocate memory for SSL session (HEX)"); ++ myfree((char *)data); ++ return 0; ++ } ++ for (n = 0; n < len; n++) { ++ hexdata[n * 2] = hexcodes[(data[n] & 0xf0) >> 4]; ++ hexdata[(n * 2) + 1] = hexcodes[(data[n] & 0x0f)]; ++ } ++ hexdata[len * 2] = '\0'; ++ ++ /* ++ * The session id is a hex string, all uppercase. We are using SDBM as ++ * compiled into Postfix with 8kB maximum entry size, so we set a limit ++ * when caching. If the session is not cached, we have to renegotiate, ++ * not more, not less. For a real session, this limit should never be ++ * met ++ */ ++ if (strlen(idstring) + strlen(hexdata) < 8000) ++ dict_put(scache_db, idstring, hexdata); ++ ++ myfree(hexdata); ++ myfree((char *)data); ++ return (1); ++} ++ ++ ++ /* ++ * pfixtls_exchange_seed: read bytes from the seed exchange-file (expect ++ * 1024 bytes)and immediately write back random bytes. Do so with EXCLUSIVE ++ * lock, so * that each process will find a completely different (and ++ * reseeded) file. ++ */ ++static void pfixtls_exchange_seed(void) ++{ ++ unsigned char buffer[1024]; ++ ++ if (rand_exch_fd == -1) ++ return; ++ ++ if (myflock(rand_exch_fd, INTERNAL_LOCK, MYFLOCK_OP_EXCLUSIVE) != 0) ++ msg_info("Could not lock random exchange file: %s", ++ strerror(errno)); ++ ++ lseek(rand_exch_fd, 0, SEEK_SET); ++ if (read(rand_exch_fd, buffer, 1024) < 0) ++ msg_fatal("reading exchange file failed"); ++ RAND_seed(buffer, 1024); ++ ++ RAND_bytes(buffer, 1024); ++ lseek(rand_exch_fd, 0, SEEK_SET); ++ if (write(rand_exch_fd, buffer, 1024) != 1024) ++ msg_fatal("Writing exchange file failed"); ++ ++ if (myflock(rand_exch_fd, INTERNAL_LOCK, MYFLOCK_OP_NONE) != 0) ++ msg_fatal("Could not unlock random exchange file: %s", ++ strerror(errno)); ++} ++ ++ /* ++ * 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 off = 0; ++ int verify_flags = SSL_VERIFY_NONE; ++ int rand_bytes; ++ int rand_source_dev_fd; ++ int rand_source_socket_fd; ++ unsigned char buffer[255]; ++ char *CApath; ++ char *CAfile; ++ char *s_cert_file; ++ char *s_key_file; ++ char *s_dcert_file; ++ char *s_dkey_file; ++ FILE *paramfile; ++ ++ 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(); ++ OpenSSL_add_ssl_algorithms(); ++ ++ /* ++ * Side effect, call a non-existing function to disable TLS usage with an ++ * outdated OpenSSL version. There is a security reason (verify_result ++ * is not stored with the session data). ++ */ ++#if (OPENSSL_VERSION_NUMBER < 0x00905100L) ++ needs_openssl_095_or_later(); ++#endif ++ ++ /* ++ * Initialize the PRNG Pseudo Random Number Generator with some seed. ++ */ ++ randseed.pid = getpid(); ++ GETTIMEOFDAY(&randseed.tv); ++ RAND_seed(&randseed, sizeof(randseed_t)); ++ ++ /* ++ * Access the external sources for random seed. We will only query them ++ * once, this should be sufficient and we will stir our entropy by using ++ * the prng-exchange file anyway. ++ * For reliability, we don't consider failure to access the additional ++ * source fatal, as we can run happily without it (considering that we ++ * still have the exchange-file). We also don't care how much entropy ++ * we get back, as we must run anyway. We simply stir in the buffer ++ * regardless how many bytes are actually in it. ++ */ ++ if (*var_tls_daemon_rand_source) { ++ if (!strncmp(var_tls_daemon_rand_source, "dev:", 4)) { ++ /* ++ * Source is a random device ++ */ ++ rand_source_dev_fd = open(var_tls_daemon_rand_source + 4, 0, 0); ++ if (rand_source_dev_fd == -1) ++ msg_info("Could not open entropy device %s", ++ var_tls_daemon_rand_source); ++ else { ++ if (var_tls_daemon_rand_bytes > 255) ++ var_tls_daemon_rand_bytes = 255; ++ read(rand_source_dev_fd, buffer, var_tls_daemon_rand_bytes); ++ RAND_seed(buffer, var_tls_daemon_rand_bytes); ++ close(rand_source_dev_fd); ++ } ++ } else if (!strncmp(var_tls_daemon_rand_source, "egd:", 4)) { ++ /* ++ * Source is a EGD compatible socket ++ */ ++ rand_source_socket_fd = unix_connect(var_tls_daemon_rand_source +4, ++ BLOCKING, 10); ++ if (rand_source_socket_fd == -1) ++ msg_info("Could not connect to %s", var_tls_daemon_rand_source); ++ else { ++ if (var_tls_daemon_rand_bytes > 255) ++ var_tls_daemon_rand_bytes = 255; ++ buffer[0] = 1; ++ buffer[1] = var_tls_daemon_rand_bytes; ++ if (write(rand_source_socket_fd, buffer, 2) != 2) ++ msg_info("Could not talk to %s", ++ var_tls_daemon_rand_source); ++ else if (read(rand_source_socket_fd, buffer, 1) != 1) ++ msg_info("Could not read info from %s", ++ var_tls_daemon_rand_source); ++ else { ++ rand_bytes = buffer[0]; ++ read(rand_source_socket_fd, buffer, rand_bytes); ++ RAND_seed(buffer, rand_bytes); ++ } ++ close(rand_source_socket_fd); ++ } ++ } else { ++ RAND_load_file(var_tls_daemon_rand_source, ++ var_tls_daemon_rand_bytes); ++ } ++ } ++ ++ if (*var_tls_rand_exch_name) { ++ rand_exch_fd = open(var_tls_rand_exch_name, O_RDWR | O_CREAT, 0600); ++ if (rand_exch_fd != -1) ++ pfixtls_exchange_seed(); ++ } ++ ++ randseed.pid = getpid(); ++ GETTIMEOFDAY(&randseed.tv); ++ RAND_seed(&randseed, sizeof(randseed_t)); ++ ++ /* ++ * 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. ++ */ ++ if (var_smtpd_tls_loglevel >= 2) ++ SSL_CTX_set_info_callback(ctx, apps_ssl_info_callback); ++ ++ /* ++ * Set the list of ciphers, if explicitely given; otherwise the ++ * (reasonable) default list is kept. ++ */ ++ if (strlen(var_smtpd_tls_cipherlist) != 0) ++ if (SSL_CTX_set_cipher_list(ctx, var_smtpd_tls_cipherlist) == 0) { ++ pfixtls_print_errors(); ++ return (-1); ++ } ++ ++ /* ++ * 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 (CAfile || CApath) { ++ if (!SSL_CTX_load_verify_locations(ctx, CAfile, CApath)) { ++ msg_info("TLS engine: cannot load CA data"); ++ pfixtls_print_errors(); ++ return (-1); ++ } ++ if (!SSL_CTX_set_default_verify_paths(ctx)) { ++ msg_info("TLS engine: cannot set verify paths"); ++ 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 (we do not support ADH anonymous Diffie-Hellman ++ * ciphers as of now). ++ * We can use RSA certificates ("cert") and DSA certificates ("dcert"), ++ * both can be made available at the same time. The CA certificates for ++ * both are handled in the same setup already finished. ++ * Which one is used depends on the cipher negotiated (that is: the first ++ * cipher listed by the client which does match the server). A client with ++ * RSA only (e.g. Netscape) will use the RSA certificate only. ++ * A client with openssl-library will use RSA first if not especially ++ * changed in the cipher setup. ++ */ ++ 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 (strlen(var_smtpd_tls_dcert_file) == 0) ++ s_dcert_file = NULL; ++ else ++ s_dcert_file = var_smtpd_tls_dcert_file; ++ if (strlen(var_smtpd_tls_dkey_file) == 0) ++ s_dkey_file = NULL; ++ else ++ s_dkey_file = var_smtpd_tls_dkey_file; ++ ++ if (s_cert_file) { ++ if (!set_cert_stuff(ctx, s_cert_file, s_key_file)) { ++ msg_info("TLS engine: cannot load RSA cert/key data"); ++ pfixtls_print_errors(); ++ return (-1); ++ } ++ } ++ if (s_dcert_file) { ++ if (!set_cert_stuff(ctx, s_dcert_file, s_dkey_file)) { ++ msg_info("TLS engine: cannot load DSA cert/key data"); ++ pfixtls_print_errors(); ++ return (-1); ++ } ++ } ++ if (!s_cert_file && !s_dcert_file) { ++ msg_info("TLS engine: do need at least RSA _or_ DSA cert/key data"); ++ 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); ++ ++ /* ++ * We might also need dh parameters, which can either be loaded from ++ * file (preferred) or we simply take the compiled in values. ++ * First, set the callback that will select the values when requested, ++ * then load the (possibly) available DH parameters from files. ++ * We are generous with the error handling, since we do have default ++ * values compiled in, so we will not abort but just log the error message. ++ */ ++ SSL_CTX_set_tmp_dh_callback(ctx, tmp_dh_cb); ++ if (strlen(var_smtpd_tls_dh1024_param_file) != 0) { ++ if ((paramfile = fopen(var_smtpd_tls_dh1024_param_file, "r")) != NULL) { ++ dh_1024 = PEM_read_DHparams(paramfile, NULL, NULL, NULL); ++ if (dh_1024 == NULL) { ++ msg_info("TLS engine: cannot load 1024bit DH parameters"); ++ pfixtls_print_errors(); ++ } ++ } ++ else { ++ msg_info("TLS engine: cannot load 1024bit DH parameters: %s: %s", ++ var_smtpd_tls_dh1024_param_file, strerror(errno)); ++ } ++ } ++ if (strlen(var_smtpd_tls_dh512_param_file) != 0) { ++ if ((paramfile = fopen(var_smtpd_tls_dh512_param_file, "r")) != NULL) { ++ dh_512 = PEM_read_DHparams(paramfile, NULL, NULL, NULL); ++ if (dh_512 == NULL) { ++ msg_info("TLS engine: cannot load 512bit DH parameters"); ++ pfixtls_print_errors(); ++ } ++ } ++ else { ++ msg_info("TLS engine: cannot load 512bit DH parameters: %s: %s", ++ var_smtpd_tls_dh512_param_file, strerror(errno)); ++ } ++ } ++ ++ /* ++ * 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. ++ */ ++ if (askcert) ++ verify_flags = SSL_VERIFY_PEER | 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)); ++ ++ /* ++ * 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); ++ SSL_CTX_set_session_id_context(ctx, (void*)&server_session_id_context, ++ sizeof(server_session_id_context)); ++ ++ /* ++ * The session cache is realized by an external database file, that ++ * must be opened before going to chroot jail. Since the session cache ++ * data can become quite large, "[n]dbm" cannot be used as it has a ++ * size limit that is by far to small. ++ */ ++ if (*var_smtpd_tls_scache_db) { ++ /* ++ * Insert a test against other dbms here, otherwise while writing ++ * a session (content to large), we will receive a fatal error! ++ */ ++ if (strncmp(var_smtpd_tls_scache_db, "sdbm:", 5)) ++ msg_warn("Only sdbm: type allowed for %s", ++ var_smtpd_tls_scache_db); ++ else ++ scache_db = dict_open(var_smtpd_tls_scache_db, O_RDWR, ++ DICT_FLAG_DUP_REPLACE | DICT_FLAG_LOCK | DICT_FLAG_SYNC_UPDATE); ++ if (scache_db) { ++ SSL_CTX_set_session_cache_mode(ctx, ++ SSL_SESS_CACHE_SERVER|SSL_SESS_CACHE_NO_AUTO_CLEAR); ++ SSL_CTX_sess_set_get_cb(ctx, get_session_cb); ++ SSL_CTX_sess_set_new_cb(ctx, new_session_cb); ++ SSL_CTX_sess_set_remove_cb(ctx, remove_session_cb); ++ } ++ else ++ msg_warn("Could not open session cache %s", ++ var_smtpd_tls_scache_db); ++ } ++ ++ /* ++ * Finally create the global index to access TLScontext information ++ * inside verify_callback. ++ */ ++ TLScontext_index = SSL_get_ex_new_index(0, "TLScontext ex_data index", ++ NULL, NULL, NULL); ++ ++ 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(VSTREAM *stream, int timeout, ++ const char *peername, const char *peeraddr, ++ tls_info_t *tls_info, int requirecert) ++{ ++ int sts; ++ int j; ++ int verify_flags; ++ unsigned int n; ++ TLScontext_t *TLScontext; ++ 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 from %s[%s]", peername, peeraddr); ++ ++ /* ++ * Allocate a new TLScontext for the new connection and get an SSL ++ * structure. Add the location of TLScontext to the SSL to later ++ * retrieve the information inside the verify_callback(). ++ */ ++ TLScontext = (TLScontext_t *)mymalloc(sizeof(TLScontext_t)); ++ if (!TLScontext) { ++ msg_fatal("Could not allocate 'TLScontext' with mymalloc"); ++ } ++ if ((TLScontext->con = (SSL *) SSL_new(ctx)) == NULL) { ++ msg_info("Could not allocate 'TLScontext->con' with SSL_new()"); ++ pfixtls_print_errors(); ++ myfree((char *)TLScontext); ++ return (-1); ++ } ++ if (!SSL_set_ex_data(TLScontext->con, TLScontext_index, TLScontext)) { ++ msg_info("Could not set application data for 'TLScontext->con'"); ++ pfixtls_print_errors(); ++ SSL_free(TLScontext->con); ++ myfree((char *)TLScontext); ++ return (-1); ++ } ++ ++ /* ++ * Set the verification parameters to be checked in verify_callback(). ++ */ ++ if (requirecert) { ++ verify_flags = SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE; ++ verify_flags |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT; ++ TLScontext->enforce_verify_errors = 1; ++ SSL_set_verify(TLScontext->con, verify_flags, verify_callback); ++ } ++ else { ++ TLScontext->enforce_verify_errors = 0; ++ } ++ TLScontext->enforce_CN = 0; ++ ++ /* ++ * The TLS connection is realized by a BIO_pair, so obtain the pair. ++ */ ++ if (!BIO_new_bio_pair(&TLScontext->internal_bio, BIO_bufsiz, ++ &TLScontext->network_bio, BIO_bufsiz)) { ++ msg_info("Could not obtain BIO_pair"); ++ pfixtls_print_errors(); ++ SSL_free(TLScontext->con); ++ myfree((char *)TLScontext); ++ return (-1); ++ } ++ ++ /* ++ * Before really starting anything, try to seed the PRNG a little bit ++ * more. ++ */ ++ pfixtls_stir_seed(); ++ pfixtls_exchange_seed(); ++ ++ /* ++ * 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(TLScontext->con); ++ ++ /* ++ * Connect the SSL-connection with the postfix side of the BIO-pair for ++ * reading and writing. ++ */ ++ SSL_set_bio(TLScontext->con, TLScontext->internal_bio, ++ TLScontext->internal_bio); ++ ++ /* ++ * 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(TLScontext->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. ++ */ ++ sts = do_tls_operation(vstream_fileno(stream), timeout, TLScontext, ++ SSL_accept, NULL, NULL, NULL, 0); ++ if (sts <= 0) { ++ msg_info("SSL_accept error from %s[%s]: %d", peername, peeraddr, sts); ++ pfixtls_print_errors(); ++ SSL_free(TLScontext->con); ++ myfree((char *)TLScontext); ++ return (-1); ++ } ++ ++ /* Only loglevel==4 dumps everything */ ++ if (var_smtpd_tls_loglevel < 4) ++ do_dump = 0; ++ ++ /* ++ * 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(TLScontext->con); ++ if (peer != NULL) { ++ if (SSL_get_verify_result(TLScontext->con) == X509_V_OK) ++ tls_info->peer_verified = 1; ++ ++ X509_NAME_oneline(X509_get_subject_name(peer), ++ TLScontext->peer_subject, CCERT_BUFSIZ); ++ if (var_smtpd_tls_loglevel >= 2) ++ msg_info("subject=%s", TLScontext->peer_subject); ++ tls_info->peer_subject = TLScontext->peer_subject; ++ X509_NAME_oneline(X509_get_issuer_name(peer), ++ TLScontext->peer_issuer, CCERT_BUFSIZ); ++ if (var_smtpd_tls_loglevel >= 2) ++ msg_info("issuer=%s", TLScontext->peer_issuer); ++ tls_info->peer_issuer = TLScontext->peer_issuer; ++ if (X509_digest(peer, EVP_md5(), TLScontext->md, &n)) { ++ for (j = 0; j < (int) n; j++) { ++ TLScontext->fingerprint[j * 3] = ++ hexcodes[(TLScontext->md[j] & 0xf0) >> 4]; ++ TLScontext->fingerprint[(j * 3) + 1] = ++ hexcodes[(TLScontext->md[j] & 0x0f)]; ++ if (j + 1 != (int) n) ++ TLScontext->fingerprint[(j * 3) + 2] = ':'; ++ else ++ TLScontext->fingerprint[(j * 3) + 2] = '\0'; ++ } ++ if (var_smtpd_tls_loglevel >= 1) ++ msg_info("fingerprint=%s", TLScontext->fingerprint); ++ tls_info->peer_fingerprint = TLScontext->fingerprint; ++ } ++ ++ TLScontext->peer_CN[0] = '\0'; ++ if (!X509_NAME_get_text_by_NID(X509_get_subject_name(peer), ++ NID_commonName, TLScontext->peer_CN, CCERT_BUFSIZ)) { ++ msg_info("Could not parse client's subject CN"); ++ pfixtls_print_errors(); ++ } ++ tls_info->peer_CN = TLScontext->peer_CN; ++ ++ TLScontext->issuer_CN[0] = '\0'; ++ if (!X509_NAME_get_text_by_NID(X509_get_issuer_name(peer), ++ NID_commonName, TLScontext->issuer_CN, CCERT_BUFSIZ)) { ++ msg_info("Could not parse client's issuer CN"); ++ pfixtls_print_errors(); ++ } ++ if (!TLScontext->issuer_CN[0]) { ++ /* No issuer CN field, use Organization instead */ ++ if (!X509_NAME_get_text_by_NID(X509_get_issuer_name(peer), ++ NID_organizationName, TLScontext->issuer_CN, CCERT_BUFSIZ)) { ++ msg_info("Could not parse client's issuer Organization"); ++ pfixtls_print_errors(); ++ } ++ } ++ tls_info->issuer_CN = TLScontext->issuer_CN; ++ ++ if (var_smtpd_tls_loglevel >= 1) { ++ if (tls_info->peer_verified) ++ msg_info("Verified: subject_CN=%s, issuer=%s", ++ TLScontext->peer_CN, TLScontext->issuer_CN); ++ else ++ msg_info("Unverified: subject_CN=%s, issuer=%s", ++ TLScontext->peer_CN, TLScontext->issuer_CN); ++ } ++ ++ X509_free(peer); ++ } ++ ++ /* ++ * At this point we should have a certificate when required. ++ * We may however have a cached session, so the callback would never ++ * be called. We therefore double-check to make sure and remove the ++ * session, if applicable. ++ */ ++ if (requirecert) { ++ if (!tls_info->peer_verified || !tls_info->peer_CN) { ++ msg_info("Re-used session without peer certificate removed"); ++ session = SSL_get_session(TLScontext->con); ++ SSL_CTX_remove_session(ctx, session); ++ return (-1); ++ } ++ } ++ ++ /* ++ * Finally, collect information about protocol and cipher for logging ++ */ ++ tls_info->protocol = SSL_get_version(TLScontext->con); ++ cipher = SSL_get_current_cipher(TLScontext->con); ++ tls_info->cipher_name = SSL_CIPHER_get_name(cipher); ++ tls_info->cipher_usebits = SSL_CIPHER_get_bits(cipher, ++ &(tls_info->cipher_algbits)); ++ ++ pfixtls_serveractive = 1; ++ ++ /* ++ * The TLS engine is active, switch to the pfixtls_timed_read/write() ++ * functions and store the context. ++ */ ++ vstream_control(stream, ++ VSTREAM_CTL_READ_FN, pfixtls_timed_read, ++ VSTREAM_CTL_WRITE_FN, pfixtls_timed_write, ++ VSTREAM_CTL_CONTEXT, (void *)TLScontext, ++ VSTREAM_CTL_END); ++ ++ msg_info("TLS connection established from %s[%s]: %s with cipher %s (%d/%d bits)", ++ peername, peeraddr, ++ tls_info->protocol, tls_info->cipher_name, ++ tls_info->cipher_usebits, tls_info->cipher_algbits); ++ pfixtls_stir_seed(); ++ ++ 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(VSTREAM *stream, int timeout, int failure, ++ tls_info_t *tls_info) ++{ ++ TLScontext_t *TLScontext; ++ int retval; ++ ++ if (pfixtls_serveractive) { ++ TLScontext = (TLScontext_t *)vstream_context(stream); ++ /* ++ * Perform SSL_shutdown() twice, as the first attempt may return ++ * to early: it will only send out the shutdown alert but it will ++ * not wait for the peer's shutdown alert. Therefore, when we are ++ * the first party to send the alert, we must call SSL_shutdown() ++ * again. ++ * On failure we don't want to resume the session, so we will not ++ * perform SSL_shutdown() and the session will be removed as being ++ * bad. ++ */ ++ if (!failure) { ++ retval = do_tls_operation(vstream_fileno(stream), timeout, ++ TLScontext, SSL_shutdown, NULL, NULL, NULL, 0); ++ if (retval == 0) ++ do_tls_operation(vstream_fileno(stream), timeout, TLScontext, ++ SSL_shutdown, NULL, NULL, NULL, 0); ++ } ++ /* ++ * Free the SSL structure and the BIOs. Warning: the internal_bio is ++ * connected to the SSL structure and is automatically freed with ++ * it. Do not free it again (core dump)!! ++ * Only free the network_bio. ++ */ ++ SSL_free(TLScontext->con); ++ BIO_free(TLScontext->network_bio); ++ myfree((char *)TLScontext); ++ vstream_control(stream, ++ VSTREAM_CTL_READ_FN, (VSTREAM_FN) NULL, ++ VSTREAM_CTL_WRITE_FN, (VSTREAM_FN) NULL, ++ VSTREAM_CTL_CONTEXT, (void *) NULL, ++ VSTREAM_CTL_END); ++ SSL_CTX_flush_sessions(ctx, time(NULL)); ++ ++ pfixtls_stir_seed(); ++ pfixtls_exchange_seed(); ++ ++ *tls_info = tls_info_zero; ++ 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; ++ int rand_bytes; ++ int rand_source_dev_fd; ++ int rand_source_socket_fd; ++ unsigned char buffer[255]; ++ 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(); ++ OpenSSL_add_ssl_algorithms(); ++ ++ /* ++ * Side effect, call a non-existing function to disable TLS usage with an ++ * outdated OpenSSL version. There is a security reason (verify_result ++ * is not stored with the session data). ++ */ ++#if (OPENSSL_VERSION_NUMBER < 0x00905100L) ++ needs_openssl_095_or_later(); ++#endif ++ ++ /* ++ * Initialize the PRNG Pseudo Random Number Generator with some seed. ++ */ ++ randseed.pid = getpid(); ++ GETTIMEOFDAY(&randseed.tv); ++ RAND_seed(&randseed, sizeof(randseed_t)); ++ ++ /* ++ * Access the external sources for random seed. We will only query them ++ * once, this should be sufficient and we will stir our entropy by using ++ * the prng-exchange file anyway. ++ * For reliability, we don't consider failure to access the additional ++ * source fatal, as we can run happily without it (considering that we ++ * still have the exchange-file). We also don't care how much entropy ++ * we get back, as we must run anyway. We simply stir in the buffer ++ * regardless how many bytes are actually in it. ++ */ ++ if (*var_tls_daemon_rand_source) { ++ if (!strncmp(var_tls_daemon_rand_source, "dev:", 4)) { ++ /* ++ * Source is a random device ++ */ ++ rand_source_dev_fd = open(var_tls_daemon_rand_source + 4, 0, 0); ++ if (rand_source_dev_fd == -1) ++ msg_info("Could not open entropy device %s", ++ var_tls_daemon_rand_source); ++ else { ++ if (var_tls_daemon_rand_bytes > 255) ++ var_tls_daemon_rand_bytes = 255; ++ read(rand_source_dev_fd, buffer, var_tls_daemon_rand_bytes); ++ RAND_seed(buffer, var_tls_daemon_rand_bytes); ++ close(rand_source_dev_fd); ++ } ++ } else if (!strncmp(var_tls_daemon_rand_source, "egd:", 4)) { ++ /* ++ * Source is a EGD compatible socket ++ */ ++ rand_source_socket_fd = unix_connect(var_tls_daemon_rand_source +4, ++ BLOCKING, 10); ++ if (rand_source_socket_fd == -1) ++ msg_info("Could not connect to %s", var_tls_daemon_rand_source); ++ else { ++ if (var_tls_daemon_rand_bytes > 255) ++ var_tls_daemon_rand_bytes = 255; ++ buffer[0] = 1; ++ buffer[1] = var_tls_daemon_rand_bytes; ++ if (write(rand_source_socket_fd, buffer, 2) != 2) ++ msg_info("Could not talk to %s", ++ var_tls_daemon_rand_source); ++ else if (read(rand_source_socket_fd, buffer, 1) != 1) ++ msg_info("Could not read info from %s", ++ var_tls_daemon_rand_source); ++ else { ++ rand_bytes = buffer[0]; ++ read(rand_source_socket_fd, buffer, rand_bytes); ++ RAND_seed(buffer, rand_bytes); ++ } ++ close(rand_source_socket_fd); ++ } ++ } else { ++ RAND_load_file(var_tls_daemon_rand_source, ++ var_tls_daemon_rand_bytes); ++ } ++ } ++ ++ if (*var_tls_rand_exch_name) { ++ rand_exch_fd = open(var_tls_rand_exch_name, O_RDWR | O_CREAT, 0600); ++ if (rand_exch_fd != -1) ++ pfixtls_exchange_seed(); ++ } ++ ++ randseed.pid = getpid(); ++ GETTIMEOFDAY(&randseed.tv); ++ RAND_seed(&randseed, sizeof(randseed_t)); ++ ++ /* ++ * 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. ++ */ ++ if (var_smtp_tls_loglevel >= 2) ++ SSL_CTX_set_info_callback(ctx, apps_ssl_info_callback); ++ ++ /* ++ * Set the list of ciphers, if explicitely given; otherwise the ++ * (reasonable) default list is kept. ++ */ ++ if (strlen(var_smtp_tls_cipherlist) != 0) ++ if (SSL_CTX_set_cipher_list(ctx, var_smtp_tls_cipherlist) == 0) { ++ pfixtls_print_errors(); ++ return (-1); ++ } ++ ++ /* ++ * 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)) { ++ msg_info("TLS engine: cannot load CA data"); ++ pfixtls_print_errors(); ++ return (-1); ++ } ++ if (!SSL_CTX_set_default_verify_paths(ctx)) { ++ msg_info("TLS engine: cannot set verify paths"); ++ 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". ++ */ ++ SSL_CTX_set_verify(ctx, verify_flags, verify_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); ++ ++ /* ++ * The session cache is realized by an external database file, that ++ * must be opened before going to chroot jail. Since the session cache ++ * data can become quite large, "[n]dbm" cannot be used as it has a ++ * size limit that is by far to small. ++ */ ++ if (*var_smtp_tls_scache_db) { ++ /* ++ * Insert a test against other dbms here, otherwise while writing ++ * a session (content to large), we will receive a fatal error! ++ */ ++ if (strncmp(var_smtp_tls_scache_db, "sdbm:", 5)) ++ msg_warn("Only sdbm: type allowed for %s", ++ var_smtp_tls_scache_db); ++ else ++ scache_db = dict_open(var_smtp_tls_scache_db, O_RDWR, ++ DICT_FLAG_DUP_REPLACE | DICT_FLAG_LOCK | DICT_FLAG_SYNC_UPDATE); ++ if (!scache_db) ++ msg_warn("Could not open session cache %s", ++ var_smtp_tls_scache_db); ++ /* ++ * It is practical to have OpenSSL automatically save newly created ++ * sessions for us by callback. Therefore we have to enable the ++ * internal session cache for the client side. Disable automatic ++ * clearing, as smtp has limited lifetime anyway and we can call ++ * the cleanup routine at will. ++ */ ++ SSL_CTX_set_session_cache_mode(ctx, ++ SSL_SESS_CACHE_CLIENT|SSL_SESS_CACHE_NO_AUTO_CLEAR); ++ SSL_CTX_sess_set_new_cb(ctx, new_session_cb); ++ } ++ ++ /* ++ * Finally create the global index to access TLScontext information ++ * inside verify_callback. ++ */ ++ TLScontext_index = SSL_get_ex_new_index(0, "TLScontext ex_data index", ++ NULL, NULL, NULL); ++ TLSpeername_index = SSL_SESSION_get_ex_new_index(0, ++ "TLSpeername ex_data index", ++ new_peername_func, ++ dup_peername_func, ++ free_peername_func); ++ ++ 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(VSTREAM *stream, int timeout, ++ int enforce_peername, ++ const char *peername, ++ tls_info_t *tls_info) ++{ ++ int sts; ++ SSL_SESSION *session, *old_session; ++ SSL_CIPHER *cipher; ++ X509 *peer; ++ int verify_flags; ++ TLScontext_t *TLScontext; ++ ++ 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 to %s", peername); ++ ++ /* ++ * Allocate a new TLScontext for the new connection and get an SSL ++ * structure. Add the location of TLScontext to the SSL to later ++ * retrieve the information inside the verify_callback(). ++ */ ++ TLScontext = (TLScontext_t *)mymalloc(sizeof(TLScontext_t)); ++ if (!TLScontext) { ++ msg_fatal("Could not allocate 'TLScontext' with mymalloc"); ++ } ++ if ((TLScontext->con = (SSL *) SSL_new(ctx)) == NULL) { ++ msg_info("Could not allocate 'TLScontext->con' with SSL_new()"); ++ pfixtls_print_errors(); ++ myfree((char *)TLScontext); ++ return (-1); ++ } ++ if (!SSL_set_ex_data(TLScontext->con, TLScontext_index, TLScontext)) { ++ msg_info("Could not set application data for 'TLScontext->con'"); ++ pfixtls_print_errors(); ++ SSL_free(TLScontext->con); ++ myfree((char *)TLScontext); ++ return (-1); ++ } ++ ++ /* ++ * Set the verification parameters to be checked in verify_callback(). ++ */ ++ if (enforce_peername) { ++ verify_flags = SSL_VERIFY_PEER; ++ TLScontext->enforce_verify_errors = 1; ++ TLScontext->enforce_CN = 1; ++ SSL_set_verify(TLScontext->con, verify_flags, verify_callback); ++ } ++ else { ++ TLScontext->enforce_verify_errors = 0; ++ TLScontext->enforce_CN = 0; ++ } ++ ++ /* ++ * The TLS connection is realized by a BIO_pair, so obtain the pair. ++ */ ++ if (!BIO_new_bio_pair(&TLScontext->internal_bio, BIO_bufsiz, ++ &TLScontext->network_bio, BIO_bufsiz)) { ++ msg_info("Could not obtain BIO_pair"); ++ pfixtls_print_errors(); ++ SSL_free(TLScontext->con); ++ myfree((char *)TLScontext); ++ return (-1); ++ } ++ ++ old_session = NULL; ++ ++ /* ++ * Find out the hashed HostID for the client cache and try to ++ * load the session from the cache. ++ */ ++ strncpy(TLScontext->peername_save, peername, ID_MAXLENGTH + 1); ++ TLScontext->peername_save[ID_MAXLENGTH] = '\0'; /* just in case */ ++ (void)lowercase(TLScontext->peername_save); ++ if (scache_db) { ++ old_session = load_clnt_session(peername, enforce_peername); ++ if (old_session) { ++ SSL_set_session(TLScontext->con, old_session); ++#if (OPENSSL_VERSION_NUMBER < 0x00906011L) || (OPENSSL_VERSION_NUMBER == 0x00907000L) ++ /* ++ * Ugly Hack: OpenSSL before 0.9.6a does not store the verify ++ * result in sessions for the client side. ++ * We modify the session directly which is version specific, ++ * but this bug is version specific, too. ++ * ++ * READ: 0-09-06-01-1 = 0-9-6-a-beta1: all versions before ++ * beta1 have this bug, it has been fixed during development ++ * of 0.9.6a. The development version of 0.9.7 can have this ++ * bug, too. It has been fixed on 2000/11/29. ++ */ ++ SSL_set_verify_result(TLScontext->con, old_session->verify_result); ++#endif ++ ++ } ++ } ++ ++ /* ++ * Before really starting anything, try to seed the PRNG a little bit ++ * more. ++ */ ++ pfixtls_stir_seed(); ++ pfixtls_exchange_seed(); ++ ++ /* ++ * 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(TLScontext->con); ++ ++ /* ++ * Connect the SSL-connection with the postfix side of the BIO-pair for ++ * reading and writing. ++ */ ++ SSL_set_bio(TLScontext->con, TLScontext->internal_bio, ++ TLScontext->internal_bio); ++ ++ /* ++ * 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(TLScontext->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. ++ */ ++ sts = do_tls_operation(vstream_fileno(stream), timeout, TLScontext, ++ SSL_connect, NULL, NULL, NULL, 0); ++ if (sts <= 0) { ++ msg_info("SSL_connect error to %s: %d", peername, sts); ++ pfixtls_print_errors(); ++ session = SSL_get_session(TLScontext->con); ++ if (session) { ++ SSL_CTX_remove_session(ctx, session); ++ if (var_smtp_tls_loglevel >= 2) ++ msg_info("SSL session removed"); ++ } ++ if ((old_session) && (!SSL_session_reused(TLScontext->con))) ++ SSL_SESSION_free(old_session); /* Must also be removed */ ++ SSL_free(TLScontext->con); ++ myfree((char *)TLScontext); ++ return (-1); ++ } ++ ++ if (!SSL_session_reused(TLScontext->con)) { ++ SSL_SESSION_free(old_session); /* Remove unused session */ ++ } ++ else if (var_smtp_tls_loglevel >= 3) ++ msg_info("Reusing old session"); ++ ++ /* Only loglevel==4 dumps everything */ ++ if (var_smtp_tls_loglevel < 4) ++ do_dump = 0; ++ ++ /* ++ * 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(TLScontext->con); ++ if (peer != NULL) { ++ if (SSL_get_verify_result(TLScontext->con) == X509_V_OK) ++ tls_info->peer_verified = 1; ++ ++ TLScontext->peer_CN[0] = '\0'; ++ if (!X509_NAME_get_text_by_NID(X509_get_subject_name(peer), ++ NID_commonName, TLScontext->peer_CN, CCERT_BUFSIZ)) { ++ msg_info("Could not parse server's subject CN"); ++ pfixtls_print_errors(); ++ } ++ tls_info->peer_CN = TLScontext->peer_CN; ++ ++ TLScontext->issuer_CN[0] = '\0'; ++ if (!X509_NAME_get_text_by_NID(X509_get_issuer_name(peer), ++ NID_commonName, TLScontext->issuer_CN, CCERT_BUFSIZ)) { ++ msg_info("Could not parse server's issuer CN"); ++ pfixtls_print_errors(); ++ } ++ if (!TLScontext->issuer_CN[0]) { ++ /* No issuer CN field, use Organization instead */ ++ if (!X509_NAME_get_text_by_NID(X509_get_issuer_name(peer), ++ NID_organizationName, TLScontext->issuer_CN, CCERT_BUFSIZ)) { ++ msg_info("Could not parse server's issuer Organization"); ++ pfixtls_print_errors(); ++ } ++ } ++ tls_info->issuer_CN = TLScontext->issuer_CN; ++ ++ if (var_smtp_tls_loglevel >= 1) { ++ if (tls_info->peer_verified) ++ msg_info("Verified: subject_CN=%s, issuer=%s", ++ TLScontext->peer_CN, TLScontext->issuer_CN); ++ else ++ msg_info("Unverified: subject_CN=%s, issuer=%s", ++ TLScontext->peer_CN, TLScontext->issuer_CN); ++ } ++ X509_free(peer); ++ } ++ ++ /* ++ * Finally, collect information about protocol and cipher for logging ++ */ ++ tls_info->protocol = SSL_get_version(TLScontext->con); ++ cipher = SSL_get_current_cipher(TLScontext->con); ++ tls_info->cipher_name = SSL_CIPHER_get_name(cipher); ++ tls_info->cipher_usebits = SSL_CIPHER_get_bits(cipher, ++ &(tls_info->cipher_algbits)); ++ ++ pfixtls_clientactive = 1; ++ ++ /* ++ * The TLS engine is active, switch to the pfixtls_timed_read/write() ++ * functions. ++ */ ++ vstream_control(stream, ++ VSTREAM_CTL_READ_FN, pfixtls_timed_read, ++ VSTREAM_CTL_WRITE_FN, pfixtls_timed_write, ++ VSTREAM_CTL_CONTEXT, (void *)TLScontext, ++ VSTREAM_CTL_END); ++ ++ msg_info("TLS connection established to %s: %s with cipher %s (%d/%d bits)", ++ peername, ++ tls_info->protocol, tls_info->cipher_name, ++ tls_info->cipher_usebits, tls_info->cipher_algbits); ++ ++ pfixtls_stir_seed(); ++ ++ 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(VSTREAM *stream, int timeout, int failure, ++ tls_info_t *tls_info) ++{ ++ TLScontext_t *TLScontext; ++ int retval; ++ ++ if (pfixtls_clientactive) { ++ TLScontext = (TLScontext_t *)vstream_context(stream); ++ /* ++ * Perform SSL_shutdown() twice, as the first attempt may return ++ * to early: it will only send out the shutdown alert but it will ++ * not wait for the peer's shutdown alert. Therefore, when we are ++ * the first party to send the alert, we must call SSL_shutdown() ++ * again. ++ * On failure we don't want to resume the session, so we will not ++ * perform SSL_shutdown() and the session will be removed as being ++ * bad. ++ */ ++ if (!failure) { ++ retval = do_tls_operation(vstream_fileno(stream), timeout, ++ TLScontext, SSL_shutdown, NULL, NULL, NULL, 0); ++ if (retval == 0) ++ do_tls_operation(vstream_fileno(stream), timeout, TLScontext, ++ SSL_shutdown, NULL, NULL, NULL, 0); ++ } ++ /* ++ * Free the SSL structure and the BIOs. Warning: the internal_bio is ++ * connected to the SSL structure and is automatically freed with ++ * it. Do not free it again (core dump)!! ++ * Only free the network_bio. ++ */ ++ SSL_free(TLScontext->con); ++ BIO_free(TLScontext->network_bio); ++ myfree((char *)TLScontext); ++ vstream_control(stream, ++ VSTREAM_CTL_READ_FN, (VSTREAM_FN) NULL, ++ VSTREAM_CTL_WRITE_FN, (VSTREAM_FN) NULL, ++ VSTREAM_CTL_CONTEXT, (void *) NULL, ++ VSTREAM_CTL_END); ++ SSL_CTX_flush_sessions(ctx, time(NULL)); ++ ++ pfixtls_stir_seed(); ++ pfixtls_exchange_seed(); ++ ++ *tls_info = tls_info_zero; ++ pfixtls_clientactive = 0; ++ ++ } ++ ++ return (0); ++} ++ ++ ++#endif /* HAS_SSL */ +diff -Pur postfix-1.1.11-20020613-orig/src/global/pfixtls.h postfix-1.1.11-20020613/src/global/pfixtls.h +--- postfix-1.1.11-20020613-orig/src/global/pfixtls.h Thu Jan 1 01:00:00 1970 ++++ postfix-1.1.11-20020613/src/global/pfixtls.h Wed Jun 26 15:26:48 2002 +@@ -0,0 +1,76 @@ ++/*++ ++/* NAME ++/* pfixtls 3h ++/* SUMMARY ++/* TLS routines ++/* SYNOPSIS ++/* include "pfixtls.h" ++/* DESCRIPTION ++/* .nf ++/*--*/ ++ ++#ifndef PFIXTLS_H_INCLUDED ++#define PFIXTLS_H_INCLUDED ++ ++typedef struct { ++ int peer_verified; ++ char *peer_subject; ++ char *peer_issuer; ++ char *peer_fingerprint; ++ char *peer_CN; ++ char *issuer_CN; ++ const char *protocol; ++ const char *cipher_name; ++ int cipher_usebits; ++ int cipher_algbits; ++} tls_info_t; ++ ++extern const tls_info_t tls_info_zero; ++ ++#ifdef HAS_SSL ++ ++typedef struct { ++ long scache_db_version; ++ long openssl_version; ++ time_t timestamp; /* We could add other info here... */ ++ int enforce_peername; ++} pfixtls_scache_info_t; ++ ++extern const long scache_db_version; ++extern const long openssl_version; ++ ++int pfixtls_timed_read(int fd, void *buf, unsigned len, int timout, ++ void *unused_timeout); ++int pfixtls_timed_write(int fd, void *buf, unsigned len, int timeout, ++ void *unused_timeout); ++ ++extern int pfixtls_serverengine; ++int pfixtls_init_serverengine(int verifydepth, int askcert); ++int pfixtls_start_servertls(VSTREAM *stream, int timeout, ++ const char *peername, const char *peeraddr, ++ tls_info_t *tls_info, int require_cert); ++int pfixtls_stop_servertls(VSTREAM *stream, int timeout, int failure, ++ tls_info_t *tls_info); ++ ++extern int pfixtls_clientengine; ++int pfixtls_init_clientengine(int verifydepth); ++int pfixtls_start_clienttls(VSTREAM *stream, int timeout, ++ int enforce_peername, ++ const char *peername, ++ tls_info_t *tls_info); ++int pfixtls_stop_clienttls(VSTREAM *stream, int timeout, int failure, ++ tls_info_t *tls_info); ++ ++#endif /* PFIXTLS_H_INCLUDED */ ++#endif ++ ++/* LICENSE ++/* .ad ++/* .fi ++/* AUTHOR(S) ++/* Lutz Jaenicke ++/* BTU Cottbus ++/* Allgemeine Elektrotechnik ++/* Universitaetsplatz 3-4 ++/* D-03044 Cottbus, Germany ++/*--*/ +diff -Pur postfix-1.1.11-20020613-orig/src/global/resolve_local.c postfix-1.1.11-20020613/src/global/resolve_local.c +--- postfix-1.1.11-20020613-orig/src/global/resolve_local.c Thu Jan 31 20:56:29 2002 ++++ postfix-1.1.11-20020613/src/global/resolve_local.c Wed Jun 26 15:26:48 2002 +@@ -42,6 +42,7 @@ + #include <netinet/in.h> + #include <arpa/inet.h> + #include <string.h> ++#include <netdb.h> + + #ifndef INADDR_NONE + #define INADDR_NONE 0xffffffff +@@ -79,7 +80,12 @@ + { + char *saved_addr = mystrdup(addr); + char *dest; ++#ifdef INET6 ++ struct addrinfo hints, *res, *res0; ++ int error; ++#else + struct in_addr ipaddr; ++#endif + int len; + + #define RETURN(x) { myfree(saved_addr); return(x); } +@@ -111,9 +117,25 @@ + if (*dest == '[' && dest[len - 1] == ']') { + dest++; + dest[len -= 2] = 0; ++#ifdef INET6 ++ memset(&hints, 0, sizeof(hints)); ++ hints.ai_family = PF_UNSPEC; ++ hints.ai_socktype = SOCK_DGRAM; ++ error = getaddrinfo(dest, NULL, &hints, &res0); ++ if (!error) { ++ for (res = res0; res; res = res->ai_next) { ++ if (own_inet_addr(res->ai_addr)) { ++ freeaddrinfo(res0); ++ RETURN(1); ++ } ++ } ++ freeaddrinfo(res0); ++ } ++#else + if ((ipaddr.s_addr = inet_addr(dest)) != INADDR_NONE + && own_inet_addr(&ipaddr)) + RETURN(1); ++#endif + } + + /* +diff -Pur postfix-1.1.11-20020613-orig/src/global/wildcard_inet_addr.c postfix-1.1.11-20020613/src/global/wildcard_inet_addr.c +--- postfix-1.1.11-20020613-orig/src/global/wildcard_inet_addr.c Thu Jan 1 01:00:00 1970 ++++ postfix-1.1.11-20020613/src/global/wildcard_inet_addr.c Wed Jun 26 15:26:48 2002 +@@ -0,0 +1,82 @@ ++/* System library. */ ++ ++#include <sys_defs.h> ++#include <netinet/in.h> ++#include <arpa/inet.h> ++#include <string.h> ++#ifdef INET6 ++#include <sys/socket.h> ++#endif ++#include <netdb.h> ++ ++#ifdef STRCASECMP_IN_STRINGS_H ++#include <strings.h> ++#endif ++ ++/* Utility library. */ ++ ++#include <msg.h> ++#include <mymalloc.h> ++#include <inet_addr_list.h> ++#include <inet_addr_local.h> ++#include <inet_addr_host.h> ++#include <stringops.h> ++ ++/* Global library. */ ++ ++#include <mail_params.h> ++#include <wildcard_inet_addr.h> ++ ++/* Application-specific. */ ++static INET_ADDR_LIST addr_list; ++ ++/* wildcard_inet_addr_init - initialize my own address list */ ++ ++static void wildcard_inet_addr_init(INET_ADDR_LIST *addr_list) ++{ ++#ifdef INET6 ++ struct addrinfo hints, *res, *res0; ++ char hbuf[NI_MAXHOST]; ++ int error; ++#ifdef NI_WITHSCOPEID ++ const int niflags = NI_NUMERICHOST | NI_WITHSCOPEID; ++#else ++ const int niflags = NI_NUMERICHOST; ++#endif ++ ++ inet_addr_list_init(addr_list); ++ ++ memset(&hints, 0, sizeof(hints)); ++ hints.ai_family = PF_UNSPEC; ++ hints.ai_socktype = SOCK_STREAM; ++ hints.ai_flags = AI_PASSIVE; ++ error = getaddrinfo(NULL, "0", &hints, &res0); ++ if (error) ++ msg_fatal("could not get list of wildcard addresses"); ++ for (res = res0; res; res = res->ai_next) { ++ if (res->ai_family != AF_INET && res->ai_family != AF_INET6) ++ continue; ++ if (getnameinfo(res->ai_addr, res->ai_addrlen, hbuf, sizeof(hbuf), ++ NULL, 0, niflags) != 0) ++ continue; ++ if (inet_addr_host(addr_list, hbuf) == 0) ++ continue; /* msg_fatal("config variable %s: host not found: %s", ++ VAR_INET_INTERFACES, hbuf); */ ++ } ++ freeaddrinfo(res0); ++#else ++ if (inet_addr_host(addr_list, "0.0.0.0") == 0) ++ msg_fatal("config variable %s: host not found: %s", ++ VAR_INET_INTERFACES, "0.0.0.0"); ++#endif ++} ++ ++/* wildcard_inet_addr_list - return list of addresses */ ++ ++INET_ADDR_LIST *wildcard_inet_addr_list(void) ++{ ++ if (addr_list.used == 0) ++ wildcard_inet_addr_init(&addr_list); ++ ++ return (&addr_list); ++} +diff -Pur postfix-1.1.11-20020613-orig/src/global/wildcard_inet_addr.h postfix-1.1.11-20020613/src/global/wildcard_inet_addr.h +--- postfix-1.1.11-20020613-orig/src/global/wildcard_inet_addr.h Thu Jan 1 01:00:00 1970 ++++ postfix-1.1.11-20020613/src/global/wildcard_inet_addr.h Wed Jun 26 15:26:48 2002 +@@ -0,0 +1,36 @@ ++#ifndef _WILDCARD_INET_ADDR_H_INCLUDED_ ++#define _WILDCARD_INET_ADDR_H_INCLUDED_ ++ ++/*++ ++/* NAME ++/* wildcard_inet_addr_list 3h ++/* SUMMARY ++/* grab the list of wildcard IP addresses. ++/* SYNOPSIS ++/* #include <own_inet_addr.h> ++/* DESCRIPTION ++/* .nf ++/*--*/ ++ ++ /* ++ * System library. ++ */ ++#include <netinet/in.h> ++#ifdef INET6 ++#include <sys/socket.h> ++#endif ++ ++ /* ++ * External interface. ++ */ ++extern struct INET_ADDR_LIST *wildcard_inet_addr_list(void); ++ ++/* LICENSE ++/* .ad ++/* .fi ++/* foo ++/* AUTHOR(S) ++/* Jun-ichiro itojun Hagino ++/*--*/ ++ ++#endif +diff -Pur postfix-1.1.11-20020613-orig/src/lmtp/lmtp.c postfix-1.1.11-20020613/src/lmtp/lmtp.c +--- postfix-1.1.11-20020613-orig/src/lmtp/lmtp.c Sat May 11 02:07:05 2002 ++++ postfix-1.1.11-20020613/src/lmtp/lmtp.c Wed Jun 26 15:26:48 2002 +@@ -190,6 +190,12 @@ + /* .IP \fBlmtp_quit_timeout\fR + /* Timeout for sending the \fBQUIT\fR command, and for + /* receiving the server response. ++/* .IP \fBlmtp_bind_address\fR ++/* Numerical source network address (IPv4) to bind to when making ++/* a connection. ++/* .IP \fBlmtp_bind_address6\fR ++/* Numerical source network address (IPv6) to bind to when making ++/* a connection. + /* SEE ALSO + /* bounce(8) non-delivery status reports + /* local(8) local mail delivery +@@ -276,6 +282,8 @@ + char *var_lmtp_sasl_opts; + char *var_lmtp_sasl_passwd; + bool var_lmtp_sasl_enable; ++char *var_lmtp_bind_addr; ++char *var_lmtp_bind_addr6; + + /* + * Global variables. +@@ -514,6 +522,10 @@ + VAR_ERROR_RCPT, DEF_ERROR_RCPT, &var_error_rcpt, 1, 0, + VAR_LMTP_SASL_PASSWD, DEF_LMTP_SASL_PASSWD, &var_lmtp_sasl_passwd, 0, 0, + VAR_LMTP_SASL_OPTS, DEF_LMTP_SASL_OPTS, &var_lmtp_sasl_opts, 0, 0, ++ VAR_LMTP_BIND_ADDR, DEF_LMTP_BIND_ADDR, &var_lmtp_bind_addr, 0, 0, ++#ifdef INET6 ++ VAR_LMTP_BIND_ADDR6, DEF_LMTP_BIND_ADDR6, &var_lmtp_bind_addr6, 0, 0, ++#endif + 0, + }; + static CONFIG_INT_TABLE int_table[] = { +diff -Pur postfix-1.1.11-20020613-orig/src/lmtp/lmtp_connect.c postfix-1.1.11-20020613/src/lmtp/lmtp_connect.c +--- postfix-1.1.11-20020613-orig/src/lmtp/lmtp_connect.c Thu Mar 22 02:01:46 2001 ++++ postfix-1.1.11-20020613/src/lmtp/lmtp_connect.c Wed Jun 26 15:26:48 2002 +@@ -92,11 +92,13 @@ + #include <iostuff.h> + #include <timed_connect.h> + #include <stringops.h> ++#include <inet_addr_list.h> + + /* Global library. */ + + #include <mail_params.h> + #include <mail_proto.h> ++#include <own_inet_addr.h> + + /* DNS library. */ + +@@ -166,13 +168,42 @@ + const char *destination, VSTRING *why) + { + char *myname = "lmtp_connect_addr"; +- struct sockaddr_in sin; +- int sock; ++#ifdef INET6 ++ struct sockaddr_storage ss; ++#else ++ struct sockaddr ss; ++#endif ++ struct sockaddr *sa; ++ struct sockaddr_in *sin; ++#ifdef INET6 ++ struct sockaddr_in6 *sin6; ++#endif ++ SOCKADDR_SIZE salen; ++#ifdef INET6 ++ char hbuf[NI_MAXHOST]; ++#else ++ char hbuf[sizeof("255.255.255.255") + 1]; ++#endif ++ int sock = -1; ++ INET_ADDR_LIST *addr_list; ++ char *bind_addr; ++ ++ sa = (struct sockaddr *)&ss; ++ sin = (struct sockaddr_in *)&ss; ++#ifdef INET6 ++ sin6 = (struct sockaddr_in6 *)&ss; ++#endif + + /* + * Sanity checks. + */ +- if (addr->data_len > sizeof(sin.sin_addr)) { ++#ifdef INET6 ++ if (((addr->type==T_A) && (addr->data_len > sizeof(sin->sin_addr))) || ++ ((addr->type==T_AAAA) && (addr->data_len > sizeof(sin6->sin6_addr)))) ++#else ++ if (addr->data_len > sizeof(sin->sin_addr)) ++#endif ++ { + msg_warn("%s: skip address with length %d", myname, addr->data_len); + lmtp_errno = LMTP_RETRY; + return (0); +@@ -181,25 +212,168 @@ + /* + * Initialize. + */ +- memset((char *) &sin, 0, sizeof(sin)); +- sin.sin_family = AF_INET; ++ switch (addr->type) { ++#ifdef INET6 ++ case T_AAAA: ++ bind_addr = var_lmtp_bind_addr6; ++ memset(sin6, 0, sizeof(*sin6)); ++ sin6->sin6_family = AF_INET6; ++ salen = sizeof(*sin6); ++ break; ++#endif ++ default: /* T_A: */ ++ bind_addr = var_lmtp_bind_addr; ++ memset(sin, 0, sizeof(*sin)); ++ sin->sin_family = AF_INET; ++ salen = sizeof(*sin); ++ break; ++ }; ++#ifdef HAS_SALEN ++ sa->sa_len = salen; ++#endif + +- if ((sock = socket(sin.sin_family, SOCK_STREAM, 0)) < 0) ++ if ((sock = socket(sa->sa_family, SOCK_STREAM, 0)) < 0) + msg_fatal("%s: socket: %m", myname); + + /* ++ * Allow the sysadmin to specify the source address ++ */ ++ ++ if (*bind_addr) { ++#ifndef INET6 ++ struct sockaddr_in sin; ++ ++ memset(&sin, 0, sizeof(sin)); ++ sin.sin_family = AF_INET; ++#ifdef HAS_SA_LEN ++ sin.sin_len = sizeof(sin); ++#endif ++ sin.sin_addr.s_addr = inet_addr(bind_addr); ++ if (sin.sin_addr.s_addr == INADDR_NONE) ++ msg_fatal("%s: bad %s parameter: %s", ++ myname, VAR_LMTP_BIND_ADDR, var_smtp_bind_addr); ++ if (bind(sock, (struct sockaddr *) &sin, sizeof(sin)) < 0) ++ msg_warn("%s: bind %s: %m", myname, inet_ntoa(sin.sin_addr)); ++ if (msg_verbose) ++ msg_info("%s: bind %s", myname, inet_ntoa(sin.sin_addr)); ++#else ++ char hbufl[NI_MAXHOST]; ++ struct addrinfo hints, *res; ++ ++ memset(&hints, 0, sizeof(hints)); ++ hints.ai_family = sa->sa_family; ++ hints.ai_socktype = SOCK_STREAM; ++ hints.ai_flags = AI_PASSIVE|AI_NUMERICHOST; ++ snprintf(hbufl, sizeof(hbufl)-1, "%s", bind_addr); ++ if (getaddrinfo(hbufl, NULL, &hints, &res) == 0) { ++ (void)getnameinfo(res->ai_addr, res->ai_addrlen, hbufl, ++ sizeof(hbufl), NULL, 0, NI_NUMERICHOST); ++ if (bind(sock, res->ai_addr, res->ai_addrlen) < 0) ++ msg_warn("%s: bind %s: %m", myname, hbufl); ++ freeaddrinfo(res); ++ if (msg_verbose) ++ msg_info("%s: bind %s", myname, hbufl); ++ } ++#endif ++ } ++ ++ /* ++ * If running on a virtual host, start connections from the ++ * right address. ++ */ ++ ++ else if ((addr_list = own_inet_addr_list())->used == 1) { ++#ifndef INET6 ++ struct sockaddr_in sin; ++ unsigned long inaddr; /* XXX BAD!*/ ++ ++ memset(&sin, 0, sizeof(sin)); ++ sin.sin_family = AF_INET; ++#ifdef HAS_SA_LEN ++ sin.sin_len = sizeof(sin); ++#endif ++ memcpy((char *)&sin.sin_addr, addr_list->addrs, sizeof(sin.sin_addr)); ++ inaddr = (unsigned long)ntohl(sin.sin_addr.s_addr); ++ if (!IN_CLASSA(inaddr) ++ /* XXX Are the two following lines correct? */ ++ || !(((inaddr & IN_CLASSA_NET) >> IN_CLASSA_NSHIFT) == ++ IN_LOOPBACKNET)) { ++ if (bind(sock, (struct sockaddr *)&sin, sizeof(sin)) < 0) ++ msg_warn("%s: bind %s: %m", myname, inet_ntoa(sin.sin_addr)); ++ if (msg_verbose) ++ msg_info("%s: bind %s", myname, inet_ntoa(sin.sin_addr)); ++ } ++#else ++ char hbufl[NI_MAXHOST]; ++ struct addrinfo hints, *res = NULL, *loopback = NULL; ++ ++ memset(&hints, 0, sizeof(hints)); ++ hints.ai_family = sa->sa_family; ++ hints.ai_socktype = SOCK_STREAM; ++ if (getaddrinfo(NULL, "0", &hints, &loopback) != 0) ++ loopback = NULL; ++ ++ /* ++ * getnameinfo -> getaddrinfo loop is here so that we can ++ * get rid of port ++ */ ++ (void)getnameinfo((struct sockaddr *)addr_list->addrs, ++ SA_LEN((struct sockaddr *)addr_list->addrs), ++ hbufl, sizeof(hbufl), NULL, 0, NI_NUMERICHOST); ++ hbufl[sizeof(hbufl) - 1] = 0; ++ memset(&hints, 0, sizeof(hints)); ++ hints.ai_family = sa->sa_family; ++ hints.ai_socktype = SOCK_STREAM; ++ hints.ai_flags = AI_PASSIVE|AI_NUMERICHOST; ++ if (getaddrinfo(hbufl, NULL, &hints, &res) == 0 && ++ !(res->ai_addrlen == loopback->ai_addrlen && ++ memcmp(res->ai_addr, loopback->ai_addr, res->ai_addrlen) == 0)) { ++ if (bind(sock, res->ai_addr, res->ai_addrlen) < 0) ++ msg_warn("%s: bind %s: %m", myname, hbufl); ++ if (msg_verbose) ++ msg_info("%s: bind %s", myname, hbufl); ++ } ++ if (res) ++ freeaddrinfo(res); ++ if (loopback) ++ freeaddrinfo(loopback); ++#endif ++ } ++ ++ /* + * Connect to the LMTP server. + */ +- sin.sin_port = port; +- memcpy((char *) &sin.sin_addr, addr->data, sizeof(sin.sin_addr)); ++ switch (addr->type) { ++#ifdef INET6 ++ case T_AAAA: ++ /* XXX scope-unfriendly */ ++ memset(sin6, 0, sizeof(*sin6)); ++ sin6->sin6_port = port; ++ sin6->sin6_family = AF_INET6; ++ salen = sizeof(*sin6); ++ memcpy(&sin6->sin6_addr, addr->data, sizeof(sin6->sin6_addr)); ++ inet_ntop(AF_INET6, &sin6->sin6_addr, hbuf, sizeof(hbuf)); ++ break; ++#endif ++ default: /* T_A: */ ++ memset(sin, 0, sizeof(*sin)); ++ sin->sin_port = port; ++ sin->sin_family = AF_INET; ++ salen = sizeof(*sin6); ++ memcpy(&sin->sin_addr, addr->data, sizeof(sin->sin_addr)); ++ inet_ntop(AF_INET, &sin->sin_addr, hbuf, sizeof(hbuf)); ++ break; ++ } ++#ifdef HAS_SA_LEN ++ sa->sa_len = salen; ++#endif + + if (msg_verbose) + msg_info("%s: trying: %s[%s] port %d...", +- myname, addr->name, inet_ntoa(sin.sin_addr), ntohs(port)); ++ myname, addr->name, hbuf, ntohs(port)); + +- return (lmtp_connect_sock(sock, (struct sockaddr *) & sin, sizeof(sin), +- addr->name, inet_ntoa(sin.sin_addr), +- destination, why)); ++ return (lmtp_connect_sock(sock, (struct sockaddr *)sa, salen, ++ addr->name, hbuf, destination, why)); + } + + /* lmtp_connect_sock - connect a socket over some transport */ +diff -Pur postfix-1.1.11-20020613-orig/src/master/master_ent.c postfix-1.1.11-20020613/src/master/master_ent.c +--- postfix-1.1.11-20020613-orig/src/master/master_ent.c Sun Dec 23 20:08:58 2001 ++++ postfix-1.1.11-20020613/src/master/master_ent.c Wed Jun 26 15:26:48 2002 +@@ -284,8 +284,13 @@ + inet_addr_host(MASTER_INET_ADDRLIST(serv), host); + serv->listen_fd_count = MASTER_INET_ADDRLIST(serv)->used; + } else if (strcasecmp(var_inet_interfaces, DEF_INET_INTERFACES) == 0) { ++#ifdef INET6 ++ MASTER_INET_ADDRLIST(serv) = wildcard_inet_addr_list(); ++ serv->listen_fd_count = MASTER_INET_ADDRLIST(serv)->used; ++#else + MASTER_INET_ADDRLIST(serv) = 0; /* wild-card */ + serv->listen_fd_count = 1; ++#endif + } else { + MASTER_INET_ADDRLIST(serv) = own_inet_addr_list(); /* virtual */ + serv->listen_fd_count = MASTER_INET_ADDRLIST(serv)->used; +diff -Pur postfix-1.1.11-20020613-orig/src/master/master_listen.c postfix-1.1.11-20020613/src/master/master_listen.c +--- postfix-1.1.11-20020613-orig/src/master/master_listen.c Tue May 1 00:47:57 2001 ++++ postfix-1.1.11-20020613/src/master/master_listen.c Wed Jun 26 15:26:48 2002 +@@ -64,13 +64,22 @@ + + #include "master.h" + ++#ifdef INET6 ++#include <netdb.h> ++#include <stdio.h> ++#endif ++ + /* master_listen_init - enable connection requests */ + + void master_listen_init(MASTER_SERV *serv) + { + char *myname = "master_listen_init"; + char *end_point; +- int n; ++ int n,m,tmpfd; ++#ifdef INET6 ++ char hbuf[NI_MAXHOST]; ++ SOCKADDR_SIZE salen; ++#endif + + /* + * Find out what transport we should use, then create one or more +@@ -111,18 +120,31 @@ + serv->listen_fd[0] = + inet_listen(MASTER_INET_PORT(serv), + serv->max_proc > var_proc_limit ? +- serv->max_proc : var_proc_limit, NON_BLOCKING); ++ serv->max_proc : var_proc_limit, NON_BLOCKING, 1); + close_on_exec(serv->listen_fd[0], CLOSE_ON_EXEC); + } else { /* virtual or host:port */ +- for (n = 0; n < serv->listen_fd_count; n++) { ++ for (m = n = 0; n < serv->listen_fd_count; n++) { ++#ifdef INET6 ++ if (getnameinfo((struct sockaddr *)&MASTER_INET_ADDRLIST(serv)->addrs[n], ++ SA_LEN((struct sockaddr *)&MASTER_INET_ADDRLIST(serv)->addrs[n]), ++ hbuf, sizeof(hbuf), NULL, 0, NI_NUMERICHOST)) { ++ strncpy(hbuf, "?????", sizeof(hbuf)); ++ } ++ end_point = concatenate(hbuf, ":", MASTER_INET_PORT(serv), (char *) 0); ++#else + end_point = concatenate(inet_ntoa(MASTER_INET_ADDRLIST(serv)->addrs[n]), + ":", MASTER_INET_PORT(serv), (char *) 0); +- serv->listen_fd[n] ++#endif ++ tmpfd + = inet_listen(end_point, serv->max_proc > var_proc_limit ? +- serv->max_proc : var_proc_limit, NON_BLOCKING); +- close_on_exec(serv->listen_fd[n], CLOSE_ON_EXEC); ++ serv->max_proc : var_proc_limit, NON_BLOCKING, 0); ++ if (tmpfd >= 0) { ++ serv->listen_fd[m] = tmpfd; ++ close_on_exec(serv->listen_fd[m++], CLOSE_ON_EXEC); ++ } + myfree(end_point); + } ++ serv->listen_fd_count=m; + } + break; + default: +diff -Pur postfix-1.1.11-20020613-orig/src/qmgr/qmgr_message.c postfix-1.1.11-20020613/src/qmgr/qmgr_message.c +--- postfix-1.1.11-20020613-orig/src/qmgr/qmgr_message.c Tue Jun 11 01:55:20 2002 ++++ postfix-1.1.11-20020613/src/qmgr/qmgr_message.c Wed Jun 26 15:26:48 2002 +@@ -507,7 +507,11 @@ + * every front-ent program. + */ + if ((at = strrchr(recipient->address, '@')) != 0 ++#ifdef INET6 ++ && (at + 1)[strspn(at + 1, "[]0123456789.:abcdef")] != 0 ++#else + && (at + 1)[strspn(at + 1, "[]0123456789.")] != 0 ++#endif + && valid_hostname(at + 1, DONT_GRIPE) == 0) { + qmgr_bounce_recipient(message, recipient, + "bad host/domain syntax: \"%s\"", at + 1); +diff -Pur postfix-1.1.11-20020613-orig/src/qmqpd/qmqpd_peer.c postfix-1.1.11-20020613/src/qmqpd/qmqpd_peer.c +--- postfix-1.1.11-20020613-orig/src/qmqpd/qmqpd_peer.c Thu Jul 5 22:09:35 2001 ++++ postfix-1.1.11-20020613/src/qmqpd/qmqpd_peer.c Wed Jun 26 15:29:19 2002 +@@ -70,6 +70,11 @@ + ) + #endif + ++#ifdef INET6 ++#define GAI_STRERROR(error) \ ++ ((error = EAI_SYSTEM) ? gai_strerror(error) : strerror(errno)) ++#endif ++ + /* Utility library. */ + + #include <msg.h> +@@ -79,7 +84,6 @@ + + /* Global library. */ + +- + /* Application-specific. */ + + #include "qmqpd.h" +@@ -88,16 +92,23 @@ + + void qmqpd_peer_init(QMQPD_STATE *state) + { +- struct sockaddr_in sin; +- SOCKADDR_SIZE len = sizeof(sin); ++#ifdef INET6 ++ struct sockaddr_storage ss; ++#else ++ struct sockaddr ss; ++ struct in_addr *in; + struct hostent *hp; +- int i; ++#endif ++ struct sockaddr *sa; ++ SOCKADDR_SIZE len; ++ ++ sa = (struct sockaddr *)&ss; ++ len = sizeof(ss); + + /* + * Look up the peer address information. + */ +- if (getpeername(vstream_fileno(state->client), +- (struct sockaddr *) & sin, &len) >= 0) { ++ if (getpeername(vstream_fileno(state->client), sa, &len) >= 0) { + errno = 0; + } + +@@ -112,16 +123,50 @@ + /* + * Look up and "verify" the client hostname. + */ +- else if (errno == 0 && sin.sin_family == AF_INET) { +- state->addr = mystrdup(inet_ntoa(sin.sin_addr)); +- hp = gethostbyaddr((char *) &(sin.sin_addr), +- sizeof(sin.sin_addr), AF_INET); +- if (hp == 0) { ++ else if (errno == 0 && (sa->sa_family == AF_INET ++#ifdef INET6 ++ || sa->sa_family == AF_INET6 ++#endif ++ )) { ++#ifdef INET6 ++ char hbuf[NI_MAXHOST]; ++ char abuf[NI_MAXHOST]; ++ struct addrinfo hints, *rnull = NULL; ++#else ++ char abuf[sizeof("255.255.255.255") + 1]; ++ char *hbuf; ++#endif ++ int error = -1; ++ ++#ifdef INET6 ++ (void)getnameinfo(sa, len, abuf, sizeof(abuf), NULL, 0, ++ NI_NUMERICHOST); ++#else ++ in = &((struct sockaddr_in *)sa)->sin_addr; ++ inet_ntop(AF_INET, in, abuf, sizeof(abuf)); ++#endif ++ ++ state->addr = mystrdup(abuf); ++#ifdef INET6 ++ error = getnameinfo(sa, len, hbuf, sizeof(hbuf), NULL, 0, ++ NI_NAMEREQD); ++#else ++ hbuf = NULL; ++ hp = gethostbyaddr((char *)in, sizeof(*in), AF_INET); ++ if (hp) { ++ error = 0; ++ hbuf = mystrdup(hp->h_name); ++ state->name = mystrdup("unknown"); ++ } else { ++ error = 1; ++ } ++#endif ++ if (error) { + state->name = mystrdup("unknown"); +- } else if (!valid_hostname(hp->h_name, DONT_GRIPE)) { ++ } else if (!valid_hostname(hbuf, DONT_GRIPE)) { + state->name = mystrdup("unknown"); + } else { +- state->name = mystrdup(hp->h_name); /* hp->name is clobbered!! */ ++ state->name = mystrdup(hbuf); /* hp->name is clobbered!! */ + + /* + * Reject the hostname if it does not list the peer address. +@@ -131,16 +176,31 @@ + state->name = mystrdup("unknown"); \ + } + ++#ifdef INET6 ++ memset(&hints, 0, sizeof(hints)); ++ hints.ai_family = AF_UNSPEC; ++ hints.ai_socktype = SOCK_STREAM; ++ error = getaddrinfo(state->name, NULL, &hints, &rnull); ++ if (error) { ++ msg_warn("%s: hostname %s verification failed: %s", ++ state->addr, state->name, GAI_STRERROR(error)); ++ REJECT_PEER_NAME(state); ++ } ++ /* memcmp() isn't needed if we use getaddrinfo */ ++ if (rnull) ++ freeaddrinfo(rnull); ++#else + hp = gethostbyname(state->name); /* clobbers hp->name!! */ + if (hp == 0) { + msg_warn("%s: hostname %s verification failed: %s", + state->addr, state->name, HSTRERROR(h_errno)); + REJECT_PEER_NAME(state); +- } else if (hp->h_length != sizeof(sin.sin_addr)) { ++ } else if (hp->h_length != sizeof(*in)) { + msg_warn("%s: hostname %s verification failed: bad address size %d", + state->addr, state->name, hp->h_length); + REJECT_PEER_NAME(state); + } else { ++ int i; + for (i = 0; /* void */ ; i++) { + if (hp->h_addr_list[i] == 0) { + msg_warn("%s: address not listed for hostname %s", +@@ -148,12 +208,12 @@ + REJECT_PEER_NAME(state); + break; + } +- if (memcmp(hp->h_addr_list[i], +- (char *) &sin.sin_addr, +- sizeof(sin.sin_addr)) == 0) ++ if (memcmp(hp->h_addr_list[i], (char *)in, ++ sizeof(*in)) == 0) + break; /* keep peer name */ + } + } ++#endif + } + } + +diff -Pur postfix-1.1.11-20020613-orig/src/smtp/Makefile.in postfix-1.1.11-20020613/src/smtp/Makefile.in +--- postfix-1.1.11-20020613-orig/src/smtp/Makefile.in Tue Jun 11 03:14:03 2002 ++++ postfix-1.1.11-20020613/src/smtp/Makefile.in Wed Jun 26 15:26:48 2002 +@@ -84,6 +84,7 @@ + smtp.o: ../../include/iostuff.h + smtp.o: ../../include/attr.h + smtp.o: ../../include/mail_server.h ++smtp.o: ../../include/pfixtls.h + smtp.o: smtp.h + smtp.o: smtp_sasl.h + smtp_addr.o: smtp_addr.c +@@ -103,6 +104,7 @@ + smtp_addr.o: ../../include/argv.h + smtp_addr.o: ../../include/deliver_request.h + smtp_addr.o: ../../include/recipient_list.h ++smtp_addr.o: ../../include/pfixtls.h + smtp_addr.o: smtp_addr.h + smtp_chat.o: smtp_chat.c + smtp_chat.o: ../../include/sys_defs.h +@@ -123,6 +125,7 @@ + smtp_chat.o: ../../include/cleanup_user.h + smtp_chat.o: ../../include/mail_error.h + smtp_chat.o: ../../include/name_mask.h ++smtp_chat.o: ../../include/pfixtls.h + smtp_chat.o: smtp.h + smtp_connect.o: smtp_connect.c + smtp_connect.o: ../../include/sys_defs.h +@@ -139,10 +142,12 @@ + smtp_connect.o: ../../include/mail_params.h + smtp_connect.o: ../../include/own_inet_addr.h + smtp_connect.o: ../../include/dns.h ++smtp_connect.o: ../../include/get_port.h + smtp_connect.o: smtp.h + 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 +@@ -174,6 +179,7 @@ + smtp_proto.o: ../../include/attr.h + smtp_proto.o: ../../include/mime_state.h + smtp_proto.o: ../../include/header_opts.h ++smtp_proto.o: ../../include/pfixtls.h + smtp_proto.o: smtp.h + smtp_proto.o: ../../include/argv.h + smtp_proto.o: smtp_sasl.h +@@ -219,9 +225,12 @@ + smtp_session.o: ../../include/stringops.h + smtp_session.o: ../../include/vstring.h + smtp_session.o: smtp.h ++smtp_session.o: ../../include/mail_params.h ++smtp_session.o: ../../include/pfixtls.h + smtp_session.o: ../../include/argv.h + smtp_session.o: ../../include/deliver_request.h + smtp_session.o: ../../include/recipient_list.h ++smtp_session.o: ../../include/maps.h + smtp_state.o: smtp_state.c + smtp_state.o: ../../include/sys_defs.h + smtp_state.o: ../../include/mymalloc.h +@@ -235,6 +244,7 @@ + smtp_state.o: ../../include/argv.h + smtp_state.o: ../../include/deliver_request.h + smtp_state.o: ../../include/recipient_list.h ++smtp_state.o: ../../include/pfixtls.h + smtp_state.o: smtp_sasl.h + smtp_trouble.o: smtp_trouble.c + smtp_trouble.o: ../../include/sys_defs.h +@@ -254,6 +264,7 @@ + smtp_trouble.o: ../../include/name_mask.h + smtp_trouble.o: smtp.h + smtp_trouble.o: ../../include/argv.h ++smtp_trouble.o: ../../include/pfixtls.h + smtp_unalias.o: smtp_unalias.c + smtp_unalias.o: ../../include/sys_defs.h + smtp_unalias.o: ../../include/htable.h +@@ -266,3 +277,4 @@ + smtp_unalias.o: ../../include/argv.h + smtp_unalias.o: ../../include/deliver_request.h + smtp_unalias.o: ../../include/recipient_list.h ++smtp_unalias.o: ../../include/pfixtls.h +diff -Pur postfix-1.1.11-20020613-orig/src/smtp/sasl_problem_diff postfix-1.1.11-20020613/src/smtp/sasl_problem_diff +--- postfix-1.1.11-20020613-orig/src/smtp/sasl_problem_diff Thu Jan 1 01:00:00 1970 ++++ postfix-1.1.11-20020613/src/smtp/sasl_problem_diff Wed Jun 26 15:26:48 2002 +@@ -0,0 +1,17 @@ ++--- smtp_proto.c.old Wed May 15 14:01:56 2002 +++++ smtp_proto.c Fri May 24 21:13:50 2002 ++@@ -372,8 +372,13 @@ ++ else if (strcasecmp(word, "STARTTLS") == 0) ++ state->features |= SMTP_FEATURE_STARTTLS; ++ #ifdef USE_SASL_AUTH ++- else if (var_smtp_sasl_enable && strcasecmp(word, "AUTH") == 0) +++ else if (var_smtp_sasl_enable && strcasecmp(word, "AUTH") == 0) { +++ if (state->sasl_mechanism_list) { +++ myfree(state->sasl_mechanism_list); +++ state->sasl_mechanism_list = 0; +++ } ++ smtp_sasl_helo_auth(state, words); +++ } ++ #endif ++ } ++ } +diff -Pur postfix-1.1.11-20020613-orig/src/smtp/smtp.c postfix-1.1.11-20020613/src/smtp/smtp.c +--- postfix-1.1.11-20020613-orig/src/smtp/smtp.c Mon May 27 01:07:04 2002 ++++ postfix-1.1.11-20020613/src/smtp/smtp.c Wed Jun 26 15:26:48 2002 +@@ -100,7 +100,11 @@ + /* .IP \fBsmtp_never_send_ehlo\fR + /* Never send EHLO at the start of a connection. + /* .IP \fBsmtp_bind_address\fR +-/* Numerical source network address to bind to when making a connection. ++/* Numerical source network address (IPv4) to bind to when making ++/* a connection. ++/* .IP \fBsmtp_bind_address6\fR ++/* Numerical source network address (IPv6) to bind to when making ++/* a connection. + /* .IP \fBsmtp_line_length_limit\fR + /* Length limit for SMTP message content lines. Zero means no limit. + /* Some SMTP servers misbehave on long lines. +@@ -240,6 +244,7 @@ + #include <debug_peer.h> + #include <mail_error.h> + #include <deliver_pass.h> ++#include <pfixtls.h> + + /* Single server skeleton. */ + +@@ -256,6 +261,7 @@ + */ + int var_smtp_conn_tmout; + int var_smtp_helo_tmout; ++int var_smtp_starttls_tmout; + int var_smtp_mail_tmout; + int var_smtp_rcpt_tmout; + int var_smtp_data0_tmout; +@@ -277,11 +283,20 @@ + char *var_smtp_sasl_passwd; + bool var_smtp_sasl_enable; + char *var_smtp_bind_addr; ++#ifdef INET6 ++char *var_smtp_bind_addr6; ++#endif + bool var_smtp_rand_addr; + int var_smtp_pix_thresh; + int var_smtp_pix_delay; + int var_smtp_line_limit; + char *var_smtp_helo_name; ++int var_smtp_use_tls; ++int var_smtp_enforce_tls; ++int var_smtp_tls_enforce_peername; ++char *var_smtp_tls_per_site; ++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 +@@ -391,6 +406,7 @@ + + static void pre_init(char *unused_name, char **unused_argv) + { ++ + debug_peer_init(); + + if (var_smtp_sasl_enable) +@@ -400,6 +416,14 @@ + msg_warn("%s is true, but SASL support is not compiled in", + VAR_SMTP_SASL_ENABLE); + #endif ++ /* ++ * Initialize the TLS data before entering the chroot jail ++ */ ++#ifdef HAS_SSL ++ if (var_smtp_use_tls || var_smtp_enforce_tls || var_smtp_tls_per_site[0]) ++ pfixtls_init_clientengine(var_smtp_tls_scert_vd); ++ smtp_tls_list_init(); ++#endif + } + + /* pre_accept - see if tables have changed */ +@@ -434,7 +458,11 @@ + VAR_SMTP_SASL_PASSWD, DEF_SMTP_SASL_PASSWD, &var_smtp_sasl_passwd, 0, 0, + VAR_SMTP_SASL_OPTS, DEF_SMTP_SASL_OPTS, &var_smtp_sasl_opts, 0, 0, + VAR_SMTP_BIND_ADDR, DEF_SMTP_BIND_ADDR, &var_smtp_bind_addr, 0, 0, ++#ifdef INET6 ++ VAR_SMTP_BIND_ADDR6, DEF_SMTP_BIND_ADDR6, &var_smtp_bind_addr6, 0, 0, ++#endif + VAR_SMTP_HELO_NAME, DEF_SMTP_HELO_NAME, &var_smtp_helo_name, 1, 0, ++ VAR_SMTP_TLS_PER_SITE, DEF_SMTP_TLS_PER_SITE, &var_smtp_tls_per_site, 0, 0, + 0, + }; + static CONFIG_TIME_TABLE time_table[] = { +@@ -448,6 +476,7 @@ + VAR_SMTP_QUIT_TMOUT, DEF_SMTP_QUIT_TMOUT, &var_smtp_quit_tmout, 1, 0, + VAR_SMTP_PIX_THRESH, DEF_SMTP_PIX_THRESH, &var_smtp_pix_thresh, 0, 0, + VAR_SMTP_PIX_DELAY, DEF_SMTP_PIX_DELAY, &var_smtp_pix_delay, 1, 0, ++ VAR_SMTP_STARTTLS_TMOUT, DEF_SMTP_STARTTLS_TMOUT, &var_smtp_starttls_tmout, 1, 0, + 0, + }; + static CONFIG_INT_TABLE int_table[] = { +@@ -463,6 +492,10 @@ + VAR_SMTP_NEVER_EHLO, DEF_SMTP_NEVER_EHLO, &var_smtp_never_ehlo, + VAR_SMTP_SASL_ENABLE, DEF_SMTP_SASL_ENABLE, &var_smtp_sasl_enable, + VAR_SMTP_RAND_ADDR, DEF_SMTP_RAND_ADDR, &var_smtp_rand_addr, ++ 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_ENFORCE_PN, DEF_SMTP_TLS_ENFORCE_PN, &var_smtp_tls_enforce_peername, ++ VAR_SMTP_TLS_NOTEOFFER, DEF_SMTP_TLS_NOTEOFFER, &var_smtp_tls_note_starttls_offer, + 0, + }; + +diff -Pur postfix-1.1.11-20020613-orig/src/smtp/smtp.h postfix-1.1.11-20020613/src/smtp/smtp.h +--- postfix-1.1.11-20020613-orig/src/smtp/smtp.h Thu May 23 21:18:02 2002 ++++ postfix-1.1.11-20020613/src/smtp/smtp.h Wed Jun 26 15:26:48 2002 +@@ -27,6 +27,7 @@ + * Global library. + */ + #include <deliver_request.h> ++#include <pfixtls.h> + + /* + * State information associated with each SMTP delivery. We're bundling the +@@ -79,9 +80,14 @@ + 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 */ ++ int tls_enforce_peername; /* cert must match */ ++ tls_info_t tls_info; /* TLS connection state */ + } SMTP_SESSION; + +-extern SMTP_SESSION *smtp_session_alloc(VSTREAM *, char *, char *); ++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 -Pur postfix-1.1.11-20020613-orig/src/smtp/smtp_addr.c postfix-1.1.11-20020613/src/smtp/smtp_addr.c +--- postfix-1.1.11-20020613-orig/src/smtp/smtp_addr.c Sun Jul 8 17:05:26 2001 ++++ postfix-1.1.11-20020613/src/smtp/smtp_addr.c Wed Jun 26 15:26:48 2002 +@@ -134,18 +134,68 @@ + static void smtp_print_addr(char *what, DNS_RR *addr_list) + { + DNS_RR *addr; +- struct in_addr in_addr; ++#ifdef INET6 ++ struct sockaddr_storage ss; ++#else ++ struct sockaddr ss; ++#endif ++ struct sockaddr_in *sin; ++#ifdef INET6 ++ struct sockaddr_in6 *sin6; ++ char hbuf[NI_MAXHOST]; ++#else ++ char hbuf[sizeof("255.255.255.255") + 1]; ++#endif + + msg_info("begin %s address list", what); + for (addr = addr_list; addr; addr = addr->next) { +- if (addr->data_len > sizeof(addr)) { +- msg_warn("skipping address length %d", addr->data_len); +- } else { +- memcpy((char *) &in_addr, addr->data, sizeof(in_addr)); +- msg_info("pref %4d host %s/%s", +- addr->pref, addr->name, +- inet_ntoa(in_addr)); ++ if (addr->class != C_IN) { ++ msg_warn("skipping unsupported address (class=%u)", addr->class); ++ continue; + } ++ switch (addr->type) { ++ case T_A: ++ if (addr->data_len != sizeof(sin->sin_addr)) { ++ msg_warn("skipping invalid address (AAAA, len=%u)", ++ addr->data_len); ++ continue; ++ } ++ sin = (struct sockaddr_in *)&ss; ++ memset(sin, 0, sizeof(*sin)); ++ sin->sin_family = AF_INET; ++#ifdef HAS_SA_LEN ++ sin->sin_len = sizeof(*sin); ++#endif ++ memcpy(&sin->sin_addr, addr->data, sizeof(sin->sin_addr)); ++ break; ++#ifdef INET6 ++ case T_AAAA: ++ if (addr->data_len != sizeof(sin6->sin6_addr)) { ++ msg_warn("skipping invalid address (AAAA, len=%u)", ++ addr->data_len); ++ continue; ++ } ++ sin6 = (struct sockaddr_in6 *)&ss; ++ memset(sin6, 0, sizeof(*sin6)); ++ sin6->sin6_family = AF_INET6; ++#ifdef HAS_SA_LEN ++ sin6->sin6_len = sizeof(*sin6); ++#endif ++ memcpy(&sin6->sin6_addr, addr->data, sizeof(sin6->sin6_addr)); ++ break; ++#endif ++ default: ++ msg_warn("skipping unsupported address (type=%u)", addr->type); ++ continue; ++ } ++ ++#ifdef INET6 ++ (void)getnameinfo((struct sockaddr *)&ss, SS_LEN(ss), ++ hbuf, sizeof(hbuf), NULL, 0, NI_NUMERICHOST); ++#else ++ (void)inet_ntop(AF_INET, &sin->sin_addr, hbuf, sizeof(hbuf)); ++#endif ++ msg_info("pref %4d host %s/%s", addr->pref, addr->name, hbuf); + } + msg_info("end %s address list", what); + } +@@ -155,15 +205,23 @@ + static DNS_RR *smtp_addr_one(DNS_RR *addr_list, char *host, unsigned pref, VSTRING *why) + { + char *myname = "smtp_addr_one"; ++#ifndef INET6 + struct in_addr inaddr; +- DNS_FIXED fixed; + DNS_RR *addr = 0; + DNS_RR *rr; + struct hostent *hp; ++#else ++ struct addrinfo hints, *res0, *res; ++ int error = -1; ++ char *addr; ++ size_t addrlen; ++#endif ++ DNS_FIXED fixed; + + if (msg_verbose) + msg_info("%s: host %s", myname, host); + ++#ifndef INET6 + /* + * Interpret a numerical name as an address. + */ +@@ -216,6 +274,48 @@ + smtp_errno = SMTP_FAIL; + break; + } ++#else ++ memset(&hints, 0, sizeof(hints)); ++ hints.ai_family = PF_UNSPEC; ++ hints.ai_socktype = SOCK_STREAM; ++ error = getaddrinfo(host, NULL, &hints, &res0); ++ if (error) { ++ switch (error) { ++ case EAI_AGAIN: ++ smtp_errno = SMTP_RETRY; ++ break; ++ default: ++ vstring_sprintf(why, "[%s]: %s",host,gai_strerror(error)); ++ smtp_errno = SMTP_FAIL; ++ break; ++ } ++ return (addr_list); ++ } ++ for (res = res0; res; res = res->ai_next) { ++ memset((char *) &fixed, 0, sizeof(fixed)); ++ switch(res->ai_family) { ++ case AF_INET6: ++ /* XXX not scope friendly */ ++ fixed.type = T_AAAA; ++ addr = (char *)&((struct sockaddr_in6 *)res->ai_addr)->sin6_addr; ++ addrlen = sizeof(struct in6_addr); ++ break; ++ case AF_INET: ++ fixed.type = T_A; ++ addr = (char *)&((struct sockaddr_in *)res->ai_addr)->sin_addr; ++ addrlen = sizeof(struct in_addr); ++ break; ++ default: ++ msg_warn("%s: unknown address family %d for %s", ++ myname, res->ai_family, host); ++ continue; ++ } ++ addr_list = dns_rr_append(addr_list, ++ dns_rr_create(host, &fixed, pref, addr, addrlen)); ++ } ++ if (res0) ++ freeaddrinfo(res0); ++#endif + return (addr_list); + } + +@@ -251,6 +351,9 @@ + INET_ADDR_LIST *self; + DNS_RR *addr; + int i; ++#ifdef INET6 ++ struct sockaddr *sa; ++#endif + + /* + * Find the first address that lists any address that this mail system is +@@ -260,12 +363,36 @@ + + self = own_inet_addr_list(); + for (addr = addr_list; addr; addr = addr->next) { +- for (i = 0; i < self->used; i++) ++ for (i = 0; i < self->used; i++) { ++#ifdef INET6 ++ sa = (struct sockaddr *)&self->addrs[i]; ++ switch(addr->type) { ++ case T_AAAA: ++ /* XXX scope */ ++ if (sa->sa_family != AF_INET6) ++ break; ++ if (memcmp(&((struct sockaddr_in6 *)sa)->sin6_addr, ++ addr->data, sizeof(struct in6_addr)) == 0) { ++ return(addr); ++ } ++ break; ++ case T_A: ++ if (sa->sa_family != AF_INET) ++ break; ++ if (memcmp(&((struct sockaddr_in *)sa)->sin_addr, ++ addr->data, sizeof(struct in_addr)) == 0) { ++ return(addr); ++ } ++ break; ++ } ++#else + if (INADDRP(addr->data)->s_addr == self->addrs[i].s_addr) { + if (msg_verbose) + msg_info("%s: found at pref %d", myname, addr->pref); + return (addr); + } ++#endif ++ } + } + + /* +diff -Pur postfix-1.1.11-20020613-orig/src/smtp/smtp_connect.c postfix-1.1.11-20020613/src/smtp/smtp_connect.c +--- postfix-1.1.11-20020613-orig/src/smtp/smtp_connect.c Sun Jul 8 21:40:03 2001 ++++ postfix-1.1.11-20020613/src/smtp/smtp_connect.c Wed Jun 26 15:26:48 2002 +@@ -81,6 +81,7 @@ + /* System library. */ + + #include <sys_defs.h> ++#include <stdlib.h> + #include <sys/socket.h> + #include <netinet/in.h> + #include <arpa/inet.h> +@@ -110,12 +111,14 @@ + #include <inet_addr_list.h> + #include <iostuff.h> + #include <timed_connect.h> ++#include <get_port.h> + #include <stringops.h> + + /* Global library. */ + + #include <mail_params.h> + #include <own_inet_addr.h> ++#include <pfixtls.h> + + /* DNS library. */ + +@@ -128,23 +131,50 @@ + + /* smtp_connect_addr - connect to explicit address */ + +-static SMTP_SESSION *smtp_connect_addr(DNS_RR *addr, unsigned port, ++static SMTP_SESSION *smtp_connect_addr(char *dest, DNS_RR *addr, unsigned port, + VSTRING *why) + { + char *myname = "smtp_connect_addr"; +- struct sockaddr_in sin; +- int sock; ++#ifdef INET6 ++ struct sockaddr_storage ss; ++#else ++ struct sockaddr ss; ++#endif ++ struct sockaddr *sa; ++ struct sockaddr_in *sin; ++#ifdef INET6 ++ struct sockaddr_in6 *sin6; ++#endif ++ SOCKADDR_SIZE salen; ++#ifdef INET6 ++ char hbuf[NI_MAXHOST]; ++#else ++ char hbuf[sizeof("255.255.255.255") + 1]; ++#endif ++ int sock = -1; + INET_ADDR_LIST *addr_list; + int conn_stat; + int saved_errno; + VSTREAM *stream; + int ch; +- unsigned long inaddr; ++ char *bind_addr; ++ ++ sa = (struct sockaddr *)&ss; ++ sin = (struct sockaddr_in *)&ss; ++#ifdef INET6 ++ sin6 = (struct sockaddr_in6 *)&ss; ++#endif + + /* + * Sanity checks. + */ +- if (addr->data_len > sizeof(sin.sin_addr)) { ++#ifdef INET6 ++ if (((addr->type==T_A) && (addr->data_len > sizeof(sin->sin_addr))) || ++ ((addr->type==T_AAAA) && (addr->data_len > sizeof(sin6->sin6_addr)))) ++#else ++ if (addr->data_len > sizeof(sin->sin_addr)) ++#endif ++ { + msg_warn("%s: skip address with length %d", myname, addr->data_len); + smtp_errno = SMTP_RETRY; + return (0); +@@ -153,18 +183,42 @@ + /* + * Initialize. + */ +- memset((char *) &sin, 0, sizeof(sin)); +- sin.sin_family = AF_INET; +- +- if ((sock = socket(sin.sin_family, SOCK_STREAM, 0)) < 0) +- msg_fatal("%s: socket: %m", myname); +- ++ switch (addr->type) { ++#ifdef INET6 ++ case T_AAAA: ++ bind_addr = var_smtp_bind_addr6; ++ memset(sin6, 0, sizeof(*sin6)); ++ sin6->sin6_family = AF_INET6; ++ salen = sizeof(*sin6); ++ break; ++#endif ++ default: /* T_A: */ ++ bind_addr = var_smtp_bind_addr; ++ memset(sin, 0, sizeof(*sin)); ++ sin->sin_family = AF_INET; ++ salen = sizeof(*sin); ++ break; ++ } ++#ifdef HAS_SA_LEN ++ sa->sa_len = salen; ++#endif ++ if ((sock = socket(sa->sa_family, SOCK_STREAM, 0)) < 0) ++ msg_warn("%s: socket: %m", myname); ++ + /* + * Allow the sysadmin to specify the source address, for example, as "-o + * smtp_bind_address=x.x.x.x" in the master.cf file. + */ +- if (*var_smtp_bind_addr) { +- sin.sin_addr.s_addr = inet_addr(var_smtp_bind_addr); ++ if (*bind_addr) { ++#ifndef INET6 ++ struct sockaddr_in sin; ++ ++ memset(&sin, 0, sizeof(sin)); ++ sin.sin_family = AF_INET; ++#ifdef HAS_SA_LEN ++ sin.sin_len = sizeof(sin); ++#endif ++ sin.sin_addr.s_addr = inet_addr(bind_addr); + if (sin.sin_addr.s_addr == INADDR_NONE) + msg_fatal("%s: bad %s parameter: %s", + myname, VAR_SMTP_BIND_ADDR, var_smtp_bind_addr); +@@ -172,6 +226,25 @@ + msg_warn("%s: bind %s: %m", myname, inet_ntoa(sin.sin_addr)); + if (msg_verbose) + msg_info("%s: bind %s", myname, inet_ntoa(sin.sin_addr)); ++#else ++ char hbufl[NI_MAXHOST]; ++ struct addrinfo hints, *res; ++ ++ memset(&hints, 0, sizeof(hints)); ++ hints.ai_family = sa->sa_family; ++ hints.ai_socktype = SOCK_STREAM; ++ hints.ai_flags = AI_PASSIVE|AI_NUMERICHOST; ++ snprintf(hbufl, sizeof(hbufl)-1, "%s", bind_addr); ++ if (getaddrinfo(hbufl, NULL, &hints, &res) == 0) { ++ (void)getnameinfo(res->ai_addr, res->ai_addrlen, hbufl, ++ sizeof(hbufl), NULL, 0, NI_NUMERICHOST); ++ if (bind(sock, res->ai_addr, res->ai_addrlen) < 0) ++ msg_warn("%s: bind %s: %m", myname, hbufl); ++ freeaddrinfo(res); ++ if (msg_verbose) ++ msg_info("%s: bind %s", myname, hbufl); ++ } ++#endif + } + + /* +@@ -179,8 +252,17 @@ + * the mail appears to come from the "right" machine address. + */ + else if ((addr_list = own_inet_addr_list())->used == 1) { ++#ifndef INET6 ++ struct sockaddr_in sin; ++ unsigned long inaddr; /*XXX BAD!*/ ++ ++ memset(&sin, 0, sizeof(sin)); ++ sin.sin_family = AF_INET; ++#ifdef HAS_SA_LEN ++ sin.sin_len = sizeof(sin); ++#endif + memcpy((char *) &sin.sin_addr, addr_list->addrs, sizeof(sin.sin_addr)); +- inaddr = ntohl(sin.sin_addr.s_addr); ++ inaddr = (unsigned long)ntohl(sin.sin_addr.s_addr); + if (!IN_CLASSA(inaddr) + || !(((inaddr & IN_CLASSA_NET) >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET)) { + if (bind(sock, (struct sockaddr *) & sin, sizeof(sin)) < 0) +@@ -188,30 +270,85 @@ + if (msg_verbose) + msg_info("%s: bind %s", myname, inet_ntoa(sin.sin_addr)); + } ++#else ++ char hbufl[NI_MAXHOST]; ++ struct addrinfo hints, *res = NULL, *loopback = NULL; ++ ++ memset(&hints, 0, sizeof(hints)); ++ hints.ai_family = sa->sa_family; ++ hints.ai_socktype = SOCK_STREAM; ++ if (getaddrinfo(NULL, "0", &hints, &loopback) != 0) ++ loopback = NULL; ++ ++ /* ++ * getnameinfo -> getaddrinfo loop is here so that we can ++ * get rid of port. ++ */ ++ (void)getnameinfo((struct sockaddr *)addr_list->addrs, SA_LEN((struct sockaddr *)addr_list->addrs), ++ hbufl, sizeof(hbufl), NULL, 0, NI_NUMERICHOST); ++ hbufl[sizeof(hbufl)-1] = 0; ++ memset(&hints, 0, sizeof(hints)); ++ hints.ai_family = sa->sa_family; ++ hints.ai_socktype = SOCK_STREAM; ++ hints.ai_flags = AI_PASSIVE|AI_NUMERICHOST; ++ if (getaddrinfo(hbufl, NULL, &hints, &res) == 0 && ++ !(res->ai_addrlen == loopback->ai_addrlen && ++ memcmp(res->ai_addr, loopback->ai_addr, res->ai_addrlen) == 0)) { ++ if (bind(sock, res->ai_addr, res->ai_addrlen) < 0) ++ msg_warn("%s: bind %s: %m", myname, hbufl); ++ if (msg_verbose) ++ msg_info("%s: bind %s", myname, hbufl); ++ } ++ if (res) ++ freeaddrinfo(res); ++ if (loopback) ++ freeaddrinfo(loopback); ++#endif + } + + /* + * Connect to the SMTP server. + */ +- sin.sin_port = port; +- memcpy((char *) &sin.sin_addr, addr->data, sizeof(sin.sin_addr)); ++ switch (addr->type) { ++#ifdef INET6 ++ case T_AAAA: ++ /* XXX scope unfriendly */ ++ memset(sin6, 0, sizeof(*sin6)); ++ sin6->sin6_port = port; ++ sin6->sin6_family = AF_INET6; ++ salen = sizeof(*sin6); ++ memcpy(&sin6->sin6_addr, addr->data, sizeof(sin6->sin6_addr)); ++ inet_ntop(AF_INET6, &sin6->sin6_addr, hbuf, sizeof(hbuf)); ++ break; ++#endif ++ default: /* T_A */ ++ memset(sin, 0, sizeof(*sin)); ++ sin->sin_port = port; ++ sin->sin_family = AF_INET; ++ salen = sizeof(*sin); ++ memcpy(&sin->sin_addr, addr->data, sizeof(sin->sin_addr)); ++ inet_ntop(AF_INET, &sin->sin_addr, hbuf, sizeof(hbuf)); ++ break; ++ } ++#ifdef HAS_SA_LEN ++ sa->sa_len = salen; ++#endif + + if (msg_verbose) + msg_info("%s: trying: %s[%s] port %d...", +- myname, addr->name, inet_ntoa(sin.sin_addr), ntohs(port)); ++ myname, addr->name, hbuf, ntohs(port)); + if (var_smtp_conn_tmout > 0) { + non_blocking(sock, NON_BLOCKING); +- conn_stat = timed_connect(sock, (struct sockaddr *) & sin, +- sizeof(sin), var_smtp_conn_tmout); ++ conn_stat = timed_connect(sock, sa, salen, var_smtp_conn_tmout); + saved_errno = errno; + non_blocking(sock, BLOCKING); + errno = saved_errno; + } else { +- conn_stat = connect(sock, (struct sockaddr *) & sin, sizeof(sin)); ++ conn_stat = connect(sock, sa, salen); + } + if (conn_stat < 0) { + vstring_sprintf(why, "connect to %s[%s]: %m", +- addr->name, inet_ntoa(sin.sin_addr)); ++ addr->name, hbuf); + smtp_errno = SMTP_RETRY; + close(sock); + return (0); +@@ -221,8 +358,8 @@ + * Skip this host if it takes no action within some time limit. + */ + if (read_wait(sock, var_smtp_helo_tmout) < 0) { +- vstring_sprintf(why, "connect to %s[%s]: read timeout", +- addr->name, inet_ntoa(sin.sin_addr)); ++ vstring_sprintf(why, "connect to %s [%s]: read timeout", ++ addr->name, hbuf); + smtp_errno = SMTP_RETRY; + close(sock); + return (0); +@@ -233,8 +370,8 @@ + */ + stream = vstream_fdopen(sock, O_RDWR); + if ((ch = VSTREAM_GETC(stream)) == VSTREAM_EOF) { +- vstring_sprintf(why, "connect to %s[%s]: server dropped connection", +- addr->name, inet_ntoa(sin.sin_addr)); ++ vstring_sprintf(why, "connect to %s [%s]: server dropped connection", ++ addr->name, hbuf); + smtp_errno = SMTP_RETRY; + vstream_fclose(stream); + return (0); +@@ -246,7 +383,7 @@ + */ + if (ch == '4' && var_smtp_skip_4xx_greeting) { + vstring_sprintf(why, "connect to %s[%s]: server refused mail service", +- addr->name, inet_ntoa(sin.sin_addr)); ++ addr->name, hbuf); + smtp_errno = SMTP_RETRY; + vstream_fclose(stream); + return (0); +@@ -257,12 +394,12 @@ + */ + if (ch == '5' && var_smtp_skip_5xx_greeting) { + vstring_sprintf(why, "connect to %s[%s]: server refused mail service", +- addr->name, inet_ntoa(sin.sin_addr)); ++ addr->name, hbuf); + smtp_errno = SMTP_RETRY; + vstream_fclose(stream); + return (0); + } +- return (smtp_session_alloc(stream, addr->name, inet_ntoa(sin.sin_addr))); ++ return (smtp_session_alloc(dest, stream, addr->name, hbuf)); + } + + /* smtp_connect_host - direct connection to host */ +@@ -272,7 +409,7 @@ + SMTP_SESSION *session = 0; + DNS_RR *addr_list; + DNS_RR *addr; +- ++ + /* + * Try each address in the specified order until we find one that works. + * The addresses belong to the same A record, so we have no information +@@ -280,7 +417,7 @@ + */ + addr_list = smtp_host_addr(host, why); + for (addr = addr_list; addr; addr = addr->next) { +- if ((session = smtp_connect_addr(addr, port, why)) != 0) { ++ if ((session = smtp_connect_addr(host, addr, port, why)) != 0) { + session->best = 1; + break; + } +@@ -309,7 +446,7 @@ + */ + addr_list = smtp_domain_addr(name, why, found_myself); + for (addr = addr_list; addr; addr = addr->next) { +- if ((session = smtp_connect_addr(addr, port, why)) != 0) { ++ if ((session = smtp_connect_addr(name, addr, port, why)) != 0) { + session->best = (addr->pref == addr_list->pref); + break; + } +@@ -379,6 +516,7 @@ + msg_fatal("unknown service: %s/%s", service, protocol); + *portp = sp->s_port; + } ++ + return (buf); + } + +diff -Pur postfix-1.1.11-20020613-orig/src/smtp/smtp_proto.c postfix-1.1.11-20020613/src/smtp/smtp_proto.c +--- postfix-1.1.11-20020613-orig/src/smtp/smtp_proto.c Sat Jun 1 15:07:27 2002 ++++ postfix-1.1.11-20020613/src/smtp/smtp_proto.c Wed Jun 26 15:26:48 2002 +@@ -103,6 +103,7 @@ + #include <quote_821_local.h> + #include <mail_proto.h> + #include <mime_state.h> ++#include <pfixtls.h> + + /* Application-specific. */ + +@@ -170,6 +171,8 @@ + char *words; + char *word; + int n; ++ int oldfeatures; ++ int rval; + + /* + * Prepare for disaster. +@@ -231,7 +234,8 @@ + session->namaddr, + translit(resp->str, "\n", " "))); + } +- ++ if (var_smtp_always_ehlo) ++ state->features |= SMTP_FEATURE_ESMTP; + /* + * 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 +@@ -243,6 +247,7 @@ + * MicroSoft implemented AUTH based on an old draft. + */ + 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) +@@ -259,6 +264,8 @@ + state->size_limit = off_cvt_string(word); + } + } ++ else if (strcasecmp(word, "STARTTLS") == 0) ++ state->features |= SMTP_FEATURE_STARTTLS; + #ifdef USE_SASL_AUTH + else if (var_smtp_sasl_enable && strcasecmp(word, "AUTH") == 0) + smtp_sasl_helo_auth(state, words); +@@ -276,6 +283,128 @@ + msg_info("server features: 0x%x size %.0f", + state->features, (double) state->size_limit); + ++#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: not offered")); ++ } ++ if ((session->tls_enforce_tls) && !pfixtls_clientengine) { ++ /* ++ * We would like to start client TLS, but our own TLS-engine is ++ * not running. ++ */ ++ return (smtp_site_fail(state, 450, ++ "Could not start TLS: our TLS-engine not running")); ++ } ++ if ((state->features & SMTP_FEATURE_STARTTLS) && ++ ((session->tls_use_tls && pfixtls_clientengine) || ++ (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->stream, ++ var_smtp_starttls_tmout, ++ session->tls_enforce_peername, ++ session->host, ++ &(session->tls_info))) ++ return (smtp_site_fail(state, 450, ++ "Could not start TLS: client failure")); ++ ++ ++ /* ++ * Now the connection is established and maybe we do have a ++ * validated cert with a CommonName in it. ++ * In enforce_peername state, the handshake would already have ++ * been terminated so the check here is for logging only! ++ */ ++ if (session->tls_info.peer_CN != NULL) { ++ if (!session->tls_info.peer_verified) { ++ msg_info("Peer certficate could not be verified"); ++ if (session->tls_enforce_tls) { ++ pfixtls_stop_clienttls(session->stream, ++ var_smtp_starttls_tmout, 1, ++ &(session->tls_info)); ++ return(smtp_site_fail(state, 450, "TLS-failure: Could not verify certificate")); ++ } ++ } ++ } else if (session->tls_enforce_tls) { ++ pfixtls_stop_clienttls(session->stream, ++ var_smtp_starttls_tmout, 1, ++ &(session->tls_info)); ++ 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; ++#ifdef USE_SASL_AUTH ++ if (state->sasl_mechanism_list) { ++ myfree(state->sasl_mechanism_list); ++ state->sasl_mechanism_list = 0; ++ } ++#endif ++ 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; ++#ifdef USE_SASL_AUTH ++ else if (var_smtp_sasl_enable && ++ strcasecmp(word, "AUTH") == 0) ++ smtp_sasl_helo_auth(state, words); ++#endif ++ } ++ } ++ /* ++ * 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 + #ifdef USE_SASL_AUTH + if (var_smtp_sasl_enable && (state->features & SMTP_FEATURE_AUTH)) + return (smtp_sasl_helo_login(state)); +diff -Pur postfix-1.1.11-20020613-orig/src/smtp/smtp_session.c postfix-1.1.11-20020613/src/smtp/smtp_session.c +--- postfix-1.1.11-20020613-orig/src/smtp/smtp_session.c Mon Nov 20 19:06:05 2000 ++++ postfix-1.1.11-20020613/src/smtp/smtp_session.c Wed Jun 26 15:26:48 2002 +@@ -42,15 +42,42 @@ + #include <vstream.h> + #include <stringops.h> + ++#include <mail_params.h> ++#include <maps.h> ++#include <pfixtls.h> ++ + /* Application-specific. */ + + #include "smtp.h" + ++#ifdef HAS_SSL ++/* static lists */ ++static MAPS *tls_per_site; ++ ++/* smtp_tls_list_init - initialize lists */ ++ ++void smtp_tls_list_init(void) ++{ ++ tls_per_site = maps_create(VAR_SMTP_TLS_PER_SITE, var_smtp_tls_per_site, ++ DICT_FLAG_LOCK); ++} ++#endif ++ + /* smtp_session_alloc - allocate and initialize SMTP_SESSION structure */ + +-SMTP_SESSION *smtp_session_alloc(VSTREAM *stream, char *host, char *addr) ++SMTP_SESSION *smtp_session_alloc(char *dest, VSTREAM *stream, char *host, char *addr) + { + SMTP_SESSION *session; ++ const char *lookup; ++ char *lookup_key; ++ int host_dont_use = 0; ++ int host_use = 0; ++ int host_enforce = 0; ++ int host_enforce_peername = 0; ++ int recipient_dont_use = 0; ++ int recipient_use = 0; ++ int recipient_enforce = 0; ++ int recipient_enforce_peername = 0; + + session = (SMTP_SESSION *) mymalloc(sizeof(*session)); + session->stream = stream; +@@ -58,6 +85,61 @@ + session->addr = mystrdup(addr); + session->namaddr = concatenate(host, "[", addr, "]", (char *) 0); + session->best = 1; ++ session->tls_use_tls = session->tls_enforce_tls = 0; ++ session->tls_enforce_peername = 0; ++#ifdef HAS_SSL ++ lookup_key = lowercase(mystrdup(host)); ++ if (lookup = maps_find(tls_per_site, lookup_key, 0)) { ++ if (!strcasecmp(lookup, "NONE")) ++ host_dont_use = 1; ++ else if (!strcasecmp(lookup, "MAY")) ++ host_use = 1; ++ else if (!strcasecmp(lookup, "MUST")) ++ host_enforce = host_enforce_peername = 1; ++ else if (!strcasecmp(lookup, "MUST_NOPEERMATCH")) ++ host_enforce = 1; ++ else ++ msg_warn("Unknown TLS state for receiving host %s: '%s', using default policy", session->host, lookup); ++ } ++ myfree(lookup_key); ++ lookup_key = lowercase(mystrdup(dest)); ++ if (lookup = maps_find(tls_per_site, dest, 0)) { ++ if (!strcasecmp(lookup, "NONE")) ++ recipient_dont_use = 1; ++ else if (!strcasecmp(lookup, "MAY")) ++ recipient_use = 1; ++ else if (!strcasecmp(lookup, "MUST")) ++ recipient_enforce = recipient_enforce_peername = 1; ++ else if (!strcasecmp(lookup, "MUST_NOPEERMATCH")) ++ recipient_enforce = 1; ++ else ++ msg_warn("Unknown TLS state for recipient domain %s: '%s', using default policy", dest, lookup); ++ } ++ myfree(lookup_key); ++ ++ if ((var_smtp_enforce_tls && !host_dont_use && !recipient_dont_use) || host_enforce || ++ recipient_enforce) ++ session->tls_enforce_tls = session->tls_use_tls = 1; ++ ++ /* ++ * Set up peername checking. We want to make sure that a MUST* entry in ++ * the tls_per_site table always has precedence. MUST always must lead to ++ * a peername check, MUST_NOPEERMATCH must always disable it. Only when ++ * no explicit setting has been found, the default will be used. ++ * There is the case left, that both "host" and "recipient" settings ++ * conflict. In this case, the "host" setting wins. ++ */ ++ if (host_enforce && host_enforce_peername) ++ session->tls_enforce_peername = 1; ++ else if (recipient_enforce && recipient_enforce_peername) ++ session->tls_enforce_peername = 1; ++ else if (var_smtp_enforce_tls && var_smtp_tls_enforce_peername) ++ session->tls_enforce_peername = 1; ++ ++ else if ((var_smtp_use_tls && !host_dont_use && !recipient_dont_use) || host_use || recipient_use) ++ session->tls_use_tls = 1; ++#endif ++ session->tls_info = tls_info_zero; + return (session); + } + +@@ -65,6 +147,11 @@ + + void smtp_session_free(SMTP_SESSION *session) + { ++#ifdef HAS_SSL ++ vstream_fflush(session->stream); ++ pfixtls_stop_clienttls(session->stream, var_smtp_starttls_tmout, 0, ++ &(session->tls_info)); ++#endif + vstream_fclose(session->stream); + myfree(session->host); + myfree(session->addr); +diff -Pur postfix-1.1.11-20020613-orig/src/smtp/smtp_unalias.c postfix-1.1.11-20020613/src/smtp/smtp_unalias.c +--- postfix-1.1.11-20020613-orig/src/smtp/smtp_unalias.c Thu Sep 28 19:06:09 2000 ++++ postfix-1.1.11-20020613/src/smtp/smtp_unalias.c Wed Jun 26 15:26:48 2002 +@@ -86,7 +86,11 @@ + if ((result = htable_find(cache, name)) == 0) { + fqdn = vstring_alloc(10); + if (dns_lookup_types(name, smtp_unalias_flags, (DNS_RR **) 0, +- fqdn, (VSTRING *) 0, T_MX, T_A, 0) != DNS_OK) ++ fqdn, (VSTRING *) 0, T_MX, T_A, ++#ifdef INET6 ++ T_AAAA, ++#endif ++ 0) != DNS_OK) + vstring_strcpy(fqdn, name); + htable_enter(cache, name, result = vstring_export(fqdn)); + } +diff -Pur postfix-1.1.11-20020613-orig/src/smtpd/Makefile.in postfix-1.1.11-20020613/src/smtpd/Makefile.in +--- postfix-1.1.11-20020613-orig/src/smtpd/Makefile.in Tue Jun 11 03:13:44 2002 ++++ postfix-1.1.11-20020613/src/smtpd/Makefile.in Wed Jun 26 15:26:48 2002 +@@ -134,6 +134,7 @@ + smtpd.o: ../../include/quote_flags.h + smtpd.o: ../../include/lex_822.h + smtpd.o: ../../include/mail_server.h ++smtpd.o: ../../include/pfixtls.h + smtpd.o: smtpd_token.h + smtpd.o: smtpd.h + smtpd.o: smtpd_check.h +@@ -162,6 +163,7 @@ + smtpd_chat.o: ../../include/cleanup_user.h + smtpd_chat.o: ../../include/mail_error.h + smtpd_chat.o: ../../include/name_mask.h ++smtpd_chat.o: ../../include/pfixtls.h + smtpd_chat.o: smtpd.h + smtpd_chat.o: ../../include/mail_stream.h + smtpd_chat.o: smtpd_chat.h +@@ -197,6 +199,7 @@ + smtpd_check.o: ../../include/mail_addr_find.h + smtpd_check.o: ../../include/match_parent_style.h + smtpd_check.o: ../../include/strip_addr.h ++smtpd_check.o: ../../include/pfixtls.h + smtpd_check.o: smtpd.h + smtpd_check.o: ../../include/mail_stream.h + smtpd_check.o: smtpd_sasl_glue.h +@@ -213,6 +216,7 @@ + smtpd_peer.o: ../../include/vstream.h + smtpd_peer.o: ../../include/argv.h + smtpd_peer.o: ../../include/mail_stream.h ++smtpd_peer.o: ../../include/pfixtls.h + smtpd_sasl_glue.o: smtpd_sasl_glue.c + smtpd_sasl_glue.o: ../../include/sys_defs.h + smtpd_sasl_glue.o: ../../include/msg.h +@@ -266,6 +270,7 @@ + smtpd_state.o: ../../include/vstring.h + smtpd_state.o: ../../include/argv.h + smtpd_state.o: ../../include/mail_stream.h ++smtpd_state.o: ../../include/pfixtls.h + smtpd_state.o: smtpd_chat.h + smtpd_state.o: smtpd_sasl_glue.h + smtpd_token.o: smtpd_token.c +@@ -275,3 +280,4 @@ + smtpd_token.o: smtpd_token.h + smtpd_token.o: ../../include/vstring.h + smtpd_token.o: ../../include/vbuf.h ++smtpd_token.o: ../../include/pfixtls.h +diff -Pur postfix-1.1.11-20020613-orig/src/smtpd/smtpd.c postfix-1.1.11-20020613/src/smtpd/smtpd.c +--- postfix-1.1.11-20020613-orig/src/smtpd/smtpd.c Tue May 28 19:08:56 2002 ++++ postfix-1.1.11-20020613/src/smtpd/smtpd.c Wed Jun 26 15:26:48 2002 +@@ -314,6 +314,7 @@ + #include <string_list.h> + #include <quote_822_local.h> + #include <lex_822.h> ++#include <pfixtls.h> + + /* Single-threaded server skeleton. */ + +@@ -338,6 +339,7 @@ + */ + int var_smtpd_rcpt_limit; + int var_smtpd_tmout; ++char *var_relay_ccerts; + int var_smtpd_soft_erlim; + int var_smtpd_hard_erlim; + int var_queue_minfree; /* XXX use off_t */ +@@ -385,6 +387,15 @@ + char *var_smtpd_noop_cmds; + char *var_smtpd_null_key; + int var_smtpd_hist_thrsh; ++int var_smtpd_starttls_tmout; ++int var_smtpd_tls_wrappermode; ++int var_smtpd_use_tls; ++int var_smtpd_enforce_tls; ++int var_smtpd_tls_auth_only; ++int var_smtpd_tls_ask_ccert; ++int var_smtpd_tls_req_ccert; ++int var_smtpd_tls_ccert_vd; ++int var_smtpd_tls_received_header; + + /* + * Silly little macros. +@@ -489,11 +500,21 @@ + if (var_disable_vrfy_cmd == 0) + smtpd_chat_reply(state, "250-VRFY"); + 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 + #ifdef USE_SASL_AUTH + if (var_smtpd_sasl_enable) { ++#ifdef HAS_SSL ++ if (!state->tls_auth_only || state->tls_active) { ++#endif + smtpd_chat_reply(state, "250-AUTH %s", state->sasl_mechanism_list); + if (var_broken_auth_clients) + smtpd_chat_reply(state, "250-AUTH=%s", state->sasl_mechanism_list); ++#ifdef HAS_SSL ++ } ++#endif + } + #endif + smtpd_chat_reply(state, "250-%s", VERP_CMD); +@@ -918,11 +939,76 @@ + state->rcpt_count = 0; + } + ++/* CN_sanitize - make sure, the CN-string is well behaved */ ++ ++static void CN_sanitize(char *CNstring) ++{ ++ int i; ++ int len; ++ int parencount; ++ ++ /* ++ * The information included in the CN (CommonName) of the peer and its ++ * issuer can be included into the Received: header line. The characters ++ * allowed as well as comment nesting are limited by RFC822. ++ */ ++ ++ len = strlen(CNstring); ++ /* ++ * The Received: header can only contain characters. Make sure that only ++ * acceptable characters are printed. Maybe we could allow more, but ++ * not everything makes sense inside a CommonName. ++ */ ++ for (i = 0; i < len; i++) ++ if (!((CNstring[i] >= 'A') && (CNstring[i] <='Z')) && ++ !((CNstring[i] >= 'a') && (CNstring[i] <='z')) && ++ !((CNstring[i] >= '0') && (CNstring[i] <='9')) && ++ (CNstring[i] != '(') && (CNstring[i] != ')') && ++ (CNstring[i] != '[') && (CNstring[i] != ']') && ++ (CNstring[i] != '{') && (CNstring[i] != '}') && ++ (CNstring[i] != '<') && (CNstring[i] != '>') && ++ (CNstring[i] != '?') && (CNstring[i] != '!') && ++ (CNstring[i] != ';') && (CNstring[i] != ':') && ++ (CNstring[i] != '"') && (CNstring[i] != '\'') && ++ (CNstring[i] != '/') && (CNstring[i] != '|') && ++ (CNstring[i] != '+') && (CNstring[i] != '&') && ++ (CNstring[i] != '~') && (CNstring[i] != '@') && ++ (CNstring[i] != '#') && (CNstring[i] != '$') && ++ (CNstring[i] != '%') && (CNstring[i] != '&') && ++ (CNstring[i] != '^') && (CNstring[i] != '*') && ++ (CNstring[i] != '_') && (CNstring[i] != '-') && ++ (CNstring[i] != '.') && (CNstring[i] != ' ')) ++ CNstring[i] = '?'; ++ ++ /* ++ * This information will go into the Received: header inside a comment. ++ * Since comments can be nested, parentheses '(' and ')' must match. ++ */ ++ parencount = 0; ++ for (i = 0; i < len; i++) { ++ if (CNstring[i] == '(') ++ parencount++; ++ else if (CNstring[i] == ')') ++ parencount--; ++ } ++ /* ++ * The necessary condition is violated. Do YOU know, where to correct? ++ * I don't know, so I will practically remove all parentheses. ++ */ ++ if (parencount != 0) { ++ for (i = 0; i < len; i++) ++ if ((CNstring[i] == '(') || (CNstring[i] == ')')) ++ CNstring[i] = '/'; ++ } ++} ++ + /* data_cmd - process DATA command */ + + static int data_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *unused_argv) + { + char *start; ++ char *peer_CN; ++ char *issuer_CN; + int len; + int curr_rec_type; + int prev_rec_type; +@@ -961,6 +1047,35 @@ + "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, ++ "\t(using %s with cipher %s (%d/%d bits))", ++ state->tls_info.protocol, state->tls_info.cipher_name, ++ state->tls_info.cipher_usebits, ++ state->tls_info.cipher_algbits); ++ if (state->tls_info.peer_CN) { ++ peer_CN = mystrdup(state->tls_info.peer_CN); ++ CN_sanitize(peer_CN); ++ issuer_CN = mystrdup(state->tls_info.issuer_CN); ++ CN_sanitize(issuer_CN); ++ if (state->tls_info.peer_verified) ++ rec_fprintf(state->cleanup, REC_TYPE_NORM, ++ "\t(Client CN \"%s\", Issuer \"%s\" (verified OK))", ++ peer_CN, issuer_CN); ++ else ++ rec_fprintf(state->cleanup, REC_TYPE_NORM, ++ "\t(Client CN \"%s\", Issuer \"%s\" (not verified))", ++ peer_CN, issuer_CN); ++ myfree(issuer_CN); ++ myfree(peer_CN); ++ } ++ else if (var_smtpd_tls_ask_ccert) ++ rec_fprintf(state->cleanup, REC_TYPE_NORM, ++ "\t(Client did not present a certificate)"); ++ else ++ rec_fprintf(state->cleanup, REC_TYPE_NORM, ++ "\t(No client certificate requested)"); ++ } + if (state->rcpt_count == 1 && state->recipient) { + rec_fprintf(state->cleanup, REC_TYPE_NORM, + "\tby %s (%s) with %s id %s", +@@ -1310,6 +1425,77 @@ + } + } + ++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"); ++ return (0); ++ } ++ smtpd_chat_reply(state, "220 Ready to start TLS"); ++ vstream_fflush(state->client); ++ /* ++ * When deciding about continuing the handshake, we will stop when a ++ * client certificate was _required_ and none was presented or the ++ * verification failed. This however does only make sense when TLS is ++ * enforced. Otherwise we would happily perform perform the SMTP ++ * transaction without any STARTTLS at all! So only have the handshake ++ * fail when TLS is also enforced. ++ */ ++ if (pfixtls_start_servertls(state->client, var_smtpd_starttls_tmout, ++ state->name, state->addr, &(state->tls_info), ++ (var_smtpd_tls_req_ccert && state->tls_enforce_tls))) { ++ /* ++ * 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_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(state->client, var_smtpd_starttls_tmout, ++ failure, &(state->tls_info)); ++#endif ++ state->tls_active = 0; ++} ++ + /* + * The table of all SMTP commands that we know. Set the junk limit flag on + * any command that can be repeated an arbitrary number of times without +@@ -1328,6 +1514,10 @@ + "HELO", helo_cmd, SMTPD_CMD_FLAG_LIMIT, + "EHLO", ehlo_cmd, SMTPD_CMD_FLAG_LIMIT, + ++#ifdef HAS_SSL ++ "STARTTLS", starttls_cmd, 0, ++#endif ++ + #ifdef USE_SASL_AUTH + "AUTH", smtpd_sasl_auth_cmd, 0, + #endif +@@ -1438,9 +1628,28 @@ + 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) ++ 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->flags & SMTPD_CMD_FLAG_LIMIT) + && state->junk_cmds++ > var_smtpd_junk_cmd_limit) + state->error_count++; +@@ -1464,6 +1673,7 @@ + * Cleanup whatever information the client gave us during the SMTP + * dialog. + */ ++ tls_reset(state); + helo_reset(state); + #ifdef USE_SASL_AUTH + if (var_smtpd_sasl_enable) +@@ -1496,6 +1706,46 @@ + * machines. + */ + smtpd_state_init(&state, stream); ++#ifdef HAS_SSL ++ if (SMTPD_STAND_ALONE((&state))) { ++ state.tls_use_tls = 0; ++ state.tls_enforce_tls = 0; ++ state.tls_auth_only = 0; ++ } ++ else { ++ state.tls_use_tls = var_smtpd_use_tls | var_smtpd_enforce_tls; ++ state.tls_enforce_tls = var_smtpd_enforce_tls; ++ if (var_smtpd_tls_wrappermode) { ++ /* ++ * TLS has been set to wrapper mode, meaning that we run on a ++ * seperate port and we must switch to TLS layer before actually ++ * performing the SMTP protocol. This implies enforce-mode. ++ */ ++ state.tls_use_tls = state.tls_enforce_tls = 1; ++ if (pfixtls_start_servertls(state.client, var_smtpd_starttls_tmout, ++ state.name, state.addr, &state.tls_info, ++ var_smtpd_tls_req_ccert)) { ++ /* ++ * 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; ++ } ++ state.tls_active = 1; ++ } ++ if (var_smtpd_tls_auth_only || state.tls_enforce_tls) ++ state.tls_auth_only = 1; ++ } ++#else ++ state.tls_use_tls = 0; ++ state.tls_enforce_tls = 0; ++ state.tls_auth_only = 0; ++#endif ++ ++ /* ++ * Provide the SMTP service. ++ */ + + /* + * See if we need to turn on verbose logging for this client. +@@ -1513,10 +1763,6 @@ + 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); + + /* +@@ -1542,7 +1788,6 @@ + + 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. +@@ -1558,6 +1803,12 @@ + msg_warn("%s is true, but SASL support is not compiled in", + VAR_SMTPD_SASL_ENABLE); + #endif ++ ++#ifdef HAS_SSL ++ if (var_smtpd_use_tls || var_smtpd_enforce_tls || var_smtpd_tls_wrappermode) ++ pfixtls_init_serverengine(var_smtpd_tls_ccert_vd, ++ var_smtpd_tls_ask_ccert); ++#endif + } + + /* main - the main program */ +@@ -1580,6 +1831,7 @@ + VAR_NON_FQDN_CODE, DEF_NON_FQDN_CODE, &var_non_fqdn_code, 0, 0, + VAR_SMTPD_JUNK_CMD, DEF_SMTPD_JUNK_CMD, &var_smtpd_junk_cmd_limit, 1, 0, + VAR_SMTPD_HIST_THRSH, DEF_SMTPD_HIST_THRSH, &var_smtpd_hist_thrsh, 1, 0, ++ VAR_SMTPD_TLS_CCERT_VD, DEF_SMTPD_TLS_CCERT_VD, &var_smtpd_tls_ccert_vd, 0, 0, + 0, + }; + static CONFIG_TIME_TABLE time_table[] = { +@@ -1595,6 +1847,13 @@ + VAR_ALLOW_UNTRUST_ROUTE, DEF_ALLOW_UNTRUST_ROUTE, &var_allow_untrust_route, + VAR_SMTPD_SASL_ENABLE, DEF_SMTPD_SASL_ENABLE, &var_smtpd_sasl_enable, + VAR_BROKEN_AUTH_CLNTS, DEF_BROKEN_AUTH_CLNTS, &var_broken_auth_clients, ++ VAR_SMTPD_TLS_WRAPPER, DEF_SMTPD_TLS_WRAPPER, &var_smtpd_tls_wrappermode, ++ 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_AUTH_ONLY, DEF_SMTPD_TLS_AUTH_ONLY, &var_smtpd_tls_auth_only, ++ 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[] = { +@@ -1623,6 +1882,7 @@ + VAR_SMTPD_SND_AUTH_MAPS, DEF_SMTPD_SND_AUTH_MAPS, &var_smtpd_snd_auth_maps, 0, 0, + VAR_SMTPD_NOOP_CMDS, DEF_SMTPD_NOOP_CMDS, &var_smtpd_noop_cmds, 0, 0, + VAR_SMTPD_NULL_KEY, DEF_SMTPD_NULL_KEY, &var_smtpd_null_key, 0, 0, ++ VAR_RELAY_CCERTS, DEF_RELAY_CCERTS, &var_relay_ccerts, 0, 0, + 0, + }; + +@@ -1638,3 +1898,4 @@ + MAIL_SERVER_PRE_ACCEPT, pre_accept, + 0); + } ++ +diff -Pur postfix-1.1.11-20020613-orig/src/smtpd/smtpd.h postfix-1.1.11-20020613/src/smtpd/smtpd.h +--- postfix-1.1.11-20020613-orig/src/smtpd/smtpd.h Fri Mar 29 22:10:13 2002 ++++ postfix-1.1.11-20020613/src/smtpd/smtpd.h Wed Jun 26 15:26:48 2002 +@@ -32,6 +32,7 @@ + * Global library. + */ + #include <mail_stream.h> ++#include <pfixtls.h> + + /* + * Variables that keep track of conversation state. There is only one SMTP +@@ -81,6 +82,11 @@ + VSTRING *sasl_decoded; + #endif + int warn_if_reject; ++ int tls_active; ++ int tls_use_tls; ++ int tls_enforce_tls; ++ int tls_auth_only; ++ tls_info_t tls_info; + } SMTPD_STATE; + + extern void smtpd_state_init(SMTPD_STATE *, VSTREAM *); +diff -Pur postfix-1.1.11-20020613-orig/src/smtpd/smtpd_check.c postfix-1.1.11-20020613/src/smtpd/smtpd_check.c +--- postfix-1.1.11-20020613-orig/src/smtpd/smtpd_check.c Mon Jun 10 22:14:39 2002 ++++ postfix-1.1.11-20020613/src/smtpd/smtpd_check.c Wed Jun 26 15:26:48 2002 +@@ -280,6 +280,7 @@ + + #include <namadr_list.h> + #include <domain_list.h> ++#include <string_list.h> + #include <mail_params.h> + #include <canon_addr.h> + #include <resolve_clnt.h> +@@ -345,6 +346,9 @@ + static DOMAIN_LIST *relay_domains; + static NAMADR_LIST *mynetworks; + static NAMADR_LIST *perm_mx_networks; ++#ifdef HAS_SSL ++static MAPS *relay_ccerts; ++#endif + + /* + * How to do parent domain wildcard matching, if any. +@@ -530,6 +534,10 @@ + perm_mx_networks = + namadr_list_init(match_parent_style(VAR_PERM_MX_NETWORKS), + var_perm_mx_networks); ++#ifdef HAS_SSL ++ relay_ccerts = maps_create(VAR_RELAY_CCERTS, var_relay_ccerts, ++ DICT_FLAG_LOCK); ++#endif + + /* + * Pre-parse and pre-open the recipient maps. +@@ -932,7 +940,11 @@ + msg_info("%s: %s", myname, name); + + dns_status = dns_lookup_types(name, 0, (DNS_RR **) 0, (VSTRING *) 0, +- (VSTRING *) 0, T_A, T_MX, 0); ++ (VSTRING *) 0, T_A, T_MX, ++#ifdef INET6 ++ T_AAAA, ++#endif ++ 0); + if (dns_status != DNS_OK) + return (smtpd_check_reject(state, MAIL_ERROR_POLICY, + "%d <%s>: %s rejected: Host not found", +@@ -954,7 +966,11 @@ + msg_info("%s: %s", myname, name); + + dns_status = dns_lookup_types(name, 0, (DNS_RR **) 0, (VSTRING *) 0, +- (VSTRING *) 0, T_A, T_MX, 0); ++ (VSTRING *) 0, T_A, T_MX, ++#ifdef INET6 ++ T_AAAA, ++#endif ++ 0); + if (dns_status != DNS_OK) + return (smtpd_check_reject(state, MAIL_ERROR_POLICY, + "%d <%s>: %s rejected: Domain not found", +@@ -966,6 +982,36 @@ + + static int permit_auth_destination(SMTPD_STATE *state, char *recipient); + ++/* permit_tls_clientcerts - OK/DUNNO for message relaying */ ++ ++#ifdef HAS_SSL ++static int permit_tls_clientcerts(SMTPD_STATE *state, int permit_all_certs) ++{ ++ char *low_name; ++ const char *found; ++ ++ if (state->tls_info.peer_verified && permit_all_certs) { ++ if (msg_verbose) ++ msg_info("Relaying allowed for all verified client certificates"); ++ return(SMTPD_CHECK_OK); ++ } ++ ++ if (state->tls_info.peer_verified && state->tls_info.peer_fingerprint) { ++ low_name = lowercase(mystrdup(state->tls_info.peer_fingerprint)); ++ found = maps_find(relay_ccerts, low_name, DICT_FLAG_FIXED); ++ myfree(low_name); ++ if (found) { ++ if (msg_verbose) ++ msg_info("Relaying allowed for certified client: %s", found); ++ return (SMTPD_CHECK_OK); ++ } else if (msg_verbose) ++ msg_info("relay_clientcerts: No match for fingerprint '%s'", ++ state->tls_info.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, +@@ -1145,6 +1191,49 @@ + + static int has_my_addr(const char *host) + { ++#ifdef INET6 ++ char *myname = "has_my_addr"; ++ struct addrinfo hints, *res, *res0; ++ int error; ++ char hbuf[NI_MAXHOST]; ++ ++ if (msg_verbose) ++ msg_info("%s: host %s", myname, host); ++ ++ /* ++ * If we can't lookup the host, play safe and assume it is OK. ++ */ ++#define YUP 1 ++#define NOPE 0 ++ ++ memset(&hints, 0, sizeof(hints)); ++ hints.ai_family = PF_UNSPEC; ++ hints.ai_socktype = SOCK_DGRAM; ++ error = getaddrinfo(host, NULL, &hints, &res0); ++ if (error) { ++ if (msg_verbose) ++ msg_info("%s: host %s: %s", myname, host, gai_strerror(error)); ++ return (YUP); ++ } ++ for (res = res0; res; res = res->ai_next) { ++ if (msg_verbose) { ++ if (getnameinfo(res->ai_addr, res->ai_addrlen, hbuf, sizeof(hbuf), ++ NULL, 0, NI_NUMERICHOST)) { ++ strncpy(hbuf, "???", sizeof(hbuf)); ++ } ++ msg_info("%s: addr %s", myname, hbuf); ++ } ++ if (own_inet_addr(res->ai_addr)) { ++ freeaddrinfo(res0); ++ return (YUP); ++ } ++ } ++ freeaddrinfo(res0); ++ if (msg_verbose) ++ msg_info("%s: host %s: no match", myname, host); ++ ++ return (NOPE); ++#else + char *myname = "has_my_addr"; + struct in_addr addr; + char **cpp; +@@ -1180,6 +1269,7 @@ + msg_info("%s: host %s: no match", myname, host); + + return (NOPE); ++#endif + } + + /* i_am_mx - is this machine listed as MX relay */ +@@ -1834,7 +1924,7 @@ + static int reject_maps_rbl(SMTPD_STATE *state) + { + char *myname = "reject_maps_rbl"; +- ARGV *octets = argv_split(state->addr, "."); ++ ARGV *octets; + VSTRING *query = vstring_alloc(100); + char *saved_domains = mystrdup(var_maps_rbl_domains); + char *bp = saved_domains; +@@ -1846,17 +1936,29 @@ + int dns_status = DNS_FAIL; + int i; + int result; ++ struct in_addr a; + VSTRING *why; + + if (msg_verbose) + msg_info("%s: %s", myname, state->addr); + +- /* +- * IPv4 only for now +- */ +-#ifdef INET6 ++#ifndef INET6 ++ /* IPv4 only for now */ + if (inet_pton(AF_INET, state->addr, &a) != 1) + return SMTPD_CHECK_DUNNO; ++ octets = argv_split(state->addr, "."); ++#else ++ /* IPv4 and IPv6-mapped IPv4 only for now */ ++ if (inet_pton(AF_INET, state->addr, &a) == 1) ++ octets = argv_split(state->addr, "."); ++ else { ++ struct in6_addr a6; ++ if (inet_pton(AF_INET6, state->addr, &a6) != 1) ++ return SMTPD_CHECK_DUNNO; ++ if (!IN6_IS_ADDR_V4MAPPED(&a6) || (strrchr(state->addr,':') == NULL)) ++ return SMTPD_CHECK_DUNNO; ++ octets = argv_split(strrchr(state->addr,':')+1, "."); ++ } + #endif + + /* +@@ -2154,6 +2256,12 @@ + #else + msg_warn("restriction `%s' ignored: no SASL support", name); + #endif ++#ifdef HAS_SSL ++ } else if (strcasecmp(name, PERMIT_TLS_ALL_CLIENTCERTS) == 0) { ++ status = permit_tls_clientcerts(state, 1); ++ } else if (strcasecmp(name, PERMIT_TLS_CLIENTCERTS) == 0) { ++ status = permit_tls_clientcerts(state, 0); ++#endif + } else if (strcasecmp(name, REJECT_UNKNOWN_RCPTDOM) == 0) { + if (state->recipient) + status = reject_unknown_address(state, state->recipient, +@@ -2588,6 +2696,7 @@ + char *var_rcpt_checks = ""; + char *var_etrn_checks = ""; + char *var_relay_domains = ""; ++char *var_relay_ccerts = ""; + char *var_mynetworks = ""; + char *var_notify_classes = ""; + +diff -Pur postfix-1.1.11-20020613-orig/src/smtpd/smtpd_peer.c postfix-1.1.11-20020613/src/smtpd/smtpd_peer.c +--- postfix-1.1.11-20020613-orig/src/smtpd/smtpd_peer.c Thu Jul 5 22:09:47 2001 ++++ postfix-1.1.11-20020613/src/smtpd/smtpd_peer.c Wed Jun 26 15:26:48 2002 +@@ -63,6 +63,15 @@ + #include <netdb.h> + #include <string.h> + ++/* Utility library. */ ++ ++#include <msg.h> ++#include <mymalloc.h> ++#include <valid_hostname.h> ++#include <stringops.h> ++ ++/* Global library. */ ++ + /* + * Older systems don't have h_errno. Even modern systems don't have + * hstrerror(). +@@ -84,16 +93,11 @@ + ) + #endif + +-/* Utility library. */ +- +-#include <msg.h> +-#include <mymalloc.h> +-#include <valid_hostname.h> +-#include <stringops.h> +- +-/* Global library. */ +- +- ++#ifdef INET6 ++#define GAI_STRERROR(error) \ ++ ((error = EAI_SYSTEM) ? gai_strerror(error) : strerror(errno)) ++#endif ++ + /* Application-specific. */ + + #include "smtpd.h" +@@ -102,16 +106,23 @@ + + void smtpd_peer_init(SMTPD_STATE *state) + { +- struct sockaddr_in sin; +- SOCKADDR_SIZE len = sizeof(sin); ++#ifdef INET6 ++ struct sockaddr_storage ss; ++#else ++ struct sockaddr ss; ++ struct in_addr *in; + struct hostent *hp; +- int i; ++#endif ++ struct sockaddr *sa; ++ SOCKADDR_SIZE len; ++ ++ sa = (struct sockaddr *)&ss; ++ len = sizeof(ss); + + /* + * Look up the peer address information. + */ +- if (getpeername(vstream_fileno(state->client), +- (struct sockaddr *) & sin, &len) >= 0) { ++ if (getpeername(vstream_fileno(state->client), sa, &len) >= 0) { + errno = 0; + } + +@@ -127,18 +138,51 @@ + /* + * Look up and "verify" the client hostname. + */ +- else if (errno == 0 && sin.sin_family == AF_INET) { +- state->addr = mystrdup(inet_ntoa(sin.sin_addr)); +- hp = gethostbyaddr((char *) &(sin.sin_addr), +- sizeof(sin.sin_addr), AF_INET); +- if (hp == 0) { ++ else if (errno == 0 && (sa->sa_family == AF_INET ++#ifdef INET6 ++ || sa->sa_family == AF_INET6 ++#endif ++ )) { ++#ifdef INET6 ++ char hbuf[NI_MAXHOST]; ++ char abuf[NI_MAXHOST]; ++ struct addrinfo hints, *rnull = NULL; ++#else ++ char abuf[sizeof("255.255.255.255") + 1]; ++ char *hbuf; ++#endif ++ int error = -1; ++ ++#ifdef INET6 ++ (void)getnameinfo(sa, len, abuf, sizeof(hbuf), NULL, 0, NI_NUMERICHOST); ++#else ++ in = &((struct sockaddr_in *)sa)->sin_addr; ++ inet_ntop(AF_INET, in, abuf, sizeof(hbuf)); ++#endif ++ state->addr = mystrdup(abuf); ++#ifdef INET6 ++ error = getnameinfo(sa, len, hbuf, sizeof(hbuf), NULL, 0, NI_NAMEREQD); ++#else ++ hbuf = NULL; ++ hp = gethostbyaddr((char *)in, sizeof(*in), AF_INET); ++ if (hp) { ++ error = 0; ++ hbuf = mystrdup(hp->h_name); ++ } else ++ error = 1; ++#endif ++ if (error) { + state->name = mystrdup("unknown"); ++#ifdef INET6 ++ state->peer_code = (error == EAI_AGAIN ? 4 : 5); ++#else + state->peer_code = (h_errno == TRY_AGAIN ? 4 : 5); +- } else if (!valid_hostname(hp->h_name, DONT_GRIPE)) { ++#endif ++ } else if (!valid_hostname(hbuf, DONT_GRIPE)) { + state->name = mystrdup("unknown"); + state->peer_code = 5; + } else { +- state->name = mystrdup(hp->h_name); /* hp->name is clobbered!! */ ++ state->name = mystrdup(hbuf); /* hp->name is clobbered!! */ + state->peer_code = 2; + + /* +@@ -150,16 +194,31 @@ + state->peer_code = code; \ + } + ++#ifdef INET6 ++ memset(&hints, 0, sizeof(hints)); ++ hints.ai_family = AF_UNSPEC; ++ hints.ai_socktype = SOCK_STREAM; ++ error = getaddrinfo(state->name, NULL, &hints, &rnull); ++ if (error) { ++ msg_warn("%s: hostname %s verification failed: %s", ++ state->addr, state->name, GAI_STRERROR(error)); ++ REJECT_PEER_NAME(state, (error == EAI_AGAIN ? 4 : 5)); ++ } ++ /* memcmp() isn't needed if we use getaddrinfo */ ++ if (rnull) ++ freeaddrinfo(rnull); ++#else + hp = gethostbyname(state->name); /* clobbers hp->name!! */ + if (hp == 0) { + msg_warn("%s: hostname %s verification failed: %s", + state->addr, state->name, HSTRERROR(h_errno)); + REJECT_PEER_NAME(state, (h_errno == TRY_AGAIN ? 4 : 5)); +- } else if (hp->h_length != sizeof(sin.sin_addr)) { ++ } else if (hp->h_length != sizeof(*in)) { + msg_warn("%s: hostname %s verification failed: bad address size %d", + state->addr, state->name, hp->h_length); + REJECT_PEER_NAME(state, 5); + } else { ++ int i; + for (i = 0; /* void */ ; i++) { + if (hp->h_addr_list[i] == 0) { + msg_warn("%s: address not listed for hostname %s", +@@ -167,12 +226,11 @@ + REJECT_PEER_NAME(state, 5); + break; + } +- if (memcmp(hp->h_addr_list[i], +- (char *) &sin.sin_addr, +- sizeof(sin.sin_addr)) == 0) ++ if (memcmp(hp->h_addr_list[i], (char *)in, sizeof(*in)) == 0) + break; /* keep peer name */ + } + } ++#endif + } + } + +diff -Pur postfix-1.1.11-20020613-orig/src/smtpd/smtpd_sasl_proto.c postfix-1.1.11-20020613/src/smtpd/smtpd_sasl_proto.c +--- postfix-1.1.11-20020613-orig/src/smtpd/smtpd_sasl_proto.c Tue Sep 12 00:45:40 2000 ++++ postfix-1.1.11-20020613/src/smtpd/smtpd_sasl_proto.c Wed Jun 26 15:26:48 2002 +@@ -128,6 +128,13 @@ + smtpd_chat_reply(state, "503 Error: authentication not enabled"); + return (-1); + } ++#ifdef HAS_SSL ++ if (state->tls_auth_only && !state->tls_active) { ++ state->error_mask |= MAIL_ERROR_PROTOCOL; ++ smtpd_chat_reply(state, "538 Encryption required for requested authentication mechanism"); ++ return (-1); ++ } ++#endif + if (state->sasl_username) { + state->error_mask |= MAIL_ERROR_PROTOCOL; + smtpd_chat_reply(state, "503 Error: already authenticated"); +diff -Pur postfix-1.1.11-20020613-orig/src/smtpd/smtpd_state.c postfix-1.1.11-20020613/src/smtpd/smtpd_state.c +--- postfix-1.1.11-20020613-orig/src/smtpd/smtpd_state.c Tue Nov 6 18:35:40 2001 ++++ postfix-1.1.11-20020613/src/smtpd/smtpd_state.c Wed Jun 26 15:26:48 2002 +@@ -92,6 +92,11 @@ + state->msg_size = 0; + state->junk_cmds = 0; + state->warn_if_reject = 0; ++ state->tls_active = 0; ++ state->tls_use_tls = 0; ++ state->tls_enforce_tls = 0; ++ state->tls_info = tls_info_zero; ++ state->tls_auth_only = 0; + + #ifdef USE_SASL_AUTH + if (SMTPD_STAND_ALONE(state)) +diff -Pur postfix-1.1.11-20020613-orig/src/smtpstone/smtp-sink.c postfix-1.1.11-20020613/src/smtpstone/smtp-sink.c +--- postfix-1.1.11-20020613-orig/src/smtpstone/smtp-sink.c Sat Jun 8 20:21:41 2002 ++++ postfix-1.1.11-20020613/src/smtpstone/smtp-sink.c Wed Jun 26 15:26:48 2002 +@@ -518,7 +518,7 @@ + } else { + if (strncmp(argv[optind], "inet:", 5) == 0) + argv[optind] += 5; +- sock = inet_listen(argv[optind], backlog, BLOCKING); ++ sock = inet_listen(argv[optind], backlog, BLOCKING, 1); + } + + /* +diff -Pur postfix-1.1.11-20020613-orig/src/tlsmgr/Makefile.in postfix-1.1.11-20020613/src/tlsmgr/Makefile.in +--- postfix-1.1.11-20020613-orig/src/tlsmgr/Makefile.in Thu Jan 1 01:00:00 1970 ++++ postfix-1.1.11-20020613/src/tlsmgr/Makefile.in Wed Jun 26 15:26:48 2002 +@@ -0,0 +1,75 @@ ++SHELL = /bin/sh ++SRCS = tlsmgr.c ++OBJS = tlsmgr.o ++HDRS = ++TESTSRC = ++WARN = -W -Wformat -Wimplicit -Wmissing-prototypes \ ++ -Wparentheses -Wstrict-prototypes -Wswitch -Wuninitialized \ ++ -Wunused ++DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE) ++CFLAGS = $(DEBUG) $(OPT) $(DEFS) ++TESTPROG= ++PROG = tlsmgr ++INC_DIR = ../../include ++LIBS = ../../lib/libmaster.a ../../lib/libglobal.a ../../lib/libutil.a ++ ++.c.o:; $(CC) $(CFLAGS) -c $*.c ++ ++$(PROG): $(OBJS) $(LIBS) ++ $(CC) $(CFLAGS) -o $@ $(OBJS) $(LIBS) $(SYSLIBS) ++ ++Makefile: Makefile.in ++ (set -e; echo "# DO NOT EDIT"; $(OPTS) $(SHELL) ../../makedefs; cat $?) >$@ ++ ++test: $(TESTPROG) ++ ++update: ../../libexec/$(PROG) ++ ++../../libexec/$(PROG): $(PROG) ++ cp $(PROG) ../../libexec ++ ++printfck: $(OBJS) $(PROG) ++ rm -rf printfck ++ mkdir printfck ++ cp *.h printfck ++ sed '1,/^# do not edit/!d' Makefile >printfck/Makefile ++ set -e; for i in *.c; do printfck -f .printfck $$i >printfck/$$i; done ++ cd printfck; make "INC_DIR=../../../../include" `cd ../..; ls *.o` ++ ++lint: ++ lint $(DEFS) $(SRCS) $(LINTFIX) ++ ++clean: ++ rm -f *.o *core $(PROG) $(TESTPROG) junk ++ rm -rf printfck ++ ++tidy: clean ++ ++depend: $(MAKES) ++ (sed '1,/^# do not edit/!d' Makefile.in; \ ++ set -e; for i in [a-z][a-z0-9]*.c; do \ ++ $(CC) -E $(DEFS) $(INCL) $$i | sed -n -e '/^# *1 *"\([^"]*\)".*/{' \ ++ -e 's//'`echo $$i|sed 's/c$$/o/'`': \1/' -e 'p' -e '}'; \ ++ done) | grep -v '[.][o][:][ ][/]' >$$$$ && mv $$$$ Makefile.in ++ @make -f Makefile.in Makefile ++ ++# do not edit below this line - it is generated by 'make depend' ++tlsmgr.o: tlsmgr.c ++tlsmgr.o: ../../include/sys_defs.h ++tlsmgr.o: ../../include/msg.h ++tlsmgr.o: ../../include/events.h ++tlsmgr.o: ../../include/vstream.h ++tlsmgr.o: ../../include/vbuf.h ++tlsmgr.o: ../../include/dict.h ++tlsmgr.o: ../../include/argv.h ++tlsmgr.o: ../../include/vstring.h ++tlsmgr.o: ../../include/stringops.h ++tlsmgr.o: ../../include/mymalloc.h ++tlsmgr.o: ../../include/connect.h ++tlsmgr.o: ../../include/myflock.h ++tlsmgr.o: ../../include/mail_conf.h ++tlsmgr.o: ../../include/mail_params.h ++tlsmgr.o: ../../include/iostuff.h ++tlsmgr.o: ../../include/master_proto.h ++tlsmgr.o: ../../include/mail_server.h ++tlsmgr.o: ../../include/pfixtls.h +diff -Pur postfix-1.1.11-20020613-orig/src/tlsmgr/tlsmgr.c postfix-1.1.11-20020613/src/tlsmgr/tlsmgr.c +--- postfix-1.1.11-20020613-orig/src/tlsmgr/tlsmgr.c Thu Jan 1 01:00:00 1970 ++++ postfix-1.1.11-20020613/src/tlsmgr/tlsmgr.c Wed Jun 26 15:26:48 2002 +@@ -0,0 +1,598 @@ ++/*++ ++/* NAME ++/* tlsmgr 8 ++/* SUMMARY ++/* Postfix TLS session cache and PRNG handling manager ++/* SYNOPSIS ++/* \fBtlsmgr\fR [generic Postfix daemon options] ++/* DESCRIPTION ++/* The tlsmgr process does housekeeping on the session cache database ++/* files. It runs through the databases and removes expired entries ++/* and entries written by older (incompatible) versions. ++/* ++/* The tlsmgr is responsible for the PRNG handling. The used internal ++/* OpenSSL PRNG has a pool size of 8192 bits (= 1024 bytes). The pool ++/* is initially seeded at startup from an external source (EGD or ++/* /dev/urandom) and additional seed is obtained later during program ++/* run at a configurable period. The exact time of seed query is ++/* using random information and is equally distributed in the range of ++/* [0-\fBtls_random_reseed_period\fR] with a \fBtls_random_reseed_period\fR ++/* having a default of 1 hour. ++/* ++/* Tlsmgr can be run chrooted and with dropped privileges, as it will ++/* connect to the entropy source at startup. ++/* ++/* The PRNG is additionally seeded internally by the data found in the ++/* session cache and timevalues. ++/* ++/* Tlsmgr reads the old value of the exchange file at startup to keep ++/* entropy already collected during previous runs. ++/* ++/* From the PRNG random pool a cryptographically strong 1024 byte random ++/* sequence is written into the PRNG exchange file. The file is updated ++/* periodically with the time changing randomly from ++/* [0-\fBtls_random_prng_update_period\fR]. ++/* STANDARDS ++/* SECURITY ++/* .ad ++/* .fi ++/* Tlsmgr is not security-sensitive. It only deals with external data ++/* to be fed into the PRNG, the contents is never trusted. The session ++/* cache housekeeping will only remove entries if expired and will never ++/* touch the contents of the cached data. ++/* DIAGNOSTICS ++/* Problems and transactions are logged to the syslog daemon. ++/* BUGS ++/* There is no automatic means to limit the number of entries in the ++/* session caches and/or the size of the session cache files. ++/* 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 Session Cache ++/* .ad ++/* .fi ++/* .IP \fBsmtpd_tls_session_cache_database\fR ++/* Name of the SDBM file (type sdbm:) containing the SMTP server session ++/* cache. If the file does not exist, it is created. ++/* .IP \fBsmtpd_tls_session_cache_timeout\fR ++/* Expiry time of SMTP server session cache entries in seconds. Entries ++/* older than this are removed from the session cache. A cleanup-run is ++/* performed periodically every \fBsmtpd_tls_session_cache_timeout\fR ++/* seconds. Default is 3600 (= 1 hour). ++/* .IP \fBsmtp_tls_session_cache_database\fR ++/* Name of the SDBM file (type sdbm:) containing the SMTP client session ++/* cache. If the file does not exist, it is created. ++/* .IP \fBsmtp_tls_session_cache_timeout\fR ++/* Expiry time of SMTP client session cache entries in seconds. Entries ++/* older than this are removed from the session cache. A cleanup-run is ++/* performed periodically every \fBsmtp_tls_session_cache_timeout\fR ++/* seconds. Default is 3600 (= 1 hour). ++/* .SH Pseudo Random Number Generator ++/* .ad ++/* .fi ++/* .IP \fBtls_random_source\fR ++/* Name of the EGD socket or device or regular file to obtain entropy ++/* from. The type of entropy source must be specified by preceding the ++/* name with the appropriate type: egd:/path/to/egd_socket, ++/* dev:/path/to/devicefile, or /path/to/regular/file. ++/* tlsmgr opens \fBtls_random_source\fR and tries to read ++/* \fBtls_random_bytes\fR from it. ++/* .IP \fBtls_random_bytes\fR ++/* Number of bytes to be read from \fBtls_random_source\fR. ++/* Default value is 32 bytes. If using EGD, a maximum of 255 bytes is read. ++/* .IP \fBtls_random_exchange_name\fR ++/* Name of the file written by tlsmgr and read by smtp and smtpd at ++/* startup. The length is 1024 bytes. Default value is ++/* /etc/postfix/prng_exch. ++/* .IP \fBtls_random_reseed_period\fR ++/* Time in seconds until the next reseed from external sources is due. ++/* This is the maximum value. The actual point in time is calculated ++/* with a random factor equally distributed between 0 and this maximum ++/* value. Default is 3600 (= 60 minutes). ++/* .IP \fBtls_random_prng_update_period\fR ++/* Time in seconds until the PRNG exchange file is updated with new ++/* pseude random values. This is the maximum value. The actual point ++/* in time is calculated with a random factor equally distributed ++/* between 0 and this maximum value. Default is 60 (= 1 minute). ++/* SEE ALSO ++/* smtp(8) SMTP client ++/* smtpd(8) SMTP server ++/* LICENSE ++/* .ad ++/* .fi ++/* The Secure Mailer license must be distributed with this software. ++/* AUTHOR(S) ++/*--*/ ++ ++/* System library. */ ++ ++#include <sys_defs.h> ++#include <stdlib.h> ++#include <unistd.h> ++#include <ctype.h> ++#include <errno.h> ++#include <string.h> ++#include <sys/time.h> /* gettimeofday, not POSIX */ ++ ++/* OpenSSL library. */ ++#ifdef HAS_SSL ++#include <openssl/rand.h> /* For the PRNG */ ++#endif ++ ++/* Utility library. */ ++ ++#include <msg.h> ++#include <events.h> ++#include <dict.h> ++#include <stringops.h> ++#include <mymalloc.h> ++#include <connect.h> ++#include <myflock.h> ++ ++/* Global library. */ ++ ++#include <mail_conf.h> ++#include <mail_params.h> ++#include <pfixtls.h> ++ ++/* Master process interface */ ++ ++#include <master_proto.h> ++#include <mail_server.h> ++ ++/* Application-specific. */ ++ ++ /* ++ * Tunables. ++ */ ++char *var_tls_rand_source; ++int var_tls_rand_bytes; ++int var_tls_reseed_period; ++int var_tls_prng_upd_period; ++ ++static int rand_exch_fd; ++static int rand_source_dev_fd = -1; ++static int rand_source_socket_fd = -1; ++static int srvr_scache_db_active; ++static int clnt_scache_db_active; ++static DICT *srvr_scache_db = NULL; ++static DICT *clnt_scache_db = NULL; ++ ++static void tlsmgr_prng_upd_event(int unused_event, char *dummy) ++{ ++ struct timeval tv; ++ unsigned char buffer[1024]; ++ int next_period; ++ ++#ifdef HAS_SSL ++ /* ++ * It is time to update the PRNG exchange file. Since other processes might ++ * have added entropy, we do this in a read_stir-back_write cycle. ++ */ ++ GETTIMEOFDAY(&tv); ++ RAND_seed(&tv, sizeof(struct timeval)); ++ ++ if (myflock(rand_exch_fd, INTERNAL_LOCK, MYFLOCK_OP_EXCLUSIVE) != 0) ++ msg_fatal("Could not lock random exchange file: %s", ++ strerror(errno)); ++ ++ lseek(rand_exch_fd, 0, SEEK_SET); ++ if (read(rand_exch_fd, buffer, 1024) < 0) ++ msg_fatal("reading exchange file failed"); ++ RAND_seed(buffer, 1024); ++ ++ RAND_bytes(buffer, 1024); ++ lseek(rand_exch_fd, 0, SEEK_SET); ++ if (write(rand_exch_fd, buffer, 1024) != 1024) ++ msg_fatal("Writing exchange file failed"); ++ ++ if (myflock(rand_exch_fd, INTERNAL_LOCK, MYFLOCK_OP_NONE) != 0) ++ msg_fatal("Could not unlock random exchange file: %s", ++ strerror(errno)); ++ ++ /* ++ * Make prediction difficult for outsiders and calculate the time for the ++ * next execution randomly. ++ */ ++ next_period = (var_tls_prng_upd_period * buffer[0]) / 255; ++ event_request_timer(tlsmgr_prng_upd_event, dummy, next_period); ++#endif ++} ++ ++ ++static void tlsmgr_reseed_event(int unused_event, char *dummy) ++{ ++ int egd_success; ++ int next_period; ++ int rand_bytes; ++ char buffer[255]; ++ struct timeval tv; ++ unsigned char randbyte; ++ ++#ifdef HAS_SSL ++ /* ++ * It is time to reseed the PRNG. ++ */ ++ ++ GETTIMEOFDAY(&tv); ++ RAND_seed(&tv, sizeof(struct timeval)); ++ if (rand_source_dev_fd != -1) { ++ rand_bytes = read(rand_source_dev_fd, buffer, var_tls_rand_bytes); ++ if (rand_bytes > 0) ++ RAND_seed(buffer, rand_bytes); ++ else if (rand_bytes < 0) { ++ msg_fatal("Read from entropy device %s failed", ++ var_tls_rand_source); ++ } ++ } else if (rand_source_socket_fd != -1) { ++ egd_success = 0; ++ buffer[0] = 1; ++ buffer[1] = var_tls_rand_bytes; ++ if (write(rand_source_socket_fd, buffer, 2) != 2) ++ msg_info("Could not talk to %s", var_tls_rand_source); ++ else if (read(rand_source_socket_fd, buffer, 1) != 1) ++ msg_info("Could not read info from %s", var_tls_rand_source); ++ else { ++ rand_bytes = buffer[0]; ++ if (read(rand_source_socket_fd, buffer, rand_bytes) != rand_bytes) ++ msg_info("Could not read data from %s", var_tls_rand_source); ++ else { ++ egd_success = 1; ++ RAND_seed(buffer, rand_bytes); ++ } ++ } ++ if (!egd_success) { ++ msg_info("Lost connection to EGD-device, exiting to reconnect."); ++ exit(0); ++ } ++ } else if (*var_tls_rand_source) { ++ rand_bytes = RAND_load_file(var_tls_rand_source, var_tls_rand_bytes); ++ } ++ ++ /* ++ * Make prediction difficult for outsiders and calculate the time for the ++ * next execution randomly. ++ */ ++ RAND_bytes(&randbyte, 1); ++ next_period = (var_tls_reseed_period * randbyte) / 255; ++ event_request_timer(tlsmgr_reseed_event, dummy, next_period); ++#endif ++} ++ ++ ++static int tlsmgr_do_scache_check(DICT *scache_db, int scache_timeout, ++ int start) ++{ ++#ifdef HAS_SSL ++ int func; ++ int len; ++ int n; ++ int delete = 0; ++ int result; ++ struct timeval tv; ++ const char *member; ++ const char *value; ++ char *member_copy; ++ unsigned char nibble, *data; ++ pfixtls_scache_info_t scache_info; ++ ++ GETTIMEOFDAY(&tv); ++ RAND_seed(&tv, sizeof(struct timeval)); ++ ++ /* ++ * Run through the given dictionary and check the stored sessions. ++ * If "start" is set to 1, a new run is initiated, otherwise the next ++ * item is accessed. The state is internally kept in the DICT. ++ */ ++ if (start) ++ func = DICT_SEQ_FUN_FIRST; ++ else ++ func = DICT_SEQ_FUN_NEXT; ++ result = dict_seq(scache_db, func, &member, &value); ++ ++ if (result > 0) ++ return 0; /* End of list reached */ ++ else if (result < 0) ++ msg_fatal("Database fault, should already be caught."); ++ else { ++ member_copy = mystrdup(member); ++ len = strlen(value); ++ RAND_seed(value, len); /* Use it to increase entropy */ ++ if (len < 2 * sizeof(pfixtls_scache_info_t)) ++ delete = 1; /* Messed up, delete */ ++ else if (len > 2 * sizeof(pfixtls_scache_info_t)) ++ len = 2 * sizeof(pfixtls_scache_info_t); ++ if (!delete) { ++ data = (unsigned char *)(&scache_info); ++ memset(data, 0, len / 2); ++ for (n = 0; n < len; n++) { ++ if ((value[n] >= '0') && (value[n] <= '9')) ++ nibble = value[n] - '0'; ++ else ++ nibble = value[n] - 'A' + 10; ++ if (n % 2) ++ data[n / 2] |= nibble; ++ else ++ data[n / 2] |= (nibble << 4); ++ } ++ ++ if ((scache_info.scache_db_version != scache_db_version) || ++ (scache_info.openssl_version != openssl_version) || ++ (scache_info.timestamp + scache_timeout < time(NULL))) ++ delete = 1; ++ } ++ if (delete) ++ result = dict_del(scache_db, member_copy); ++ myfree(member_copy); ++ } ++ ++ if (delete && result) ++ msg_info("Could not delete %s", member); ++ return 1; ++ ++#else ++ return 0; ++#endif ++} ++ ++static void tlsmgr_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. ++ */ ++ clnt_scache_db_active = tlsmgr_do_scache_check(clnt_scache_db, ++ var_smtp_tls_scache_timeout, 1); ++ event_request_timer(tlsmgr_clnt_cache_run_event, dummy, ++ var_smtp_tls_scache_timeout); ++} ++ ++ ++static void tlsmgr_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. ++ */ ++ srvr_scache_db_active = tlsmgr_do_scache_check(srvr_scache_db, ++ var_smtpd_tls_scache_timeout, 1); ++ event_request_timer(tlsmgr_srvr_cache_run_event, dummy, ++ var_smtpd_tls_scache_timeout); ++} ++ ++ ++static DICT *tlsmgr_cache_open(const char *dbname) ++{ ++ DICT *retval; ++ char *dbpagname; ++ char *dbdirname; ++ ++ /* ++ * First, try to find out the real name of the database file, so that ++ * it can be removed. ++ */ ++ if (!strncmp(dbname, "sdbm:", 5)) { ++ dbpagname = concatenate(dbname + 5, ".pag", NULL); ++ REMOVE(dbpagname); ++ myfree(dbpagname); ++ dbdirname = concatenate(dbname + 5, ".dir", NULL); ++ REMOVE(dbdirname); ++ myfree(dbdirname); ++ } ++ else { ++ msg_warn("Only type sdbm: supported: %s", dbname); ++ return NULL; ++ } ++ ++ /* ++ * Now open the dictionary. Do it with O_EXCL, so that we only open a ++ * fresh file. If we cannot open it with a fresh file, then we won't ++ * touch it. ++ */ ++ retval = dict_open(dbname, O_RDWR | O_CREAT | O_EXCL, ++ DICT_FLAG_DUP_REPLACE | DICT_FLAG_LOCK | DICT_FLAG_SYNC_UPDATE); ++ if (!retval) ++ msg_warn("Could not create dictionary %s", dbname); ++ return retval; ++} ++ ++/* tlsmgr_trigger_event - respond to external trigger(s) */ ++ ++static void tlsmgr_trigger_event(char *buf, int len, ++ char *unused_service, char **argv) ++{ ++ /* ++ * Sanity check. This service takes no command-line arguments. ++ */ ++ if (argv[0]) ++ msg_fatal("unexpected command-line argument: %s", argv[0]); ++ ++} ++ ++/* tlsmgr_loop - queue manager main loop */ ++ ++static int tlsmgr_loop(char *unused_name, char **unused_argv) ++{ ++ /* ++ * This routine runs as part of the event handling loop, after the event ++ * manager has delivered a timer or I/O event (including the completion ++ * of a connection to a delivery process), or after it has waited for a ++ * specified amount of time. The result value of qmgr_loop() specifies ++ * how long the event manager should wait for the next event. ++ */ ++#define DONT_WAIT 0 ++#define WAIT_FOR_EVENT (-1) ++ ++ if (clnt_scache_db_active) ++ clnt_scache_db_active = tlsmgr_do_scache_check(clnt_scache_db, ++ var_smtp_tls_scache_timeout, 0); ++ if (srvr_scache_db_active) ++ srvr_scache_db_active = tlsmgr_do_scache_check(srvr_scache_db, ++ var_smtpd_tls_scache_timeout, 0); ++ if (clnt_scache_db_active || srvr_scache_db_active) ++ return (DONT_WAIT); ++ return (WAIT_FOR_EVENT); ++} ++ ++/* pre_accept - see if tables have changed */ ++ ++static void pre_accept(char *unused_name, char **unused_argv) ++{ ++ if (dict_changed()) { ++ msg_info("table has changed -- exiting"); ++ exit(0); ++ } ++} ++ ++/* tlsmgr_pre_init - pre-jail initialization */ ++ ++static void tlsmgr_pre_init(char *unused_name, char **unused_argv) ++{ ++ int rand_bytes; ++ unsigned char buffer[255]; ++ ++#ifdef HAS_SSL ++ /* ++ * Access the external sources for random seed. We may not be able to ++ * access them again if we are sent to chroot jail, so we must leave ++ * dev: and egd: type sources open. ++ */ ++ if (*var_tls_rand_source) { ++ if (!strncmp(var_tls_rand_source, "dev:", 4)) { ++ /* ++ * Source is a random device ++ */ ++ rand_source_dev_fd = open(var_tls_rand_source + 4, 0, 0); ++ if (rand_source_dev_fd == -1) ++ msg_fatal("Could not open entropy device %s", ++ var_tls_rand_source); ++ if (var_tls_rand_bytes > 255) ++ var_tls_rand_bytes = 255; ++ rand_bytes = read(rand_source_dev_fd, buffer, var_tls_rand_bytes); ++ RAND_seed(buffer, rand_bytes); ++ } else if (!strncmp(var_tls_rand_source, "egd:", 4)) { ++ /* ++ * Source is a EGD compatible socket ++ */ ++ rand_source_socket_fd = unix_connect(var_tls_rand_source +4, ++ BLOCKING, 10); ++ if (rand_source_socket_fd == -1) ++ msg_fatal("Could not connect to %s", var_tls_rand_source); ++ if (var_tls_rand_bytes > 255) ++ var_tls_rand_bytes = 255; ++ buffer[0] = 1; ++ buffer[1] = var_tls_rand_bytes; ++ if (write(rand_source_socket_fd, buffer, 2) != 2) ++ msg_fatal("Could not talk to %s", var_tls_rand_source); ++ if (read(rand_source_socket_fd, buffer, 1) != 1) ++ msg_fatal("Could not read info from %s", var_tls_rand_source); ++ rand_bytes = buffer[0]; ++ if (read(rand_source_socket_fd, buffer, rand_bytes) != rand_bytes) ++ msg_fatal("Could not read data from %s", var_tls_rand_source); ++ RAND_seed(buffer, rand_bytes); ++ } else { ++ rand_bytes = RAND_load_file(var_tls_rand_source, ++ var_tls_rand_bytes); ++ } ++ } ++#endif ++ ++ /* ++ * Now open the PRNG exchange file ++ */ ++ if (*var_tls_rand_exch_name) { ++ rand_exch_fd = open(var_tls_rand_exch_name, O_RDWR | O_CREAT, 0600); ++ } ++ ++ /* ++ * Finally, open the session cache files. Remove old files, if still there. ++ * If we could not remove the old files, something is pretty wrong and we ++ * won't touch it!! ++ */ ++ if (*var_smtp_tls_scache_db) ++ clnt_scache_db = tlsmgr_cache_open(var_smtp_tls_scache_db); ++ if (*var_smtpd_tls_scache_db) ++ srvr_scache_db = tlsmgr_cache_open(var_smtpd_tls_scache_db); ++} ++ ++/* qmgr_post_init - post-jail initialization */ ++ ++static void tlsmgr_post_init(char *unused_name, char **unused_argv) ++{ ++ unsigned char buffer[1024]; ++ ++ /* ++ * This routine runs after the skeleton code has entered the chroot jail. ++ * Prevent automatic process suicide after a limited number of client ++ * requests or after a limited amount of idle time. ++ */ ++ var_use_limit = 0; ++ var_idle_limit = 0; ++ ++#ifdef HAS_SSL ++ /* ++ * Complete thie initialization by reading the additional seed from the ++ * PRNG exchange file. Don't care how many bytes were actually read, just ++ * seed buffer into the PRNG, regardless of its contents. ++ */ ++ if (rand_exch_fd >= 0) { ++ if (myflock(rand_exch_fd, INTERNAL_LOCK, MYFLOCK_OP_SHARED) == -1) ++ msg_fatal("Could not lock random exchange file: %s", ++ strerror(errno)); ++ read(rand_exch_fd, buffer, 1024); ++ if (myflock(rand_exch_fd, INTERNAL_LOCK, MYFLOCK_OP_NONE) == -1) ++ msg_fatal("Could not unlock random exchange file: %s", ++ strerror(errno)); ++ RAND_seed(buffer, 1024); ++ tlsmgr_prng_upd_event(0, (char *) 0); ++ tlsmgr_reseed_event(0, (char *) 0); ++ } ++#endif ++ ++ clnt_scache_db_active = 0; ++ srvr_scache_db_active = 0; ++ if (clnt_scache_db) ++ tlsmgr_clnt_cache_run_event(0, (char *) 0); ++ if (srvr_scache_db) ++ tlsmgr_srvr_cache_run_event(0, (char *) 0); ++} ++ ++/* main - the main program */ ++ ++int main(int argc, char **argv) ++{ ++ static CONFIG_STR_TABLE str_table[] = { ++ VAR_TLS_RAND_SOURCE, DEF_TLS_RAND_SOURCE, &var_tls_rand_source, 0, 0, ++ 0, ++ }; ++ static CONFIG_TIME_TABLE time_table[] = { ++ VAR_TLS_RESEED_PERIOD, DEF_TLS_RESEED_PERIOD, &var_tls_reseed_period, 0, 0, ++ VAR_TLS_PRNG_UPD_PERIOD, DEF_TLS_PRNG_UPD_PERIOD, &var_tls_prng_upd_period, 0, 0, ++ 0, ++ }; ++ static CONFIG_INT_TABLE int_table[] = { ++ VAR_TLS_RAND_BYTES, DEF_TLS_RAND_BYTES, &var_tls_rand_bytes, 0, 0, ++ 0, ++ }; ++ ++ /* ++ * Use the trigger service skeleton, because no-one else should be ++ * monitoring our service port while this process runs, and because we do ++ * not talk back to the client. ++ */ ++ trigger_server_main(argc, argv, tlsmgr_trigger_event, ++ MAIL_SERVER_TIME_TABLE, time_table, ++ MAIL_SERVER_INT_TABLE, int_table, ++ MAIL_SERVER_STR_TABLE, str_table, ++ MAIL_SERVER_PRE_INIT, tlsmgr_pre_init, ++ MAIL_SERVER_POST_INIT, tlsmgr_post_init, ++ MAIL_SERVER_LOOP, tlsmgr_loop, ++ MAIL_SERVER_PRE_ACCEPT, pre_accept, ++ 0); ++} +diff -Pur postfix-1.1.11-20020613-orig/src/util/Makefile.in postfix-1.1.11-20020613/src/util/Makefile.in +--- postfix-1.1.11-20020613-orig/src/util/Makefile.in Tue Jun 11 03:12:45 2002 ++++ postfix-1.1.11-20020613/src/util/Makefile.in Wed Jun 26 15:26:48 2002 +@@ -8,7 +8,7 @@ + dict_tcp.c dict_unix.c dir_forest.c doze.c duplex_pipe.c \ + environ.c events.c exec_command.c fifo_listen.c fifo_trigger.c \ + file_limit.c find_inet.c fsspace.c fullname.c get_domainname.c \ +- get_hostname.c hex_quote.c htable.c inet_addr_host.c \ ++ get_hostname.c get_port.c hex_quote.c htable.c inet_addr_host.c \ + inet_addr_list.c inet_addr_local.c inet_connect.c inet_listen.c \ + inet_trigger.c inet_util.c intv.c line_wrap.c lowercase.c \ + lstat_as.c mac_expand.c mac_parse.c make_dirs.c match_list.c \ +@@ -26,7 +26,7 @@ + unix_connect.c unix_listen.c unix_trigger.c unsafe.c username.c \ + valid_hostname.c vbuf.c vbuf_print.c vstream.c vstream_popen.c \ + vstring.c vstring_vstream.c watchdog.c writable.c write_buf.c \ +- write_wait.c strcasecmp.c nvtable.c ++ write_wait.c strcasecmp.c nvtable.c dict_sdbm.c sdbm.c + OBJS = alldig.o argv.o argv_split.o attr_print0.o attr_print64.o \ + attr_scan0.o attr_scan64.o base64_code.o basename.o binhash.o \ + chroot_uid.o clean_env.o close_on_exec.o concatenate.o ctable.o \ +@@ -36,7 +36,7 @@ + dict_tcp.o dict_unix.o dir_forest.o doze.o duplex_pipe.o \ + environ.o events.o exec_command.o fifo_listen.o fifo_trigger.o \ + file_limit.o find_inet.o fsspace.o fullname.o get_domainname.o \ +- get_hostname.o hex_quote.o htable.o inet_addr_host.o \ ++ get_hostname.o get_port.o hex_quote.o htable.o inet_addr_host.o \ + inet_addr_list.o inet_addr_local.o inet_connect.o inet_listen.o \ + inet_trigger.o inet_util.o intv.o line_wrap.o lowercase.o \ + lstat_as.o mac_expand.o mac_parse.o make_dirs.o match_list.o \ +@@ -54,13 +54,13 @@ + unix_connect.o unix_listen.o unix_trigger.o unsafe.o username.o \ + valid_hostname.o vbuf.o vbuf_print.o vstream.o vstream_popen.o \ + vstring.o vstring_vstream.o watchdog.o writable.o write_buf.o \ +- write_wait.o nvtable.o $(STRCASE) ++ write_wait.o nvtable.o $(STRCASE) dict_sdbm.o sdbm.o + HDRS = argv.h attr.h base64_code.h binhash.h chroot_uid.h clean_env.h \ + connect.h ctable.h dict.h dict_db.h dict_dbm.h dict_env.h \ + dict_ht.h dict_ldap.h dict_mysql.h dict_ni.h dict_nis.h \ + dict_nisplus.h dict_pcre.h dict_regexp.h dict_static.h dict_tcp.h \ + dict_unix.h dir_forest.h events.h exec_command.h find_inet.h \ +- fsspace.h fullname.h get_domainname.h get_hostname.h hex_quote.h \ ++ fsspace.h fullname.h get_domainname.h get_hostname.h get_port.h hex_quote.h \ + htable.h inet_addr_host.h inet_addr_list.h inet_addr_local.h \ + inet_util.h intv.h iostuff.h line_wrap.h listen.h lstat_as.h \ + mac_expand.h mac_parse.h make_dirs.h match_list.h match_ops.h \ +@@ -72,7 +72,7 @@ + split_at.h stat_as.h stringops.h sys_defs.h timed_connect.h \ + timed_wait.h trigger.h username.h valid_hostname.h vbuf.h \ + vbuf_print.h vstream.h vstring.h vstring_vstream.h watchdog.h \ +- nvtable.h ++ nvtable.h dict_sdbm.h sdbm.h + TESTSRC = fifo_open.c fifo_rdwr_bug.c fifo_rdonly_bug.c select_bug.c \ + stream_test.c dup2_pass_on_exec.c + WARN = -W -Wformat -Wimplicit -Wmissing-prototypes \ +@@ -591,6 +591,7 @@ + dict_open.o: dict_unix.h + dict_open.o: dict_tcp.h + dict_open.o: dict_dbm.h ++dict_open.o: dict_sdbm.h + dict_open.o: dict_db.h + dict_open.o: dict_nis.h + dict_open.o: dict_nisplus.h +@@ -725,6 +726,7 @@ + get_domainname.o: mymalloc.h + get_domainname.o: get_hostname.h + get_domainname.o: get_domainname.h ++get_port.o: sys_defs.h + get_hostname.o: get_hostname.c + get_hostname.o: sys_defs.h + get_hostname.o: mymalloc.h +@@ -841,6 +843,7 @@ + match_list.o: stringops.h + match_list.o: argv.h + match_list.o: dict.h ++match_list.o: inet_util.h + match_list.o: match_ops.h + match_list.o: match_list.h + match_ops.o: match_ops.c +@@ -1225,3 +1228,9 @@ + write_wait.o: sys_defs.h + write_wait.o: msg.h + write_wait.o: iostuff.h ++sdbm.o: sdbm.c ++sdbm.o: sdbm.h ++dict_sdbm.o: sdbm.h ++dict_sdbm.o: dict_sdbm.c ++dict_sdbm.o: dict_sdbm.h ++dict_sdbm.o: sys_defs.h +diff -Pur postfix-1.1.11-20020613-orig/src/util/dict_open.c postfix-1.1.11-20020613/src/util/dict_open.c +--- postfix-1.1.11-20020613-orig/src/util/dict_open.c Fri Dec 21 23:18:07 2001 ++++ postfix-1.1.11-20020613/src/util/dict_open.c Wed Jun 26 15:26:48 2002 +@@ -159,6 +159,7 @@ + #include <dict_env.h> + #include <dict_unix.h> + #include <dict_tcp.h> ++#include <dict_sdbm.h> + #include <dict_dbm.h> + #include <dict_db.h> + #include <dict_nis.h> +@@ -187,6 +188,7 @@ + #if 0 + DICT_TYPE_TCP, dict_tcp_open, + #endif ++ "sdbm", dict_sdbm_open, + #ifdef HAS_DBM + DICT_TYPE_DBM, dict_dbm_open, + #endif +diff -Pur postfix-1.1.11-20020613-orig/src/util/dict_sdbm.c postfix-1.1.11-20020613/src/util/dict_sdbm.c +--- postfix-1.1.11-20020613-orig/src/util/dict_sdbm.c Thu Jan 1 01:00:00 1970 ++++ postfix-1.1.11-20020613/src/util/dict_sdbm.c Wed Jun 26 15:26:48 2002 +@@ -0,0 +1,408 @@ ++/*++ ++/* NAME ++/* dict_sdbm 3 ++/* SUMMARY ++/* dictionary manager interface to SDBM files ++/* SYNOPSIS ++/* #include <dict_sdbm.h> ++/* ++/* DICT *dict_sdbm_open(path, open_flags, dict_flags) ++/* const char *name; ++/* const char *path; ++/* int open_flags; ++/* int dict_flags; ++/* DESCRIPTION ++/* dict_sdbm_open() opens the named SDBM database and makes it available ++/* via the generic interface described in dict_open(3). ++/* DIAGNOSTICS ++/* Fatal errors: cannot open file, file write error, out of memory. ++/* SEE ALSO ++/* dict(3) generic dictionary manager ++/* sdbm(3) data base subroutines ++/* 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 ++/*--*/ ++ ++#include "sys_defs.h" ++ ++/* System library. */ ++ ++#include <sys/stat.h> ++#include <string.h> ++#include <unistd.h> ++ ++/* Utility library. */ ++ ++#include "msg.h" ++#include "mymalloc.h" ++#include "htable.h" ++#include "iostuff.h" ++#include "vstring.h" ++#include "myflock.h" ++#include "stringops.h" ++#include "dict.h" ++#include "dict_sdbm.h" ++#include "sdbm.h" ++ ++/* Application-specific. */ ++ ++typedef struct { ++ DICT dict; /* generic members */ ++ SDBM *dbm; /* open database */ ++ char *path; /* pathname */ ++} DICT_SDBM; ++ ++/* dict_sdbm_lookup - find database entry */ ++ ++static const char *dict_sdbm_lookup(DICT *dict, const char *name) ++{ ++ DICT_SDBM *dict_sdbm = (DICT_SDBM *) dict; ++ datum dbm_key; ++ datum dbm_value; ++ static VSTRING *buf; ++ const char *result = 0; ++ ++ dict_errno = 0; ++ ++ /* ++ * Acquire an exclusive lock. ++ */ ++ if ((dict->flags & DICT_FLAG_LOCK) ++ && myflock(dict->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_SHARED) < 0) ++ msg_fatal("%s: lock dictionary: %m", dict_sdbm->path); ++ ++ /* ++ * See if this DBM file was written with one null byte appended to key ++ * and value. ++ */ ++ if (dict->flags & DICT_FLAG_TRY1NULL) { ++ dbm_key.dptr = (void *) name; ++ dbm_key.dsize = strlen(name) + 1; ++ dbm_value = sdbm_fetch(dict_sdbm->dbm, dbm_key); ++ if (dbm_value.dptr != 0) { ++ dict->flags &= ~DICT_FLAG_TRY0NULL; ++ result = dbm_value.dptr; ++ } ++ } ++ ++ /* ++ * See if this DBM file was written with no null byte appended to key and ++ * value. ++ */ ++ if (result == 0 && (dict->flags & DICT_FLAG_TRY0NULL)) { ++ dbm_key.dptr = (void *) name; ++ dbm_key.dsize = strlen(name); ++ dbm_value = sdbm_fetch(dict_sdbm->dbm, dbm_key); ++ if (dbm_value.dptr != 0) { ++ if (buf == 0) ++ buf = vstring_alloc(10); ++ vstring_strncpy(buf, dbm_value.dptr, dbm_value.dsize); ++ dict->flags &= ~DICT_FLAG_TRY1NULL; ++ result = vstring_str(buf); ++ } ++ } ++ ++ /* ++ * Release the exclusive lock. ++ */ ++ if ((dict->flags & DICT_FLAG_LOCK) ++ && myflock(dict->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_NONE) < 0) ++ msg_fatal("%s: unlock dictionary: %m", dict_sdbm->path); ++ ++ return (result); ++} ++ ++/* dict_sdbm_update - add or update database entry */ ++ ++static void dict_sdbm_update(DICT *dict, const char *name, const char *value) ++{ ++ DICT_SDBM *dict_sdbm = (DICT_SDBM *) dict; ++ datum dbm_key; ++ datum dbm_value; ++ int status; ++ ++ dbm_key.dptr = (void *) name; ++ dbm_value.dptr = (void *) value; ++ dbm_key.dsize = strlen(name); ++ dbm_value.dsize = strlen(value); ++ ++ /* ++ * If undecided about appending a null byte to key and value, choose a ++ * default depending on the platform. ++ */ ++ if ((dict->flags & DICT_FLAG_TRY1NULL) ++ && (dict->flags & DICT_FLAG_TRY0NULL)) { ++#ifdef DBM_NO_TRAILING_NULL ++ dict->flags &= ~DICT_FLAG_TRY1NULL; ++#else ++ dict->flags &= ~DICT_FLAG_TRY0NULL; ++#endif ++ } ++ ++ /* ++ * Optionally append a null byte to key and value. ++ */ ++ if (dict->flags & DICT_FLAG_TRY1NULL) { ++ dbm_key.dsize++; ++ dbm_value.dsize++; ++ } ++ ++ /* ++ * Acquire an exclusive lock. ++ */ ++ if ((dict->flags & DICT_FLAG_LOCK) ++ && myflock(dict->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_EXCLUSIVE) < 0) ++ msg_fatal("%s: lock dictionary: %m", dict_sdbm->path); ++ ++ /* ++ * Do the update. ++ */ ++ if ((status = sdbm_store(dict_sdbm->dbm, dbm_key, dbm_value, ++ (dict->flags & DICT_FLAG_DUP_REPLACE) ? DBM_REPLACE : DBM_INSERT)) < 0) ++ msg_fatal("error writing SDBM database %s: %m", dict_sdbm->path); ++ if (status) { ++ if (dict->flags & DICT_FLAG_DUP_IGNORE) ++ /* void */ ; ++ else if (dict->flags & DICT_FLAG_DUP_WARN) ++ msg_warn("%s: duplicate entry: \"%s\"", dict_sdbm->path, name); ++ else ++ msg_fatal("%s: duplicate entry: \"%s\"", dict_sdbm->path, name); ++ } ++ ++ /* ++ * Release the exclusive lock. ++ */ ++ if ((dict->flags & DICT_FLAG_LOCK) ++ && myflock(dict->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_NONE) < 0) ++ msg_fatal("%s: unlock dictionary: %m", dict_sdbm->path); ++} ++ ++ ++/* dict_sdbm_delete - delete one entry from the dictionary */ ++ ++static int dict_sdbm_delete(DICT *dict, const char *name) ++{ ++ DICT_SDBM *dict_sdbm = (DICT_SDBM *) dict; ++ datum dbm_key; ++ int status = 1; ++ int flags = 0; ++ ++ /* ++ * Acquire an exclusive lock. ++ */ ++ if ((dict->flags & DICT_FLAG_LOCK) ++ && myflock(dict->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_EXCLUSIVE) < 0) ++ msg_fatal("%s: lock dictionary: %m", dict_sdbm->path); ++ ++ /* ++ * See if this DBM file was written with one null byte appended to key ++ * and value. ++ */ ++ if (dict->flags & DICT_FLAG_TRY1NULL) { ++ dbm_key.dptr = (void *) name; ++ dbm_key.dsize = strlen(name) + 1; ++ sdbm_clearerr(dict_sdbm->dbm); ++ if ((status = sdbm_delete(dict_sdbm->dbm, dbm_key)) < 0) { ++ if (sdbm_error(dict_sdbm->dbm) != 0) /* fatal error */ ++ msg_fatal("error deleting from %s: %m", dict_sdbm->path); ++ status = 1; /* not found */ ++ } else { ++ dict->flags &= ~DICT_FLAG_TRY0NULL; /* found */ ++ } ++ } ++ ++ /* ++ * See if this DBM file was written with no null byte appended to key and ++ * value. ++ */ ++ if (status > 0 && (dict->flags & DICT_FLAG_TRY0NULL)) { ++ dbm_key.dptr = (void *) name; ++ dbm_key.dsize = strlen(name); ++ sdbm_clearerr(dict_sdbm->dbm); ++ if ((status = sdbm_delete(dict_sdbm->dbm, dbm_key)) < 0) { ++ if (sdbm_error(dict_sdbm->dbm) != 0) /* fatal error */ ++ msg_fatal("error deleting from %s: %m", dict_sdbm->path); ++ status = 1; /* not found */ ++ } else { ++ dict->flags &= ~DICT_FLAG_TRY1NULL; /* found */ ++ } ++ } ++ ++ /* ++ * Release the exclusive lock. ++ */ ++ if ((dict->flags & DICT_FLAG_LOCK) ++ && myflock(dict->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_NONE) < 0) ++ msg_fatal("%s: unlock dictionary: %m", dict_sdbm->path); ++ ++ return (status); ++} ++ ++/* traverse the dictionary */ ++ ++static int dict_sdbm_sequence(DICT *dict, const int function, ++ const char **key, const char **value) ++{ ++ char *myname = "dict_sdbm_sequence"; ++ DICT_SDBM *dict_sdbm = (DICT_SDBM *) dict; ++ datum dbm_key; ++ datum dbm_value; ++ int status = 0; ++ static VSTRING *key_buf; ++ static VSTRING *value_buf; ++ ++ /* ++ * Acquire an exclusive lock. ++ */ ++ if ((dict->flags & DICT_FLAG_LOCK) ++ && myflock(dict->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_EXCLUSIVE) < 0) ++ msg_fatal("%s: lock dictionary: %m", dict_sdbm->path); ++ ++ /* ++ * Determine and execute the seek function. It returns the key. ++ */ ++ switch (function) { ++ case DICT_SEQ_FUN_FIRST: ++ dbm_key = sdbm_firstkey(dict_sdbm->dbm); ++ break; ++ case DICT_SEQ_FUN_NEXT: ++ dbm_key = sdbm_nextkey(dict_sdbm->dbm); ++ break; ++ default: ++ msg_panic("%s: invalid function: %d", myname, function); ++ } ++ ++ /* ++ * Release the exclusive lock. ++ */ ++ if ((dict->flags & DICT_FLAG_LOCK) ++ && myflock(dict->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_NONE) < 0) ++ msg_fatal("%s: unlock dictionary: %m", dict_sdbm->path); ++ ++ if (dbm_key.dptr != 0 && dbm_key.dsize > 0) { ++ ++ /* ++ * See if this DB file was written with one null byte appended to key ++ * an d value or not. If necessary, copy the key. ++ */ ++ if (((char *) dbm_key.dptr)[dbm_key.dsize - 1] == 0) { ++ *key = dbm_key.dptr; ++ } else { ++ if (key_buf == 0) ++ key_buf = vstring_alloc(10); ++ vstring_strncpy(key_buf, dbm_key.dptr, dbm_key.dsize); ++ *key = vstring_str(key_buf); ++ } ++ ++ /* ++ * Fetch the corresponding value. ++ */ ++ dbm_value = sdbm_fetch(dict_sdbm->dbm, dbm_key); ++ ++ if (dbm_value.dptr != 0 && dbm_value.dsize > 0) { ++ ++ /* ++ * See if this DB file was written with one null byte appended to ++ * key and value or not. If necessary, copy the key. ++ */ ++ if (((char *) dbm_value.dptr)[dbm_value.dsize - 1] == 0) { ++ *value = dbm_value.dptr; ++ } else { ++ if (value_buf == 0) ++ value_buf = vstring_alloc(10); ++ vstring_strncpy(value_buf, dbm_value.dptr, dbm_value.dsize); ++ *value = vstring_str(value_buf); ++ } ++ } else { ++ ++ /* ++ * Determine if we have hit the last record or an error ++ * condition. ++ */ ++ if (sdbm_error(dict_sdbm->dbm)) ++ msg_fatal("error seeking %s: %m", dict_sdbm->path); ++ return (1); /* no error: eof/not found ++ * (should not happen!) */ ++ } ++ } else { ++ ++ /* ++ * Determine if we have hit the last record or an error condition. ++ */ ++ if (sdbm_error(dict_sdbm->dbm)) ++ msg_fatal("error seeking %s: %m", dict_sdbm->path); ++ return (1); /* no error: eof/not found */ ++ } ++ return (0); ++} ++ ++/* dict_sdbm_close - disassociate from data base */ ++ ++static void dict_sdbm_close(DICT *dict) ++{ ++ DICT_SDBM *dict_sdbm = (DICT_SDBM *) dict; ++ ++ sdbm_close(dict_sdbm->dbm); ++ myfree(dict_sdbm->path); ++ myfree((char *) dict_sdbm); ++} ++ ++/* dict_sdbm_open - open SDBM data base */ ++ ++DICT *dict_sdbm_open(const char *path, int open_flags, int dict_flags) ++{ ++ DICT_SDBM *dict_sdbm; ++ struct stat st; ++ SDBM *dbm; ++ char *dbm_path; ++ int lock_fd; ++ ++ if (dict_flags & DICT_FLAG_LOCK) { ++ dbm_path = concatenate(path, ".pag", (char *) 0); ++ if ((lock_fd = open(dbm_path, open_flags, 0644)) < 0) ++ msg_fatal("open database %s: %m", dbm_path); ++ if (myflock(lock_fd, INTERNAL_LOCK, MYFLOCK_OP_SHARED) < 0) ++ msg_fatal("shared-lock database %s for open: %m", dbm_path); ++ } ++ ++ /* ++ * XXX SunOS 5.x has no const in dbm_open() prototype. ++ */ ++ if ((dbm = sdbm_open((char *) path, open_flags, 0644)) == 0) ++ msg_fatal("open database %s.{dir,pag}: %m", path); ++ ++ if (dict_flags & DICT_FLAG_LOCK) { ++ if (myflock(lock_fd, INTERNAL_LOCK, MYFLOCK_OP_NONE) < 0) ++ msg_fatal("unlock database %s for open: %m", dbm_path); ++ if (close(lock_fd) < 0) ++ msg_fatal("close database %s: %m", dbm_path); ++ myfree(dbm_path); ++ } ++ dict_sdbm = (DICT_SDBM *) mymalloc(sizeof(*dict_sdbm)); ++ dict_sdbm->dict.lookup = dict_sdbm_lookup; ++ dict_sdbm->dict.update = dict_sdbm_update; ++ dict_sdbm->dict.delete = dict_sdbm_delete; ++ dict_sdbm->dict.sequence = dict_sdbm_sequence; ++ dict_sdbm->dict.close = dict_sdbm_close; ++ dict_sdbm->dict.lock_fd = sdbm_dirfno(dbm); ++ dict_sdbm->dict.stat_fd = sdbm_pagfno(dbm); ++ if (fstat(dict_sdbm->dict.stat_fd, &st) < 0) ++ msg_fatal("dict_sdbm_open: fstat: %m"); ++ dict_sdbm->dict.mtime = st.st_mtime; ++ close_on_exec(sdbm_pagfno(dbm), CLOSE_ON_EXEC); ++ close_on_exec(sdbm_dirfno(dbm), CLOSE_ON_EXEC); ++ dict_sdbm->dict.flags = dict_flags | DICT_FLAG_FIXED; ++ if ((dict_flags & (DICT_FLAG_TRY0NULL | DICT_FLAG_TRY1NULL)) == 0) ++ dict_sdbm->dict.flags |= (DICT_FLAG_TRY0NULL | DICT_FLAG_TRY1NULL); ++ dict_sdbm->dbm = dbm; ++ dict_sdbm->path = mystrdup(path); ++ ++ return (&dict_sdbm->dict); ++} +diff -Pur postfix-1.1.11-20020613-orig/src/util/dict_sdbm.h postfix-1.1.11-20020613/src/util/dict_sdbm.h +--- postfix-1.1.11-20020613-orig/src/util/dict_sdbm.h Thu Jan 1 01:00:00 1970 ++++ postfix-1.1.11-20020613/src/util/dict_sdbm.h Wed Jun 26 15:26:48 2002 +@@ -0,0 +1,35 @@ ++#ifndef _DICT_SDBM_H_INCLUDED_ ++#define _DICT_SDBM_H_INCLUDED_ ++ ++/*++ ++/* NAME ++/* dict_dbm 3h ++/* SUMMARY ++/* dictionary manager interface to DBM files ++/* SYNOPSIS ++/* #include <dict_dbm.h> ++/* DESCRIPTION ++/* .nf ++ ++ /* ++ * Utility library. ++ */ ++#include <dict.h> ++ ++ /* ++ * External interface. ++ */ ++extern DICT *dict_sdbm_open(const char *, int, int); ++ ++/* 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 ++/*--*/ ++ ++#endif +diff -Pur postfix-1.1.11-20020613-orig/src/util/get_port.c postfix-1.1.11-20020613/src/util/get_port.c +--- postfix-1.1.11-20020613-orig/src/util/get_port.c Thu Jan 1 01:00:00 1970 ++++ postfix-1.1.11-20020613/src/util/get_port.c Wed Jun 26 15:26:49 2002 +@@ -0,0 +1,65 @@ ++/*++ ++/* NAME ++/* get_port 3 ++/* SUMMARY ++/* trivial host and port extracter ++/* SYNOPSIS ++/* #include <get_port.h> ++/* ++/* char *get_port(data) ++/* char *data; ++/* ++/* DESCRIPTION ++/* get_port() extract host name or ip address from ++/* strings such as [3ffe:902:12::10]:25, [::1] ++/* or 192.168.0.1:25, and null-terminates the ++/* \fIdata\fR at the first occurrence of port separator. ++/* DIAGNOSTICS ++/* If port not found return null pointer. ++/* LICENSE ++/* .ad ++/* .fi ++/* BSD Style (or BSD like) license. ++/* AUTHOR(S) ++/* Arkadiusz Mi¶kiewicz <misiek@pld.org.pl> ++/* Wroclaw, POLAND ++/*--*/ ++ ++/* System libraries */ ++ ++#include <sys_defs.h> ++#include <string.h> ++ ++/* Utility library. */ ++ ++#include "get_port.h" ++ ++/* get_port - extract port number from string */ ++ ++char *get_port(char *data) ++{ ++ const char *escl=strchr(data,'['); ++ const char *sepl=strchr(data,':'); ++ char *escr=strrchr(data,']'); ++ char *sepr=strrchr(data,':'); ++ ++ /* extract from "[address]:port" or "[address]"*/ ++ if (escl && escr) ++ { ++ memmove(data, data + 1, strlen(data) - strlen(escr)); ++ data[strlen(data) - strlen(escr) - 1] = 0; ++ *escr++ = 0; ++ if (*escr == ':') ++ escr++; ++ return (*escr ? escr : NULL); ++ } ++ /* extract from "address:port" or "address" */ ++ if ((sepl == sepr) && sepr && sepl) ++ { ++ *sepr++ = 0; ++ return sepr; ++ } ++ ++ /* return empty string */ ++ return NULL; ++} +diff -Pur postfix-1.1.11-20020613-orig/src/util/get_port.h postfix-1.1.11-20020613/src/util/get_port.h +--- postfix-1.1.11-20020613-orig/src/util/get_port.h Thu Jan 1 01:00:00 1970 ++++ postfix-1.1.11-20020613/src/util/get_port.h Wed Jun 26 15:26:49 2002 +@@ -0,0 +1,28 @@ ++#ifndef _GET_PORT_H_INCLUDED_ ++#define _GET_PORT_H_INCLUDED_ ++ ++/*++ ++/* NAME ++/* get_port 3h ++/* SUMMARY ++/* trivial host and port extracter ++/* SYNOPSIS ++/* #include <get_port.h> ++/* DESCRIPTION ++/* .nf ++ ++ /* External interface. */ ++ ++extern char *get_port(char *); ++ ++ ++/* LICENSE ++/* .ad ++/* .fi ++/* BSD Style (or BSD like) license. ++/* AUTHOR(S) ++/* Arkadiusz Mi¶kiewicz <misiek@pld.org.pl> ++/* Wroclaw, POLAND ++/*--*/ ++ ++#endif +diff -Pur postfix-1.1.11-20020613-orig/src/util/inet_addr_host.c postfix-1.1.11-20020613/src/util/inet_addr_host.c +--- postfix-1.1.11-20020613-orig/src/util/inet_addr_host.c Fri Dec 11 19:55:35 1998 ++++ postfix-1.1.11-20020613/src/util/inet_addr_host.c Wed Jun 26 15:26:49 2002 +@@ -38,7 +38,10 @@ + #include <sys_defs.h> + #include <netinet/in.h> + #include <arpa/inet.h> ++#include <sys/socket.h> + #include <netdb.h> ++#include <stdlib.h> ++#include <string.h> + + #ifndef INADDR_NONE + #define INADDR_NONE 0xffffffff +@@ -48,15 +51,47 @@ + + #include <inet_addr_list.h> + #include <inet_addr_host.h> ++#ifdef TEST ++#include <msg.h> ++#endif + + /* inet_addr_host - look up address list for host */ + + int inet_addr_host(INET_ADDR_LIST *addr_list, const char *hostname) + { ++#ifdef INET6 ++ int s; ++ struct addrinfo hints, *res0, *res; ++#ifdef TEST ++ char buforhosta[1024]; ++#endif ++ int error; ++#else + struct hostent *hp; + struct in_addr addr; ++#endif + int initial_count = addr_list->used; + ++#ifdef INET6 ++ memset(&hints, 0, sizeof(hints)); ++ hints.ai_family = PF_UNSPEC; ++ hints.ai_socktype = SOCK_DGRAM; ++ error = getaddrinfo(hostname, NULL, &hints, &res0); ++ if (error == 0) { ++ for (res = res0; res; res = res->ai_next) { ++ if(res->ai_family != AF_INET && res->ai_family != AF_INET6) ++ continue; ++ /* filter out address families that are not supported */ ++ s = socket(res->ai_family, SOCK_DGRAM, 0); ++ if (s < 0) ++ continue; ++ close(s); ++ ++ inet_addr_list_append(addr_list, res->ai_addr); ++ } ++ freeaddrinfo(res0); ++ } ++#else + if ((addr.s_addr = inet_addr(hostname)) != INADDR_NONE) { + inet_addr_list_append(addr_list, &addr); + } else { +@@ -65,9 +100,12 @@ + inet_addr_list_append(addr_list, + (struct in_addr *) * hp->h_addr_list++); + } ++#endif ++ + return (addr_list->used - initial_count); + } + ++ + #ifdef TEST + + #include <msg.h> +@@ -78,6 +116,8 @@ + { + INET_ADDR_LIST addr_list; + int i; ++ struct sockaddr *sa; ++ char hbuf[NI_MAXHOST]; + + msg_vstream_init(argv[0], VSTREAM_ERR); + +@@ -89,8 +129,12 @@ + if (inet_addr_host(&addr_list, *argv) == 0) + msg_fatal("not found: %s", *argv); + +- for (i = 0; i < addr_list.used; i++) +- vstream_printf("%s\n", inet_ntoa(addr_list.addrs[i])); ++ for (i = 0; i < addr_list.used; i++) { ++ sa = (struct sockaddr *)&addr_list.addrs[i]; ++ getnameinfo(sa, SA_LEN(sa), hbuf, sizeof(hbuf), NULL, 0, ++ NI_NUMERICHOST); ++ vstream_printf("%s\n", hbuf); ++ } + vstream_fflush(VSTREAM_OUT); + } + inet_addr_list_free(&addr_list); +diff -Pur postfix-1.1.11-20020613-orig/src/util/inet_addr_list.c postfix-1.1.11-20020613/src/util/inet_addr_list.c +--- postfix-1.1.11-20020613-orig/src/util/inet_addr_list.c Tue Jul 31 20:13:41 2001 ++++ postfix-1.1.11-20020613/src/util/inet_addr_list.c Wed Jun 26 15:26:49 2002 +@@ -51,6 +51,13 @@ + #include <arpa/inet.h> + #include <stdlib.h> + ++#include <netdb.h> ++ ++#ifdef INET6 ++#include <string.h> ++#include <sys/socket.h> ++#endif ++ + /* Utility library. */ + + #include <msg.h> +@@ -63,12 +70,39 @@ + { + list->used = 0; + list->size = 2; ++#ifdef INET6 ++ list->addrs = (struct sockaddr_storage *) ++#else + list->addrs = (struct in_addr *) ++#endif + mymalloc(sizeof(*list->addrs) * list->size); + } + + /* inet_addr_list_append - append address to internet address list */ + ++#ifdef INET6 ++void inet_addr_list_append(INET_ADDR_LIST *list, ++ struct sockaddr * addr) ++{ ++ char *myname = "inet_addr_list_append"; ++ char hbuf[NI_MAXHOST]; ++ ++ if (msg_verbose > 1) { ++ if (getnameinfo(addr, SA_LEN(addr), hbuf, sizeof(hbuf), NULL, 0, ++ NI_NUMERICHOST)) { ++ strncpy(hbuf, "??????", sizeof(hbuf)); ++ } ++ msg_info("%s: %s", myname, hbuf); ++ } ++ ++ if (list->used >= list->size) ++ list->size *= 2; ++ list->addrs = (struct sockaddr_storage *) ++ myrealloc((char *) list->addrs, ++ sizeof(*list->addrs) * list->size); ++ memcpy(&list->addrs[list->used++], addr, SA_LEN(addr)); ++} ++#else + void inet_addr_list_append(INET_ADDR_LIST *list, struct in_addr * addr) + { + char *myname = "inet_addr_list_append"; +@@ -83,15 +117,22 @@ + sizeof(*list->addrs) * list->size); + list->addrs[list->used++] = *addr; + } ++#endif + + /* inet_addr_list_comp - compare addresses */ + + static int inet_addr_list_comp(const void *a, const void *b) + { ++#ifdef INET6 ++ if(((struct sockaddr*)a)->sa_family != ((struct sockaddr*)b)->sa_family) ++ return ( ((struct sockaddr*)a)->sa_family - ((struct sockaddr*)b)->sa_family ); ++ return memcmp(a,b,SA_LEN((struct sockaddr*)a)); ++#else + const struct in_addr *a_addr = (const struct in_addr *) a; + const struct in_addr *b_addr = (const struct in_addr *) b; + + return (a_addr->s_addr - b_addr->s_addr); ++#endif + } + + /* inet_addr_list_uniq - weed out duplicates */ +diff -Pur postfix-1.1.11-20020613-orig/src/util/inet_addr_list.h postfix-1.1.11-20020613/src/util/inet_addr_list.h +--- postfix-1.1.11-20020613-orig/src/util/inet_addr_list.h Tue Jul 31 19:56:47 2001 ++++ postfix-1.1.11-20020613/src/util/inet_addr_list.h Wed Jun 26 15:26:49 2002 +@@ -16,19 +16,42 @@ + */ + #include <netinet/in.h> + ++#ifndef SA_LEN ++# ifndef HAS_SA_LEN ++# define SA_LEN(x) (((x)->sa_family == AF_INET6) ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in)) ++# define SS_LEN(x) (((x).ss_family == AF_INET6) ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in)) ++# else ++# define SA_LEN(x) ((x)->sa_len) ++# define SS_LEN(x) ((x).ss_len) ++# endif ++#else ++# ifndef SS_LEN ++# define SS_LEN(x) (((x).ss_family == AF_INET6) ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in)) ++# endif ++#endif ++ + /* + * External interface. + */ + typedef struct INET_ADDR_LIST { + int used; /* nr of elements in use */ + int size; /* actual list size */ ++#ifdef INET6 ++ struct sockaddr_storage *addrs; /* payload */ ++#else + struct in_addr *addrs; /* payload */ ++#endif + } INET_ADDR_LIST; + + extern void inet_addr_list_init(INET_ADDR_LIST *); + extern void inet_addr_list_free(INET_ADDR_LIST *); + extern void inet_addr_list_uniq(INET_ADDR_LIST *); ++#ifdef INET6 ++struct sockaddr; ++extern void inet_addr_list_append(INET_ADDR_LIST *, struct sockaddr *); ++#else + extern void inet_addr_list_append(INET_ADDR_LIST *, struct in_addr *); ++#endif + + /* LICENSE + /* .ad +diff -Pur postfix-1.1.11-20020613-orig/src/util/inet_addr_local.c postfix-1.1.11-20020613/src/util/inet_addr_local.c +--- postfix-1.1.11-20020613-orig/src/util/inet_addr_local.c Sun Feb 25 19:20:19 2001 ++++ postfix-1.1.11-20020613/src/util/inet_addr_local.c Wed Jun 26 15:26:49 2002 +@@ -47,6 +47,13 @@ + #endif + #include <errno.h> + #include <string.h> ++#if defined(INET6) && (defined (LINUX) || defined (LINUX2)) ++#include <netdb.h> ++#include <stdio.h> ++#endif ++#ifdef HAVE_GETIFADDRS ++#include <ifaddrs.h> ++#endif + + /* Utility library. */ + +@@ -78,18 +85,104 @@ + + int inet_addr_local(INET_ADDR_LIST *addr_list, INET_ADDR_LIST *mask_list) + { ++#ifdef HAVE_GETIFADDRS ++ char *myname = "inet_addr_local"; ++ struct ifaddrs *ifap, *ifa; ++ int initial_count = addr_list->used; ++ struct sockaddr *sa, *sam; ++#ifdef INET6 ++#ifdef __KAME__ ++ struct sockaddr_in6 addr6; ++#endif ++#else ++ void *addr,*addrm; ++#endif ++ ++ if (getifaddrs(&ifap) < 0) ++ msg_fatal("%s: getifaddrs: %m", myname); ++ ++ for (ifa = ifap; ifa; ifa = ifa->ifa_next) { ++ if (! (ifa->ifa_flags & IFF_RUNNING) || ifa->ifa_addr==NULL) ++ continue; ++ sa = ifa->ifa_addr; ++ sam = ifa->ifa_netmask; ++ switch (ifa->ifa_addr->sa_family) { ++ case AF_INET: ++#ifndef INET6 ++ addr = (void *)&((struct sockaddr_in *)ifa->ifa_addr)->sin_addr; ++ addrm = (void *)&((struct sockaddr_in *)ifa->ifa_netmask)->sin_addr; ++#endif ++ break; ++#ifdef INET6 ++ case AF_INET6: ++#ifdef __KAME__ ++ memcpy(&addr6, ifa->ifa_addr, ifa->ifa_addr->sa_len); ++ /* decode scoped address notation */ ++ if ((IN6_IS_ADDR_LINKLOCAL(&addr6.sin6_addr) || ++ IN6_IS_ADDR_SITELOCAL(&addr6.sin6_addr)) && ++ addr6.sin6_scope_id == 0) { ++ addr6.sin6_scope_id = ntohs(addr6.sin6_addr.s6_addr[3] | ++ (unsigned int)addr6.sin6_addr.s6_addr[2] << 8); ++ addr6.sin6_addr.s6_addr[2] = addr6.sin6_addr.s6_addr[3] = 0; ++ sa = (struct sockaddr *)&addr6; ++ } ++#endif ++ break; ++#endif ++ default: ++ continue; ++ } ++ ++#ifdef INET6 ++ inet_addr_list_append(addr_list, sa); ++ if (mask_list != NULL) ++ inet_addr_list_append(mask_list, sam); ++#else ++ inet_addr_list_append(addr_list, (struct in_addr *)addr); ++ if (mask_list != NULL) ++ inet_addr_list_append(mask_list, (struct in_addr *)addrm); ++#endif ++ } ++ ++ freeifaddrs(ifap); ++ return (addr_list->used - initial_count); ++#else + char *myname = "inet_addr_local"; + struct ifconf ifc; + struct ifreq *ifr; + struct ifreq *the_end; + int sock; +- VSTRING *buf = vstring_alloc(1024); ++ VSTRING *buf; + int initial_count = addr_list->used; + struct in_addr addr; + struct ifreq *ifr_mask; ++ int af = AF_INET; ++#ifdef INET6 ++#if defined (LINUX) || defined (LINUX2) ++#define _PATH_PROCNET_IFINET6 "/proc/net/if_inet6" ++ FILE *f; ++ char addr6p[8][5], addr6res[40], devname[20]; ++ int plen, scope, dad_status, if_idx, gaierror; ++ struct addrinfo hints, *res, *res0; ++#endif ++ struct sockaddr_in6 addr6; + +- if ((sock = socket(PF_INET, SOCK_DGRAM, 0)) < 0) ++other_socket_type: ++#endif ++ buf = vstring_alloc(1024); ++ ++ if ((sock = socket(af, SOCK_DGRAM, 0)) < 0) { ++#ifdef INET6 ++ if (af == AF_INET6) ++ { ++ if (msg_verbose) ++ msg_warn("%s: socket: %m", myname); ++ goto end; ++ } ++ else ++#endif + msg_fatal("%s: socket: %m", myname); ++ } + + /* + * Get the network interface list. XXX The socket API appears to have no +@@ -126,10 +219,15 @@ + */ + the_end = (struct ifreq *) (ifc.ifc_buf + ifc.ifc_len); + for (ifr = ifc.ifc_req; ifr < the_end;) { +- if (ifr->ifr_addr.sa_family == AF_INET) { /* IP interface */ ++ if ((ifr->ifr_addr.sa_family == AF_INET) && ++ (ifr->ifr_addr.sa_family == af)) { /* IP interface */ + addr = ((struct sockaddr_in *) & ifr->ifr_addr)->sin_addr; + if (addr.s_addr != INADDR_ANY) { /* has IP address */ ++#ifdef INET6 ++ inet_addr_list_append(addr_list, &ifr->ifr_addr); ++#else + inet_addr_list_append(addr_list, &addr); ++#endif + if (mask_list) { + ifr_mask = (struct ifreq *) mymalloc(IFREQ_SIZE(ifr)); + memcpy((char *) ifr_mask, (char *) ifr, IFREQ_SIZE(ifr)); +@@ -141,11 +239,61 @@ + } + } + } ++#ifdef INET6 ++ else if ((ifr->ifr_addr.sa_family == AF_INET6) && ++ (ifr->ifr_addr.sa_family == af)) { /* IPv6 interface */ ++ addr6 = *((struct sockaddr_in6 *) & ifr->ifr_addr); ++#ifdef __KAME__ ++ /* decode scoped address notation */ ++ if ((IN6_IS_ADDR_LINKLOCAL(&addr6.sin6_addr) || ++ IN6_IS_ADDR_SITELOCAL(&addr6.sin6_addr)) && ++ addr6.sin6_scope_id == 0) { ++ addr6.sin6_scope_id = ntohs(addr6.sin6_addr.s6_addr[3] | ++ (unsigned int)addr6.sin6_addr.s6_addr[2] << 8); ++ addr6.sin6_addr.s6_addr[2] = addr6.sin6_addr.s6_addr[3] = 0; ++ } ++#endif ++ if (!(IN6_IS_ADDR_UNSPECIFIED(&addr6.sin6_addr))) ++ inet_addr_list_append(addr_list, (struct sockaddr *)&addr6); ++ } ++#endif + ifr = NEXT_INTERFACE(ifr); + } + vstring_free(buf); + (void) close(sock); ++#ifdef INET6 ++end: ++ if (af != AF_INET6) { ++ af = AF_INET6; ++ goto other_socket_type; ++ } ++#if defined (LINUX) || defined (LINUX2) ++ if ((f = fopen(_PATH_PROCNET_IFINET6, "r")) != NULL) { ++ while (fscanf(f, "%4s%4s%4s%4s%4s%4s%4s%4s %02x %02x %02x %02x %20s\n", ++ addr6p[0], addr6p[1], addr6p[2], addr6p[3], addr6p[4], ++ addr6p[5], addr6p[6], addr6p[7], ++ &if_idx, &plen, &scope, &dad_status, devname) != EOF) { ++ sprintf(addr6res, "%s:%s:%s:%s:%s:%s:%s:%s", ++ addr6p[0], addr6p[1], addr6p[2], addr6p[3], ++ addr6p[4], addr6p[5], addr6p[6], addr6p[7]); ++ addr6res[sizeof(addr6res) - 1] = 0; ++ memset(&hints, 0, sizeof(hints)); ++ hints.ai_flags = AI_NUMERICHOST; ++ hints.ai_family = AF_UNSPEC; ++ hints.ai_socktype = SOCK_DGRAM; ++ gaierror = getaddrinfo(addr6res, NULL, &hints, &res0); ++ if (!gaierror) { ++ for (res = res0; res; res = res->ai_next) { ++ inet_addr_list_append(addr_list, res->ai_addr); ++ } ++ freeaddrinfo(res0); ++ } ++ } ++ } ++#endif /* linux */ ++#endif + return (addr_list->used - initial_count); ++#endif + } + + #ifdef TEST +@@ -158,6 +306,8 @@ + INET_ADDR_LIST addr_list; + INET_ADDR_LIST mask_list; + int i; ++ char abuf[NI_MAXHOST], mbuf[NI_MAXHOST]; ++ struct sockaddr *sa; + + msg_vstream_init(argv[0], VSTREAM_ERR); + +@@ -172,8 +322,17 @@ + msg_warn("found only one active network interface"); + + for (i = 0; i < addr_list.used; i++) { +- vstream_printf("%s/", inet_ntoa(addr_list.addrs[i])); +- vstream_printf("%s\n", inet_ntoa(mask_list.addrs[i])); ++ sa = (struct sockaddr *)&addr_list.addrs[i]; ++ if (getnameinfo(sa, SA_LEN(sa), abuf, sizeof(abuf), NULL, 0, ++ NI_NUMERICHOST)) { ++ strncpy(abuf, "???", sizeof(abuf)); ++ } ++ sa = (struct sockaddr *)&mask_list.addrs[i]; ++ if (getnameinfo(sa, SA_LEN(sa), mbuf, sizeof(mbuf), NULL, 0, ++ NI_NUMERICHOST)) { ++ strncpy(mbuf, "???", sizeof(mbuf)); ++ } ++ vstream_printf("%s/%s\n", abuf, mbuf); + } + vstream_fflush(VSTREAM_OUT); + inet_addr_list_free(&addr_list); +diff -Pur postfix-1.1.11-20020613-orig/src/util/inet_connect.c postfix-1.1.11-20020613/src/util/inet_connect.c +--- postfix-1.1.11-20020613-orig/src/util/inet_connect.c Mon Nov 20 19:06:31 2000 ++++ postfix-1.1.11-20020613/src/util/inet_connect.c Wed Jun 26 15:26:49 2002 +@@ -55,6 +55,9 @@ + #include <string.h> + #include <unistd.h> + #include <errno.h> ++#ifdef INET6 ++#include <netdb.h> ++#endif + + /* Utility library. */ + +@@ -73,7 +76,12 @@ + char *buf; + char *host; + char *port; ++#ifdef INET6 ++ struct addrinfo hints, *res, *res0; ++ int error; ++#else + struct sockaddr_in sin; ++#endif + int sock; + + /* +@@ -81,14 +89,58 @@ + * the local host. + */ + buf = inet_parse(addr, &host, &port); ++#ifdef INET6 ++ if (*host == 0) ++ host = NULL; ++ memset(&hints, 0, sizeof(hints)); ++ hints.ai_family = PF_UNSPEC; ++ hints.ai_socktype = SOCK_STREAM; ++ hints.ai_flags = AI_NUMERICHOST; /* find_inet_addr is numeric only */ ++ if (getaddrinfo(host, port, &hints, &res0)) ++ msg_fatal("host not found: %s", host); ++#else + if (*host == 0) + host = "localhost"; + memset((char *) &sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = find_inet_addr(host); + sin.sin_port = find_inet_port(port, "tcp"); ++#endif + myfree(buf); + ++#ifdef INET6 ++ sock = -1; ++ for (res = res0; res; res = res->ai_next) { ++ if ((res->ai_family != AF_INET) && (res->ai_family != AF_INET6)) ++ continue; ++ ++ sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol); ++ if (sock < 0) ++ continue; ++ if (timeout > 0) { ++ non_blocking(sock, NON_BLOCKING); ++ if (timed_connect(sock, res->ai_addr, res->ai_addrlen, timeout) < 0) { ++ close(sock); ++ sock = -1; ++ continue; ++ } ++ if (block_mode != NON_BLOCKING) ++ non_blocking(sock, block_mode); ++ break; ++ } else { ++ non_blocking(sock, block_mode); ++ if (connect(sock, res->ai_addr, res->ai_addrlen) < 0 ++ && errno != EINPROGRESS) { ++ close(sock); ++ sock = -1; ++ continue; ++ } ++ break; ++ } ++ } ++ freeaddrinfo(res0); ++ return sock; ++#else + /* + * Create a client socket. + */ +@@ -121,4 +173,5 @@ + } + return (sock); + } ++#endif + } +diff -Pur postfix-1.1.11-20020613-orig/src/util/inet_listen.c postfix-1.1.11-20020613/src/util/inet_listen.c +--- postfix-1.1.11-20020613-orig/src/util/inet_listen.c Mon Nov 20 19:06:32 2000 ++++ postfix-1.1.11-20020613/src/util/inet_listen.c Wed Jun 26 15:26:49 2002 +@@ -6,7 +6,7 @@ + /* SYNOPSIS + /* #include <listen.h> + /* +-/* int inet_listen(addr, backlog, block_mode) ++/* int inet_listen(addr, backlog, block_mode, addinuse_fatal) + /* const char *addr; + /* int backlog; + /* int block_mode; +@@ -51,11 +51,17 @@ + #include <sys_defs.h> + #include <sys/socket.h> + #include <netinet/in.h> ++#ifdef INET6 ++#if (! __GLIBC__ >= 2 && __GLIBC_MINOR__ >=1 ) ++#include <netinet6/in6.h> ++#endif ++#endif + #include <arpa/inet.h> + #include <netdb.h> + #ifndef MAXHOSTNAMELEN + #include <sys/param.h> + #endif ++#include <errno.h> + #include <string.h> + #include <unistd.h> + +@@ -77,35 +83,116 @@ + + /* inet_listen - create inet-domain listener */ + +-int inet_listen(const char *addr, int backlog, int block_mode) ++int inet_listen(const char *addr, int backlog, int block_mode, int addrinuse_fatal) + { ++#ifdef INET6 ++ struct addrinfo *res, *res0, hints; ++ int error; ++#else ++ struct ai { ++ int ai_family; ++ int ai_socktype; ++ int ai_protocol; ++ struct sockaddr *ai_addr; ++ SOCKADDR_SIZE ai_addrlen; ++ struct ai *ai_next; ++ } *res, *res0, resbody; + struct sockaddr_in sin; ++#endif + int sock; + int t = 1; ++ int addrinuse = 0; + char *buf; + char *host; + char *port; ++#ifdef INET6 ++ char hbuf[NI_MAXHOST], pbuf[NI_MAXSERV]; ++#else ++ char hbuf[sizeof("255.255.255.255") + 1]; ++ char pbuf[sizeof("255.255.255.255") + 1]; ++#endif ++ char *cause = "unknown"; + + /* + * Translate address information to internal form. + */ + buf = inet_parse(addr, &host, &port); +- memset((char *) &sin, 0, sizeof(sin)); ++#ifdef INET6 ++ memset(&hints, 0, sizeof(hints)); ++ hints.ai_flags = AI_PASSIVE; ++ hints.ai_family = AF_UNSPEC; ++ hints.ai_socktype = SOCK_STREAM; ++ error = getaddrinfo(*host ? host : NULL, *port ? port : "0", &hints, &res0); ++ if (error) { ++ msg_fatal("getaddrinfo: %s", gai_strerror(error)); ++ } ++ myfree(buf); ++#else ++ memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; ++#ifdef HAS_SA_LEN ++ sin.sin_len = sizeof(sin); ++#endif + sin.sin_port = find_inet_port(port, "tcp"); + sin.sin_addr.s_addr = (*host ? find_inet_addr(host) : INADDR_ANY); +- myfree(buf); + +- /* +- * Create a listener socket. +- */ +- if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) +- msg_fatal("socket: %m"); +- if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *) &t, sizeof(t)) < 0) +- msg_fatal("setsockopt: %m"); +- if (bind(sock, (struct sockaddr *) & sin, sizeof(sin)) < 0) +- msg_fatal("bind %s port %d: %m", sin.sin_addr.s_addr == INADDR_ANY ? +- "INADDR_ANY" : inet_ntoa(sin.sin_addr), ntohs(sin.sin_port)); ++ memset(&resbody, 0, sizeof(resbody)); ++ resbody.ai_socktype = SOCK_STREAM; ++ resbody.ai_family = AF_INET; ++ resbody.ai_addr = (struct sockaddr *)&sin; ++ resbody.ai_addrlen = sizeof(sin); ++ ++ res0 = &resbody; ++#endif ++ ++ sock = -1; ++ for (res = res0; res; res = res->ai_next) { ++ if ((res->ai_family != AF_INET) && (res->ai_family != AF_INET6)) ++ continue; ++ ++ /* ++ * Create a listener socket. ++ */ ++ if ((sock = socket(res->ai_family, res->ai_socktype, 0)) < 0) { ++ cause = "socket"; ++ continue; ++ } ++#ifdef IPV6_V6ONLY ++ if (res->ai_family == AF_INET6 && ++ setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (char *) &t, sizeof(t)) < 0) { ++ /* if kernel/libc don't support this simple ignore it ++ cause = "setsockopt(IPV6_V6ONLY)"; ++ close(sock); ++ sock = -1; ++ continue; ++ */ ++ ; ++ } ++#endif ++ if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *) &t, sizeof(t)) < 0) { ++ cause = "setsockopt(SO_REUSEADDR)"; ++ close(sock); ++ sock = -1; ++ continue; ++ } ++ ++ if (bind(sock, res->ai_addr, res->ai_addrlen) < 0) { ++ cause = "bind"; ++ if (errno == EADDRINUSE) ++ addrinuse = 1; ++ close(sock); ++ sock = -1; ++ continue; ++ } ++ break; ++ } ++ if (sock < 0 && (addrinuse_fatal || !addrinuse)) ++ msg_fatal("%s: %m", cause); ++#ifdef INET6 ++ freeaddrinfo(res0); ++#endif ++ if (sock < 0) ++ return -1; + non_blocking(sock, block_mode); + if (listen(sock, backlog) < 0) + msg_fatal("listen: %m"); +diff -Pur postfix-1.1.11-20020613-orig/src/util/listen.h postfix-1.1.11-20020613/src/util/listen.h +--- postfix-1.1.11-20020613-orig/src/util/listen.h Mon Mar 22 02:57:11 1999 ++++ postfix-1.1.11-20020613/src/util/listen.h Wed Jun 26 15:26:49 2002 +@@ -20,7 +20,7 @@ + * Listener external interface. + */ + extern int unix_listen(const char *, int, int); +-extern int inet_listen(const char *, int, int); ++extern int inet_listen(const char *, int, int, int); + extern int fifo_listen(const char *, int, int); + extern int stream_listen(const char *, int, int); + +diff -Pur postfix-1.1.11-20020613-orig/src/util/match_list.c postfix-1.1.11-20020613/src/util/match_list.c +--- postfix-1.1.11-20020613-orig/src/util/match_list.c Tue Nov 20 21:07:15 2001 ++++ postfix-1.1.11-20020613/src/util/match_list.c Wed Jun 26 15:26:49 2002 +@@ -118,7 +118,7 @@ + list = match_list_parse(list, vstring_str(buf)); + if (vstream_fclose(fp)) + msg_fatal("%s: read file %s: %m", myname, pattern); +- } else if (strchr(pattern, ':') != 0) { /* type:table */ ++ } else if ((strchr(pattern, ']') == 0) && (strchr(pattern, ':') != 0)) { /* type:table */ + for (cp = pattern; *cp == '!'; cp++) + /* void */ ; + if (dict_handle(pattern) == 0) +diff -Pur postfix-1.1.11-20020613-orig/src/util/match_ops.c postfix-1.1.11-20020613/src/util/match_ops.c +--- postfix-1.1.11-20020613-orig/src/util/match_ops.c Tue Nov 20 21:16:10 2001 ++++ postfix-1.1.11-20020613/src/util/match_ops.c Wed Jun 26 15:26:49 2002 +@@ -81,6 +81,307 @@ + #include <match_ops.h> + #include <stringops.h> + ++#ifdef INET6 ++/* ++ * $Id: tls+ipv6.diff,v 1.1 2002/07/22 23:09:48 raker Exp $ ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version ++ * 2 of the License, or (at your option) any later version. ++ * ++ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> ++ * ++ * Modifications: ++ * Artur Frysiak <wiget@pld.org.pl> ++ * Arkadiusz Mi¶kiewicz <misiek@pld.org.pl> ++ */ ++ ++#include <stdio.h> ++#include <stdlib.h> ++#include <unistd.h> ++#include <syslog.h> ++#include <fcntl.h> ++#include <sys/socket.h> ++#include <netinet/in.h> ++#include <string.h> ++#include <netdb.h> ++#include <arpa/inet.h> ++#include <resolv.h> ++ ++#ifndef AF_DECnet ++#define AF_DECnet 12 ++#endif ++ ++#ifndef PF_PACKET ++#define PF_PACKET 17 ++#endif ++ ++typedef struct ++{ ++ unsigned char family; ++ unsigned char bytelen; ++ signed short bitlen; ++ unsigned int data[4]; ++} inet_prefix; ++ ++/* prototypes */ ++int masked_match(char *, char *, char *); ++int get_integer(int *, char *, int); ++int get_addr_1(inet_prefix *, char *, int); ++int get_prefix_1(inet_prefix *, char *, int); ++int get_addr(inet_prefix *, char *, int); ++int get_prefix(inet_prefix *, char *, int); ++unsigned int get_addr32(char *); ++int matches(char *, char *); ++int inet_addr_match(inet_prefix *, inet_prefix *, int); ++int mask_match(char *, char *, char *); ++ ++int get_integer(int *val, char *arg, int base) ++{ ++ long res; ++ char *ptr; ++ ++ if (!arg || !*arg) ++ return -1; ++ res = strtol(arg, &ptr, base); ++ if (!ptr || ptr == arg || *ptr || res > INT_MAX || res < INT_MIN) ++ return -1; ++ *val = res; ++ return 0; ++} ++ ++int get_addr_1(inet_prefix *addr, char *name, int family) ++{ ++ char *cp; ++ unsigned char *ap = (unsigned char*)addr->data; ++ int i; ++ ++ memset(addr, 0, sizeof(*addr)); ++ ++ if (strcmp(name, "default") == 0 || strcmp(name, "any") == 0) { ++ if (family == AF_DECnet) ++ return -1; ++ addr->family = family; ++ addr->bytelen = (family == AF_INET6 ? 16 : 4); ++ addr->bitlen = -1; ++ return 0; ++ } ++ ++ if (strchr(name, ':')) { ++ addr->family = AF_INET6; ++ if (family != AF_UNSPEC && family != AF_INET6) ++ return -1; ++ if (inet_pton(AF_INET6, name, addr->data) <= 0) ++ return -1; ++ addr->bytelen = 16; ++ addr->bitlen = -1; ++ return 0; ++ } ++ addr->family = AF_INET; ++ if (family != AF_UNSPEC && family != AF_INET) ++ return -1; ++ addr->bytelen = 4; ++ addr->bitlen = -1; ++ for (cp = name, i = 0; *cp; cp++) { ++ if (*cp <= '9' && *cp >= '0') { ++ ap[i] = 10*ap[i] + (*cp-'0'); ++ continue; ++ } ++ if (*cp == '.' && ++i <= 3) ++ continue; ++ return -1; ++ } ++ return 0; ++} ++ ++int get_prefix_1(inet_prefix *dst, char *arg, int family) ++{ ++ int err; ++ unsigned plen; ++ char *slash; ++ ++ memset(dst, 0, sizeof(*dst)); ++ ++ if (strcmp(arg, "default") == 0 || strcmp(arg, "any") == 0) { ++ if (family == AF_DECnet) ++ return -1; ++ dst->family = family; ++ dst->bytelen = 0; ++ dst->bitlen = 0; ++ return 0; ++ } ++ ++ slash = strchr(arg, '/'); ++ if (slash) ++ *slash = 0; ++ err = get_addr_1(dst, arg, family); ++ if (err == 0) { ++ switch(dst->family) { ++ case AF_INET6: ++ dst->bitlen = 128; ++ break; ++ case AF_DECnet: ++ dst->bitlen = 16; ++ break; ++ default: ++ case AF_INET: ++ dst->bitlen = 32; ++ } ++ if (slash) { ++ if (get_integer(&plen, slash+1, 0) || plen > dst->bitlen) { ++ err = -1; ++ goto done; ++ } ++ dst->bitlen = plen; ++ } ++ } ++done: ++ if (slash) ++ *slash = '/'; ++ return err; ++} ++ ++int get_addr(inet_prefix *dst, char *arg, int family) ++{ ++#ifdef AF_PACKET ++ if (family == AF_PACKET) ++ return -1; ++#endif ++ if (get_addr_1(dst, arg, family)) ++ return -1; ++ return 0; ++} ++ ++int get_prefix(inet_prefix *dst, char *arg, int family) ++{ ++#ifdef AF_PACKET ++ if (family == AF_PACKET) ++ return -1; ++#endif ++ if (get_prefix_1(dst, arg, family)) ++ return -1; ++ return 0; ++} ++ ++unsigned int get_addr32(char *name) ++{ ++ inet_prefix addr; ++ if (get_addr_1(&addr, name, AF_INET)) ++ return -1; ++ return addr.data[0]; ++} ++ ++int matches(char *cmd, char *pattern) ++{ ++ int len = strlen(cmd); ++ if (len > strlen(pattern)) ++ return -1; ++ return memcmp(pattern, cmd, len); ++} ++ ++int inet_addr_match(inet_prefix *a, inet_prefix *b, int bits) ++{ ++ unsigned int *a1 = a->data; ++ unsigned int *a2 = b->data; ++ int words = bits >> 0x05; ++ ++ bits &= 0x1f; ++ ++ if (words) ++ if (memcmp(a1, a2, words << 2)) ++ return -1; ++ ++ if (bits) { ++ unsigned int w1, w2; ++ unsigned int mask; ++ ++ w1 = a1[words]; ++ w2 = a2[words]; ++ ++ mask = htonl((0xffffffff) << (0x20 - bits)); ++ ++ if ((w1 ^ w2) & mask) ++ return 1; ++ } ++ ++ return 0; ++} ++ ++/* zero if matches */ ++int mask_match(char *network, char *cprefix, char *address) ++{ ++ inet_prefix *inetwork; ++ inet_prefix *iaddress; ++ int ret, prefix; ++ ++ if (!(network && address && cprefix)) ++ return -1; ++ prefix = strtol(cprefix, (char **)NULL, 10); ++ if ((prefix < 0) || (prefix > 128)) ++ return -1; ++ if ((strlen(network) == 0) || (strlen(address) == 0)) ++ return -1; ++ ++ inetwork = malloc(sizeof(inet_prefix)); ++ iaddress = malloc(sizeof(inet_prefix)); ++ ++ if ((get_addr(iaddress, address, AF_UNSPEC) >= 0) ++ && (get_addr(inetwork, network, AF_UNSPEC) >= 0)) ++ ret = inet_addr_match(inetwork, iaddress, prefix); ++ else ++ ret = -1; ++ free(inetwork); ++ free(iaddress); ++ ++ /* 1 if matches */ ++ /* return (!ret); */ ++ /* 0 if matches */ ++ return ret; ++} ++ ++/* ++ * masked_match() - universal for IPv4 and IPv6 - 1 if matches ++ */ ++int masked_match(net_tok, mask_tok, string) ++char *net_tok; ++char *mask_tok; ++char *string; ++{ ++#ifdef INET6 ++ struct in6_addr in6[2]; ++ char v4addr[2][INET_ADDRSTRLEN]; ++ char newmask[6]; ++ int plen; ++#endif ++ ++ /* Check for NULL */ ++ if (!(net_tok && mask_tok && string)) ++ return 0; /* doesn't match!!! */ ++ ++ /* If IPv6 mapped convert to native-IPv4 */ ++#ifdef INET6 ++ if (inet_pton(AF_INET6, net_tok, &in6[0]) == 1 && ++ inet_pton(AF_INET6, string, &in6[1]) == 1 && ++ IN6_IS_ADDR_V4MAPPED(&in6[0]) && IN6_IS_ADDR_V4MAPPED(&in6[1])) { ++ plen = atoi(mask_tok); ++ if (32 < plen && plen < 129) { ++ sprintf(newmask, "%d", plen - 96); ++ mask_tok = newmask; ++ } ++ ++ (void)inet_ntop(AF_INET, &in6[0].s6_addr[12], v4addr[0], ++ sizeof(v4addr[0])); ++ net_tok = v4addr[0]; ++ (void)inet_ntop(AF_INET, &in6[1].s6_addr[12], v4addr[1], ++ sizeof(v4addr[1])); ++ string = v4addr[1]; ++ } ++#endif ++ return (!mask_match(net_tok, mask_tok, string)); ++} ++#endif ++ + /* match_string - match a string literal */ + + int match_string(int unused_flags, const char *string, const char *pattern) +@@ -177,6 +478,7 @@ + return (0); + } + ++#ifndef INET6 + /* match_parse_mask - parse net/mask pattern */ + + static int match_parse_mask(const char *pattern, unsigned long *net_bits, +@@ -198,27 +500,55 @@ + return (mask != 0); + } + ++#endif ++ + /* match_hostaddr - match host by address */ + + int match_hostaddr(int unused_flags, const char *addr, const char *pattern) + { + char *myname = "match_hostaddr"; ++#ifdef INET6 ++ char *network, *mask, *escl, *escr, *patternx; ++ struct in6_addr in6; ++ char v4addr[INET_ADDRSTRLEN]; ++#else + int mask_shift; + unsigned long mask_bits; + unsigned long net_bits; + unsigned long addr_bits; ++#endif + + if (msg_verbose) + msg_info("%s: %s ~? %s", myname, addr, pattern); + ++#ifdef INET6 ++ if (addr[strspn(addr, "01234567890./:abcdef")] != 0) ++#else + if (addr[strspn(addr, "01234567890./:")] != 0) ++#endif + return (0); + ++#ifdef INET6 ++ patternx = mystrdup(pattern); ++ escl = strchr(patternx,'['); ++ escr = strrchr(patternx,']'); ++ if (escl && escr) { ++ *escr = 0; ++ sprintf(patternx, "%s%s", escl + 1, escr + 1); ++ pattern = patternx; ++ } ++#endif ++ + /* + * Try dictionary lookup. This can be case insensitive. XXX Probably + * should also try again after stripping least significant octets. + */ +- if (strchr(pattern, ':') != 0) { ++#ifdef INET6 ++ if (!(escl && escr) && strchr(pattern, ':') != 0) ++#else ++ if (strchr(pattern, ':') != 0) ++#endif ++ { + if (dict_lookup(pattern, addr) != 0) + return (1); + if (dict_errno != 0) +@@ -229,6 +559,12 @@ + /* + * Try an exact match with the host address. + */ ++#ifdef INET6 ++ if (inet_pton(AF_INET6, addr, &in6) == 1 && IN6_IS_ADDR_V4MAPPED(&in6)) { ++ (void)inet_ntop(AF_INET, &in6.s6_addr[12], v4addr, sizeof(v4addr)); ++ addr = v4addr; ++ } ++#endif + if (strcasecmp(addr, pattern) == 0) { + return (1); + } +@@ -237,6 +573,20 @@ + * In a net/mask pattern, the mask is specified as the number of bits of + * the network part. + */ ++#ifdef INET6 ++ network = mystrdup(patternx); ++ mask = split_at(network, '/'); ++ ++ if (masked_match(network, mask, (char *)addr)) { ++ myfree(network); ++ myfree(patternx); ++ return (1); ++ } else { ++ myfree(network); ++ myfree(patternx); ++ } ++#else ++ + if (match_parse_mask(pattern, &net_bits, &mask_shift)) { + addr_bits = inet_addr(addr); + if (addr_bits == INADDR_NONE) +@@ -244,5 +594,6 @@ + mask_bits = htonl((0xffffffff) << (BITS_PER_ADDR - mask_shift)); + return ((addr_bits & mask_bits) == (net_bits & mask_bits)); + } ++#endif + return (0); + } +diff -Pur postfix-1.1.11-20020613-orig/src/util/sdbm.c postfix-1.1.11-20020613/src/util/sdbm.c +--- postfix-1.1.11-20020613-orig/src/util/sdbm.c Thu Jan 1 01:00:00 1970 ++++ postfix-1.1.11-20020613/src/util/sdbm.c Wed Jun 26 15:26:49 2002 +@@ -0,0 +1,971 @@ ++/*++ ++/* NAME ++/* sdbm 3h ++/* SUMMARY ++/* SDBM Simple DBM: ndbm work-alike hashed database library ++/* SYNOPSIS ++/* include "sdbm.h" ++/* DESCRIPTION ++/* This file includes the public domain SDBM (ndbm work-alike hashed ++/* database library), based on Per-Aake Larson's Dynamic Hashing ++/* algorithms. BIT 18 (1978). ++/* author: oz@nexus.yorku.ca ++/* status: public domain ++/* The file has been patched following the advice of Uwe Ohse ++/* <uwe@ohse.de>: ++/* -------------------------------------------------------------- ++/* this patch fixes a problem with sdbms .dir file, which arrises when ++/* a second .dir block is needed for the first time. read() returns 0 ++/* in that case, and the library forgot to initialize that new block. ++/* ++/* A related problem is that the calculation of db->maxbno is wrong. ++/* It just appends 4096*BYTESIZ bits, which is not enough except for ++/* small databases (.dir basically doubles everytime it's too small). ++/* -------------------------------------------------------------- ++/* According to Uwe Ohse, the patch has also been submitted to the ++/* author of SDBM. (The 4096*BYTESIZ bits comment may apply with a ++/* different size for Postfix/TLS, as the patch was sent against the ++/* original SDBM distributiona and for Postfix/TLS I have changed the ++/* default sizes. ++/* .nf ++/*--*/ ++ ++/* ++ * sdbm - ndbm work-alike hashed database library ++ * based on Per-Aake Larson's Dynamic Hashing algorithms. BIT 18 (1978). ++ * author: oz@nexus.yorku.ca ++ * status: public domain. ++ * ++ * core routines ++ */ ++ ++#include <stdio.h> ++#include <stdlib.h> ++#ifdef WIN32 ++#include <io.h> ++#include <errno.h> ++#else ++#include <unistd.h> ++#endif ++#include <sys/types.h> ++#include <sys/stat.h> ++#include <fcntl.h> ++#include <errno.h> ++#include <string.h> ++#ifdef __STDC__ ++#include <stddef.h> ++#endif ++ ++#include <sdbm.h> ++ ++/* ++ * useful macros ++ */ ++#define bad(x) ((x).dptr == NULL || (x).dsize <= 0) ++#define exhash(item) sdbm_hash((item).dptr, (item).dsize) ++#define ioerr(db) ((db)->flags |= DBM_IOERR) ++ ++#define OFF_PAG(off) (long) (off) * PBLKSIZ ++#define OFF_DIR(off) (long) (off) * DBLKSIZ ++ ++static long masks[] = ++{ ++ 000000000000, 000000000001, 000000000003, 000000000007, ++ 000000000017, 000000000037, 000000000077, 000000000177, ++ 000000000377, 000000000777, 000000001777, 000000003777, ++ 000000007777, 000000017777, 000000037777, 000000077777, ++ 000000177777, 000000377777, 000000777777, 000001777777, ++ 000003777777, 000007777777, 000017777777, 000037777777, ++ 000077777777, 000177777777, 000377777777, 000777777777, ++ 001777777777, 003777777777, 007777777777, 017777777777 ++}; ++ ++datum nullitem = ++{NULL, 0}; ++ ++typedef struct ++{ ++ int dirf; /* directory file descriptor */ ++ int pagf; /* page file descriptor */ ++ int flags; /* status/error flags, see below */ ++ long maxbno; /* size of dirfile in bits */ ++ long curbit; /* current bit number */ ++ long hmask; /* current hash mask */ ++ long blkptr; /* current block for nextkey */ ++ int keyptr; /* current key for nextkey */ ++ long blkno; /* current page to read/write */ ++ long pagbno; /* current page in pagbuf */ ++ char *pagbuf; /* page file block buffer */ ++ long dirbno; /* current block in dirbuf */ ++ char *dirbuf; /* directory file block buffer */ ++} DBM; ++ ++ ++/* ************************* */ ++ ++/* ++ * sdbm - ndbm work-alike hashed database library ++ * based on Per-Aake Larson's Dynamic Hashing algorithms. BIT 18 (1978). ++ * author: oz@nexus.yorku.ca ++ * status: public domain. keep it that way. ++ * ++ * hashing routine ++ */ ++ ++/* ++ * polynomial conversion ignoring overflows ++ * [this seems to work remarkably well, in fact better ++ * then the ndbm hash function. Replace at your own risk] ++ * use: 65599 nice. ++ * 65587 even better. ++ */ ++static long sdbm_hash (char *str, int len) ++{ ++ unsigned long n = 0; ++ ++#ifdef DUFF ++#define HASHC n = *str++ + 65599 * n ++ if (len > 0) ++ { ++ int loop = (len + 8 - 1) >> 3; ++ ++ switch (len & (8 - 1)) ++ { ++ case 0: ++ do ++ { ++ HASHC; ++ case 7: ++ HASHC; ++ case 6: ++ HASHC; ++ case 5: ++ HASHC; ++ case 4: ++ HASHC; ++ case 3: ++ HASHC; ++ case 2: ++ HASHC; ++ case 1: ++ HASHC; ++ } ++ while (--loop); ++ } ++ ++ } ++#else ++ while (len--) ++ n = *str++ + 65599 * n; ++#endif ++ return n; ++} ++ ++/* ++ * check page sanity: ++ * number of entries should be something ++ * reasonable, and all offsets in the index should be in order. ++ * this could be made more rigorous. ++ */ ++static int chkpage (char *pag) ++{ ++ int n; ++ int off; ++ short *ino = (short *) pag; ++ ++ if ((n = ino[0]) < 0 || n > PBLKSIZ / sizeof (short)) ++ return 0; ++ ++ if (n > 0) ++ { ++ off = PBLKSIZ; ++ for (ino++; n > 0; ino += 2) ++ { ++ if (ino[0] > off || ino[1] > off || ++ ino[1] > ino[0]) ++ return 0; ++ off = ino[1]; ++ n -= 2; ++ } ++ } ++ return 1; ++} ++ ++/* ++ * search for the key in the page. ++ * return offset index in the range 0 < i < n. ++ * return 0 if not found. ++ */ ++static int seepair (char *pag, int n, char *key, int siz) ++{ ++ int i; ++ int off = PBLKSIZ; ++ short *ino = (short *) pag; ++ ++ for (i = 1; i < n; i += 2) ++ { ++ if (siz == off - ino[i] && ++ memcmp (key, pag + ino[i], siz) == 0) ++ return i; ++ off = ino[i + 1]; ++ } ++ return 0; ++} ++ ++#ifdef SEEDUPS ++static int duppair (char *pag, datum key) ++{ ++ short *ino = (short *) pag; ++ ++ return ino[0] > 0 && seepair (pag, ino[0], key.dptr, key.dsize) > 0; ++} ++ ++#endif ++ ++/* ************************* */ ++ ++/* ++ * sdbm - ndbm work-alike hashed database library ++ * based on Per-Aake Larson's Dynamic Hashing algorithms. BIT 18 (1978). ++ * author: oz@nexus.yorku.ca ++ * status: public domain. ++ * ++ * page-level routines ++ */ ++ ++/* ++ * page format: ++ * +------------------------------+ ++ * ino | n | keyoff | datoff | keyoff | ++ * +------------+--------+--------+ ++ * | datoff | - - - ----> | ++ * +--------+---------------------+ ++ * | F R E E A R E A | ++ * +--------------+---------------+ ++ * | <---- - - - | data | ++ * +--------+-----+----+----------+ ++ * | key | data | key | ++ * +--------+----------+----------+ ++ * ++ * calculating the offsets for free area: if the number ++ * of entries (ino[0]) is zero, the offset to the END of ++ * the free area is the block size. Otherwise, it is the ++ * nth (ino[ino[0]]) entry's offset. ++ */ ++ ++static int fitpair (char *pag, int need) ++{ ++ int n; ++ int off; ++ int avail; ++ short *ino = (short *) pag; ++ ++ off = ((n = ino[0]) > 0) ? ino[n] : PBLKSIZ; ++ avail = off - (n + 1) * sizeof (short); ++ need += 2 * sizeof (short); ++ ++ return need <= avail; ++} ++ ++static void putpair (char *pag, datum key, datum val) ++{ ++ int n; ++ int off; ++ short *ino = (short *) pag; ++ ++ off = ((n = ino[0]) > 0) ? ino[n] : PBLKSIZ; ++/* ++ * enter the key first ++ */ ++ off -= key.dsize; ++ (void) memcpy (pag + off, key.dptr, key.dsize); ++ ino[n + 1] = off; ++/* ++ * now the data ++ */ ++ off -= val.dsize; ++ (void) memcpy (pag + off, val.dptr, val.dsize); ++ ino[n + 2] = off; ++/* ++ * adjust item count ++ */ ++ ino[0] += 2; ++} ++ ++static datum getpair (char *pag, datum key) ++{ ++ int i; ++ int n; ++ datum val; ++ short *ino = (short *) pag; ++ ++ if ((n = ino[0]) == 0) ++ return nullitem; ++ ++ if ((i = seepair (pag, n, key.dptr, key.dsize)) == 0) ++ return nullitem; ++ ++ val.dptr = pag + ino[i + 1]; ++ val.dsize = ino[i] - ino[i + 1]; ++ return val; ++} ++ ++static datum getnkey (char *pag, int num) ++{ ++ datum key; ++ int off; ++ short *ino = (short *) pag; ++ ++ num = num * 2 - 1; ++ if (ino[0] == 0 || num > ino[0]) ++ return nullitem; ++ ++ off = (num > 1) ? ino[num - 1] : PBLKSIZ; ++ ++ key.dptr = pag + ino[num]; ++ key.dsize = off - ino[num]; ++ ++ return key; ++} ++ ++static int delpair (char *pag, datum key) ++{ ++ int n; ++ int i; ++ short *ino = (short *) pag; ++ ++ if ((n = ino[0]) == 0) ++ return 0; ++ ++ if ((i = seepair (pag, n, key.dptr, key.dsize)) == 0) ++ return 0; ++/* ++ * found the key. if it is the last entry ++ * [i.e. i == n - 1] we just adjust the entry count. ++ * hard case: move all data down onto the deleted pair, ++ * shift offsets onto deleted offsets, and adjust them. ++ * [note: 0 < i < n] ++ */ ++ if (i < n - 1) ++ { ++ int m; ++ char *dst = pag + (i == 1 ? PBLKSIZ : ino[i - 1]); ++ char *src = pag + ino[i + 1]; ++ int zoo = dst - src; ++ ++/* ++ * shift data/keys down ++ */ ++ m = ino[i + 1] - ino[n]; ++#ifdef DUFF ++#define MOVB *--dst = *--src ++ if (m > 0) ++ { ++ int loop = (m + 8 - 1) >> 3; ++ ++ switch (m & (8 - 1)) ++ { ++ case 0: ++ do ++ { ++ MOVB; ++ case 7: ++ MOVB; ++ case 6: ++ MOVB; ++ case 5: ++ MOVB; ++ case 4: ++ MOVB; ++ case 3: ++ MOVB; ++ case 2: ++ MOVB; ++ case 1: ++ MOVB; ++ } ++ while (--loop); ++ } ++ } ++#else ++ dst -= m; ++ src -= m; ++ memmove (dst, src, m); ++#endif ++/* ++ * adjust offset index up ++ */ ++ while (i < n - 1) ++ { ++ ino[i] = ino[i + 2] + zoo; ++ i++; ++ } ++ } ++ ino[0] -= 2; ++ return 1; ++} ++ ++static void splpage (char *pag, char *new, long sbit) ++{ ++ datum key; ++ datum val; ++ ++ int n; ++ int off = PBLKSIZ; ++ char cur[PBLKSIZ]; ++ short *ino = (short *) cur; ++ ++ (void) memcpy (cur, pag, PBLKSIZ); ++ (void) memset (pag, 0, PBLKSIZ); ++ (void) memset (new, 0, PBLKSIZ); ++ ++ n = ino[0]; ++ for (ino++; n > 0; ino += 2) ++ { ++ key.dptr = cur + ino[0]; ++ key.dsize = off - ino[0]; ++ val.dptr = cur + ino[1]; ++ val.dsize = ino[0] - ino[1]; ++/* ++ * select the page pointer (by looking at sbit) and insert ++ */ ++ (void) putpair ((exhash (key) & sbit) ? new : pag, key, val); ++ ++ off = ino[1]; ++ n -= 2; ++ } ++} ++ ++static int getdbit (DBM * db, long dbit) ++{ ++ long c; ++ long dirb; ++ ++ c = dbit / BYTESIZ; ++ dirb = c / DBLKSIZ; ++ ++ if (dirb != db->dirbno) ++ { ++ int got; ++ if (lseek (db->dirf, OFF_DIR (dirb), SEEK_SET) < 0 ++ || (got = read(db->dirf, db->dirbuf, DBLKSIZ)) < 0) ++ return 0; ++ if (got==0) ++ memset(db->dirbuf,0,DBLKSIZ); ++ db->dirbno = dirb; ++ } ++ ++ return db->dirbuf[c % DBLKSIZ] & (1 << dbit % BYTESIZ); ++} ++ ++static int setdbit (DBM * db, long dbit) ++{ ++ long c; ++ long dirb; ++ ++ c = dbit / BYTESIZ; ++ dirb = c / DBLKSIZ; ++ ++ if (dirb != db->dirbno) ++ { ++ int got; ++ if (lseek (db->dirf, OFF_DIR (dirb), SEEK_SET) < 0 ++ || (got = read(db->dirf, db->dirbuf, DBLKSIZ)) < 0) ++ return 0; ++ if (got==0) ++ memset(db->dirbuf,0,DBLKSIZ); ++ db->dirbno = dirb; ++ } ++ ++ db->dirbuf[c % DBLKSIZ] |= (1 << dbit % BYTESIZ); ++ ++#if 0 ++ if (dbit >= db->maxbno) ++ db->maxbno += DBLKSIZ * BYTESIZ; ++#else ++ if (OFF_DIR((dirb+1))*BYTESIZ > db->maxbno) ++ db->maxbno=OFF_DIR((dirb+1))*BYTESIZ; ++#endif ++ ++ if (lseek (db->dirf, OFF_DIR (dirb), SEEK_SET) < 0 ++ || write (db->dirf, db->dirbuf, DBLKSIZ) < 0) ++ return 0; ++ ++ return 1; ++} ++ ++/* ++ * getnext - get the next key in the page, and if done with ++ * the page, try the next page in sequence ++ */ ++static datum getnext (DBM * db) ++{ ++ datum key; ++ ++ for (;;) ++ { ++ db->keyptr++; ++ key = getnkey (db->pagbuf, db->keyptr); ++ if (key.dptr != NULL) ++ return key; ++/* ++ * we either run out, or there is nothing on this page.. ++ * try the next one... If we lost our position on the ++ * file, we will have to seek. ++ */ ++ db->keyptr = 0; ++ if (db->pagbno != db->blkptr++) ++ if (lseek (db->pagf, OFF_PAG (db->blkptr), SEEK_SET) < 0) ++ break; ++ db->pagbno = db->blkptr; ++ if (read (db->pagf, db->pagbuf, PBLKSIZ) <= 0) ++ break; ++ if (!chkpage (db->pagbuf)) ++ break; ++ } ++ ++ return ioerr (db), nullitem; ++} ++ ++/* ++ * all important binary trie traversal ++ */ ++static int getpage (DBM * db, long hash) ++{ ++ int hbit; ++ long dbit; ++ long pagb; ++ ++ dbit = 0; ++ hbit = 0; ++ while (dbit < db->maxbno && getdbit (db, dbit)) ++ dbit = 2 * dbit + ((hash & (1 << hbit++)) ? 2 : 1); ++ ++ db->curbit = dbit; ++ db->hmask = masks[hbit]; ++ ++ pagb = hash & db->hmask; ++/* ++ * see if the block we need is already in memory. ++ * note: this lookaside cache has about 10% hit rate. ++ */ ++ if (pagb != db->pagbno) ++ { ++/* ++ * note: here, we assume a "hole" is read as 0s. ++ * if not, must zero pagbuf first. ++ */ ++ if (lseek (db->pagf, OFF_PAG (pagb), SEEK_SET) < 0 ++ || read (db->pagf, db->pagbuf, PBLKSIZ) < 0) ++ return 0; ++ if (!chkpage (db->pagbuf)) ++ return 0; ++ db->pagbno = pagb; ++ } ++ return 1; ++} ++ ++/* ++ * makroom - make room by splitting the overfull page ++ * this routine will attempt to make room for SPLTMAX times before ++ * giving up. ++ */ ++static int makroom (DBM * db, long hash, int need) ++{ ++ long newp; ++ char twin[PBLKSIZ]; ++ char *pag = db->pagbuf; ++ char *new = twin; ++ int smax = SPLTMAX; ++ ++ do ++ { ++/* ++ * split the current page ++ */ ++ (void) splpage (pag, new, db->hmask + 1); ++/* ++ * address of the new page ++ */ ++ newp = (hash & db->hmask) | (db->hmask + 1); ++ ++/* ++ * write delay, read avoidence/cache shuffle: ++ * select the page for incoming pair: if key is to go to the new page, ++ * write out the previous one, and copy the new one over, thus making ++ * it the current page. If not, simply write the new page, and we are ++ * still looking at the page of interest. current page is not updated ++ * here, as sdbm_store will do so, after it inserts the incoming pair. ++ */ ++ if (hash & (db->hmask + 1)) ++ { ++ if (lseek (db->pagf, OFF_PAG (db->pagbno), SEEK_SET) < 0 ++ || write (db->pagf, db->pagbuf, PBLKSIZ) < 0) ++ return 0; ++ db->pagbno = newp; ++ (void) memcpy (pag, new, PBLKSIZ); ++ } ++ else if (lseek (db->pagf, OFF_PAG (newp), SEEK_SET) < 0 ++ || write (db->pagf, new, PBLKSIZ) < 0) ++ return 0; ++ ++ if (!setdbit (db, db->curbit)) ++ return 0; ++/* ++ * see if we have enough room now ++ */ ++ if (fitpair (pag, need)) ++ return 1; ++/* ++ * try again... update curbit and hmask as getpage would have ++ * done. because of our update of the current page, we do not ++ * need to read in anything. BUT we have to write the current ++ * [deferred] page out, as the window of failure is too great. ++ */ ++ db->curbit = 2 * db->curbit + ++ ((hash & (db->hmask + 1)) ? 2 : 1); ++ db->hmask |= db->hmask + 1; ++ ++ if (lseek (db->pagf, OFF_PAG (db->pagbno), SEEK_SET) < 0 ++ || write (db->pagf, db->pagbuf, PBLKSIZ) < 0) ++ return 0; ++ ++ } ++ while (--smax); ++/* ++ * if we are here, this is real bad news. After SPLTMAX splits, ++ * we still cannot fit the key. say goodnight. ++ */ ++#ifdef BADMESS ++ (void) write (2, "sdbm: cannot insert after SPLTMAX attempts.\n", 44); ++#endif ++ return 0; ++ ++} ++ ++static SDBM *sdbm_prep (char *dirname, char *pagname, int flags, int mode) ++{ ++ SDBM *db; ++ struct stat dstat; ++ ++ if ((db = (SDBM *) mymalloc (sizeof (SDBM))) == NULL) ++ return errno = ENOMEM, (SDBM *) NULL; ++ ++ db->flags = 0; ++ db->blkptr = 0; ++ db->keyptr = 0; ++/* ++ * adjust user flags so that WRONLY becomes RDWR, ++ * as required by this package. Also set our internal ++ * flag for RDONLY if needed. ++ */ ++ if (flags & O_WRONLY) ++ flags = (flags & ~O_WRONLY) | O_RDWR; ++ else if ((flags & 03) == O_RDONLY) ++ db->flags = DBM_RDONLY; ++#if defined(OS2) || defined(MSDOS) || defined(WIN32) ++ flags |= O_BINARY; ++#endif ++ ++/* ++ * Make sure to ignore the O_EXCL option, as the file might exist due ++ * to the locking. ++ */ ++ flags &= ~O_EXCL; ++ ++/* ++ * open the files in sequence, and stat the dirfile. ++ * If we fail anywhere, undo everything, return NULL. ++ */ ++ ++ if ((db->pagf = open (pagname, flags, mode)) > -1) ++ { ++ if ((db->dirf = open (dirname, flags, mode)) > -1) ++ { ++/* ++ * need the dirfile size to establish max bit number. ++ */ ++ if (fstat (db->dirf, &dstat) == 0) ++ { ++ /* ++ * success ++ */ ++ return db; ++ } ++ msg_info ("closing dirf"); ++ (void) close (db->dirf); ++ } ++ msg_info ("closing pagf"); ++ (void) close (db->pagf); ++ } ++ myfree ((char *) db); ++ return (SDBM *) NULL; ++} ++ ++static DBM *sdbm_internal_open (SDBM * sdbm) ++{ ++ DBM *db; ++ struct stat dstat; ++ ++ if ((db = (DBM *) mymalloc (sizeof (DBM))) == NULL) ++ return errno = ENOMEM, (DBM *) NULL; ++ ++ db->flags = sdbm->flags; ++ db->hmask = 0; ++ db->blkptr = sdbm->blkptr; ++ db->keyptr = sdbm->keyptr; ++ db->pagf = sdbm->pagf; ++ db->dirf = sdbm->dirf; ++ db->pagbuf = sdbm->pagbuf; ++ db->dirbuf = sdbm->dirbuf; ++ ++/* ++ * need the dirfile size to establish max bit number. ++ */ ++ if (fstat (db->dirf, &dstat) == 0) ++ { ++/* ++ * zero size: either a fresh database, or one with a single, ++ * unsplit data page: dirpage is all zeros. ++ */ ++ db->dirbno = (!dstat.st_size) ? 0 : -1; ++ db->pagbno = -1; ++ db->maxbno = dstat.st_size * BYTESIZ; ++ ++ (void) memset (db->pagbuf, 0, PBLKSIZ); ++ (void) memset (db->dirbuf, 0, DBLKSIZ); ++ return db; ++ } ++ myfree ((char *) db); ++ return (DBM *) NULL; ++} ++ ++static void sdbm_internal_close (DBM * db) ++{ ++ if (db == NULL) ++ errno = EINVAL; ++ else ++ { ++ myfree ((char *) db); ++ } ++} ++ ++datum sdbm_fetch (SDBM * sdb, datum key) ++{ ++ datum retval; ++ DBM *db; ++ ++ if (sdb == NULL || bad (key)) ++ return errno = EINVAL, nullitem; ++ ++ if (!(db = sdbm_internal_open (sdb))) ++ return errno = EINVAL, nullitem; ++ ++ if (getpage (db, exhash (key))) ++ { ++ retval = getpair (db->pagbuf, key); ++ sdbm_internal_close (db); ++ return retval; ++ } ++ ++ sdbm_internal_close (db); ++ ++ return ioerr (sdb), nullitem; ++} ++ ++int sdbm_delete (SDBM * sdb, datum key) ++{ ++ int retval; ++ DBM *db; ++ ++ if (sdb == NULL || bad (key)) ++ return errno = EINVAL, -1; ++ if (sdbm_rdonly (sdb)) ++ return errno = EPERM, -1; ++ ++ if (!(db = sdbm_internal_open (sdb))) ++ return errno = EINVAL, -1; ++ ++ if (getpage (db, exhash (key))) ++ { ++ if (!delpair (db->pagbuf, key)) ++ retval = -1; ++/* ++ * update the page file ++ */ ++ else if (lseek (db->pagf, OFF_PAG (db->pagbno), SEEK_SET) < 0 ++ || write (db->pagf, db->pagbuf, PBLKSIZ) < 0) ++ retval = ioerr (sdb), -1; ++ else ++ retval = 0; ++ } ++ else ++ retval = ioerr (sdb), -1; ++ ++ sdbm_internal_close (db); ++ ++ return retval; ++} ++ ++int sdbm_store (SDBM * sdb, datum key, datum val, int flags) ++{ ++ int need; ++ int retval; ++ long hash; ++ DBM *db; ++ ++ if (sdb == NULL || bad (key)) ++ return errno = EINVAL, -1; ++ if (sdbm_rdonly (sdb)) ++ return errno = EPERM, -1; ++ ++ need = key.dsize + val.dsize; ++/* ++ * is the pair too big (or too small) for this database ?? ++ */ ++ if (need < 0 || need > PAIRMAX) ++ return errno = EINVAL, -1; ++ ++ if (!(db = sdbm_internal_open (sdb))) ++ return errno = EINVAL, -1; ++ ++ if (getpage (db, (hash = exhash (key)))) ++ { ++/* ++ * if we need to replace, delete the key/data pair ++ * first. If it is not there, ignore. ++ */ ++ if (flags == DBM_REPLACE) ++ (void) delpair (db->pagbuf, key); ++#ifdef SEEDUPS ++ else if (duppair (db->pagbuf, key)) ++ { ++ sdbm_internal_close (db); ++ return 1; ++ } ++#endif ++/* ++ * if we do not have enough room, we have to split. ++ */ ++ if (!fitpair (db->pagbuf, need)) ++ if (!makroom (db, hash, need)) ++ { ++ sdbm_internal_close (db); ++ return ioerr (db), -1; ++ } ++/* ++ * we have enough room or split is successful. insert the key, ++ * and update the page file. ++ */ ++ (void) putpair (db->pagbuf, key, val); ++ ++ if (lseek (db->pagf, OFF_PAG (db->pagbno), SEEK_SET) < 0 ++ || write (db->pagf, db->pagbuf, PBLKSIZ) < 0) ++ { ++ sdbm_internal_close (db); ++ return ioerr (db), -1; ++ } ++ /* ++ * success ++ */ ++ sdbm_internal_close (db); ++ return 0; ++ } ++ ++ sdbm_internal_close (db); ++ return ioerr (sdb), -1; ++} ++ ++/* ++ * the following two routines will break if ++ * deletions aren't taken into account. (ndbm bug) ++ */ ++datum sdbm_firstkey (SDBM * sdb) ++{ ++ datum retval; ++ DBM *db; ++ ++ if (sdb == NULL) ++ return errno = EINVAL, nullitem; ++ ++ if (!(db = sdbm_internal_open (sdb))) ++ return errno = EINVAL, nullitem; ++ ++/* ++ * start at page 0 ++ */ ++ if (lseek (db->pagf, OFF_PAG (0), SEEK_SET) < 0 ++ || read (db->pagf, db->pagbuf, PBLKSIZ) < 0) ++ { ++ sdbm_internal_close (db); ++ return ioerr (sdb), nullitem; ++ } ++ db->pagbno = 0; ++ db->blkptr = 0; ++ db->keyptr = 0; ++ ++ retval = getnext (db); ++ sdb->blkptr = db->blkptr; ++ sdb->keyptr = db->keyptr; ++ sdbm_internal_close (db); ++ return retval; ++} ++ ++datum sdbm_nextkey (SDBM * sdb) ++{ ++ datum retval; ++ DBM *db; ++ ++ if (sdb == NULL) ++ return errno = EINVAL, nullitem; ++ ++ if (!(db = sdbm_internal_open (sdb))) ++ return errno = EINVAL, nullitem; ++ ++ retval = getnext (db); ++ sdb->blkptr = db->blkptr; ++ sdb->keyptr = db->keyptr; ++ sdbm_internal_close (db); ++ return retval; ++} ++ ++void sdbm_close (SDBM * db) ++{ ++ if (db == NULL) ++ errno = EINVAL; ++ else ++ { ++ (void) close (db->dirf); ++ (void) close (db->pagf); ++ myfree ((char *) db); ++ } ++} ++ ++SDBM *sdbm_open (char *file, int flags, int mode) ++{ ++ SDBM *db; ++ char *dirname; ++ char *pagname; ++ int n; ++ ++ if (file == NULL || !*file) ++ return errno = EINVAL, (SDBM *) NULL; ++/* ++ * need space for two seperate filenames ++ */ ++ n = strlen (file) * 2 + strlen (DIRFEXT) + strlen (PAGFEXT) + 2; ++ ++ if ((dirname = (char *) mymalloc ((unsigned) n)) == NULL) ++ return errno = ENOMEM, (SDBM *) NULL; ++/* ++ * build the file names ++ */ ++ dirname = strcat (strcpy (dirname, file), DIRFEXT); ++ pagname = strcpy (dirname + strlen (dirname) + 1, file); ++ pagname = strcat (pagname, PAGFEXT); ++ ++ db = sdbm_prep (dirname, pagname, flags, mode); ++ myfree ((char *) dirname); ++ return db; ++} ++ +diff -Pur postfix-1.1.11-20020613-orig/src/util/sdbm.h postfix-1.1.11-20020613/src/util/sdbm.h +--- postfix-1.1.11-20020613-orig/src/util/sdbm.h Thu Jan 1 01:00:00 1970 ++++ postfix-1.1.11-20020613/src/util/sdbm.h Wed Jun 26 15:26:49 2002 +@@ -0,0 +1,97 @@ ++/*++ ++/* NAME ++/* sdbm 3h ++/* SUMMARY ++/* SDBM Simple DBM: ndbm work-alike hashed database library ++/* SYNOPSIS ++/* include "sdbm.h" ++/* DESCRIPTION ++/* .nf ++/*--*/ ++ ++#ifndef UTIL_SDBM_H ++#define UTIL_SDBM_H ++ ++/* ++ * sdbm - ndbm work-alike hashed database library ++ * based on Per-Ake Larson's Dynamic Hashing algorithms. BIT 18 (1978). ++ * author: oz@nexus.yorku.ca ++ * status: public domain. ++ */ ++ ++#define DUFF /* go ahead and use the loop-unrolled version */ ++ ++#include <stdio.h> ++ ++#define DBLKSIZ 16384 /* SSL cert chains require more */ ++#define PBLKSIZ 8192 /* SSL cert chains require more */ ++#define PAIRMAX 8008 /* arbitrary on PBLKSIZ-N */ ++#define SPLTMAX 10 /* maximum allowed splits */ ++ /* for a single insertion */ ++#define DIRFEXT ".dir" ++#define PAGFEXT ".pag" ++ ++typedef struct { ++ int dirf; /* directory file descriptor */ ++ int pagf; /* page file descriptor */ ++ int flags; /* status/error flags, see below */ ++ long blkptr; /* current block for nextkey */ ++ int keyptr; /* current key for nextkey */ ++ char pagbuf[PBLKSIZ]; /* page file block buffer */ ++ char dirbuf[DBLKSIZ]; /* directory file block buffer */ ++} SDBM; ++ ++#define DBM_RDONLY 0x1 /* data base open read-only */ ++#define DBM_IOERR 0x2 /* data base I/O error */ ++ ++/* ++ * utility macros ++ */ ++#define sdbm_rdonly(db) ((db)->flags & DBM_RDONLY) ++#define sdbm_error(db) ((db)->flags & DBM_IOERR) ++ ++#define sdbm_clearerr(db) ((db)->flags &= ~DBM_IOERR) /* ouch */ ++ ++#define sdbm_dirfno(db) ((db)->dirf) ++#define sdbm_pagfno(db) ((db)->pagf) ++ ++typedef struct { ++ char *dptr; ++ int dsize; ++} datum; ++ ++extern datum nullitem; ++ ++/* ++ * flags to sdbm_store ++ */ ++#define DBM_INSERT 0 ++#define DBM_REPLACE 1 ++ ++/* ++ * ndbm interface ++ */ ++extern SDBM *sdbm_open(char *, int, int); ++extern void sdbm_close(SDBM *); ++extern datum sdbm_fetch(SDBM *, datum); ++extern int sdbm_delete(SDBM *, datum); ++extern int sdbm_store(SDBM *, datum, datum, int); ++extern datum sdbm_firstkey(SDBM *); ++extern datum sdbm_nextkey(SDBM *); ++ ++/* ++ * sdbm - ndbm work-alike hashed database library ++ * tuning and portability constructs [not nearly enough] ++ * author: oz@nexus.yorku.ca ++ */ ++ ++#define BYTESIZ 8 ++ ++/* ++ * important tuning parms (hah) ++ */ ++ ++#define SEEDUPS /* always detect duplicates */ ++#define BADMESS /* generate a message for worst case: ++ cannot make room after SPLTMAX splits */ ++#endif /* UTIL_SDBM_H */ +diff -Pur postfix-1.1.11-20020613-orig/src/util/sys_defs.h postfix-1.1.11-20020613/src/util/sys_defs.h +--- postfix-1.1.11-20020613-orig/src/util/sys_defs.h Mon Apr 15 23:56:08 2002 ++++ postfix-1.1.11-20020613/src/util/sys_defs.h Wed Jun 26 15:26:49 2002 +@@ -73,6 +73,10 @@ + #define DEF_MAILBOX_LOCK "flock, dotlock" + #endif + ++#if ((defined(__NetBSD_Version__) && __NetBSD_Version__ >= 105000000) || defined(USAGI_LIBINET6)) ++#define HAVE_GETIFADDRS ++#endif ++ + /* + * UNIX on MAC. + */ +diff -Pur postfix-1.1.11-20020613-orig/src/util/valid_hostname.c postfix-1.1.11-20020613/src/util/valid_hostname.c +--- postfix-1.1.11-20020613-orig/src/util/valid_hostname.c Sun Jan 28 15:10:18 2001 ++++ postfix-1.1.11-20020613/src/util/valid_hostname.c Wed Jun 26 15:26:49 2002 +@@ -47,6 +47,13 @@ + #include <string.h> + #include <ctype.h> + ++#ifdef INET6 ++#include <netinet/in.h> ++#include <sys/socket.h> ++#include <arpa/inet.h> ++#include <netdb.h> ++#endif ++ + /* Utility library. */ + + #include "msg.h" +@@ -103,7 +110,23 @@ + msg_warn("%s: misplaced hyphen: %.100s", myname, name); + return (0); + } +- } else { ++ } ++#ifdef INET6 ++ else if (ch == ':') { ++ struct addrinfo hints, *res; ++ ++ memset(&hints, 0, sizeof(hints)); ++ hints.ai_family = AF_INET6; ++ hints.ai_socktype = SOCK_STREAM; /*dummy*/ ++ hints.ai_flags = AI_NUMERICHOST; ++ if (getaddrinfo(name, "0", &hints, &res) == 0) { ++ freeaddrinfo(res); ++ return 1; ++ } else ++ return 0; ++ } ++#endif ++ else { + if (gripe) + msg_warn("%s: invalid character %d(decimal): %.100s", + myname, ch, name); +@@ -135,6 +158,9 @@ + int byte_count = 0; + int byte_val = 0; + int ch; ++#ifdef INET6 ++ struct addrinfo hints, *res; ++#endif + + #define BYTES_NEEDED 4 + +@@ -146,6 +172,17 @@ + msg_warn("%s: empty address", myname); + return (0); + } ++ ++#ifdef INET6 ++ memset(&hints, 0, sizeof(hints)); ++ hints.ai_family = AF_INET6; ++ hints.ai_socktype = SOCK_STREAM; /*dummy*/ ++ hints.ai_flags = AI_NUMERICHOST; ++ if (getaddrinfo(addr, "0", &hints, &res) == 0) { ++ freeaddrinfo(res); ++ return 1; ++ } ++#endif + + /* + * Scary code to avoid sscanf() overflow nasties. diff --git a/net-mail/postfix/postfix-1.1.11.20020613.ebuild b/net-mail/postfix/postfix-1.1.11.20020613.ebuild new file mode 100644 index 000000000000..e2e69a64eb12 --- /dev/null +++ b/net-mail/postfix/postfix-1.1.11.20020613.ebuild @@ -0,0 +1,138 @@ +# Copyright 1999-2002 Gentoo Technologies, Inc. +# Distributed under the terms of the GNU General Public License, v2 or later +# $Header: /var/cvsroot/gentoo-x86/net-mail/postfix/postfix-1.1.11.20020613.ebuild,v 1.1 2002/07/22 23:09:48 raker Exp $ + +DESCRIPTION="A fast and secure drop-in replacement for sendmail" +HOMEPAGE="http://www.postfix.org/" + +POSTFIX_VER="1.1.11-20020613" +S=${WORKDIR}/postfix-${POSTFIX_VER} +SRC_URI="ftp://ftp.porcupine.org/mirrors/postfix-release/experimental/postfix-${POSTFIX_VER}.tar.gz" + +PROVIDE="virtual/mta" +DEPEND=">=sys-libs/db-3.2 + >=dev-libs/libpcre-3.4 + sasl? ( >=dev-libs/cyrus-sasl-2.1.6 ) + ldap? ( >=net-nds/openldap-1.2 ) + mysql? ( >=dev-db/mysql-3.23.28 ) + ssl? ( >=dev-libs/openssl-0.9.6d )" +RDEPEND="${DEPEND} + >=net-mail/mailbase-0.00 + !virtual/mta" +LICENSE="IPL-1" +SLOT="0" + +pkg_setup() { + if ! grep -q ^postdrop: /etc/group ; then + groupadd postdrop || die "problem adding group postdrop" + fi +} + +src_unpack() { + unpack ${A} + + if [ "`use ipv6`" ] + then + cd ${S} + patch -p1 < ${FILESDIR}/tls+ipv6.diff || die + cd ${WORKDIR} + fi + + cd ${S}/conf + cp main.cf main.cf.orig + sed -e "s:/usr/libexec/postfix:/usr/lib/postfix:" main.cf.orig > main.cf + + cd ${S}/src/global + cp mail_params.h mail_params.h.orig + sed -e "s:/usr/libexec/postfix:/usr/lib/postfix:" mail_params.h.orig > mail_params.h + + use mysql \ + && CCARGS="${CCARGS} -DHAS_MYSQL -I/usr/include/mysql" \ + && AUXLIBS="${AUXLIBS} -lmysqlclient -lm" + + use ldap \ + && CCARGS="${CCARGS} -DHAS_LDAP" \ + && AUXLIBS="${AUXLIBS} -lldap -llber" + + use ssl \ + && CCARGS="${CCARGS} -DHAS_SSL" \ + && AUXLIBS="${AUXLIBS} -lssl" + + # note: if sasl is built w/ pam, then postfix _MUST_ be built w/ pam + use pam && AUXLIBS="${AUXLIBS} -lpam" + + # stuff we always want... + CCARGS="${CCARGS} -I/usr/include -DHAS_PCRE" + AUXLIBS="${AUXLIBS} -L/usr/lib -lpcre -ldl -lcrypt" + + if [ "`use sasl`" ] + then + AUXLIBS="${AUXLIBS} -lsasl2" + CCARGS="${CCARGS} -DUSE_SASL_AUTH" + cd ${S} + patch -p1 < ${FILESDIR}/saslv2.diff + fi + + DEBUG="" + + cd ${S} + make tidy || die + make makefiles CC="cc" OPT="${CFLAGS}" DEBUG="${DEBUG}" \ + CCARGS="${CCARGS}" AUXLIBS="${AUXLIBS}" || die +} + +src_compile() { + make || die "oooops" +} + +src_install () { + dodir /usr/bin /usr/sbin /usr/lib/postfix /etc/postfix/sample + + cd ${S}/bin + dosbin post* sendmail + chown root.postdrop ${D}/usr/sbin/{postdrop,postqueue} + chmod 2755 ${D}/usr/sbin/{postdrop,postqueue} + + dosym /usr/sbin/sendmail /usr/bin/mail + dosym /usr/sbin/sendmail /usr/bin/mailq + dosym /usr/sbin/sendmail /usr/bin/newaliases + dosym /usr/sbin/sendmail /usr/lib/sendmail + + cd ${S}/libexec + exeinto /usr/lib/postfix + doexe * + + cd ${S}/man + doman man*/* + + cd ${S} + dodoc *README COMPATIBILITY HISTORY LICENSE PORTING RELEASE_NOTES INSTALL + dohtml html/* + + cd ${S}/conf + insinto /etc/postfix/sample + doins access aliases canonical relocated transport \ + pcre_table regexp_table postfix-script* *.cf + + exeinto /etc/postfix + doexe postfix-script post-install postfix-files || die + + insinto /etc/postfix + doins ${FILESDIR}/main.cf master.cf || die + + exeinto /etc/init.d ; newexe ${FILESDIR}/postfix.rc6 postfix + insinto /etc/pam.d ; newins ${FILESDIR}/smtp.pam smtp + insinto /etc/sasl ; doins ${FILESDIR}/smtpd.conf +} + +pkg_postinst() { + install -d 0755 ${ROOT}/var/spool/postfix + + einfo "***************************************************************" + einfo "* NOTE: If config file protection is enabled and you upgraded *" + einfo "* from an earlier version of postfix you must update *" + einfo "* /etc/postfix/master.cf to the latest version *" + einfo "* (/etc/postfix/._cfg????_master.cf). Otherwise postfix *" + einfo "* will not work correctly. *" + einfo "***************************************************************" +} |