diff -Nur snapshot-20010228-orig/Makefile.in snapshot-20010228/Makefile.in
--- snapshot-20010228-orig/Makefile.in Wed Mar 21 13:26:27 2001
+++ snapshot-20010228/Makefile.in Wed Mar 21 13:35:29 2001
@@ -6,7 +6,7 @@
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/postsuper \
- src/nqmgr src/spawn src/flush src/virtual # proto man html
+ src/nqmgr src/spawn src/flush src/virtual src/tlsmgr # proto man html
default: update
diff -Nur snapshot-20010228-orig/conf/master.cf snapshot-20010228/conf/master.cf
--- snapshot-20010228-orig/conf/master.cf Wed Mar 21 13:26:22 2001
+++ snapshot-20010228/conf/master.cf Wed Mar 21 13:32:23 2001
@@ -68,10 +68,13 @@
# (yes) (yes) (yes) (never) (50)
# ==========================================================================
smtp inet n - n - - smtpd
+#smtps inet n - y - - smtpd -o smtpd_tls_wrappermode=yes -o smtpd_sasl_auth_enable=yes
+#submission inet n - y - - smtpd -o smtpd_enforce_tls=yes -o smtpd_sasl_auth_enable=yes
pickup fifo n n n 60 1 pickup
cleanup unix - - 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 -Nur snapshot-20010228-orig/conf/sample-smtp.cf snapshot-20010228/conf/sample-smtp.cf
--- snapshot-20010228-orig/conf/sample-smtp.cf Wed Mar 21 13:26:23 2001
+++ snapshot-20010228/conf/sample-smtp.cf Wed Mar 21 13:32:23 2001
@@ -145,6 +145,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 -Nur snapshot-20010228-orig/conf/sample-smtpd.cf snapshot-20010228/conf/sample-smtpd.cf
--- snapshot-20010228-orig/conf/sample-smtpd.cf Wed Mar 21 13:26:23 2001
+++ snapshot-20010228/conf/sample-smtpd.cf Wed Mar 21 13:32:23 2001
@@ -73,6 +73,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 -Nur snapshot-20010228-orig/conf/sample-tls.cf snapshot-20010228/conf/sample-tls.cf
--- snapshot-20010228-orig/conf/sample-tls.cf Thu Jan 1 01:00:00 1970
+++ snapshot-20010228/conf/sample-tls.cf Wed Mar 21 13:32:23 2001
@@ -0,0 +1,483 @@
+# 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 here:
+#
+smtpd_use_tls = yes
+
+# You can ENFORCE the use of TLS, so that no commands (except QUIT of course)
+# are allowed without TLS. According to RFC2487 this MUST NOT be applied
+# in case of a publicly-referenced SMTP server. So this option is off
+# by default and should only seldom be used. Using this option implies
+# smtpd_use_tls = yes
+#
+# smtpd_enforce_tls = no
+
+# 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
+
+# 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 -Nur snapshot-20010228-orig/html/ssl/conf.html snapshot-20010228/html/ssl/conf.html
--- snapshot-20010228-orig/html/ssl/conf.html Thu Jan 1 01:00:00 1970
+++ snapshot-20010228/html/ssl/conf.html Wed Mar 21 13:38:29 2001
@@ -0,0 +1,537 @@
+
+
+
+
+Postfix/TLS - Configuring main.cf and master.cf
+
+
+Postfix/TLS - Configuring main.cf and master.cf
+
+To use the TLS extension you need to feed some information to
+postfix. Please see also the conf/sample-tls.cf
file.
+
+main.cf: smtpd (server) specific variables
+
+
+# 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 here:
+#
+smtpd_use_tls = yes
+
+# You can ENFORCE the use of TLS, so that no commands (except QUIT of course)
+# are allowed without TLS. According to RFC2487 this MUST NOT be applied
+# in case of a publicly-referenced SMTP server. So this option is off
+# by default and should only seldom be used. Using this option implies
+# smtpd_use_tls = yes
+#
+# smtpd_enforce_tls = no
+
+# 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
+
+# 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
+
+
+main.cf: smtp (client) specific variables
+
+
+# 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
+
+
+main.cf: general variables
+
+
+# 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
+
+
+master.cf: tlsmgr daemon
+
+If you don't have a /dev/urandom device and/or use session caching,
+you must run the "tlsmgr" daemon (see conf/master.cf). The tlsmgr
+needs to access entropy sources and can (currently) not be
+chrooted. It can drop its privileges, if the entropy sources (e.g.
+/dev/urandom or an EGD socket) don't have access restrictions.
+
+
+# ==========================================================================
+# service type private unpriv chroot wakeup maxproc command + args
+# (yes) (yes) (yes) (never) (50)
+# ==========================================================================
+tlsmgr fifo - - n 300 1 tlsmgr
+
+
+master.cf: additional services
+
+It can be useful to have postfix listen on additional ports, namely
+"submission"=587 for email submission as defined in RFC2476; this
+is especially useful if you want to allow AUTH with plaintext
+passwords (PLAIN, LOGIN) and hence run on a port with encryption
+enforcement. Another useful port may be "smtps"=465 which was
+intended with TLS-wrapping and is still used by Outlook (Express).
+
+Both example entries already contain the flags to enable SASL
+authentication (which may be disabled on the normal port). Since
+the actual service names are used, smtps and submission must be
+defined in /etc/services (and probably also in
+/var/spool/postfix/etc/services if chrooted)!!! (Use the port
+numbers otherwise.)
+
+
+# ==========================================================================
+# service type private unpriv chroot wakeup maxproc command + args
+# (yes) (yes) (yes) (never) (50)
+# ==========================================================================
+smtps inet n - y - - smtpd -o smtpd_tls_wrappermode=yes -o smtpd_sasl_auth_enable=yes
+submission inet n - y - - smtpd -o smtpd_enforce_tls=yes -o smtpd_sasl_auth_enable=yes
+
+
+
+
diff -Nur snapshot-20010228-orig/html/ssl/index.html snapshot-20010228/html/ssl/index.html
--- snapshot-20010228-orig/html/ssl/index.html Thu Jan 1 01:00:00 1970
+++ snapshot-20010228/html/ssl/index.html Wed Mar 21 13:38:29 2001
@@ -0,0 +1,50 @@
+
+
+
+
+Postfix/TLS - A TLS extension for POSTFIX
+
+
+Postfix/TLS - A TLS extension for POSTFIX
+
+Contents
+
+
+
+
+PLEASE REMEMBER THAT EXPORT/IMPORT AND/OR USE OF STRONG
+CRYPTOGRAPHY SOFTWARE, PROVIDING CRYPTOGRAPHY HOOKS OR EVEN JUST
+COMMUNICATING TECHNICAL DETAILS ABOUT CRYPTOGRAPHY SOFTWARE IS
+ILLEGAL IN SOME PARTS OF THE WORLD. SO, WHEN YOU IMPORT THIS PACKAGE
+TO YOUR COUNTRY, RE-DISTRIBUTE IT FROM THERE OR EVEN JUST EMAIL
+TECHNICAL SUGGESTIONS OR EVEN SOURCE PATCHES TO THE AUTHOR OR
+OTHER PEOPLE YOU ARE STRONGLY ADVICED TO PAY CLOSE ATTENTION TO ANY
+EXPORT/IMPORT AND/OR USE LAWS WHICH APPLY TO YOU. THE AUTHOR OF
+POSTFIX/TLS IS NOT LIABLE FOR ANY VIOLATIONS YOU MAKE HERE. SO BE
+CAREFULLY YOURSELF, IT IS YOUR RESPONSIBILITY.
+
+
+Lutz Jänicke, Homepage,
+Email:
+Lutz.Jaenicke@aet.TU-Cottbus.DE
+
+
+
diff -Nur snapshot-20010228-orig/html/ssl/install.html snapshot-20010228/html/ssl/install.html
--- snapshot-20010228-orig/html/ssl/install.html Thu Jan 1 01:00:00 1970
+++ snapshot-20010228/html/ssl/install.html Wed Mar 21 13:38:29 2001
@@ -0,0 +1,90 @@
+
+
+
+
+Postfix/TLS - Installation
+
+
+Postfix/TLS - Installing the patchkit
+
+Prerequisits
+
+This patchkit is prepared for
+
+
+
+You may also need to update your "patch" utility (see below).
+
+Patching
+
+The changes to the postfix source code as well as the additional
+files are included in the "pfixtls.diff
" in the main
+directory of the patch kit. It is a unified diff.
+
+To apply the patches, go to the directory one level below the
+original postfix source tree (you should see
+"postfix-xxxxxxx
" or "snapshot-xxxxxxx
"
+when doing an "ls -al
"
+at this point. The patch is then applied with:
+
+
+patch -p0 < path-to/pfixtls.diff
+
+
+If you experience problems during the patch process (e.g. with the
+HP-UX 10.20 included patch), you might need to update your patch
+program, e.g. to an actual GNU-patch.
+
+If you need to apply the patchkit to a different version of
+patchlevel of postfix, you might try the following:
+
+
+cd postfix-directory ; patch -p1 < path-to/pfixtls.diff
+
+
+Since the patch is in unified form, it might also apply to a mildly
+changed source, as long as no conflicts appear.
+
+Compiling
+
+After patching postfix will configure and compile as before. In
+order to enable the TLS functions, you must specify the path to the
+OpenSSL header files as well as the appropriate libraries, and you
+must define HAS_SSL
. Your command for configuration
+might then be:
+
+
+make makefiles CCARGS="-DHAS_SSL -I/usr/local/ssl/include" AUXLIBS="-L/usr/local/ssl/lib -lssl -lcrypto"
+
+
+You might need additional customization e.g. for using Berkeley-DB
+as listed in the postfix INSTALL instructions. You can then
+continue in the usual way with:
+
+
+make
+
+
+and then follow the instructions in the postfix INSTALL file.
+
+
+
+
diff -Nur snapshot-20010228-orig/html/ssl/intro.html snapshot-20010228/html/ssl/intro.html
--- snapshot-20010228-orig/html/ssl/intro.html Thu Jan 1 01:00:00 1970
+++ snapshot-20010228/html/ssl/intro.html Wed Mar 21 13:38:29 2001
@@ -0,0 +1,184 @@
+
+
+
+
+Postfix/TLS - Introduction
+
+
+Postfix/TLS - Introduction
+
+Postfix/TLS is an extension of the Postfix [POSTFIX] MTA software to support the
+TLS protocol.
+
+A note about the start of the project
+
+When I started writing this software, I had a sophisticated way to
+allow relaying for roaming users in
+mind. In the meantime, this project is living on its own.
+
+RFC2246: The TLS (former SSL) protocol
+
+By default all communication on the Internet is done without
+encryption and without strong authentication. That does mean that
+everybody with physical access to the communication line along
+which a network packet will travel can eavesdrop on your
+communication. Even worse, it might be possible to redirect or
+alter your communication so that information, that you want to send
+to a party can be lost or changed without your notice.
+
+In order to solve these security issues, the SSL protocol
+(Secure Socket Layers) was introduced by Netscape, Inc., which now
+has evolved into the standardised TLS protocol (Transportation
+Layer Security) as RFC2246. It offers
+both encryption of the communication (stopping eavesdropping) and
+strong authentication (making sure that both parties of a
+communication are correctly identified and that the communication
+cannot be altered).
+
+Postfix/TLS does not realize the TLS protocol itself; it rather
+uses the OpenSSL package [OPENSSL] for this task. At the
+OpenSSL WWW-site you can also find links to in-depth documentation
+of the protocol and its features, so that it is not necessary to
+included them here. (And, of course, there is no use of re-writing
+what other people already wrote down, it just introduces additional
+errors.)
+
+RFC2487: Introducing TLS to SMTP
+
+The integration of the TLS protocol to Internet mail, SMTP (Simple
+Mail Transport Protocol) is described in
+RFC2487.
+
+Unlike the first incarnations of SSL as a wrapper
+around normal network communications [STUNNEL] [JONAMA], the TLS protocol is now
+completely integrated into the ESMTP: during the startup
+negotiation (EHLO) the server offers the support of TLS by
+advertising the STARTTLS feature. The client can
+now send the STARTTLS command to do authentication
+and switch to encrypted communication.
+
+Postfix/TLS: what can it do for you
+
+The list of features presented here should be understood as a list
+of ideas. Not all of them are realized yet, please see the notes at
+each feature.
+
+
+- Encrypted email transfer from one host to another.
+Status: realized.
+Comment: Once the STARTTLS negotiation is finished, the
+communication between both parties is encrypted.
+This also includes the MAIL FROM: and RCPT TO: envelop sender
+and recipient negotiation, so that an eavesdropper will not be able
+to get these informations.
+
+- Authentication of the receiving host to prevent
+interception.
+Status: realized.
+Comment: This is a quite important feature that is not difficult to
+implement. The problem lies in the fact, that not all hosts (read
+this: by now nearly no one) support this protocol. The sender must
+hence maintain a list of receivers which must identify by TLS,
+otherwise one could just intercept the communication and not offer
+STARTTLS, so that no authentication is done. One must also be
+careful to use the correct name of the host (see CNAMEs), but this
+problem is the same for http-servers.
+
+- Authentication of the sending host to prevent forgery.
+Status: Difficult to do.
+Comment: The transmission of emails is just a connection to the
+SMTP port (25) of the receiving host. This is done by either
+another MTA (Mail Transport Agent) or a MUA (Mail User Agent). In
+the first case, the sending MTA should present a client certificate
+issued on the name of the sending host. In the latter case however,
+the user has no access to the host's certificate and will (or not)
+present his own personal certificate. At this point I think that a
+satisfying and reliable solution is hardly possible (do
+you want your users' email bounce without reason?), so it has least
+priority.
+
+- Authentication of the sending host to allow relaying.
+Status: realized.
+Comment: This was the intention I had in mind when starting this
+project, so it was realized first. Based on the certificate the
+client MTA or MUA presents to the server, relaying can be
+allowed.
+
+- Any more ideas???
+Status: Send me an email.
+
+
+Postfix/TLS: what it cannot do for you
+
+There is one thing that I explicitly want to point out:
+
+
+- Securing the privacy of your email.
+Status: Cannot be done.
+Comment: RFC2487 only takes care of the transportation between mail
+servers. To assure that nobody can eavesdrop on your private email
+communication, it would be necessary that
+
+
+- all of the mailhubs in between are enforcing TLS.
+
+- all mailhubs themselves are trustworthy, as the email is only
+encrypted during transport, not when queued or spooled.
+
+- the destination is trustworthy, as the mail is spooled in clear
+and everybody who can access your mailbox (read this: at least the
+superuser) can read your mail!
+
+
+Hence, if you want privacy, you have to send out your
+email encrypted, e.g. using S/MIME or the traditional PGP
+package.
+
+- Authenticate the sender of an email.
+Status: Cannot be done.
+Comment: A lot of MUAs send out emails by just connecting the SMTP
+port of the sending host or nearest mailhub. There is no way to
+assure that the sender listed in the email is the real sender of
+the email. And even if it would be possible to identify the sender,
+the contents of the email might have been altered in between.
+To ensure the identity of the sender and the integrity of the
+email, you can again use S/MIME or PGP.
+
+
+Other OpenSource packages
+
+As of version sendmail-8.11, sendmail includes RFC2487 support [SENDMAIL].
+
+Frederik Vermeulen has realized an RFC2487 extension [QMAILTLS] for the Qmail [QMAIL] MTA.
+
+Matti Aarnio has integrated RFC2487 into ZMailer [ZMAILER].
+
+Michal Trojnara is currently integrating basic SMTP support into
+his stunnel software, starting with stunnel-3.3 [STUNNEL].
+
+Trey Childs is also working on a "wrapper" solution [SMTPS].
+
+Commercial implementations
+
+The commercial version of sendmail includes RFC2487 support [SENDMAIL.INC].
+
+Netscape Enterprise Server and Microsoft Exchange Server do offer
+RFC2487 functionality.
+
+The CommunigatePro mailserver software also supports RFC2487
+[COMMUNIGATE].
+
+
+
+
diff -Nur snapshot-20010228-orig/html/ssl/loadCAcert.pl snapshot-20010228/html/ssl/loadCAcert.pl
--- snapshot-20010228-orig/html/ssl/loadCAcert.pl Thu Jan 1 01:00:00 1970
+++ snapshot-20010228/html/ssl/loadCAcert.pl Wed Mar 21 13:38:29 2001
@@ -0,0 +1,23 @@
+#!/usr/local/bin/perl -T
+
+require 5.003;
+use strict;
+use CGI;
+
+my $cert_dir = "/usr/local/ssl/certs";
+my $cert_file = "CAcert.pem";
+
+my $query = new CGI;
+
+my $kind = $query->param('FORMAT');
+if($kind eq 'DER') { $cert_file = "CAcert.der"; }
+
+my $cert_path = "$cert_dir/$cert_file";
+
+open(CERT, "<$cert_path");
+my $data = join '', ;
+close(CERT);
+print "Content-Type: application/x-x509-ca-cert\n";
+print "Content-Length: ", length($data), "\n\n$data";
+
+1;
diff -Nur snapshot-20010228-orig/html/ssl/myownca.html snapshot-20010228/html/ssl/myownca.html
--- snapshot-20010228-orig/html/ssl/myownca.html Thu Jan 1 01:00:00 1970
+++ snapshot-20010228/html/ssl/myownca.html Wed Mar 21 13:38:29 2001
@@ -0,0 +1,175 @@
+
+
+
+
+Postfix/TLS - Being your on CA
+
+
+Postfix/TLS - Lutz's very short course on being your own
+CA
+
+This section is kept quite short as there are already a lot of
+pages explaining these things (e.g. [INTROCERT]). There are also
+projects under way to make this task easier [OPENCA], so I wont't waste your time
+(and mine) by writing a book about it.
+
+Be your own CA
+
+If you want to do relaying based on client certificates you may
+want to issue your own client certificates; hence you want to be
+your own certificate authority (CA). Of course nobody else will
+accept your certificates, so the damage you do is not so high (the
+requirements for a good "professional" CA are very high, as you
+should have the CA key on a private host without network for
+security, be strict about checking the identity of requesters etc).
+
+
+For laziness, we also don't care about the (worthful)
+possibility to generate certificates for specific purposes (e.g.
+for servers, clients, email-signing) and simply generate "unlimited
+general purpose" certificates. So a certificate issued for the
+person "John Doe" is also valid for the "John Doe"-server.
+
+Using OpenSSL it is quite simple to become your own CA. Just
+run
+
+
+CA.pl -newca
+
+
+and you are done. Just make sure, that you select a useful CN
+(Common Name)! By just using your name, you might create a lot of
+confusion, as the CA certificate for "Lutz Jaenicke" looks quite
+the same as the personal client certificate for "Lutz Jaenicke" (I
+can tell you). Of course you can further improve this private CA by
+editing the openssl.cnf
file, especially the comment.
+
+If you want the full comfort of being your own CA, you must
+import your CA certificate to Netscape. Unfortunately Netscape does
+not offer an explicit function to perform this task (unlike for
+client certificates). If you have an http-server available (and I
+think you do), you can add the
+loadCAcert.pl script to your cgi-bin
directory. If
+you call it from Netscape (or Internet Explorer), you can load the
+certificate! (Taken from [6])
+
+Create your site certificate
+
+Ok, you now must create a site certificate for your postfix server.
+As your clients will use it for verification, it must contain the
+name of your host as common name (CN): host.in.domain.
+
+You want your postfix system to start up at boot time without
+trouble? Then your server private key must not be encrypted. So
+when you create the key you must add the -nodes
option
+in CA.pl
to the line with the -newcert
+and/or -newreq
command:
+
+
+*** CA.pl Wed Mar 24 10:30:38 1999
+--- CA1.pl Sat Mar 27 19:36:47 1999
+***************
+*** 56,67 ****
+ exit 0;
+ } elsif (/^-newcert$/) {
+ # create a certificate
+! system ("$REQ -new -x509 -keyout newreq.pem -out newreq.pem $DAYS");
+ $RET=$?;
+ print "Certificate (and private key) is in newreq.pem\n"
+ } elsif (/^-newreq$/) {
+ # create a certificate request
+! system ("$REQ -new -keyout newreq.pem -out newreq.pem $DAYS");
+ $RET=$?;
+ print "Request (and private key) is in newreq.pem\n";
+ } elsif (/^-newca$/) {
+--- 56,67 ----
+ exit 0;
+ } elsif (/^-newcert$/) {
+ # create a certificate
+! system ("$REQ -new -x509 -nodes -keyout newreq.pem -out newreq.pem $DAYS");
+ $RET=$?;
+ print "Certificate (and private key) is in newreq.pem\n"
+ } elsif (/^-newreq$/) {
+ # create a certificate request
+! system ("$REQ -new -nodes -keyout newreq.pem -out newreq.pem $DAYS");
+ $RET=$?;
+ print "Request (and private key) is in newreq.pem\n";
+ } elsif (/^-newca$/) {
+
+
+For sslwrap or stunnel the authors propose to use self signed certs
+created with -newcert
. I rather propose to create an
+ordinary certificate request with
+
+
+CA.pl -newreq
+
+
+and then sign it with your CA:
+
+
+CA.pl -sign
+
+
+Now you can install the cert from cacert.pem
to
+/etc/postfix/CAcert.pem
, the created certificate from
+newcert.pem
to /etc/postfix/cert.pem
and the
+key part form newreq.pem
to
+/etc/postfix/key.pem
. Please be aware, that the
+key.pem
is not protected by password, so you have to protect
+it by file access privileges. As the information is read before
+smtpd changes to chroot jail, it still has root privileges, so you
+should
+
+
+chown root /etc/postfix/key.pem ; chmod 400 /etc/postfix/key.pem
+
+
+Create a client certificate
+
+Creating a client certificate is as easy as a site certificate. At
+least, if you are doing it as a CA. First you create and sign a
+pair of key and certificate. Be sure to add the correct common name
+(CN) for the client:
+
+
+CA.pl -newreq
+CA.pl -sign
+
+
+If you want to do client certificate based relaying, you do need
+the fingerprint of the certificate, which can be obtained with
+
+
+openssl x509 -fingerprint -in newcert.pem
+
+
+Now this certificate must be imported into netscape. Therefore the
+data you just created must be converted to a ".p12" file in PKCS#12
+format. You do need the pkcs12
utility [PKCS12], which is included in the
+OpenSSL package as of version 0.9.3. The necessary command is:
+
+
+pkcs12 -export -in newcert.pem -inkey newreq.pem \
+ -certfile /usr/local/ssl/CAcert.pem -name "Name" -out newcert.p12
+
+
+Of course your filenames may vary. Please take special care to
+supply a good name to your certificate. First: The name will be
+listed every time when a client certificate is to be send by
+netcape. As a person may have several certificates, the name might
+include a hint on the CA (e.g. "Lutz Jaenicke (Lutz CA)").
+If you want to have a lot of fun, you can just omit the name.
+Netscape will happily import the certificate, but you won't see it
+in the list of user certificates. And as you don't see it, you
+cannot select it. And as Netscape will not overwrite it, if you
+offer the same (corrected) certificate with a name, you want to
+delete it, but as you cannot select it, you cannot delete it. You
+got the point?
+
+
+
diff -Nur snapshot-20010228-orig/html/ssl/prng.html snapshot-20010228/html/ssl/prng.html
--- snapshot-20010228-orig/html/ssl/prng.html Thu Jan 1 01:00:00 1970
+++ snapshot-20010228/html/ssl/prng.html Wed Mar 21 13:38:29 2001
@@ -0,0 +1,97 @@
+
+
+
+
+Postfix/TLS - PRNG Pseudo Random Number Generator
+
+
+Postfix/TLS - PRNG Pseudo Random Number Generator
+
+One of the crucial points of encryption is the generation of the
+keys, for which random numbers are required. As of OpenSSL 0.9.5,
+the seeding of the included PRNG Pseudo Random Number Generator is
+checked. Starting with Postfix/TLS 0.5.4, an architecture to
+collect entropy is included.
+
+Included PRNG
+
+OpenSSL features a quite sophisticated PRNG. In order to generate
+random numbers of lengths of more then 1024bit, a 8192bit (=1kB)
+pool is kept and used to generate these random numbers. To achieve
+full complexity for an attacker, it is necessary to have the full
+range of random numbers available and not restrict the search space
+used for searching keys, hence an according amount of entropy is
+necessary.
+
+Obtaining Entropy
+
+To get entropy, unpredictable events are needed. Unfortunately,
+computers and software tend to be very predictable, so that a lot
+of effort is necessary to collect unpredictable events. The
+mathematical techniques are discussed in the excellent book of
+Schneier "Applied Cryptography".
+
+We use at least one feature: if you have collected a pool of
+data with entropy in it, you can add up more data without losing
+the entropy already there, so that we can mix external sources and
+internal bits to only increase the entropy.
+
+External sources
+
+Only few operating systems provide good entropy collection.
+
+/dev/random and /dev/urandom
+
+Linux offers the /dev/random and /dev/urandom
+devices, some BSD derivatives as well.
+
+/dev/random will provide high quality random data, but
+it will block until enough entropy is available, if too much random
+data is requested to fast. /dev/urandom will fill up the
+real entropy data with data from an internal PRNG and will never
+block. For a system with automated startup /dev/urandom should be
+used. Reading from /dev/urandom will however trigger kernel
+activity to satisfy the demands. Imagine starting up postfix with a
+large number of emails in the queue. 50 (default) smtp processes
+want to start at the same time and access
+/dev/urandom.
+
+Entropy Gathering Daemon
+
+A replacement for operating systems without good random number
+collection is the EGD Entropy
+Gathering Daemon. It will also extract entropy from a lot of
+sources.
+
+EGD has a command driven interface, there is a command for
+blocking and one for non-blocking read. Unlike
+/dev/urandom the non-blocking command will not trigger an
+internal PRNG to fill up, but will simply return a smaller number
+of bytes than requested, even 0 if totally drained.
+
+EGD should hence not be used for direct feeding of smtp[d]
+processes. Again, imagine 50 smtp processes starting delivery at
+the same time.
+
+To circumvent this problem, I have witten my own daemon,
+that has a EGD compatible interface but can never run dry, just
+like /dev/urandom. Check out PRNGD for details.
+
+Intermediate File
+
+Hence, Postfix/TLS maintains its own pool of entropy by means
+of the tlsmgr daemon. It will collect entropy from an
+external source at startup and periodically during runtime to ever
+increase the entropy in the pool. The smtp[d] processes are fed
+from an PRNG exchange file that is updated in short periods. Upon
+restart, tlsmgr will also read entropy from this file, so that the
+large entropy pool is fully utilized.
+
+The single smtp[d] daemons can also access an external source. Their
+collected entropy is also stirred into the intermediate file, so that
+a significant amount of entropy is available alltogether.
+
+
+
+
diff -Nur snapshot-20010228-orig/html/ssl/references.html snapshot-20010228/html/ssl/references.html
--- snapshot-20010228-orig/html/ssl/references.html Thu Jan 1 01:00:00 1970
+++ snapshot-20010228/html/ssl/references.html Wed Mar 21 13:38:29 2001
@@ -0,0 +1,101 @@
+
+
+
+
+Postfix/TLS - References
+
+
+Postfix/TLS - References
+
+
+- [POSTFIX] The Postfix (formerly VMailer) Home
+Page:
+http://www.postfix.org/.
+
+- [OPENSSL] OpenSSL: The Open Source
+toolkit for SSL/TLS:
+http://www.openssl.org/.
+
+- [PKCS12]OpenSSL PKCS#12 Program FAQ:
+http://www.drh-consultancy.demon.co.uk/pkcs12faq.html.
+
+- [SSLWRAP] SSLwrap Homepage:
+http://www.rickk.com/sslwrap/.
+
+- [STUNNEL] Stunnel Homepage:
+http://mike.daewoo.com.pl/computer/stunnel/.
+
+- [INTROCERT] Introducing SSL and
+Certificates using SSLeay:
+http://www.camb.opengroup.org/RI/www/prism/wwwj/index.html.
+
+- [IMC] Internet Mail Consortium: http://www.imc.org/.
+
+- [IETF-APPS-TLS] ietf-apps-tls
+mailing list:
+http://www.imc.org/ietf-apps-tls/
+
+- [OPENCA] The OpenCA Project: http://www.openca.org/.
+
+- [DFNCERT] DFN-CERT: http://www.cert.dfn.de/.
+
+- [SENDMAIL] Sendmail: http://www.sendmail.org/.
+
+- [SENDMAIL.INC] Sendmail Inc: http://www.sendmail.com/.
+
+- [QMAIL] Qmail: http://www.qmail.org/.
+
+- [QMAILTLS] Qmail/TLS:
+http://www.esat.kuleuven.ac.be/~vermeule/qmail/tls.patch.
+
+- [ZMAILER] ZMailer: http://www.zmailer.org/.
+
+- [JONAMA] Jonama:
+http://www.multimania.com/jonama/.
+
+- [SMTPS] Trey Child's STARTTLS wrapper:
+http://sites.netscape.net/tc15163/homepage.
+
+- [SAFEGOSSIP] Safegossip universal
+TLS-wrapper:
+http://www.skygate.co.uk/safegossip/.
+
+- [SENDMAIL-TLS] Jeremy Beker's
+sendmail-tls wrapper:
+http://opensource.3gi.com/.
+
+- [COMMUNIGATE] Stalker Software's
+CommunigatePro mailserver product:
+http://www.stalker.com/.
+
+- [EGD] Entropy Gathering Daemon:
+http://www.lothar.com/tech/crypto/.
+
+- [PRNGD] Pseudo Random Number Generator
+Daemon:
+http://www.aet.tu-cottbus.de/personen/jaenicke/postfix_tls/prngd.html.
+
+- [Outlook/SSL] Outlook (Express) and
+STARTTLS info:
+http://support.microsoft.com/support/kb/articles/Q218/4/30.ASP.
+
+
+
+
diff -Nur snapshot-20010228-orig/html/ssl/relaycert.html snapshot-20010228/html/ssl/relaycert.html
--- snapshot-20010228-orig/html/ssl/relaycert.html Thu Jan 1 01:00:00 1970
+++ snapshot-20010228/html/ssl/relaycert.html Wed Mar 21 13:38:29 2001
@@ -0,0 +1,124 @@
+
+
+
+
+Postfix/TLS - Initial Motivation
+
+
+Postfix/TLS - Initial Motivation
+
+This introduction shall point out the motivation, why I spend my
+time writing this TLS extension for postfix.
+
+Roaming users problem
+
+It quite often happens that my users want to access their mailboxes
+and to send emails from hosts outside our network. The main reasons
+are the access from home via Internet service providers (ISP) or
+from abroad during business trips (in our case typically to other
+universities around the world). Sending and accessing leads to two
+loosely coupled problems.
+
+UCE control
+
+One problem is sending emails, because from abroad it is seldom
+possible to predict the sending hostname we will have and when
+using an ISP the assigned hostname is typically random. As we of
+course must have UCE control in effect, I either must open up
+relaying complete ISP domains on my users request (Arrgghh!) or
+must introduce an authentication beside the hostname or IP address.
+
+
+Passwords and insecure networks
+
+This directly leads to the second problem. Recent versions of
+Netscape do offer password based authentication. This solves the
+UCE problem but introduces another one, which I consider far more
+severe: The users have to send a password in plain text over the
+network. Of course I could solve this problem by issuing special
+passwords just for this reasons, but some of my users don't have a
+clue of what is going on between the keyboard and the screen, so
+they would happily try their real password.
+
+The same problem of course also applies to the POP and IMAP
+services. I tackled them first, because they are typically attacked
+by port scanners, so I closed them down by tcpwrappers (Hi Wietse!)
+to only allow my local hosts to access them.
+
+Encryption via SSL
+
+The solution to the plain text password problem was easily found
+with the use of SSL. You just tunnel the POP or IMAP connection
+through SSL, using either SSLwrap [SSLWRAP] or stunnel
+[STUNNEL].
+
+Netscape supports IMAP with SSL tunneling since version 4, I
+have one user with Outlook Express, who uses POP3 with SSL
+tunneling, so this solves the plain text password problem by
+encryption.
+
+Netscape 4.5
+
+Starting with Netscape 4.5, also sending with SSL encryption is
+supported. As Netscape also supports client certificates, this
+seemed to be an easy solution for the UCE control problem. So I
+happily added an "smtps" service with SSL wrapper and client
+certificate verification. Unfortunately it didn't work and the
+connection just hung! After some digging around I found out, that
+Netscape 4.5 seems to realize the protocol described in RFC 2487 [IMC].
+
+RFC 2487 - SMTP Service Extension for Secure SMTP over TLS
+
+RFC 2487 describes how to include TLS (the successor of SSL) into
+the normal Extended SMTP protocol. During the normal EHLO start
+negotiation the server offers the STARTTLS option to the client,
+which then issues the STARTTLS command. After the server accepts
+the command (220), the normal SSL handshake will start.
+
+Unfortunately it is impossible to handle this situation with a
+normal tunneling software, as they are not prepared to do clear
+text negotiation before running SSL and don't have the slightest
+idea on the SMTP protocol. Therefore the way to go was to extend a
+given mail server software. The first candidate was sendmail-8.9.3,
+as I was a long term sendmail user. After digging around some I
+came to the conclusion, that even though possible, the source code
+was quite difficult to understand and adding the necessary
+configuration options didn't look inviting.
+
+Postfix
+
+At this point (February 1999) I checked other mail servers and was
+immedideately fascinated by postfix source. It was very good to
+read and understand, so I decided that if I would take the time,
+then postfix would be the way to go.
+
+I then started to first change our site to postfix. It took some
+hours to do this, because our mail system is running on a common
+network I administrate for several chairs, each of them with its
+own mail server and domain, but a common user base, so a lot of
+rewriting takes place, we need virtual services for symbolic names
+like "webmaster" etc.
+
+Postfix/TLS
+
+Some time after having done this I finally found the time to write
+my TLS extensions for postfix. I took the source of the
+s_server
of the OpenSSL package and added a simplified
+version of it to postfix, so that by now we can run the SMTP
+protocol encrypted on the server side. This would also allow us to
+use plain text password authentication, but as it is available
+without cost, I rather decided to go with client certificates. If
+you can offer a client certificate to our server, that is included
+in a list on our server, you can relay your emails through our
+server!
+
+Summary
+
+Postfix/TLS is an addition to the smtpd server, which implements the RFC 2487
+ TLS Service Extension and allows UCE control based on client certificates.
+
+
+
diff -Nur snapshot-20010228-orig/html/ssl/rfc2246.txt snapshot-20010228/html/ssl/rfc2246.txt
--- snapshot-20010228-orig/html/ssl/rfc2246.txt Thu Jan 1 01:00:00 1970
+++ snapshot-20010228/html/ssl/rfc2246.txt Wed Mar 21 13:38:29 2001
@@ -0,0 +1,4483 @@
+
+
+
+
+
+
+Network Working Group T. Dierks
+Request for Comments: 2246 Certicom
+Category: Standards Track C. Allen
+ Certicom
+ January 1999
+
+
+ The TLS Protocol
+ Version 1.0
+
+Status of this Memo
+
+ This document specifies an Internet standards track protocol for the
+ Internet community, and requests discussion and suggestions for
+ improvements. Please refer to the current edition of the "Internet
+ Official Protocol Standards" (STD 1) for the standardization state
+ and status of this protocol. Distribution of this memo is unlimited.
+
+Copyright Notice
+
+ Copyright (C) The Internet Society (1999). All Rights Reserved.
+
+Abstract
+
+ This document specifies Version 1.0 of the Transport Layer Security
+ (TLS) protocol. The TLS protocol provides communications privacy over
+ the Internet. The protocol allows client/server applications to
+ communicate in a way that is designed to prevent eavesdropping,
+ tampering, or message forgery.
+
+Table of Contents
+
+ 1. Introduction 3
+ 2. Goals 4
+ 3. Goals of this document 5
+ 4. Presentation language 5
+ 4.1. Basic block size 6
+ 4.2. Miscellaneous 6
+ 4.3. Vectors 6
+ 4.4. Numbers 7
+ 4.5. Enumerateds 7
+ 4.6. Constructed types 8
+ 4.6.1. Variants 9
+ 4.7. Cryptographic attributes 10
+ 4.8. Constants 11
+ 5. HMAC and the pseudorandom function 11
+ 6. The TLS Record Protocol 13
+ 6.1. Connection states 14
+
+
+
+Dierks & Allen Standards Track [Page 1]
+
+RFC 2246 The TLS Protocol Version 1.0 January 1999
+
+
+ 6.2. Record layer 16
+ 6.2.1. Fragmentation 16
+ 6.2.2. Record compression and decompression 17
+ 6.2.3. Record payload protection 18
+ 6.2.3.1. Null or standard stream cipher 19
+ 6.2.3.2. CBC block cipher 19
+ 6.3. Key calculation 21
+ 6.3.1. Export key generation example 22
+ 7. The TLS Handshake Protocol 23
+ 7.1. Change cipher spec protocol 24
+ 7.2. Alert protocol 24
+ 7.2.1. Closure alerts 25
+ 7.2.2. Error alerts 26
+ 7.3. Handshake Protocol overview 29
+ 7.4. Handshake protocol 32
+ 7.4.1. Hello messages 33
+ 7.4.1.1. Hello request 33
+ 7.4.1.2. Client hello 34
+ 7.4.1.3. Server hello 36
+ 7.4.2. Server certificate 37
+ 7.4.3. Server key exchange message 39
+ 7.4.4. Certificate request 41
+ 7.4.5. Server hello done 42
+ 7.4.6. Client certificate 43
+ 7.4.7. Client key exchange message 43
+ 7.4.7.1. RSA encrypted premaster secret message 44
+ 7.4.7.2. Client Diffie-Hellman public value 45
+ 7.4.8. Certificate verify 45
+ 7.4.9. Finished 46
+ 8. Cryptographic computations 47
+ 8.1. Computing the master secret 47
+ 8.1.1. RSA 48
+ 8.1.2. Diffie-Hellman 48
+ 9. Mandatory Cipher Suites 48
+ 10. Application data protocol 48
+ A. Protocol constant values 49
+ A.1. Record layer 49
+ A.2. Change cipher specs message 50
+ A.3. Alert messages 50
+ A.4. Handshake protocol 51
+ A.4.1. Hello messages 51
+ A.4.2. Server authentication and key exchange messages 52
+ A.4.3. Client authentication and key exchange messages 53
+ A.4.4. Handshake finalization message 54
+ A.5. The CipherSuite 54
+ A.6. The Security Parameters 56
+ B. Glossary 57
+ C. CipherSuite definitions 61
+
+
+
+Dierks & Allen Standards Track [Page 2]
+
+RFC 2246 The TLS Protocol Version 1.0 January 1999
+
+
+ D. Implementation Notes 64
+ D.1. Temporary RSA keys 64
+ D.2. Random Number Generation and Seeding 64
+ D.3. Certificates and authentication 65
+ D.4. CipherSuites 65
+ E. Backward Compatibility With SSL 66
+ E.1. Version 2 client hello 67
+ E.2. Avoiding man-in-the-middle version rollback 68
+ F. Security analysis 69
+ F.1. Handshake protocol 69
+ F.1.1. Authentication and key exchange 69
+ F.1.1.1. Anonymous key exchange 69
+ F.1.1.2. RSA key exchange and authentication 70
+ F.1.1.3. Diffie-Hellman key exchange with authentication 71
+ F.1.2. Version rollback attacks 71
+ F.1.3. Detecting attacks against the handshake protocol 72
+ F.1.4. Resuming sessions 72
+ F.1.5. MD5 and SHA 72
+ F.2. Protecting application data 72
+ F.3. Final notes 73
+ G. Patent Statement 74
+ Security Considerations 75
+ References 75
+ Credits 77
+ Comments 78
+ Full Copyright Statement 80
+
+1. Introduction
+
+ The primary goal of the TLS Protocol is to provide privacy and data
+ integrity between two communicating applications. The protocol is
+ composed of two layers: the TLS Record Protocol and the TLS Handshake
+ Protocol. At the lowest level, layered on top of some reliable
+ transport protocol (e.g., TCP[TCP]), is the TLS Record Protocol. The
+ TLS Record Protocol provides connection security that has two basic
+ properties:
+
+ - The connection is private. Symmetric cryptography is used for
+ data encryption (e.g., DES [DES], RC4 [RC4], etc.) The keys for
+ this symmetric encryption are generated uniquely for each
+ connection and are based on a secret negotiated by another
+ protocol (such as the TLS Handshake Protocol). The Record
+ Protocol can also be used without encryption.
+
+ - The connection is reliable. Message transport includes a message
+ integrity check using a keyed MAC. Secure hash functions (e.g.,
+ SHA, MD5, etc.) are used for MAC computations. The Record
+ Protocol can operate without a MAC, but is generally only used in
+
+
+
+Dierks & Allen Standards Track [Page 3]
+
+RFC 2246 The TLS Protocol Version 1.0 January 1999
+
+
+ this mode while another protocol is using the Record Protocol as
+ a transport for negotiating security parameters.
+
+ The TLS Record Protocol is used for encapsulation of various higher
+ level protocols. One such encapsulated protocol, the TLS Handshake
+ Protocol, allows the server and client to authenticate each other and
+ to negotiate an encryption algorithm and cryptographic keys before
+ the application protocol transmits or receives its first byte of
+ data. The TLS Handshake Protocol provides connection security that
+ has three basic properties:
+
+ - The peer's identity can be authenticated using asymmetric, or
+ public key, cryptography (e.g., RSA [RSA], DSS [DSS], etc.). This
+ authentication can be made optional, but is generally required
+ for at least one of the peers.
+
+ - The negotiation of a shared secret is secure: the negotiated
+ secret is unavailable to eavesdroppers, and for any authenticated
+ connection the secret cannot be obtained, even by an attacker who
+ can place himself in the middle of the connection.
+
+ - The negotiation is reliable: no attacker can modify the
+ negotiation communication without being detected by the parties
+ to the communication.
+
+ One advantage of TLS is that it is application protocol independent.
+ Higher level protocols can layer on top of the TLS Protocol
+ transparently. The TLS standard, however, does not specify how
+ protocols add security with TLS; the decisions on how to initiate TLS
+ handshaking and how to interpret the authentication certificates
+ exchanged are left up to the judgment of the designers and
+ implementors of protocols which run on top of TLS.
+
+2. Goals
+
+ The goals of TLS Protocol, in order of their priority, are:
+
+ 1. Cryptographic security: TLS should be used to establish a secure
+ connection between two parties.
+
+ 2. Interoperability: Independent programmers should be able to
+ develop applications utilizing TLS that will then be able to
+ successfully exchange cryptographic parameters without knowledge
+ of one another's code.
+
+ 3. Extensibility: TLS seeks to provide a framework into which new
+ public key and bulk encryption methods can be incorporated as
+ necessary. This will also accomplish two sub-goals: to prevent
+
+
+
+Dierks & Allen Standards Track [Page 4]
+
+RFC 2246 The TLS Protocol Version 1.0 January 1999
+
+
+ the need to create a new protocol (and risking the introduction
+ of possible new weaknesses) and to avoid the need to implement an
+ entire new security library.
+
+ 4. Relative efficiency: Cryptographic operations tend to be highly
+ CPU intensive, particularly public key operations. For this
+ reason, the TLS protocol has incorporated an optional session
+ caching scheme to reduce the number of connections that need to
+ be established from scratch. Additionally, care has been taken to
+ reduce network activity.
+
+3. Goals of this document
+
+ This document and the TLS protocol itself are based on the SSL 3.0
+ Protocol Specification as published by Netscape. The differences
+ between this protocol and SSL 3.0 are not dramatic, but they are
+ significant enough that TLS 1.0 and SSL 3.0 do not interoperate
+ (although TLS 1.0 does incorporate a mechanism by which a TLS
+ implementation can back down to SSL 3.0). This document is intended
+ primarily for readers who will be implementing the protocol and those
+ doing cryptographic analysis of it. The specification has been
+ written with this in mind, and it is intended to reflect the needs of
+ those two groups. For that reason, many of the algorithm-dependent
+ data structures and rules are included in the body of the text (as
+ opposed to in an appendix), providing easier access to them.
+
+ This document is not intended to supply any details of service
+ definition nor interface definition, although it does cover select
+ areas of policy as they are required for the maintenance of solid
+ security.
+
+4. Presentation language
+
+ This document deals with the formatting of data in an external
+ representation. The following very basic and somewhat casually
+ defined presentation syntax will be used. The syntax draws from
+ several sources in its structure. Although it resembles the
+ programming language "C" in its syntax and XDR [XDR] in both its
+ syntax and intent, it would be risky to draw too many parallels. The
+ purpose of this presentation language is to document TLS only, not to
+ have general application beyond that particular goal.
+
+
+
+
+
+
+
+
+
+
+Dierks & Allen Standards Track [Page 5]
+
+RFC 2246 The TLS Protocol Version 1.0 January 1999
+
+
+4.1. Basic block size
+
+ The representation of all data items is explicitly specified. The
+ basic data block size is one byte (i.e. 8 bits). Multiple byte data
+ items are concatenations of bytes, from left to right, from top to
+ bottom. From the bytestream a multi-byte item (a numeric in the
+ example) is formed (using C notation) by:
+
+ value = (byte[0] << 8*(n-1)) | (byte[1] << 8*(n-2)) |
+ ... | byte[n-1];
+
+ This byte ordering for multi-byte values is the commonplace network
+ byte order or big endian format.
+
+4.2. Miscellaneous
+
+ Comments begin with "/*" and end with "*/".
+
+ Optional components are denoted by enclosing them in "[[ ]]" double
+ brackets.
+
+ Single byte entities containing uninterpreted data are of type
+ opaque.
+
+4.3. Vectors
+
+ A vector (single dimensioned array) is a stream of homogeneous data
+ elements. The size of the vector may be specified at documentation
+ time or left unspecified until runtime. In either case the length
+ declares the number of bytes, not the number of elements, in the
+ vector. The syntax for specifying a new type T' that is a fixed
+ length vector of type T is
+
+ T T'[n];
+
+ Here T' occupies n bytes in the data stream, where n is a multiple of
+ the size of T. The length of the vector is not included in the
+ encoded stream.
+
+ In the following example, Datum is defined to be three consecutive
+ bytes that the protocol does not interpret, while Data is three
+ consecutive Datum, consuming a total of nine bytes.
+
+ opaque Datum[3]; /* three uninterpreted bytes */
+ Datum Data[9]; /* 3 consecutive 3 byte vectors */
+
+
+
+
+
+
+Dierks & Allen Standards Track [Page 6]
+
+RFC 2246 The TLS Protocol Version 1.0 January 1999
+
+
+ Variable length vectors are defined by specifying a subrange of legal
+ lengths, inclusively, using the notation . When
+ encoded, the actual length precedes the vector's contents in the byte
+ stream. The length will be in the form of a number consuming as many
+ bytes as required to hold the vector's specified maximum (ceiling)
+ length. A variable length vector with an actual length field of zero
+ is referred to as an empty vector.
+
+ T T';
+
+ In the following example, mandatory is a vector that must contain
+ between 300 and 400 bytes of type opaque. It can never be empty. The
+ actual length field consumes two bytes, a uint16, sufficient to
+ represent the value 400 (see Section 4.4). On the other hand, longer
+ can represent up to 800 bytes of data, or 400 uint16 elements, and it
+ may be empty. Its encoding will include a two byte actual length
+ field prepended to the vector. The length of an encoded vector must
+ be an even multiple of the length of a single element (for example, a
+ 17 byte vector of uint16 would be illegal).
+
+ opaque mandatory<300..400>;
+ /* length field is 2 bytes, cannot be empty */
+ uint16 longer<0..800>;
+ /* zero to 400 16-bit unsigned integers */
+
+4.4. Numbers
+
+ The basic numeric data type is an unsigned byte (uint8). All larger
+ numeric data types are formed from fixed length series of bytes
+ concatenated as described in Section 4.1 and are also unsigned. The
+ following numeric types are predefined.
+
+ uint8 uint16[2];
+ uint8 uint24[3];
+ uint8 uint32[4];
+ uint8 uint64[8];
+
+ All values, here and elsewhere in the specification, are stored in
+ "network" or "big-endian" order; the uint32 represented by the hex
+ bytes 01 02 03 04 is equivalent to the decimal value 16909060.
+
+4.5. Enumerateds
+
+ An additional sparse data type is available called enum. A field of
+ type enum can only assume the values declared in the definition.
+ Each definition is a different type. Only enumerateds of the same
+ type may be assigned or compared. Every element of an enumerated must
+
+
+
+
+Dierks & Allen Standards Track [Page 7]
+
+RFC 2246 The TLS Protocol Version 1.0 January 1999
+
+
+ be assigned a value, as demonstrated in the following example. Since
+ the elements of the enumerated are not ordered, they can be assigned
+ any unique value, in any order.
+
+ enum { e1(v1), e2(v2), ... , en(vn) [[, (n)]] } Te;
+
+ Enumerateds occupy as much space in the byte stream as would its
+ maximal defined ordinal value. The following definition would cause
+ one byte to be used to carry fields of type Color.
+
+ enum { red(3), blue(5), white(7) } Color;
+
+ One may optionally specify a value without its associated tag to
+ force the width definition without defining a superfluous element.
+ In the following example, Taste will consume two bytes in the data
+ stream but can only assume the values 1, 2 or 4.
+
+ enum { sweet(1), sour(2), bitter(4), (32000) } Taste;
+
+ The names of the elements of an enumeration are scoped within the
+ defined type. In the first example, a fully qualified reference to
+ the second element of the enumeration would be Color.blue. Such
+ qualification is not required if the target of the assignment is well
+ specified.
+
+ Color color = Color.blue; /* overspecified, legal */
+ Color color = blue; /* correct, type implicit */
+
+ For enumerateds that are never converted to external representation,
+ the numerical information may be omitted.
+
+ enum { low, medium, high } Amount;
+
+4.6. Constructed types
+
+ Structure types may be constructed from primitive types for
+ convenience. Each specification declares a new, unique type. The
+ syntax for definition is much like that of C.
+
+ struct {
+ T1 f1;
+ T2 f2;
+ ...
+ Tn fn;
+ } [[T]];
+
+
+
+
+
+
+Dierks & Allen Standards Track [Page 8]
+
+RFC 2246 The TLS Protocol Version 1.0 January 1999
+
+
+ The fields within a structure may be qualified using the type's name
+ using a syntax much like that available for enumerateds. For example,
+ T.f2 refers to the second field of the previous declaration.
+ Structure definitions may be embedded.
+
+4.6.1. Variants
+
+ Defined structures may have variants based on some knowledge that is
+ available within the environment. The selector must be an enumerated
+ type that defines the possible variants the structure defines. There
+ must be a case arm for every element of the enumeration declared in
+ the select. The body of the variant structure may be given a label
+ for reference. The mechanism by which the variant is selected at
+ runtime is not prescribed by the presentation language.
+
+ struct {
+ T1 f1;
+ T2 f2;
+ ....
+ Tn fn;
+ select (E) {
+ case e1: Te1;
+ case e2: Te2;
+ ....
+ case en: Ten;
+ } [[fv]];
+ } [[Tv]];
+
+ For example:
+
+ enum { apple, orange } VariantTag;
+ struct {
+ uint16 number;
+ opaque string<0..10>; /* variable length */
+ } V1;
+ struct {
+ uint32 number;
+ opaque string[10]; /* fixed length */
+ } V2;
+ struct {
+ select (VariantTag) { /* value of selector is implicit */
+ case apple: V1; /* VariantBody, tag = apple */
+ case orange: V2; /* VariantBody, tag = orange */
+ } variant_body; /* optional label on variant */
+ } VariantRecord;
+
+ Variant structures may be qualified (narrowed) by specifying a value
+ for the selector prior to the type. For example, a
+
+
+
+Dierks & Allen Standards Track [Page 9]
+
+RFC 2246 The TLS Protocol Version 1.0 January 1999
+
+
+ orange VariantRecord
+
+ is a narrowed type of a VariantRecord containing a variant_body of
+ type V2.
+
+4.7. Cryptographic attributes
+
+ The four cryptographic operations digital signing, stream cipher
+ encryption, block cipher encryption, and public key encryption are
+ designated digitally-signed, stream-ciphered, block-ciphered, and
+ public-key-encrypted, respectively. A field's cryptographic
+ processing is specified by prepending an appropriate key word
+ designation before the field's type specification. Cryptographic keys
+ are implied by the current session state (see Section 6.1).
+
+ In digital signing, one-way hash functions are used as input for a
+ signing algorithm. A digitally-signed element is encoded as an opaque
+ vector <0..2^16-1>, where the length is specified by the signing
+ algorithm and key.
+
+ In RSA signing, a 36-byte structure of two hashes (one SHA and one
+ MD5) is signed (encrypted with the private key). It is encoded with
+ PKCS #1 block type 0 or type 1 as described in [PKCS1].
+
+ In DSS, the 20 bytes of the SHA hash are run directly through the
+ Digital Signing Algorithm with no additional hashing. This produces
+ two values, r and s. The DSS signature is an opaque vector, as above,
+ the contents of which are the DER encoding of:
+
+ Dss-Sig-Value ::= SEQUENCE {
+ r INTEGER,
+ s INTEGER
+ }
+
+ In stream cipher encryption, the plaintext is exclusive-ORed with an
+ identical amount of output generated from a cryptographically-secure
+ keyed pseudorandom number generator.
+
+ In block cipher encryption, every block of plaintext encrypts to a
+ block of ciphertext. All block cipher encryption is done in CBC
+ (Cipher Block Chaining) mode, and all items which are block-ciphered
+ will be an exact multiple of the cipher block length.
+
+ In public key encryption, a public key algorithm is used to encrypt
+ data in such a way that it can be decrypted only with the matching
+ private key. A public-key-encrypted element is encoded as an opaque
+ vector <0..2^16-1>, where the length is specified by the signing
+ algorithm and key.
+
+
+
+Dierks & Allen Standards Track [Page 10]
+
+RFC 2246 The TLS Protocol Version 1.0 January 1999
+
+
+ An RSA encrypted value is encoded with PKCS #1 block type 2 as
+ described in [PKCS1].
+
+ In the following example:
+
+ stream-ciphered struct {
+ uint8 field1;
+ uint8 field2;
+ digitally-signed opaque hash[20];
+ } UserType;
+
+ The contents of hash are used as input for the signing algorithm,
+ then the entire structure is encrypted with a stream cipher. The
+ length of this structure, in bytes would be equal to 2 bytes for
+ field1 and field2, plus two bytes for the length of the signature,
+ plus the length of the output of the signing algorithm. This is known
+ due to the fact that the algorithm and key used for the signing are
+ known prior to encoding or decoding this structure.
+
+4.8. Constants
+
+ Typed constants can be defined for purposes of specification by
+ declaring a symbol of the desired type and assigning values to it.
+ Under-specified types (opaque, variable length vectors, and
+ structures that contain opaque) cannot be assigned values. No fields
+ of a multi-element structure or vector may be elided.
+
+ For example,
+
+ struct {
+ uint8 f1;
+ uint8 f2;
+ } Example1;
+
+ Example1 ex1 = {1, 4}; /* assigns f1 = 1, f2 = 4 */
+
+5. HMAC and the pseudorandom function
+
+ A number of operations in the TLS record and handshake layer required
+ a keyed MAC; this is a secure digest of some data protected by a
+ secret. Forging the MAC is infeasible without knowledge of the MAC
+ secret. The construction we use for this operation is known as HMAC,
+ described in [HMAC].
+
+ HMAC can be used with a variety of different hash algorithms. TLS
+ uses it in the handshake with two different algorithms: MD5 and SHA-
+ 1, denoting these as HMAC_MD5(secret, data) and HMAC_SHA(secret,
+
+
+
+
+Dierks & Allen Standards Track [Page 11]
+
+RFC 2246 The TLS Protocol Version 1.0 January 1999
+
+
+ data). Additional hash algorithms can be defined by cipher suites and
+ used to protect record data, but MD5 and SHA-1 are hard coded into
+ the description of the handshaking for this version of the protocol.
+
+ In addition, a construction is required to do expansion of secrets
+ into blocks of data for the purposes of key generation or validation.
+ This pseudo-random function (PRF) takes as input a secret, a seed,
+ and an identifying label and produces an output of arbitrary length.
+
+ In order to make the PRF as secure as possible, it uses two hash
+ algorithms in a way which should guarantee its security if either
+ algorithm remains secure.
+
+ First, we define a data expansion function, P_hash(secret, data)
+ which uses a single hash function to expand a secret and seed into an
+ arbitrary quantity of output:
+
+ P_hash(secret, seed) = HMAC_hash(secret, A(1) + seed) +
+ HMAC_hash(secret, A(2) + seed) +
+ HMAC_hash(secret, A(3) + seed) + ...
+
+ Where + indicates concatenation.
+
+ A() is defined as:
+ A(0) = seed
+ A(i) = HMAC_hash(secret, A(i-1))
+
+ P_hash can be iterated as many times as is necessary to produce the
+ required quantity of data. For example, if P_SHA-1 was being used to
+ create 64 bytes of data, it would have to be iterated 4 times
+ (through A(4)), creating 80 bytes of output data; the last 16 bytes
+ of the final iteration would then be discarded, leaving 64 bytes of
+ output data.
+
+ TLS's PRF is created by splitting the secret into two halves and
+ using one half to generate data with P_MD5 and the other half to
+ generate data with P_SHA-1, then exclusive-or'ing the outputs of
+ these two expansion functions together.
+
+ S1 and S2 are the two halves of the secret and each is the same
+ length. S1 is taken from the first half of the secret, S2 from the
+ second half. Their length is created by rounding up the length of the
+ overall secret divided by two; thus, if the original secret is an odd
+ number of bytes long, the last byte of S1 will be the same as the
+ first byte of S2.
+
+ L_S = length in bytes of secret;
+ L_S1 = L_S2 = ceil(L_S / 2);
+
+
+
+Dierks & Allen Standards Track [Page 12]
+
+RFC 2246 The TLS Protocol Version 1.0 January 1999
+
+
+ The secret is partitioned into two halves (with the possibility of
+ one shared byte) as described above, S1 taking the first L_S1 bytes
+ and S2 the last L_S2 bytes.
+
+ The PRF is then defined as the result of mixing the two pseudorandom
+ streams by exclusive-or'ing them together.
+
+ PRF(secret, label, seed) = P_MD5(S1, label + seed) XOR
+ P_SHA-1(S2, label + seed);
+
+ The label is an ASCII string. It should be included in the exact form
+ it is given without a length byte or trailing null character. For
+ example, the label "slithy toves" would be processed by hashing the
+ following bytes:
+
+ 73 6C 69 74 68 79 20 74 6F 76 65 73
+
+ Note that because MD5 produces 16 byte outputs and SHA-1 produces 20
+ byte outputs, the boundaries of their internal iterations will not be
+ aligned; to generate a 80 byte output will involve P_MD5 being
+ iterated through A(5), while P_SHA-1 will only iterate through A(4).
+
+6. The TLS Record Protocol
+
+ The TLS Record Protocol is a layered protocol. At each layer,
+ messages may include fields for length, description, and content.
+ The Record Protocol takes messages to be transmitted, fragments the
+ data into manageable blocks, optionally compresses the data, applies
+ a MAC, encrypts, and transmits the result. Received data is
+ decrypted, verified, decompressed, and reassembled, then delivered to
+ higher level clients.
+
+ Four record protocol clients are described in this document: the
+ handshake protocol, the alert protocol, the change cipher spec
+ protocol, and the application data protocol. In order to allow
+ extension of the TLS protocol, additional record types can be
+ supported by the record protocol. Any new record types should
+ allocate type values immediately beyond the ContentType values for
+ the four record types described here (see Appendix A.2). If a TLS
+ implementation receives a record type it does not understand, it
+ should just ignore it. Any protocol designed for use over TLS must be
+ carefully designed to deal with all possible attacks against it.
+ Note that because the type and length of a record are not protected
+ by encryption, care should be take to minimize the value of traffic
+ analysis of these values.
+
+
+
+
+
+
+Dierks & Allen Standards Track [Page 13]
+
+RFC 2246 The TLS Protocol Version 1.0 January 1999
+
+
+6.1. Connection states
+
+ A TLS connection state is the operating environment of the TLS Record
+ Protocol. It specifies a compression algorithm, encryption algorithm,
+ and MAC algorithm. In addition, the parameters for these algorithms
+ are known: the MAC secret and the bulk encryption keys and IVs for
+ the connection in both the read and the write directions. Logically,
+ there are always four connection states outstanding: the current read
+ and write states, and the pending read and write states. All records
+ are processed under the current read and write states. The security
+ parameters for the pending states can be set by the TLS Handshake
+ Protocol, and the Handshake Protocol can selectively make either of
+ the pending states current, in which case the appropriate current
+ state is disposed of and replaced with the pending state; the pending
+ state is then reinitialized to an empty state. It is illegal to make
+ a state which has not been initialized with security parameters a
+ current state. The initial current state always specifies that no
+ encryption, compression, or MAC will be used.
+
+ The security parameters for a TLS Connection read and write state are
+ set by providing the following values:
+
+ connection end
+ Whether this entity is considered the "client" or the "server" in
+ this connection.
+
+ bulk encryption algorithm
+ An algorithm to be used for bulk encryption. This specification
+ includes the key size of this algorithm, how much of that key is
+ secret, whether it is a block or stream cipher, the block size of
+ the cipher (if appropriate), and whether it is considered an
+ "export" cipher.
+
+ MAC algorithm
+ An algorithm to be used for message authentication. This
+ specification includes the size of the hash which is returned by
+ the MAC algorithm.
+
+ compression algorithm
+ An algorithm to be used for data compression. This specification
+ must include all information the algorithm requires to do
+ compression.
+
+ master secret
+ A 48 byte secret shared between the two peers in the connection.
+
+ client random
+ A 32 byte value provided by the client.
+
+
+
+Dierks & Allen Standards Track [Page 14]
+
+RFC 2246 The TLS Protocol Version 1.0 January 1999
+
+
+ server random
+ A 32 byte value provided by the server.
+
+ These parameters are defined in the presentation language as:
+
+ enum { server, client } ConnectionEnd;
+
+ enum { null, rc4, rc2, des, 3des, des40 } BulkCipherAlgorithm;
+
+ enum { stream, block } CipherType;
+
+ enum { true, false } IsExportable;
+
+ enum { null, md5, sha } MACAlgorithm;
+
+ enum { null(0), (255) } CompressionMethod;
+
+ /* The algorithms specified in CompressionMethod,
+ BulkCipherAlgorithm, and MACAlgorithm may be added to. */
+
+ struct {
+ ConnectionEnd entity;
+ BulkCipherAlgorithm bulk_cipher_algorithm;
+ CipherType cipher_type;
+ uint8 key_size;
+ uint8 key_material_length;
+ IsExportable is_exportable;
+ MACAlgorithm mac_algorithm;
+ uint8 hash_size;
+ CompressionMethod compression_algorithm;
+ opaque master_secret[48];
+ opaque client_random[32];
+ opaque server_random[32];
+ } SecurityParameters;
+
+ The record layer will use the security parameters to generate the
+ following six items:
+
+ client write MAC secret
+ server write MAC secret
+ client write key
+ server write key
+ client write IV (for block ciphers only)
+ server write IV (for block ciphers only)
+
+ The client write parameters are used by the server when receiving and
+ processing records and vice-versa. The algorithm used for generating
+ these items from the security parameters is described in section 6.3.
+
+
+
+Dierks & Allen Standards Track [Page 15]
+
+RFC 2246 The TLS Protocol Version 1.0 January 1999
+
+
+ Once the security parameters have been set and the keys have been
+ generated, the connection states can be instantiated by making them
+ the current states. These current states must be updated for each
+ record processed. Each connection state includes the following
+ elements:
+
+ compression state
+ The current state of the compression algorithm.
+
+ cipher state
+ The current state of the encryption algorithm. This will consist
+ of the scheduled key for that connection. In addition, for block
+ ciphers running in CBC mode (the only mode specified for TLS),
+ this will initially contain the IV for that connection state and
+ be updated to contain the ciphertext of the last block encrypted
+ or decrypted as records are processed. For stream ciphers, this
+ will contain whatever the necessary state information is to allow
+ the stream to continue to encrypt or decrypt data.
+
+ MAC secret
+ The MAC secret for this connection as generated above.
+
+ sequence number
+ Each connection state contains a sequence number, which is
+ maintained separately for read and write states. The sequence
+ number must be set to zero whenever a connection state is made
+ the active state. Sequence numbers are of type uint64 and may not
+ exceed 2^64-1. A sequence number is incremented after each
+ record: specifically, the first record which is transmitted under
+ a particular connection state should use sequence number 0.
+
+6.2. Record layer
+
+ The TLS Record Layer receives uninterpreted data from higher layers
+ in non-empty blocks of arbitrary size.
+
+6.2.1. Fragmentation
+
+ The record layer fragments information blocks into TLSPlaintext
+ records carrying data in chunks of 2^14 bytes or less. Client message
+ boundaries are not preserved in the record layer (i.e., multiple
+ client messages of the same ContentType may be coalesced into a
+ single TLSPlaintext record, or a single message may be fragmented
+ across several records).
+
+ struct {
+ uint8 major, minor;
+ } ProtocolVersion;
+
+
+
+Dierks & Allen Standards Track [Page 16]
+
+RFC 2246 The TLS Protocol Version 1.0 January 1999
+
+
+ enum {
+ change_cipher_spec(20), alert(21), handshake(22),
+ application_data(23), (255)
+ } ContentType;
+
+ struct {
+ ContentType type;
+ ProtocolVersion version;
+ uint16 length;
+ opaque fragment[TLSPlaintext.length];
+ } TLSPlaintext;
+
+ type
+ The higher level protocol used to process the enclosed fragment.
+
+ version
+ The version of the protocol being employed. This document
+ describes TLS Version 1.0, which uses the version { 3, 1 }. The
+ version value 3.1 is historical: TLS version 1.0 is a minor
+ modification to the SSL 3.0 protocol, which bears the version
+ value 3.0. (See Appendix A.1).
+
+ length
+ The length (in bytes) of the following TLSPlaintext.fragment.
+ The length should not exceed 2^14.
+
+ fragment
+ The application data. This data is transparent and treated as an
+ independent block to be dealt with by the higher level protocol
+ specified by the type field.
+
+ Note: Data of different TLS Record layer content types may be
+ interleaved. Application data is generally of lower precedence
+ for transmission than other content types.
+
+6.2.2. Record compression and decompression
+
+ All records are compressed using the compression algorithm defined in
+ the current session state. There is always an active compression
+ algorithm; however, initially it is defined as
+ CompressionMethod.null. The compression algorithm translates a
+ TLSPlaintext structure into a TLSCompressed structure. Compression
+ functions are initialized with default state information whenever a
+ connection state is made active.
+
+
+
+
+
+
+
+Dierks & Allen Standards Track [Page 17]
+
+RFC 2246 The TLS Protocol Version 1.0 January 1999
+
+
+ Compression must be lossless and may not increase the content length
+ by more than 1024 bytes. If the decompression function encounters a
+ TLSCompressed.fragment that would decompress to a length in excess of
+ 2^14 bytes, it should report a fatal decompression failure error.
+
+ struct {
+ ContentType type; /* same as TLSPlaintext.type */
+ ProtocolVersion version;/* same as TLSPlaintext.version */
+ uint16 length;
+ opaque fragment[TLSCompressed.length];
+ } TLSCompressed;
+
+ length
+ The length (in bytes) of the following TLSCompressed.fragment.
+ The length should not exceed 2^14 + 1024.
+
+ fragment
+ The compressed form of TLSPlaintext.fragment.
+
+ Note: A CompressionMethod.null operation is an identity operation; no
+ fields are altered.
+
+ Implementation note:
+ Decompression functions are responsible for ensuring that
+ messages cannot cause internal buffer overflows.
+
+6.2.3. Record payload protection
+
+ The encryption and MAC functions translate a TLSCompressed structure
+ into a TLSCiphertext. The decryption functions reverse the process.
+ The MAC of the record also includes a sequence number so that
+ missing, extra or repeated messages are detectable.
+
+ struct {
+ ContentType type;
+ ProtocolVersion version;
+ uint16 length;
+ select (CipherSpec.cipher_type) {
+ case stream: GenericStreamCipher;
+ case block: GenericBlockCipher;
+ } fragment;
+ } TLSCiphertext;
+
+ type
+ The type field is identical to TLSCompressed.type.
+
+ version
+ The version field is identical to TLSCompressed.version.
+
+
+
+Dierks & Allen Standards Track [Page 18]
+
+RFC 2246 The TLS Protocol Version 1.0 January 1999
+
+
+ length
+ The length (in bytes) of the following TLSCiphertext.fragment.
+ The length may not exceed 2^14 + 2048.
+
+ fragment
+ The encrypted form of TLSCompressed.fragment, with the MAC.
+
+6.2.3.1. Null or standard stream cipher
+
+ Stream ciphers (including BulkCipherAlgorithm.null - see Appendix
+ A.6) convert TLSCompressed.fragment structures to and from stream
+ TLSCiphertext.fragment structures.
+
+ stream-ciphered struct {
+ opaque content[TLSCompressed.length];
+ opaque MAC[CipherSpec.hash_size];
+ } GenericStreamCipher;
+
+ The MAC is generated as:
+
+ HMAC_hash(MAC_write_secret, seq_num + TLSCompressed.type +
+ TLSCompressed.version + TLSCompressed.length +
+ TLSCompressed.fragment));
+
+ where "+" denotes concatenation.
+
+ seq_num
+ The sequence number for this record.
+
+ hash
+ The hashing algorithm specified by
+ SecurityParameters.mac_algorithm.
+
+ Note that the MAC is computed before encryption. The stream cipher
+ encrypts the entire block, including the MAC. For stream ciphers that
+ do not use a synchronization vector (such as RC4), the stream cipher
+ state from the end of one record is simply used on the subsequent
+ packet. If the CipherSuite is TLS_NULL_WITH_NULL_NULL, encryption
+ consists of the identity operation (i.e., the data is not encrypted
+ and the MAC size is zero implying that no MAC is used).
+ TLSCiphertext.length is TLSCompressed.length plus
+ CipherSpec.hash_size.
+
+6.2.3.2. CBC block cipher
+
+ For block ciphers (such as RC2 or DES), the encryption and MAC
+ functions convert TLSCompressed.fragment structures to and from block
+ TLSCiphertext.fragment structures.
+
+
+
+Dierks & Allen Standards Track [Page 19]
+
+RFC 2246 The TLS Protocol Version 1.0 January 1999
+
+
+ block-ciphered struct {
+ opaque content[TLSCompressed.length];
+ opaque MAC[CipherSpec.hash_size];
+ uint8 padding[GenericBlockCipher.padding_length];
+ uint8 padding_length;
+ } GenericBlockCipher;
+
+ The MAC is generated as described in Section 6.2.3.1.
+
+ padding
+ Padding that is added to force the length of the plaintext to be
+ an integral multiple of the block cipher's block length. The
+ padding may be any length up to 255 bytes long, as long as it
+ results in the TLSCiphertext.length being an integral multiple of
+ the block length. Lengths longer than necessary might be
+ desirable to frustrate attacks on a protocol based on analysis of
+ the lengths of exchanged messages. Each uint8 in the padding data
+ vector must be filled with the padding length value.
+
+ padding_length
+ The padding length should be such that the total size of the
+ GenericBlockCipher structure is a multiple of the cipher's block
+ length. Legal values range from zero to 255, inclusive. This
+ length specifies the length of the padding field exclusive of the
+ padding_length field itself.
+
+ The encrypted data length (TLSCiphertext.length) is one more than the
+ sum of TLSCompressed.length, CipherSpec.hash_size, and
+ padding_length.
+
+ Example: If the block length is 8 bytes, the content length
+ (TLSCompressed.length) is 61 bytes, and the MAC length is 20
+ bytes, the length before padding is 82 bytes. Thus, the
+ padding length modulo 8 must be equal to 6 in order to make
+ the total length an even multiple of 8 bytes (the block
+ length). The padding length can be 6, 14, 22, and so on,
+ through 254. If the padding length were the minimum necessary,
+ 6, the padding would be 6 bytes, each containing the value 6.
+ Thus, the last 8 octets of the GenericBlockCipher before block
+ encryption would be xx 06 06 06 06 06 06 06, where xx is the
+ last octet of the MAC.
+
+ Note: With block ciphers in CBC mode (Cipher Block Chaining) the
+ initialization vector (IV) for the first record is generated with
+ the other keys and secrets when the security parameters are set.
+ The IV for subsequent records is the last ciphertext block from
+ the previous record.
+
+
+
+
+Dierks & Allen Standards Track [Page 20]
+
+RFC 2246 The TLS Protocol Version 1.0 January 1999
+
+
+6.3. Key calculation
+
+ The Record Protocol requires an algorithm to generate keys, IVs, and
+ MAC secrets from the security parameters provided by the handshake
+ protocol.
+
+ The master secret is hashed into a sequence of secure bytes, which
+ are assigned to the MAC secrets, keys, and non-export IVs required by
+ the current connection state (see Appendix A.6). CipherSpecs require
+ a client write MAC secret, a server write MAC secret, a client write
+ key, a server write key, a client write IV, and a server write IV,
+ which are generated from the master secret in that order. Unused
+ values are empty.
+
+ When generating keys and MAC secrets, the master secret is used as an
+ entropy source, and the random values provide unencrypted salt
+ material and IVs for exportable ciphers.
+
+ To generate the key material, compute
+
+ key_block = PRF(SecurityParameters.master_secret,
+ "key expansion",
+ SecurityParameters.server_random +
+ SecurityParameters.client_random);
+
+ until enough output has been generated. Then the key_block is
+ partitioned as follows:
+
+ client_write_MAC_secret[SecurityParameters.hash_size]
+ server_write_MAC_secret[SecurityParameters.hash_size]
+ client_write_key[SecurityParameters.key_material_length]
+ server_write_key[SecurityParameters.key_material_length]
+ client_write_IV[SecurityParameters.IV_size]
+ server_write_IV[SecurityParameters.IV_size]
+
+ The client_write_IV and server_write_IV are only generated for non-
+ export block ciphers. For exportable block ciphers, the
+ initialization vectors are generated later, as described below. Any
+ extra key_block material is discarded.
+
+ Implementation note:
+ The cipher spec which is defined in this document which requires
+ the most material is 3DES_EDE_CBC_SHA: it requires 2 x 24 byte
+ keys, 2 x 20 byte MAC secrets, and 2 x 8 byte IVs, for a total of
+ 104 bytes of key material.
+
+
+
+
+
+
+Dierks & Allen Standards Track [Page 21]
+
+RFC 2246 The TLS Protocol Version 1.0 January 1999
+
+
+ Exportable encryption algorithms (for which CipherSpec.is_exportable
+ is true) require additional processing as follows to derive their
+ final write keys:
+
+ final_client_write_key =
+ PRF(SecurityParameters.client_write_key,
+ "client write key",
+ SecurityParameters.client_random +
+ SecurityParameters.server_random);
+ final_server_write_key =
+ PRF(SecurityParameters.server_write_key,
+ "server write key",
+ SecurityParameters.client_random +
+ SecurityParameters.server_random);
+
+ Exportable encryption algorithms derive their IVs solely from the
+ random values from the hello messages:
+
+ iv_block = PRF("", "IV block", SecurityParameters.client_random +
+ SecurityParameters.server_random);
+
+ The iv_block is partitioned into two initialization vectors as the
+ key_block was above:
+
+ client_write_IV[SecurityParameters.IV_size]
+ server_write_IV[SecurityParameters.IV_size]
+
+ Note that the PRF is used without a secret in this case: this just
+ means that the secret has a length of zero bytes and contributes
+ nothing to the hashing in the PRF.
+
+6.3.1. Export key generation example
+
+ TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5 requires five random bytes for
+ each of the two encryption keys and 16 bytes for each of the MAC
+ keys, for a total of 42 bytes of key material. The PRF output is
+ stored in the key_block. The key_block is partitioned, and the write
+ keys are salted because this is an exportable encryption algorithm.
+
+ key_block = PRF(master_secret,
+ "key expansion",
+ server_random +
+ client_random)[0..41]
+ client_write_MAC_secret = key_block[0..15]
+ server_write_MAC_secret = key_block[16..31]
+ client_write_key = key_block[32..36]
+ server_write_key = key_block[37..41]
+
+
+
+
+Dierks & Allen Standards Track [Page 22]
+
+RFC 2246 The TLS Protocol Version 1.0 January 1999
+
+
+ final_client_write_key = PRF(client_write_key,
+ "client write key",
+ client_random +
+ server_random)[0..15]
+ final_server_write_key = PRF(server_write_key,
+ "server write key",
+ client_random +
+ server_random)[0..15]
+
+ iv_block = PRF("", "IV block", client_random +
+ server_random)[0..15]
+ client_write_IV = iv_block[0..7]
+ server_write_IV = iv_block[8..15]
+
+7. The TLS Handshake Protocol
+
+ The TLS Handshake Protocol consists of a suite of three sub-protocols
+ which are used to allow peers to agree upon security parameters for
+ the record layer, authenticate themselves, instantiate negotiated
+ security parameters, and report error conditions to each other.
+
+ The Handshake Protocol is responsible for negotiating a session,
+ which consists of the following items:
+
+ session identifier
+ An arbitrary byte sequence chosen by the server to identify an
+ active or resumable session state.
+
+ peer certificate
+ X509v3 [X509] certificate of the peer. This element of the state
+ may be null.
+
+ compression method
+ The algorithm used to compress data prior to encryption.
+
+ cipher spec
+ Specifies the bulk data encryption algorithm (such as null, DES,
+ etc.) and a MAC algorithm (such as MD5 or SHA). It also defines
+ cryptographic attributes such as the hash_size. (See Appendix A.6
+ for formal definition)
+
+ master secret
+ 48-byte secret shared between the client and server.
+
+ is resumable
+ A flag indicating whether the session can be used to initiate new
+ connections.
+
+
+
+
+Dierks & Allen Standards Track [Page 23]
+
+RFC 2246 The TLS Protocol Version 1.0 January 1999
+
+
+ These items are then used to create security parameters for use by
+ the Record Layer when protecting application data. Many connections
+ can be instantiated using the same session through the resumption
+ feature of the TLS Handshake Protocol.
+
+7.1. Change cipher spec protocol
+
+ The change cipher spec protocol exists to signal transitions in
+ ciphering strategies. The protocol consists of a single message,
+ which is encrypted and compressed under the current (not the pending)
+ connection state. The message consists of a single byte of value 1.
+
+ struct {
+ enum { change_cipher_spec(1), (255) } type;
+ } ChangeCipherSpec;
+
+ The change cipher spec message is sent by both the client and server
+ to notify the receiving party that subsequent records will be
+ protected under the newly negotiated CipherSpec and keys. Reception
+ of this message causes the receiver to instruct the Record Layer to
+ immediately copy the read pending state into the read current state.
+ Immediately after sending this message, the sender should instruct
+ the record layer to make the write pending state the write active
+ state. (See section 6.1.) The change cipher spec message is sent
+ during the handshake after the security parameters have been agreed
+ upon, but before the verifying finished message is sent (see section
+ 7.4.9).
+
+7.2. Alert protocol
+
+ One of the content types supported by the TLS Record layer is the
+ alert type. Alert messages convey the severity of the message and a
+ description of the alert. Alert messages with a level of fatal result
+ in the immediate termination of the connection. In this case, other
+ connections corresponding to the session may continue, but the
+ session identifier must be invalidated, preventing the failed session
+ from being used to establish new connections. Like other messages,
+ alert messages are encrypted and compressed, as specified by the
+ current connection state.
+
+ enum { warning(1), fatal(2), (255) } AlertLevel;
+
+ enum {
+ close_notify(0),
+ unexpected_message(10),
+ bad_record_mac(20),
+ decryption_failed(21),
+ record_overflow(22),
+
+
+
+Dierks & Allen Standards Track [Page 24]
+
+RFC 2246 The TLS Protocol Version 1.0 January 1999
+
+
+ decompression_failure(30),
+ handshake_failure(40),
+ bad_certificate(42),
+ unsupported_certificate(43),
+ certificate_revoked(44),
+ certificate_expired(45),
+ certificate_unknown(46),
+ illegal_parameter(47),
+ unknown_ca(48),
+ access_denied(49),
+ decode_error(50),
+ decrypt_error(51),
+ export_restriction(60),
+ protocol_version(70),
+ insufficient_security(71),
+ internal_error(80),
+ user_canceled(90),
+ no_renegotiation(100),
+ (255)
+ } AlertDescription;
+
+ struct {
+ AlertLevel level;
+ AlertDescription description;
+ } Alert;
+
+7.2.1. Closure alerts
+
+ The client and the server must share knowledge that the connection is
+ ending in order to avoid a truncation attack. Either party may
+ initiate the exchange of closing messages.
+
+ close_notify
+ This message notifies the recipient that the sender will not send
+ any more messages on this connection. The session becomes
+ unresumable if any connection is terminated without proper
+ close_notify messages with level equal to warning.
+
+ Either party may initiate a close by sending a close_notify alert.
+ Any data received after a closure alert is ignored.
+
+ Each party is required to send a close_notify alert before closing
+ the write side of the connection. It is required that the other party
+ respond with a close_notify alert of its own and close down the
+ connection immediately, discarding any pending writes. It is not
+ required for the initiator of the close to wait for the responding
+ close_notify alert before closing the read side of the connection.
+
+
+
+
+Dierks & Allen Standards Track [Page 25]
+
+RFC 2246 The TLS Protocol Version 1.0 January 1999
+
+
+ If the application protocol using TLS provides that any data may be
+ carried over the underlying transport after the TLS connection is
+ closed, the TLS implementation must receive the responding
+ close_notify alert before indicating to the application layer that
+ the TLS connection has ended. If the application protocol will not
+ transfer any additional data, but will only close the underlying
+ transport connection, then the implementation may choose to close the
+ transport without waiting for the responding close_notify. No part of
+ this standard should be taken to dictate the manner in which a usage
+ profile for TLS manages its data transport, including when
+ connections are opened or closed.
+
+ NB: It is assumed that closing a connection reliably delivers
+ pending data before destroying the transport.
+
+7.2.2. Error alerts
+
+ Error handling in the TLS Handshake protocol is very simple. When an
+ error is detected, the detecting party sends a message to the other
+ party. Upon transmission or receipt of an fatal alert message, both
+ parties immediately close the connection. Servers and clients are
+ required to forget any session-identifiers, keys, and secrets
+ associated with a failed connection. The following error alerts are
+ defined:
+
+ unexpected_message
+ An inappropriate message was received. This alert is always fatal
+ and should never be observed in communication between proper
+ implementations.
+
+ bad_record_mac
+ This alert is returned if a record is received with an incorrect
+ MAC. This message is always fatal.
+
+ decryption_failed
+ A TLSCiphertext decrypted in an invalid way: either it wasn`t an
+ even multiple of the block length or its padding values, when
+ checked, weren`t correct. This message is always fatal.
+
+ record_overflow
+ A TLSCiphertext record was received which had a length more than
+ 2^14+2048 bytes, or a record decrypted to a TLSCompressed record
+ with more than 2^14+1024 bytes. This message is always fatal.
+
+ decompression_failure
+ The decompression function received improper input (e.g. data
+ that would expand to excessive length). This message is always
+ fatal.
+
+
+
+Dierks & Allen Standards Track [Page 26]
+
+RFC 2246 The TLS Protocol Version 1.0 January 1999
+
+
+ handshake_failure
+ Reception of a handshake_failure alert message indicates that the
+ sender was unable to negotiate an acceptable set of security
+ parameters given the options available. This is a fatal error.
+
+ bad_certificate
+ A certificate was corrupt, contained signatures that did not
+ verify correctly, etc.
+
+ unsupported_certificate
+ A certificate was of an unsupported type.
+
+ certificate_revoked
+ A certificate was revoked by its signer.
+
+ certificate_expired
+ A certificate has expired or is not currently valid.
+
+ certificate_unknown
+ Some other (unspecified) issue arose in processing the
+ certificate, rendering it unacceptable.
+
+ illegal_parameter
+ A field in the handshake was out of range or inconsistent with
+ other fields. This is always fatal.
+
+ unknown_ca
+ A valid certificate chain or partial chain was received, but the
+ certificate was not accepted because the CA certificate could not
+ be located or couldn`t be matched with a known, trusted CA. This
+ message is always fatal.
+
+ access_denied
+ A valid certificate was received, but when access control was
+ applied, the sender decided not to proceed with negotiation.
+ This message is always fatal.
+
+ decode_error
+ A message could not be decoded because some field was out of the
+ specified range or the length of the message was incorrect. This
+ message is always fatal.
+
+ decrypt_error
+ A handshake cryptographic operation failed, including being
+ unable to correctly verify a signature, decrypt a key exchange,
+ or validate a finished message.
+
+
+
+
+
+Dierks & Allen Standards Track [Page 27]
+
+RFC 2246 The TLS Protocol Version 1.0 January 1999
+
+
+ export_restriction
+ A negotiation not in compliance with export restrictions was
+ detected; for example, attempting to transfer a 1024 bit
+ ephemeral RSA key for the RSA_EXPORT handshake method. This
+ message is always fatal.
+
+ protocol_version
+ The protocol version the client has attempted to negotiate is
+ recognized, but not supported. (For example, old protocol
+ versions might be avoided for security reasons). This message is
+ always fatal.
+
+ insufficient_security
+ Returned instead of handshake_failure when a negotiation has
+ failed specifically because the server requires ciphers more
+ secure than those supported by the client. This message is always
+ fatal.
+
+ internal_error
+ An internal error unrelated to the peer or the correctness of the
+ protocol makes it impossible to continue (such as a memory
+ allocation failure). This message is always fatal.
+
+ user_canceled
+ This handshake is being canceled for some reason unrelated to a
+ protocol failure. If the user cancels an operation after the
+ handshake is complete, just closing the connection by sending a
+ close_notify is more appropriate. This alert should be followed
+ by a close_notify. This message is generally a warning.
+
+ no_renegotiation
+ Sent by the client in response to a hello request or by the
+ server in response to a client hello after initial handshaking.
+ Either of these would normally lead to renegotiation; when that
+ is not appropriate, the recipient should respond with this alert;
+ at that point, the original requester can decide whether to
+ proceed with the connection. One case where this would be
+ appropriate would be where a server has spawned a process to
+ satisfy a request; the process might receive security parameters
+ (key length, authentication, etc.) at startup and it might be
+ difficult to communicate changes to these parameters after that
+ point. This message is always a warning.
+
+ For all errors where an alert level is not explicitly specified, the
+ sending party may determine at its discretion whether this is a fatal
+ error or not; if an alert with a level of warning is received, the
+
+
+
+
+
+Dierks & Allen Standards Track [Page 28]
+
+RFC 2246 The TLS Protocol Version 1.0 January 1999
+
+
+ receiving party may decide at its discretion whether to treat this as
+ a fatal error or not. However, all messages which are transmitted
+ with a level of fatal must be treated as fatal messages.
+
+7.3. Handshake Protocol overview
+
+ The cryptographic parameters of the session state are produced by the
+ TLS Handshake Protocol, which operates on top of the TLS Record
+ Layer. When a TLS client and server first start communicating, they
+ agree on a protocol version, select cryptographic algorithms,
+ optionally authenticate each other, and use public-key encryption
+ techniques to generate shared secrets.
+
+ The TLS Handshake Protocol involves the following steps:
+
+ - Exchange hello messages to agree on algorithms, exchange random
+ values, and check for session resumption.
+
+ - Exchange the necessary cryptographic parameters to allow the
+ client and server to agree on a premaster secret.
+
+ - Exchange certificates and cryptographic information to allow the
+ client and server to authenticate themselves.
+
+ - Generate a master secret from the premaster secret and exchanged
+ random values.
+
+ - Provide security parameters to the record layer.
+
+ - Allow the client and server to verify that their peer has
+ calculated the same security parameters and that the handshake
+ occurred without tampering by an attacker.
+
+ Note that higher layers should not be overly reliant on TLS always
+ negotiating the strongest possible connection between two peers:
+ there are a number of ways a man in the middle attacker can attempt
+ to make two entities drop down to the least secure method they
+ support. The protocol has been designed to minimize this risk, but
+ there are still attacks available: for example, an attacker could
+ block access to the port a secure service runs on, or attempt to get
+ the peers to negotiate an unauthenticated connection. The fundamental
+ rule is that higher levels must be cognizant of what their security
+ requirements are and never transmit information over a channel less
+ secure than what they require. The TLS protocol is secure, in that
+ any cipher suite offers its promised level of security: if you
+ negotiate 3DES with a 1024 bit RSA key exchange with a host whose
+ certificate you have verified, you can expect to be that secure.
+
+
+
+
+Dierks & Allen Standards Track [Page 29]
+
+RFC 2246 The TLS Protocol Version 1.0 January 1999
+
+
+ However, you should never send data over a link encrypted with 40 bit
+ security unless you feel that data is worth no more than the effort
+ required to break that encryption.
+
+ These goals are achieved by the handshake protocol, which can be
+ summarized as follows: The client sends a client hello message to
+ which the server must respond with a server hello message, or else a
+ fatal error will occur and the connection will fail. The client hello
+ and server hello are used to establish security enhancement
+ capabilities between client and server. The client hello and server
+ hello establish the following attributes: Protocol Version, Session
+ ID, Cipher Suite, and Compression Method. Additionally, two random
+ values are generated and exchanged: ClientHello.random and
+ ServerHello.random.
+
+ The actual key exchange uses up to four messages: the server
+ certificate, the server key exchange, the client certificate, and the
+ client key exchange. New key exchange methods can be created by
+ specifying a format for these messages and defining the use of the
+ messages to allow the client and server to agree upon a shared
+ secret. This secret should be quite long; currently defined key
+ exchange methods exchange secrets which range from 48 to 128 bytes in
+ length.
+
+ Following the hello messages, the server will send its certificate,
+ if it is to be authenticated. Additionally, a server key exchange
+ message may be sent, if it is required (e.g. if their server has no
+ certificate, or if its certificate is for signing only). If the
+ server is authenticated, it may request a certificate from the
+ client, if that is appropriate to the cipher suite selected. Now the
+ server will send the server hello done message, indicating that the
+ hello-message phase of the handshake is complete. The server will
+ then wait for a client response. If the server has sent a certificate
+ request message, the client must send the certificate message. The
+ client key exchange message is now sent, and the content of that
+ message will depend on the public key algorithm selected between the
+ client hello and the server hello. If the client has sent a
+ certificate with signing ability, a digitally-signed certificate
+ verify message is sent to explicitly verify the certificate.
+
+ At this point, a change cipher spec message is sent by the client,
+ and the client copies the pending Cipher Spec into the current Cipher
+ Spec. The client then immediately sends the finished message under
+ the new algorithms, keys, and secrets. In response, the server will
+ send its own change cipher spec message, transfer the pending to the
+ current Cipher Spec, and send its finished message under the new
+
+
+
+
+
+Dierks & Allen Standards Track [Page 30]
+
+RFC 2246 The TLS Protocol Version 1.0 January 1999
+
+
+ Cipher Spec. At this point, the handshake is complete and the client
+ and server may begin to exchange application layer data. (See flow
+ chart below.)
+
+ Client Server
+
+ ClientHello -------->
+ ServerHello
+ Certificate*
+ ServerKeyExchange*
+ CertificateRequest*
+ <-------- ServerHelloDone
+ Certificate*
+ ClientKeyExchange
+ CertificateVerify*
+ [ChangeCipherSpec]
+ Finished -------->
+ [ChangeCipherSpec]
+ <-------- Finished
+ Application Data <-------> Application Data
+
+ Fig. 1 - Message flow for a full handshake
+
+ * Indicates optional or situation-dependent messages that are not
+ always sent.
+
+ Note: To help avoid pipeline stalls, ChangeCipherSpec is an
+ independent TLS Protocol content type, and is not actually a TLS
+ handshake message.
+
+ When the client and server decide to resume a previous session or
+ duplicate an existing session (instead of negotiating new security
+ parameters) the message flow is as follows:
+
+ The client sends a ClientHello using the Session ID of the session to
+ be resumed. The server then checks its session cache for a match. If
+ a match is found, and the server is willing to re-establish the
+ connection under the specified session state, it will send a
+ ServerHello with the same Session ID value. At this point, both
+ client and server must send change cipher spec messages and proceed
+ directly to finished messages. Once the re-establishment is complete,
+ the client and server may begin to exchange application layer data.
+ (See flow chart below.) If a Session ID match is not found, the
+ server generates a new session ID and the TLS client and server
+ perform a full handshake.
+
+
+
+
+
+
+Dierks & Allen Standards Track [Page 31]
+
+RFC 2246 The TLS Protocol Version 1.0 January 1999
+
+
+ Client Server
+
+ ClientHello -------->
+ ServerHello
+ [ChangeCipherSpec]
+ <-------- Finished
+ [ChangeCipherSpec]
+ Finished -------->
+ Application Data <-------> Application Data
+
+ Fig. 2 - Message flow for an abbreviated handshake
+
+ The contents and significance of each message will be presented in
+ detail in the following sections.
+
+7.4. Handshake protocol
+
+ The TLS Handshake Protocol is one of the defined higher level clients
+ of the TLS Record Protocol. This protocol is used to negotiate the
+ secure attributes of a session. Handshake messages are supplied to
+ the TLS Record Layer, where they are encapsulated within one or more
+ TLSPlaintext structures, which are processed and transmitted as
+ specified by the current active session state.
+
+ enum {
+ hello_request(0), client_hello(1), server_hello(2),
+ certificate(11), server_key_exchange (12),
+ certificate_request(13), server_hello_done(14),
+ certificate_verify(15), client_key_exchange(16),
+ finished(20), (255)
+ } HandshakeType;
+
+ struct {
+ HandshakeType msg_type; /* handshake type */
+ uint24 length; /* bytes in message */
+ select (HandshakeType) {
+ case hello_request: HelloRequest;
+ case client_hello: ClientHello;
+ case server_hello: ServerHello;
+ case certificate: Certificate;
+ case server_key_exchange: ServerKeyExchange;
+ case certificate_request: CertificateRequest;
+ case server_hello_done: ServerHelloDone;
+ case certificate_verify: CertificateVerify;
+ case client_key_exchange: ClientKeyExchange;
+ case finished: Finished;
+ } body;
+ } Handshake;
+
+
+
+Dierks & Allen Standards Track [Page 32]
+
+RFC 2246 The TLS Protocol Version 1.0 January 1999
+
+
+ The handshake protocol messages are presented below in the order they
+ must be sent; sending handshake messages in an unexpected order
+ results in a fatal error. Unneeded handshake messages can be omitted,
+ however. Note one exception to the ordering: the Certificate message
+ is used twice in the handshake (from server to client, then from
+ client to server), but described only in its first position. The one
+ message which is not bound by these ordering rules in the Hello
+ Request message, which can be sent at any time, but which should be
+ ignored by the client if it arrives in the middle of a handshake.
+
+7.4.1. Hello messages
+
+ The hello phase messages are used to exchange security enhancement
+ capabilities between the client and server. When a new session
+ begins, the Record Layer's connection state encryption, hash, and
+ compression algorithms are initialized to null. The current
+ connection state is used for renegotiation messages.
+
+7.4.1.1. Hello request
+
+ When this message will be sent:
+ The hello request message may be sent by the server at any time.
+
+ Meaning of this message:
+ Hello request is a simple notification that the client should
+ begin the negotiation process anew by sending a client hello
+ message when convenient. This message will be ignored by the
+ client if the client is currently negotiating a session. This
+ message may be ignored by the client if it does not wish to
+ renegotiate a session, or the client may, if it wishes, respond
+ with a no_renegotiation alert. Since handshake messages are
+ intended to have transmission precedence over application data,
+ it is expected that the negotiation will begin before no more
+ than a few records are received from the client. If the server
+ sends a hello request but does not receive a client hello in
+ response, it may close the connection with a fatal alert.
+
+ After sending a hello request, servers should not repeat the request
+ until the subsequent handshake negotiation is complete.
+
+ Structure of this message:
+ struct { } HelloRequest;
+
+ Note: This message should never be included in the message hashes which
+ are maintained throughout the handshake and used in the finished
+ messages and the certificate verify message.
+
+
+
+
+
+Dierks & Allen Standards Track [Page 33]
+
+RFC 2246 The TLS Protocol Version 1.0 January 1999
+
+
+7.4.1.2. Client hello
+
+ When this message will be sent:
+ When a client first connects to a server it is required to send
+ the client hello as its first message. The client can also send a
+ client hello in response to a hello request or on its own
+ initiative in order to renegotiate the security parameters in an
+ existing connection.
+
+ Structure of this message:
+ The client hello message includes a random structure, which is
+ used later in the protocol.
+
+ struct {
+ uint32 gmt_unix_time;
+ opaque random_bytes[28];
+ } Random;
+
+ gmt_unix_time
+ The current time and date in standard UNIX 32-bit format (seconds
+ since the midnight starting Jan 1, 1970, GMT) according to the
+ sender's internal clock. Clocks are not required to be set
+ correctly by the basic TLS Protocol; higher level or application
+ protocols may define additional requirements.
+
+ random_bytes
+ 28 bytes generated by a secure random number generator.
+
+ The client hello message includes a variable length session
+ identifier. If not empty, the value identifies a session between the
+ same client and server whose security parameters the client wishes to
+ reuse. The session identifier may be from an earlier connection, this
+ connection, or another currently active connection. The second option
+ is useful if the client only wishes to update the random structures
+ and derived values of a connection, while the third option makes it
+ possible to establish several independent secure connections without
+ repeating the full handshake protocol. These independent connections
+ may occur sequentially or simultaneously; a SessionID becomes valid
+ when the handshake negotiating it completes with the exchange of
+ Finished messages and persists until removed due to aging or because
+ a fatal error was encountered on a connection associated with the
+ session. The actual contents of the SessionID are defined by the
+ server.
+
+ opaque SessionID<0..32>;
+
+
+
+
+
+
+Dierks & Allen Standards Track [Page 34]
+
+RFC 2246 The TLS Protocol Version 1.0 January 1999
+
+
+ Warning:
+ Because the SessionID is transmitted without encryption or
+ immediate MAC protection, servers must not place confidential
+ information in session identifiers or let the contents of fake
+ session identifiers cause any breach of security. (Note that the
+ content of the handshake as a whole, including the SessionID, is
+ protected by the Finished messages exchanged at the end of the
+ handshake.)
+
+ The CipherSuite list, passed from the client to the server in the
+ client hello message, contains the combinations of cryptographic
+ algorithms supported by the client in order of the client's
+ preference (favorite choice first). Each CipherSuite defines a key
+ exchange algorithm, a bulk encryption algorithm (including secret key
+ length) and a MAC algorithm. The server will select a cipher suite
+ or, if no acceptable choices are presented, return a handshake
+ failure alert and close the connection.
+
+ uint8 CipherSuite[2]; /* Cryptographic suite selector */
+
+ The client hello includes a list of compression algorithms supported
+ by the client, ordered according to the client's preference.
+
+ enum { null(0), (255) } CompressionMethod;
+
+ struct {
+ ProtocolVersion client_version;
+ Random random;
+ SessionID session_id;
+ CipherSuite cipher_suites<2..2^16-1>;
+ CompressionMethod compression_methods<1..2^8-1>;
+ } ClientHello;
+
+ client_version
+ The version of the TLS protocol by which the client wishes to
+ communicate during this session. This should be the latest
+ (highest valued) version supported by the client. For this
+ version of the specification, the version will be 3.1 (See
+ Appendix E for details about backward compatibility).
+
+ random
+ A client-generated random structure.
+
+ session_id
+ The ID of a session the client wishes to use for this connection.
+ This field should be empty if no session_id is available or the
+ client wishes to generate new security parameters.
+
+
+
+
+Dierks & Allen Standards Track [Page 35]
+
+RFC 2246 The TLS Protocol Version 1.0 January 1999
+
+
+ cipher_suites
+ This is a list of the cryptographic options supported by the
+ client, with the client's first preference first. If the
+ session_id field is not empty (implying a session resumption
+ request) this vector must include at least the cipher_suite from
+ that session. Values are defined in Appendix A.5.
+
+ compression_methods
+ This is a list of the compression methods supported by the
+ client, sorted by client preference. If the session_id field is
+ not empty (implying a session resumption request) it must include
+ the compression_method from that session. This vector must
+ contain, and all implementations must support,
+ CompressionMethod.null. Thus, a client and server will always be
+ able to agree on a compression method.
+
+ After sending the client hello message, the client waits for a server
+ hello message. Any other handshake message returned by the server
+ except for a hello request is treated as a fatal error.
+
+ Forward compatibility note:
+ In the interests of forward compatibility, it is permitted for a
+ client hello message to include extra data after the compression
+ methods. This data must be included in the handshake hashes, but
+ must otherwise be ignored. This is the only handshake message for
+ which this is legal; for all other messages, the amount of data
+ in the message must match the description of the message
+ precisely.
+
+7.4.1.3. Server hello
+
+ When this message will be sent:
+ The server will send this message in response to a client hello
+ message when it was able to find an acceptable set of algorithms.
+ If it cannot find such a match, it will respond with a handshake
+ failure alert.
+
+ Structure of this message:
+ struct {
+ ProtocolVersion server_version;
+ Random random;
+ SessionID session_id;
+ CipherSuite cipher_suite;
+ CompressionMethod compression_method;
+ } ServerHello;
+
+
+
+
+
+
+Dierks & Allen Standards Track [Page 36]
+
+RFC 2246 The TLS Protocol Version 1.0 January 1999
+
+
+ server_version
+ This field will contain the lower of that suggested by the client
+ in the client hello and the highest supported by the server. For
+ this version of the specification, the version is 3.1 (See
+ Appendix E for details about backward compatibility).
+
+ random
+ This structure is generated by the server and must be different
+ from (and independent of) ClientHello.random.
+
+ session_id
+ This is the identity of the session corresponding to this
+ connection. If the ClientHello.session_id was non-empty, the
+ server will look in its session cache for a match. If a match is
+ found and the server is willing to establish the new connection
+ using the specified session state, the server will respond with
+ the same value as was supplied by the client. This indicates a
+ resumed session and dictates that the parties must proceed
+ directly to the finished messages. Otherwise this field will
+ contain a different value identifying the new session. The server
+ may return an empty session_id to indicate that the session will
+ not be cached and therefore cannot be resumed. If a session is
+ resumed, it must be resumed using the same cipher suite it was
+ originally negotiated with.
+
+ cipher_suite
+ The single cipher suite selected by the server from the list in
+ ClientHello.cipher_suites. For resumed sessions this field is the
+ value from the state of the session being resumed.
+
+ compression_method
+ The single compression algorithm selected by the server from the
+ list in ClientHello.compression_methods. For resumed sessions
+ this field is the value from the resumed session state.
+
+7.4.2. Server certificate
+
+ When this message will be sent:
+ The server must send a certificate whenever the agreed-upon key
+ exchange method is not an anonymous one. This message will always
+ immediately follow the server hello message.
+
+ Meaning of this message:
+ The certificate type must be appropriate for the selected cipher
+ suite's key exchange algorithm, and is generally an X.509v3
+ certificate. It must contain a key which matches the key exchange
+ method, as follows. Unless otherwise specified, the signing
+
+
+
+
+Dierks & Allen Standards Track [Page 37]
+
+RFC 2246 The TLS Protocol Version 1.0 January 1999
+
+
+ algorithm for the certificate must be the same as the algorithm
+ for the certificate key. Unless otherwise specified, the public
+ key may be of any length.
+
+ Key Exchange Algorithm Certificate Key Type
+
+ RSA RSA public key; the certificate must
+ allow the key to be used for encryption.
+
+ RSA_EXPORT RSA public key of length greater than
+ 512 bits which can be used for signing,
+ or a key of 512 bits or shorter which
+ can be used for either encryption or
+ signing.
+
+ DHE_DSS DSS public key.
+
+ DHE_DSS_EXPORT DSS public key.
+
+ DHE_RSA RSA public key which can be used for
+ signing.
+
+ DHE_RSA_EXPORT RSA public key which can be used for
+ signing.
+
+ DH_DSS Diffie-Hellman key. The algorithm used
+ to sign the certificate should be DSS.
+
+ DH_RSA Diffie-Hellman key. The algorithm used
+ to sign the certificate should be RSA.
+
+ All certificate profiles, key and cryptographic formats are defined
+ by the IETF PKIX working group [PKIX]. When a key usage extension is
+ present, the digitalSignature bit must be set for the key to be
+ eligible for signing, as described above, and the keyEncipherment bit
+ must be present to allow encryption, as described above. The
+ keyAgreement bit must be set on Diffie-Hellman certificates.
+
+ As CipherSuites which specify new key exchange methods are specified
+ for the TLS Protocol, they will imply certificate format and the
+ required encoded keying information.
+
+ Structure of this message:
+ opaque ASN.1Cert<1..2^24-1>;
+
+ struct {
+ ASN.1Cert certificate_list<0..2^24-1>;
+ } Certificate;
+
+
+
+Dierks & Allen Standards Track [Page 38]
+
+RFC 2246 The TLS Protocol Version 1.0 January 1999
+
+
+ certificate_list
+ This is a sequence (chain) of X.509v3 certificates. The sender's
+ certificate must come first in the list. Each following
+ certificate must directly certify the one preceding it. Because
+ certificate validation requires that root keys be distributed
+ independently, the self-signed certificate which specifies the
+ root certificate authority may optionally be omitted from the
+ chain, under the assumption that the remote end must already
+ possess it in order to validate it in any case.
+
+ The same message type and structure will be used for the client's
+ response to a certificate request message. Note that a client may
+ send no certificates if it does not have an appropriate certificate
+ to send in response to the server's authentication request.
+
+ Note: PKCS #7 [PKCS7] is not used as the format for the certificate
+ vector because PKCS #6 [PKCS6] extended certificates are not
+ used. Also PKCS #7 defines a SET rather than a SEQUENCE, making
+ the task of parsing the list more difficult.
+
+7.4.3. Server key exchange message
+
+ When this message will be sent:
+ This message will be sent immediately after the server
+ certificate message (or the server hello message, if this is an
+ anonymous negotiation).
+
+ The server key exchange message is sent by the server only when
+ the server certificate message (if sent) does not contain enough
+ data to allow the client to exchange a premaster secret. This is
+ true for the following key exchange methods:
+
+ RSA_EXPORT (if the public key in the server certificate is
+ longer than 512 bits)
+ DHE_DSS
+ DHE_DSS_EXPORT
+ DHE_RSA
+ DHE_RSA_EXPORT
+ DH_anon
+
+ It is not legal to send the server key exchange message for the
+ following key exchange methods:
+
+ RSA
+ RSA_EXPORT (when the public key in the server certificate is
+ less than or equal to 512 bits in length)
+ DH_DSS
+ DH_RSA
+
+
+
+Dierks & Allen Standards Track [Page 39]
+
+RFC 2246 The TLS Protocol Version 1.0 January 1999
+
+
+ Meaning of this message:
+ This message conveys cryptographic information to allow the
+ client to communicate the premaster secret: either an RSA public
+ key to encrypt the premaster secret with, or a Diffie-Hellman
+ public key with which the client can complete a key exchange
+ (with the result being the premaster secret.)
+
+ As additional CipherSuites are defined for TLS which include new key
+ exchange algorithms, the server key exchange message will be sent if
+ and only if the certificate type associated with the key exchange
+ algorithm does not provide enough information for the client to
+ exchange a premaster secret.
+
+ Note: According to current US export law, RSA moduli larger than 512
+ bits may not be used for key exchange in software exported from
+ the US. With this message, the larger RSA keys encoded in
+ certificates may be used to sign temporary shorter RSA keys for
+ the RSA_EXPORT key exchange method.
+
+ Structure of this message:
+ enum { rsa, diffie_hellman } KeyExchangeAlgorithm;
+
+ struct {
+ opaque rsa_modulus<1..2^16-1>;
+ opaque rsa_exponent<1..2^16-1>;
+ } ServerRSAParams;
+
+ rsa_modulus
+ The modulus of the server's temporary RSA key.
+
+ rsa_exponent
+ The public exponent of the server's temporary RSA key.
+
+ struct {
+ opaque dh_p<1..2^16-1>;
+ opaque dh_g<1..2^16-1>;
+ opaque dh_Ys<1..2^16-1>;
+ } ServerDHParams; /* Ephemeral DH parameters */
+
+ dh_p
+ The prime modulus used for the Diffie-Hellman operation.
+
+ dh_g
+ The generator used for the Diffie-Hellman operation.
+
+ dh_Ys
+ The server's Diffie-Hellman public value (g^X mod p).
+
+
+
+
+Dierks & Allen Standards Track [Page 40]
+
+RFC 2246 The TLS Protocol Version 1.0 January 1999
+
+
+ struct {
+ select (KeyExchangeAlgorithm) {
+ case diffie_hellman:
+ ServerDHParams params;
+ Signature signed_params;
+ case rsa:
+ ServerRSAParams params;
+ Signature signed_params;
+ };
+ } ServerKeyExchange;
+
+ params
+ The server's key exchange parameters.
+
+ signed_params
+ For non-anonymous key exchanges, a hash of the corresponding
+ params value, with the signature appropriate to that hash
+ applied.
+
+ md5_hash
+ MD5(ClientHello.random + ServerHello.random + ServerParams);
+
+ sha_hash
+ SHA(ClientHello.random + ServerHello.random + ServerParams);
+
+ enum { anonymous, rsa, dsa } SignatureAlgorithm;
+
+ select (SignatureAlgorithm)
+ { case anonymous: struct { };
+ case rsa:
+ digitally-signed struct {
+ opaque md5_hash[16];
+ opaque sha_hash[20];
+ };
+ case dsa:
+ digitally-signed struct {
+ opaque sha_hash[20];
+ };
+ } Signature;
+
+7.4.4. Certificate request
+
+ When this message will be sent:
+ A non-anonymous server can optionally request a certificate from
+ the client, if appropriate for the selected cipher suite. This
+ message, if sent, will immediately follow the Server Key Exchange
+ message (if it is sent; otherwise, the Server Certificate
+ message).
+
+
+
+Dierks & Allen Standards Track [Page 41]
+
+RFC 2246 The TLS Protocol Version 1.0 January 1999
+
+
+ Structure of this message:
+ enum {
+ rsa_sign(1), dss_sign(2), rsa_fixed_dh(3), dss_fixed_dh(4),
+ (255)
+ } ClientCertificateType;
+
+ opaque DistinguishedName<1..2^16-1>;
+
+ struct {
+ ClientCertificateType certificate_types<1..2^8-1>;
+ DistinguishedName certificate_authorities<3..2^16-1>;
+ } CertificateRequest;
+
+ certificate_types
+ This field is a list of the types of certificates requested,
+ sorted in order of the server's preference.
+
+ certificate_authorities
+ A list of the distinguished names of acceptable certificate
+ authorities. These distinguished names may specify a desired
+ distinguished name for a root CA or for a subordinate CA;
+ thus, this message can be used both to describe known roots
+ and a desired authorization space.
+
+ Note: DistinguishedName is derived from [X509].
+
+ Note: It is a fatal handshake_failure alert for an anonymous server to
+ request client identification.
+
+7.4.5. Server hello done
+
+ When this message will be sent:
+ The server hello done message is sent by the server to indicate
+ the end of the server hello and associated messages. After
+ sending this message the server will wait for a client response.
+
+ Meaning of this message:
+ This message means that the server is done sending messages to
+ support the key exchange, and the client can proceed with its
+ phase of the key exchange.
+
+ Upon receipt of the server hello done message the client should
+ verify that the server provided a valid certificate if required
+ and check that the server hello parameters are acceptable.
+
+ Structure of this message:
+ struct { } ServerHelloDone;
+
+
+
+
+Dierks & Allen Standards Track [Page 42]
+
+RFC 2246 The TLS Protocol Version 1.0 January 1999
+
+
+7.4.6. Client certificate
+
+ When this message will be sent:
+ This is the first message the client can send after receiving a
+ server hello done message. This message is only sent if the
+ server requests a certificate. If no suitable certificate is
+ available, the client should send a certificate message
+ containing no certificates. If client authentication is required
+ by the server for the handshake to continue, it may respond with
+ a fatal handshake failure alert. Client certificates are sent
+ using the Certificate structure defined in Section 7.4.2.
+
+ Note: When using a static Diffie-Hellman based key exchange method
+ (DH_DSS or DH_RSA), if client authentication is requested, the
+ Diffie-Hellman group and generator encoded in the client's
+ certificate must match the server specified Diffie-Hellman
+ parameters if the client's parameters are to be used for the key
+ exchange.
+
+7.4.7. Client key exchange message
+
+ When this message will be sent:
+ This message is always sent by the client. It will immediately
+ follow the client certificate message, if it is sent. Otherwise
+ it will be the first message sent by the client after it receives
+ the server hello done message.
+
+ Meaning of this message:
+ With this message, the premaster secret is set, either though
+ direct transmission of the RSA-encrypted secret, or by the
+ transmission of Diffie-Hellman parameters which will allow each
+ side to agree upon the same premaster secret. When the key
+ exchange method is DH_RSA or DH_DSS, client certification has
+ been requested, and the client was able to respond with a
+ certificate which contained a Diffie-Hellman public key whose
+ parameters (group and generator) matched those specified by the
+ server in its certificate, this message will not contain any
+ data.
+
+ Structure of this message:
+ The choice of messages depends on which key exchange method has
+ been selected. See Section 7.4.3 for the KeyExchangeAlgorithm
+ definition.
+
+ struct {
+ select (KeyExchangeAlgorithm) {
+ case rsa: EncryptedPreMasterSecret;
+ case diffie_hellman: ClientDiffieHellmanPublic;
+
+
+
+Dierks & Allen Standards Track [Page 43]
+
+RFC 2246 The TLS Protocol Version 1.0 January 1999
+
+
+ } exchange_keys;
+ } ClientKeyExchange;
+
+7.4.7.1. RSA encrypted premaster secret message
+
+ Meaning of this message:
+ If RSA is being used for key agreement and authentication, the
+ client generates a 48-byte premaster secret, encrypts it using
+ the public key from the server's certificate or the temporary RSA
+ key provided in a server key exchange message, and sends the
+ result in an encrypted premaster secret message. This structure
+ is a variant of the client key exchange message, not a message in
+ itself.
+
+ Structure of this message:
+ struct {
+ ProtocolVersion client_version;
+ opaque random[46];
+ } PreMasterSecret;
+
+ client_version
+ The latest (newest) version supported by the client. This is
+ used to detect version roll-back attacks. Upon receiving the
+ premaster secret, the server should check that this value
+ matches the value transmitted by the client in the client
+ hello message.
+
+ random
+ 46 securely-generated random bytes.
+
+ struct {
+ public-key-encrypted PreMasterSecret pre_master_secret;
+ } EncryptedPreMasterSecret;
+
+ Note: An attack discovered by Daniel Bleichenbacher [BLEI] can be used
+ to attack a TLS server which is using PKCS#1 encoded RSA. The
+ attack takes advantage of the fact that by failing in different
+ ways, a TLS server can be coerced into revealing whether a
+ particular message, when decrypted, is properly PKCS#1 formatted
+ or not.
+
+ The best way to avoid vulnerability to this attack is to treat
+ incorrectly formatted messages in a manner indistinguishable from
+ correctly formatted RSA blocks. Thus, when it receives an
+ incorrectly formatted RSA block, a server should generate a
+ random 48-byte value and proceed using it as the premaster
+ secret. Thus, the server will act identically whether the
+ received RSA block is correctly encoded or not.
+
+
+
+Dierks & Allen Standards Track [Page 44]
+
+RFC 2246 The TLS Protocol Version 1.0 January 1999
+
+
+ pre_master_secret
+ This random value is generated by the client and is used to
+ generate the master secret, as specified in Section 8.1.
+
+7.4.7.2. Client Diffie-Hellman public value
+
+ Meaning of this message:
+ This structure conveys the client's Diffie-Hellman public value
+ (Yc) if it was not already included in the client's certificate.
+ The encoding used for Yc is determined by the enumerated
+ PublicValueEncoding. This structure is a variant of the client
+ key exchange message, not a message in itself.
+
+ Structure of this message:
+ enum { implicit, explicit } PublicValueEncoding;
+
+ implicit
+ If the client certificate already contains a suitable
+ Diffie-Hellman key, then Yc is implicit and does not need to
+ be sent again. In this case, the Client Key Exchange message
+ will be sent, but will be empty.
+
+ explicit
+ Yc needs to be sent.
+
+ struct {
+ select (PublicValueEncoding) {
+ case implicit: struct { };
+ case explicit: opaque dh_Yc<1..2^16-1>;
+ } dh_public;
+ } ClientDiffieHellmanPublic;
+
+ dh_Yc
+ The client's Diffie-Hellman public value (Yc).
+
+7.4.8. Certificate verify
+
+ When this message will be sent:
+ This message is used to provide explicit verification of a client
+ certificate. This message is only sent following a client
+ certificate that has signing capability (i.e. all certificates
+ except those containing fixed Diffie-Hellman parameters). When
+ sent, it will immediately follow the client key exchange message.
+
+ Structure of this message:
+ struct {
+ Signature signature;
+ } CertificateVerify;
+
+
+
+Dierks & Allen Standards Track [Page 45]
+
+RFC 2246 The TLS Protocol Version 1.0 January 1999
+
+
+ The Signature type is defined in 7.4.3.
+
+ CertificateVerify.signature.md5_hash
+ MD5(handshake_messages);
+
+ Certificate.signature.sha_hash
+ SHA(handshake_messages);
+
+ Here handshake_messages refers to all handshake messages sent or
+ received starting at client hello up to but not including this
+ message, including the type and length fields of the handshake
+ messages. This is the concatenation of all the Handshake structures
+ as defined in 7.4 exchanged thus far.
+
+7.4.9. Finished
+
+ When this message will be sent:
+ A finished message is always sent immediately after a change
+ cipher spec message to verify that the key exchange and
+ authentication processes were successful. It is essential that a
+ change cipher spec message be received between the other
+ handshake messages and the Finished message.
+
+ Meaning of this message:
+ The finished message is the first protected with the just-
+ negotiated algorithms, keys, and secrets. Recipients of finished
+ messages must verify that the contents are correct. Once a side
+ has sent its Finished message and received and validated the
+ Finished message from its peer, it may begin to send and receive
+ application data over the connection.
+
+ struct {
+ opaque verify_data[12];
+ } Finished;
+
+ verify_data
+ PRF(master_secret, finished_label, MD5(handshake_messages) +
+ SHA-1(handshake_messages)) [0..11];
+
+ finished_label
+ For Finished messages sent by the client, the string "client
+ finished". For Finished messages sent by the server, the
+ string "server finished".
+
+ handshake_messages
+ All of the data from all handshake messages up to but not
+ including this message. This is only data visible at the
+ handshake layer and does not include record layer headers.
+
+
+
+Dierks & Allen Standards Track [Page 46]
+
+RFC 2246 The TLS Protocol Version 1.0 January 1999
+
+
+ This is the concatenation of all the Handshake structures as
+ defined in 7.4 exchanged thus far.
+
+ It is a fatal error if a finished message is not preceded by a change
+ cipher spec message at the appropriate point in the handshake.
+
+ The hash contained in finished messages sent by the server
+ incorporate Sender.server; those sent by the client incorporate
+ Sender.client. The value handshake_messages includes all handshake
+ messages starting at client hello up to, but not including, this
+ finished message. This may be different from handshake_messages in
+ Section 7.4.8 because it would include the certificate verify message
+ (if sent). Also, the handshake_messages for the finished message sent
+ by the client will be different from that for the finished message
+ sent by the server, because the one which is sent second will include
+ the prior one.
+
+ Note: Change cipher spec messages, alerts and any other record types
+ are not handshake messages and are not included in the hash
+ computations. Also, Hello Request messages are omitted from
+ handshake hashes.
+
+8. Cryptographic computations
+
+ In order to begin connection protection, the TLS Record Protocol
+ requires specification of a suite of algorithms, a master secret, and
+ the client and server random values. The authentication, encryption,
+ and MAC algorithms are determined by the cipher_suite selected by the
+ server and revealed in the server hello message. The compression
+ algorithm is negotiated in the hello messages, and the random values
+ are exchanged in the hello messages. All that remains is to calculate
+ the master secret.
+
+8.1. Computing the master secret
+
+ For all key exchange methods, the same algorithm is used to convert
+ the pre_master_secret into the master_secret. The pre_master_secret
+ should be deleted from memory once the master_secret has been
+ computed.
+
+ master_secret = PRF(pre_master_secret, "master secret",
+ ClientHello.random + ServerHello.random)
+ [0..47];
+
+ The master secret is always exactly 48 bytes in length. The length of
+ the premaster secret will vary depending on key exchange method.
+
+
+
+
+
+Dierks & Allen Standards Track [Page 47]
+
+RFC 2246 The TLS Protocol Version 1.0 January 1999
+
+
+8.1.1. RSA
+
+ When RSA is used for server authentication and key exchange, a 48-
+ byte pre_master_secret is generated by the client, encrypted under
+ the server's public key, and sent to the server. The server uses its
+ private key to decrypt the pre_master_secret. Both parties then
+ convert the pre_master_secret into the master_secret, as specified
+ above.
+
+ RSA digital signatures are performed using PKCS #1 [PKCS1] block type
+ 1. RSA public key encryption is performed using PKCS #1 block type 2.
+
+8.1.2. Diffie-Hellman
+
+ A conventional Diffie-Hellman computation is performed. The
+ negotiated key (Z) is used as the pre_master_secret, and is converted
+ into the master_secret, as specified above.
+
+ Note: Diffie-Hellman parameters are specified by the server, and may
+ be either ephemeral or contained within the server's certificate.
+
+9. Mandatory Cipher Suites
+
+ In the absence of an application profile standard specifying
+ otherwise, a TLS compliant application MUST implement the cipher
+ suite TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA.
+
+10. Application data protocol
+
+ Application data messages are carried by the Record Layer and are
+ fragmented, compressed and encrypted based on the current connection
+ state. The messages are treated as transparent data to the record
+ layer.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Dierks & Allen Standards Track [Page 48]
+
+RFC 2246 The TLS Protocol Version 1.0 January 1999
+
+
+A. Protocol constant values
+
+ This section describes protocol types and constants.
+
+A.1. Record layer
+
+ struct {
+ uint8 major, minor;
+ } ProtocolVersion;
+
+ ProtocolVersion version = { 3, 1 }; /* TLS v1.0 */
+
+ enum {
+ change_cipher_spec(20), alert(21), handshake(22),
+ application_data(23), (255)
+ } ContentType;
+
+ struct {
+ ContentType type;
+ ProtocolVersion version;
+ uint16 length;
+ opaque fragment[TLSPlaintext.length];
+ } TLSPlaintext;
+
+ struct {
+ ContentType type;
+ ProtocolVersion version;
+ uint16 length;
+ opaque fragment[TLSCompressed.length];
+ } TLSCompressed;
+
+ struct {
+ ContentType type;
+ ProtocolVersion version;
+ uint16 length;
+ select (CipherSpec.cipher_type) {
+ case stream: GenericStreamCipher;
+ case block: GenericBlockCipher;
+ } fragment;
+ } TLSCiphertext;
+
+ stream-ciphered struct {
+ opaque content[TLSCompressed.length];
+ opaque MAC[CipherSpec.hash_size];
+ } GenericStreamCipher;
+
+ block-ciphered struct {
+ opaque content[TLSCompressed.length];
+
+
+
+Dierks & Allen Standards Track [Page 49]
+
+RFC 2246 The TLS Protocol Version 1.0 January 1999
+
+
+ opaque MAC[CipherSpec.hash_size];
+ uint8 padding[GenericBlockCipher.padding_length];
+ uint8 padding_length;
+ } GenericBlockCipher;
+
+A.2. Change cipher specs message
+
+ struct {
+ enum { change_cipher_spec(1), (255) } type;
+ } ChangeCipherSpec;
+
+A.3. Alert messages
+
+ enum { warning(1), fatal(2), (255) } AlertLevel;
+
+ enum {
+ close_notify(0),
+ unexpected_message(10),
+ bad_record_mac(20),
+ decryption_failed(21),
+ record_overflow(22),
+ decompression_failure(30),
+ handshake_failure(40),
+ bad_certificate(42),
+ unsupported_certificate(43),
+ certificate_revoked(44),
+ certificate_expired(45),
+ certificate_unknown(46),
+ illegal_parameter(47),
+ unknown_ca(48),
+ access_denied(49),
+ decode_error(50),
+ decrypt_error(51),
+ export_restriction(60),
+ protocol_version(70),
+ insufficient_security(71),
+ internal_error(80),
+ user_canceled(90),
+ no_renegotiation(100),
+ (255)
+ } AlertDescription;
+
+ struct {
+ AlertLevel level;
+ AlertDescription description;
+ } Alert;
+
+
+
+
+
+Dierks & Allen Standards Track [Page 50]
+
+RFC 2246 The TLS Protocol Version 1.0 January 1999
+
+
+A.4. Handshake protocol
+
+ enum {
+ hello_request(0), client_hello(1), server_hello(2),
+ certificate(11), server_key_exchange (12),
+ certificate_request(13), server_hello_done(14),
+ certificate_verify(15), client_key_exchange(16),
+ finished(20), (255)
+ } HandshakeType;
+
+ struct {
+ HandshakeType msg_type;
+ uint24 length;
+ select (HandshakeType) {
+ case hello_request: HelloRequest;
+ case client_hello: ClientHello;
+ case server_hello: ServerHello;
+ case certificate: Certificate;
+ case server_key_exchange: ServerKeyExchange;
+ case certificate_request: CertificateRequest;
+ case server_hello_done: ServerHelloDone;
+ case certificate_verify: CertificateVerify;
+ case client_key_exchange: ClientKeyExchange;
+ case finished: Finished;
+ } body;
+ } Handshake;
+
+A.4.1. Hello messages
+
+ struct { } HelloRequest;
+
+ struct {
+ uint32 gmt_unix_time;
+ opaque random_bytes[28];
+ } Random;
+
+ opaque SessionID<0..32>;
+
+ uint8 CipherSuite[2];
+
+ enum { null(0), (255) } CompressionMethod;
+
+ struct {
+ ProtocolVersion client_version;
+ Random random;
+ SessionID session_id;
+ CipherSuite cipher_suites<2..2^16-1>;
+ CompressionMethod compression_methods<1..2^8-1>;
+
+
+
+Dierks & Allen Standards Track [Page 51]
+
+RFC 2246 The TLS Protocol Version 1.0 January 1999
+
+
+ } ClientHello;
+
+ struct {
+ ProtocolVersion server_version;
+ Random random;
+ SessionID session_id;
+ CipherSuite cipher_suite;
+ CompressionMethod compression_method;
+ } ServerHello;
+
+A.4.2. Server authentication and key exchange messages
+
+ opaque ASN.1Cert<2^24-1>;
+
+ struct {
+ ASN.1Cert certificate_list<1..2^24-1>;
+ } Certificate;
+
+ enum { rsa, diffie_hellman } KeyExchangeAlgorithm;
+
+ struct {
+ opaque RSA_modulus<1..2^16-1>;
+ opaque RSA_exponent<1..2^16-1>;
+ } ServerRSAParams;
+
+ struct {
+ opaque DH_p<1..2^16-1>;
+ opaque DH_g<1..2^16-1>;
+ opaque DH_Ys<1..2^16-1>;
+ } ServerDHParams;
+
+ struct {
+ select (KeyExchangeAlgorithm) {
+ case diffie_hellman:
+ ServerDHParams params;
+ Signature signed_params;
+ case rsa:
+ ServerRSAParams params;
+ Signature signed_params;
+ };
+ } ServerKeyExchange;
+
+ enum { anonymous, rsa, dsa } SignatureAlgorithm;
+
+ select (SignatureAlgorithm)
+ { case anonymous: struct { };
+ case rsa:
+ digitally-signed struct {
+
+
+
+Dierks & Allen Standards Track [Page 52]
+
+RFC 2246 The TLS Protocol Version 1.0 January 1999
+
+
+ opaque md5_hash[16];
+ opaque sha_hash[20];
+ };
+ case dsa:
+ digitally-signed struct {
+ opaque sha_hash[20];
+ };
+ } Signature;
+
+ enum {
+ rsa_sign(1), dss_sign(2), rsa_fixed_dh(3), dss_fixed_dh(4),
+ (255)
+ } ClientCertificateType;
+
+ opaque DistinguishedName<1..2^16-1>;
+
+ struct {
+ ClientCertificateType certificate_types<1..2^8-1>;
+ DistinguishedName certificate_authorities<3..2^16-1>;
+ } CertificateRequest;
+
+ struct { } ServerHelloDone;
+
+A.4.3. Client authentication and key exchange messages
+
+ struct {
+ select (KeyExchangeAlgorithm) {
+ case rsa: EncryptedPreMasterSecret;
+ case diffie_hellman: DiffieHellmanClientPublicValue;
+ } exchange_keys;
+ } ClientKeyExchange;
+
+ struct {
+ ProtocolVersion client_version;
+ opaque random[46];
+
+ } PreMasterSecret;
+
+ struct {
+ public-key-encrypted PreMasterSecret pre_master_secret;
+ } EncryptedPreMasterSecret;
+
+ enum { implicit, explicit } PublicValueEncoding;
+
+ struct {
+ select (PublicValueEncoding) {
+ case implicit: struct {};
+ case explicit: opaque DH_Yc<1..2^16-1>;
+
+
+
+Dierks & Allen Standards Track [Page 53]
+
+RFC 2246 The TLS Protocol Version 1.0 January 1999
+
+
+ } dh_public;
+ } ClientDiffieHellmanPublic;
+
+ struct {
+ Signature signature;
+ } CertificateVerify;
+
+A.4.4. Handshake finalization message
+
+ struct {
+ opaque verify_data[12];
+ } Finished;
+
+A.5. The CipherSuite
+
+ The following values define the CipherSuite codes used in the client
+ hello and server hello messages.
+
+ A CipherSuite defines a cipher specification supported in TLS Version
+ 1.0.
+
+ TLS_NULL_WITH_NULL_NULL is specified and is the initial state of a
+ TLS connection during the first handshake on that channel, but must
+ not be negotiated, as it provides no more protection than an
+ unsecured connection.
+
+ CipherSuite TLS_NULL_WITH_NULL_NULL = { 0x00,0x00 };
+
+ The following CipherSuite definitions require that the server provide
+ an RSA certificate that can be used for key exchange. The server may
+ request either an RSA or a DSS signature-capable certificate in the
+ certificate request message.
+
+ CipherSuite TLS_RSA_WITH_NULL_MD5 = { 0x00,0x01 };
+ CipherSuite TLS_RSA_WITH_NULL_SHA = { 0x00,0x02 };
+ CipherSuite TLS_RSA_EXPORT_WITH_RC4_40_MD5 = { 0x00,0x03 };
+ CipherSuite TLS_RSA_WITH_RC4_128_MD5 = { 0x00,0x04 };
+ CipherSuite TLS_RSA_WITH_RC4_128_SHA = { 0x00,0x05 };
+ CipherSuite TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5 = { 0x00,0x06 };
+ CipherSuite TLS_RSA_WITH_IDEA_CBC_SHA = { 0x00,0x07 };
+ CipherSuite TLS_RSA_EXPORT_WITH_DES40_CBC_SHA = { 0x00,0x08 };
+ CipherSuite TLS_RSA_WITH_DES_CBC_SHA = { 0x00,0x09 };
+ CipherSuite TLS_RSA_WITH_3DES_EDE_CBC_SHA = { 0x00,0x0A };
+
+ The following CipherSuite definitions are used for server-
+ authenticated (and optionally client-authenticated) Diffie-Hellman.
+ DH denotes cipher suites in which the server's certificate contains
+ the Diffie-Hellman parameters signed by the certificate authority
+
+
+
+Dierks & Allen Standards Track [Page 54]
+
+RFC 2246 The TLS Protocol Version 1.0 January 1999
+
+
+ (CA). DHE denotes ephemeral Diffie-Hellman, where the Diffie-Hellman
+ parameters are signed by a DSS or RSA certificate, which has been
+ signed by the CA. The signing algorithm used is specified after the
+ DH or DHE parameter. The server can request an RSA or DSS signature-
+ capable certificate from the client for client authentication or it
+ may request a Diffie-Hellman certificate. Any Diffie-Hellman
+ certificate provided by the client must use the parameters (group and
+ generator) described by the server.
+
+ CipherSuite TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA = { 0x00,0x0B };
+ CipherSuite TLS_DH_DSS_WITH_DES_CBC_SHA = { 0x00,0x0C };
+ CipherSuite TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA = { 0x00,0x0D };
+ CipherSuite TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA = { 0x00,0x0E };
+ CipherSuite TLS_DH_RSA_WITH_DES_CBC_SHA = { 0x00,0x0F };
+ CipherSuite TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA = { 0x00,0x10 };
+ CipherSuite TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA = { 0x00,0x11 };
+ CipherSuite TLS_DHE_DSS_WITH_DES_CBC_SHA = { 0x00,0x12 };
+ CipherSuite TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA = { 0x00,0x13 };
+ CipherSuite TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA = { 0x00,0x14 };
+ CipherSuite TLS_DHE_RSA_WITH_DES_CBC_SHA = { 0x00,0x15 };
+ CipherSuite TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA = { 0x00,0x16 };
+
+ The following cipher suites are used for completely anonymous
+ Diffie-Hellman communications in which neither party is
+ authenticated. Note that this mode is vulnerable to man-in-the-middle
+ attacks and is therefore deprecated.
+
+ CipherSuite TLS_DH_anon_EXPORT_WITH_RC4_40_MD5 = { 0x00,0x17 };
+ CipherSuite TLS_DH_anon_WITH_RC4_128_MD5 = { 0x00,0x18 };
+ CipherSuite TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA = { 0x00,0x19 };
+ CipherSuite TLS_DH_anon_WITH_DES_CBC_SHA = { 0x00,0x1A };
+ CipherSuite TLS_DH_anon_WITH_3DES_EDE_CBC_SHA = { 0x00,0x1B };
+
+ Note: All cipher suites whose first byte is 0xFF are considered
+ private and can be used for defining local/experimental
+ algorithms. Interoperability of such types is a local matter.
+
+ Note: Additional cipher suites can be registered by publishing an RFC
+ which specifies the cipher suites, including the necessary TLS
+ protocol information, including message encoding, premaster
+ secret derivation, symmetric encryption and MAC calculation and
+ appropriate reference information for the algorithms involved.
+ The RFC editor's office may, at its discretion, choose to publish
+ specifications for cipher suites which are not completely
+ described (e.g., for classified algorithms) if it finds the
+ specification to be of technical interest and completely
+ specified.
+
+
+
+
+Dierks & Allen Standards Track [Page 55]
+
+RFC 2246 The TLS Protocol Version 1.0 January 1999
+
+
+ Note: The cipher suite values { 0x00, 0x1C } and { 0x00, 0x1D } are
+ reserved to avoid collision with Fortezza-based cipher suites in
+ SSL 3.
+
+A.6. The Security Parameters
+
+ These security parameters are determined by the TLS Handshake
+ Protocol and provided as parameters to the TLS Record Layer in order
+ to initialize a connection state. SecurityParameters includes:
+
+ enum { null(0), (255) } CompressionMethod;
+
+ enum { server, client } ConnectionEnd;
+
+ enum { null, rc4, rc2, des, 3des, des40, idea }
+ BulkCipherAlgorithm;
+
+ enum { stream, block } CipherType;
+
+ enum { true, false } IsExportable;
+
+ enum { null, md5, sha } MACAlgorithm;
+
+ /* The algorithms specified in CompressionMethod,
+ BulkCipherAlgorithm, and MACAlgorithm may be added to. */
+
+ struct {
+ ConnectionEnd entity;
+ BulkCipherAlgorithm bulk_cipher_algorithm;
+ CipherType cipher_type;
+ uint8 key_size;
+ uint8 key_material_length;
+ IsExportable is_exportable;
+ MACAlgorithm mac_algorithm;
+ uint8 hash_size;
+ CompressionMethod compression_algorithm;
+ opaque master_secret[48];
+ opaque client_random[32];
+ opaque server_random[32];
+ } SecurityParameters;
+
+
+
+
+
+
+
+
+
+
+
+Dierks & Allen Standards Track [Page 56]
+
+RFC 2246 The TLS Protocol Version 1.0 January 1999
+
+
+B. Glossary
+
+ application protocol
+ An application protocol is a protocol that normally layers
+ directly on top of the transport layer (e.g., TCP/IP). Examples
+ include HTTP, TELNET, FTP, and SMTP.
+
+ asymmetric cipher
+ See public key cryptography.
+
+ authentication
+ Authentication is the ability of one entity to determine the
+ identity of another entity.
+
+ block cipher
+ A block cipher is an algorithm that operates on plaintext in
+ groups of bits, called blocks. 64 bits is a common block size.
+
+ bulk cipher
+ A symmetric encryption algorithm used to encrypt large quantities
+ of data.
+
+ cipher block chaining (CBC)
+ CBC is a mode in which every plaintext block encrypted with a
+ block cipher is first exclusive-ORed with the previous ciphertext
+ block (or, in the case of the first block, with the
+ initialization vector). For decryption, every block is first
+ decrypted, then exclusive-ORed with the previous ciphertext block
+ (or IV).
+
+ certificate
+ As part of the X.509 protocol (a.k.a. ISO Authentication
+ framework), certificates are assigned by a trusted Certificate
+ Authority and provide a strong binding between a party's identity
+ or some other attributes and its public key.
+
+ client
+ The application entity that initiates a TLS connection to a
+ server. This may or may not imply that the client initiated the
+ underlying transport connection. The primary operational
+ difference between the server and client is that the server is
+ generally authenticated, while the client is only optionally
+ authenticated.
+
+ client write key
+ The key used to encrypt data written by the client.
+
+
+
+
+
+Dierks & Allen Standards Track [Page 57]
+
+RFC 2246 The TLS Protocol Version 1.0 January 1999
+
+
+ client write MAC secret
+ The secret data used to authenticate data written by the client.
+
+ connection
+ A connection is a transport (in the OSI layering model
+ definition) that provides a suitable type of service. For TLS,
+ such connections are peer to peer relationships. The connections
+ are transient. Every connection is associated with one session.
+
+ Data Encryption Standard
+ DES is a very widely used symmetric encryption algorithm. DES is
+ a block cipher with a 56 bit key and an 8 byte block size. Note
+ that in TLS, for key generation purposes, DES is treated as
+ having an 8 byte key length (64 bits), but it still only provides
+ 56 bits of protection. (The low bit of each key byte is presumed
+ to be set to produce odd parity in that key byte.) DES can also
+ be operated in a mode where three independent keys and three
+ encryptions are used for each block of data; this uses 168 bits
+ of key (24 bytes in the TLS key generation method) and provides
+ the equivalent of 112 bits of security. [DES], [3DES]
+
+ Digital Signature Standard (DSS)
+ A standard for digital signing, including the Digital Signing
+ Algorithm, approved by the National Institute of Standards and
+ Technology, defined in NIST FIPS PUB 186, "Digital Signature
+ Standard," published May, 1994 by the U.S. Dept. of Commerce.
+ [DSS]
+
+ digital signatures
+ Digital signatures utilize public key cryptography and one-way
+ hash functions to produce a signature of the data that can be
+ authenticated, and is difficult to forge or repudiate.
+
+ handshake
+ An initial negotiation between client and server that establishes
+ the parameters of their transactions.
+
+ Initialization Vector (IV)
+ When a block cipher is used in CBC mode, the initialization
+ vector is exclusive-ORed with the first plaintext block prior to
+ encryption.
+
+ IDEA
+ A 64-bit block cipher designed by Xuejia Lai and James Massey.
+ [IDEA]
+
+
+
+
+
+
+Dierks & Allen Standards Track [Page 58]
+
+RFC 2246 The TLS Protocol Version 1.0 January 1999
+
+
+ Message Authentication Code (MAC)
+ A Message Authentication Code is a one-way hash computed from a
+ message and some secret data. It is difficult to forge without
+ knowing the secret data. Its purpose is to detect if the message
+ has been altered.
+
+ master secret
+ Secure secret data used for generating encryption keys, MAC
+ secrets, and IVs.
+
+ MD5
+ MD5 is a secure hashing function that converts an arbitrarily
+ long data stream into a digest of fixed size (16 bytes). [MD5]
+
+ public key cryptography
+ A class of cryptographic techniques employing two-key ciphers.
+ Messages encrypted with the public key can only be decrypted with
+ the associated private key. Conversely, messages signed with the
+ private key can be verified with the public key.
+
+ one-way hash function
+ A one-way transformation that converts an arbitrary amount of
+ data into a fixed-length hash. It is computationally hard to
+ reverse the transformation or to find collisions. MD5 and SHA are
+ examples of one-way hash functions.
+
+ RC2
+ A block cipher developed by Ron Rivest at RSA Data Security, Inc.
+ [RSADSI] described in [RC2].
+
+ RC4
+ A stream cipher licensed by RSA Data Security [RSADSI]. A
+ compatible cipher is described in [RC4].
+
+ RSA
+ A very widely used public-key algorithm that can be used for
+ either encryption or digital signing. [RSA]
+
+ salt
+ Non-secret random data used to make export encryption keys resist
+ precomputation attacks.
+
+ server
+ The server is the application entity that responds to requests
+ for connections from clients. See also under client.
+
+
+
+
+
+
+Dierks & Allen Standards Track [Page 59]
+
+RFC 2246 The TLS Protocol Version 1.0 January 1999
+
+
+ session
+ A TLS session is an association between a client and a server.
+ Sessions are created by the handshake protocol. Sessions define a
+ set of cryptographic security parameters, which can be shared
+ among multiple connections. Sessions are used to avoid the
+ expensive negotiation of new security parameters for each
+ connection.
+
+ session identifier
+ A session identifier is a value generated by a server that
+ identifies a particular session.
+
+ server write key
+ The key used to encrypt data written by the server.
+
+ server write MAC secret
+ The secret data used to authenticate data written by the server.
+
+ SHA
+ The Secure Hash Algorithm is defined in FIPS PUB 180-1. It
+ produces a 20-byte output. Note that all references to SHA
+ actually use the modified SHA-1 algorithm. [SHA]
+
+ SSL
+ Netscape's Secure Socket Layer protocol [SSL3]. TLS is based on
+ SSL Version 3.0
+
+ stream cipher
+ An encryption algorithm that converts a key into a
+ cryptographically-strong keystream, which is then exclusive-ORed
+ with the plaintext.
+
+ symmetric cipher
+ See bulk cipher.
+
+ Transport Layer Security (TLS)
+ This protocol; also, the Transport Layer Security working group
+ of the Internet Engineering Task Force (IETF). See "Comments" at
+ the end of this document.
+
+
+
+
+
+
+
+
+
+
+
+
+Dierks & Allen Standards Track [Page 60]
+
+RFC 2246 The TLS Protocol Version 1.0 January 1999
+
+
+C. CipherSuite definitions
+
+CipherSuite Is Key Cipher Hash
+ Exportable Exchange
+
+TLS_NULL_WITH_NULL_NULL * NULL NULL NULL
+TLS_RSA_WITH_NULL_MD5 * RSA NULL MD5
+TLS_RSA_WITH_NULL_SHA * RSA NULL SHA
+TLS_RSA_EXPORT_WITH_RC4_40_MD5 * RSA_EXPORT RC4_40 MD5
+TLS_RSA_WITH_RC4_128_MD5 RSA RC4_128 MD5
+TLS_RSA_WITH_RC4_128_SHA RSA RC4_128 SHA
+TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5 * RSA_EXPORT RC2_CBC_40 MD5
+TLS_RSA_WITH_IDEA_CBC_SHA RSA IDEA_CBC SHA
+TLS_RSA_EXPORT_WITH_DES40_CBC_SHA * RSA_EXPORT DES40_CBC SHA
+TLS_RSA_WITH_DES_CBC_SHA RSA DES_CBC SHA
+TLS_RSA_WITH_3DES_EDE_CBC_SHA RSA 3DES_EDE_CBC SHA
+TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA * DH_DSS_EXPORT DES40_CBC SHA
+TLS_DH_DSS_WITH_DES_CBC_SHA DH_DSS DES_CBC SHA
+TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA DH_DSS 3DES_EDE_CBC SHA
+TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA * DH_RSA_EXPORT DES40_CBC SHA
+TLS_DH_RSA_WITH_DES_CBC_SHA DH_RSA DES_CBC SHA
+TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA DH_RSA 3DES_EDE_CBC SHA
+TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA * DHE_DSS_EXPORT DES40_CBC SHA
+TLS_DHE_DSS_WITH_DES_CBC_SHA DHE_DSS DES_CBC SHA
+TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA DHE_DSS 3DES_EDE_CBC SHA
+TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA * DHE_RSA_EXPORT DES40_CBC SHA
+TLS_DHE_RSA_WITH_DES_CBC_SHA DHE_RSA DES_CBC SHA
+TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA DHE_RSA 3DES_EDE_CBC SHA
+TLS_DH_anon_EXPORT_WITH_RC4_40_MD5 * DH_anon_EXPORT RC4_40 MD5
+TLS_DH_anon_WITH_RC4_128_MD5 DH_anon RC4_128 MD5
+TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA DH_anon DES40_CBC SHA
+TLS_DH_anon_WITH_DES_CBC_SHA DH_anon DES_CBC SHA
+TLS_DH_anon_WITH_3DES_EDE_CBC_SHA DH_anon 3DES_EDE_CBC SHA
+
+
+ * Indicates IsExportable is True
+
+ Key
+ Exchange
+ Algorithm Description Key size limit
+
+ DHE_DSS Ephemeral DH with DSS signatures None
+ DHE_DSS_EXPORT Ephemeral DH with DSS signatures DH = 512 bits
+ DHE_RSA Ephemeral DH with RSA signatures None
+ DHE_RSA_EXPORT Ephemeral DH with RSA signatures DH = 512 bits,
+ RSA = none
+ DH_anon Anonymous DH, no signatures None
+ DH_anon_EXPORT Anonymous DH, no signatures DH = 512 bits
+
+
+
+Dierks & Allen Standards Track [Page 61]
+
+RFC 2246 The TLS Protocol Version 1.0 January 1999
+
+
+ DH_DSS DH with DSS-based certificates None
+ DH_DSS_EXPORT DH with DSS-based certificates DH = 512 bits
+ DH_RSA DH with RSA-based certificates None
+ DH_RSA_EXPORT DH with RSA-based certificates DH = 512 bits,
+ RSA = none
+ NULL No key exchange N/A
+ RSA RSA key exchange None
+ RSA_EXPORT RSA key exchange RSA = 512 bits
+
+ Key size limit
+ The key size limit gives the size of the largest public key that
+ can be legally used for encryption in cipher suites that are
+ exportable.
+
+ Key Expanded Effective IV Block
+ Cipher Type Material Key Material Key Bits Size Size
+
+ NULL * Stream 0 0 0 0 N/A
+ IDEA_CBC Block 16 16 128 8 8
+ RC2_CBC_40 * Block 5 16 40 8 8
+ RC4_40 * Stream 5 16 40 0 N/A
+ RC4_128 Stream 16 16 128 0 N/A
+ DES40_CBC * Block 5 8 40 8 8
+ DES_CBC Block 8 8 56 8 8
+ 3DES_EDE_CBC Block 24 24 168 8 8
+
+ * Indicates IsExportable is true.
+
+ Type
+ Indicates whether this is a stream cipher or a block cipher
+ running in CBC mode.
+
+ Key Material
+ The number of bytes from the key_block that are used for
+ generating the write keys.
+
+ Expanded Key Material
+ The number of bytes actually fed into the encryption algorithm
+
+ Effective Key Bits
+ How much entropy material is in the key material being fed into
+ the encryption routines.
+
+ IV Size
+ How much data needs to be generated for the initialization
+ vector. Zero for stream ciphers; equal to the block size for
+ block ciphers.
+
+
+
+
+Dierks & Allen Standards Track [Page 62]
+
+RFC 2246 The TLS Protocol Version 1.0 January 1999
+
+
+ Block Size
+ The amount of data a block cipher enciphers in one chunk; a
+ block cipher running in CBC mode can only encrypt an even
+ multiple of its block size.
+
+ Hash Hash Padding
+ function Size Size
+ NULL 0 0
+ MD5 16 48
+ SHA 20 40
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Dierks & Allen Standards Track [Page 63]
+
+RFC 2246 The TLS Protocol Version 1.0 January 1999
+
+
+D. Implementation Notes
+
+ The TLS protocol cannot prevent many common security mistakes. This
+ section provides several recommendations to assist implementors.
+
+D.1. Temporary RSA keys
+
+ US Export restrictions limit RSA keys used for encryption to 512
+ bits, but do not place any limit on lengths of RSA keys used for
+ signing operations. Certificates often need to be larger than 512
+ bits, since 512-bit RSA keys are not secure enough for high-value
+ transactions or for applications requiring long-term security. Some
+ certificates are also designated signing-only, in which case they
+ cannot be used for key exchange.
+
+ When the public key in the certificate cannot be used for encryption,
+ the server signs a temporary RSA key, which is then exchanged. In
+ exportable applications, the temporary RSA key should be the maximum
+ allowable length (i.e., 512 bits). Because 512-bit RSA keys are
+ relatively insecure, they should be changed often. For typical
+ electronic commerce applications, it is suggested that keys be
+ changed daily or every 500 transactions, and more often if possible.
+ Note that while it is acceptable to use the same temporary key for
+ multiple transactions, it must be signed each time it is used.
+
+ RSA key generation is a time-consuming process. In many cases, a
+ low-priority process can be assigned the task of key generation.
+
+ Whenever a new key is completed, the existing temporary key can be
+ replaced with the new one.
+
+D.2. Random Number Generation and Seeding
+
+ TLS requires a cryptographically-secure pseudorandom number generator
+ (PRNG). Care must be taken in designing and seeding PRNGs. PRNGs
+ based on secure hash operations, most notably MD5 and/or SHA, are
+ acceptable, but cannot provide more security than the size of the
+ random number generator state. (For example, MD5-based PRNGs usually
+ provide 128 bits of state.)
+
+ To estimate the amount of seed material being produced, add the
+ number of bits of unpredictable information in each seed byte. For
+ example, keystroke timing values taken from a PC compatible's 18.2 Hz
+ timer provide 1 or 2 secure bits each, even though the total size of
+ the counter value is 16 bits or more. To seed a 128-bit PRNG, one
+ would thus require approximately 100 such timer values.
+
+
+
+
+
+Dierks & Allen Standards Track [Page 64]
+
+RFC 2246 The TLS Protocol Version 1.0 January 1999
+
+
+ Warning: The seeding functions in RSAREF and versions of BSAFE prior to
+ 3.0 are order-independent. For example, if 1000 seed bits are
+ supplied, one at a time, in 1000 separate calls to the seed
+ function, the PRNG will end up in a state which depends only
+ on the number of 0 or 1 seed bits in the seed data (i.e.,
+ there are 1001 possible final states). Applications using
+ BSAFE or RSAREF must take extra care to ensure proper seeding.
+ This may be accomplished by accumulating seed bits into a
+ buffer and processing them all at once or by processing an
+ incrementing counter with every seed bit; either method will
+ reintroduce order dependence into the seeding process.
+
+D.3. Certificates and authentication
+
+ Implementations are responsible for verifying the integrity of
+ certificates and should generally support certificate revocation
+ messages. Certificates should always be verified to ensure proper
+ signing by a trusted Certificate Authority (CA). The selection and
+ addition of trusted CAs should be done very carefully. Users should
+ be able to view information about the certificate and root CA.
+
+D.4. CipherSuites
+
+ TLS supports a range of key sizes and security levels, including some
+ which provide no or minimal security. A proper implementation will
+ probably not support many cipher suites. For example, 40-bit
+ encryption is easily broken, so implementations requiring strong
+ security should not allow 40-bit keys. Similarly, anonymous Diffie-
+ Hellman is strongly discouraged because it cannot prevent man-in-
+ the-middle attacks. Applications should also enforce minimum and
+ maximum key sizes. For example, certificate chains containing 512-bit
+ RSA keys or signatures are not appropriate for high-security
+ applications.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Dierks & Allen Standards Track [Page 65]
+
+RFC 2246 The TLS Protocol Version 1.0 January 1999
+
+
+E. Backward Compatibility With SSL
+
+ For historical reasons and in order to avoid a profligate consumption
+ of reserved port numbers, application protocols which are secured by
+ TLS 1.0, SSL 3.0, and SSL 2.0 all frequently share the same
+ connection port: for example, the https protocol (HTTP secured by SSL
+ or TLS) uses port 443 regardless of which security protocol it is
+ using. Thus, some mechanism must be determined to distinguish and
+ negotiate among the various protocols.
+
+ TLS version 1.0 and SSL 3.0 are very similar; thus, supporting both
+ is easy. TLS clients who wish to negotiate with SSL 3.0 servers
+ should send client hello messages using the SSL 3.0 record format and
+ client hello structure, sending {3, 1} for the version field to note
+ that they support TLS 1.0. If the server supports only SSL 3.0, it
+ will respond with an SSL 3.0 server hello; if it supports TLS, with a
+ TLS server hello. The negotiation then proceeds as appropriate for
+ the negotiated protocol.
+
+ Similarly, a TLS server which wishes to interoperate with SSL 3.0
+ clients should accept SSL 3.0 client hello messages and respond with
+ an SSL 3.0 server hello if an SSL 3.0 client hello is received which
+ has a version field of {3, 0}, denoting that this client does not
+ support TLS.
+
+ Whenever a client already knows the highest protocol known to a
+ server (for example, when resuming a session), it should initiate the
+ connection in that native protocol.
+
+ TLS 1.0 clients that support SSL Version 2.0 servers must send SSL
+ Version 2.0 client hello messages [SSL2]. TLS servers should accept
+ either client hello format if they wish to support SSL 2.0 clients on
+ the same connection port. The only deviations from the Version 2.0
+ specification are the ability to specify a version with a value of
+ three and the support for more ciphering types in the CipherSpec.
+
+ Warning: The ability to send Version 2.0 client hello messages will be
+ phased out with all due haste. Implementors should make every
+ effort to move forward as quickly as possible. Version 3.0
+ provides better mechanisms for moving to newer versions.
+
+ The following cipher specifications are carryovers from SSL Version
+ 2.0. These are assumed to use RSA for key exchange and
+ authentication.
+
+ V2CipherSpec TLS_RC4_128_WITH_MD5 = { 0x01,0x00,0x80 };
+ V2CipherSpec TLS_RC4_128_EXPORT40_WITH_MD5 = { 0x02,0x00,0x80 };
+ V2CipherSpec TLS_RC2_CBC_128_CBC_WITH_MD5 = { 0x03,0x00,0x80 };
+
+
+
+Dierks & Allen Standards Track [Page 66]
+
+RFC 2246 The TLS Protocol Version 1.0 January 1999
+
+
+ V2CipherSpec TLS_RC2_CBC_128_CBC_EXPORT40_WITH_MD5
+ = { 0x04,0x00,0x80 };
+ V2CipherSpec TLS_IDEA_128_CBC_WITH_MD5 = { 0x05,0x00,0x80 };
+ V2CipherSpec TLS_DES_64_CBC_WITH_MD5 = { 0x06,0x00,0x40 };
+ V2CipherSpec TLS_DES_192_EDE3_CBC_WITH_MD5 = { 0x07,0x00,0xC0 };
+
+ Cipher specifications native to TLS can be included in Version 2.0
+ client hello messages using the syntax below. Any V2CipherSpec
+ element with its first byte equal to zero will be ignored by Version
+ 2.0 servers. Clients sending any of the above V2CipherSpecs should
+ also include the TLS equivalent (see Appendix A.5):
+
+ V2CipherSpec (see TLS name) = { 0x00, CipherSuite };
+
+E.1. Version 2 client hello
+
+ The Version 2.0 client hello message is presented below using this
+ document's presentation model. The true definition is still assumed
+ to be the SSL Version 2.0 specification.
+
+ uint8 V2CipherSpec[3];
+
+ struct {
+ uint8 msg_type;
+ Version version;
+ uint16 cipher_spec_length;
+ uint16 session_id_length;
+ uint16 challenge_length;
+ V2CipherSpec cipher_specs[V2ClientHello.cipher_spec_length];
+ opaque session_id[V2ClientHello.session_id_length];
+ Random challenge;
+ } V2ClientHello;
+
+ msg_type
+ This field, in conjunction with the version field, identifies a
+ version 2 client hello message. The value should be one (1).
+
+ version
+ The highest version of the protocol supported by the client
+ (equals ProtocolVersion.version, see Appendix A.1).
+
+ cipher_spec_length
+ This field is the total length of the field cipher_specs. It
+ cannot be zero and must be a multiple of the V2CipherSpec length
+ (3).
+
+
+
+
+
+
+Dierks & Allen Standards Track [Page 67]
+
+RFC 2246 The TLS Protocol Version 1.0 January 1999
+
+
+ session_id_length
+ This field must have a value of either zero or 16. If zero, the
+ client is creating a new session. If 16, the session_id field
+ will contain the 16 bytes of session identification.
+
+ challenge_length
+ The length in bytes of the client's challenge to the server to
+ authenticate itself. This value must be 32.
+
+ cipher_specs
+ This is a list of all CipherSpecs the client is willing and able
+ to use. There must be at least one CipherSpec acceptable to the
+ server.
+
+ session_id
+ If this field's length is not zero, it will contain the
+ identification for a session that the client wishes to resume.
+
+ challenge
+ The client challenge to the server for the server to identify
+ itself is a (nearly) arbitrary length random. The TLS server will
+ right justify the challenge data to become the ClientHello.random
+ data (padded with leading zeroes, if necessary), as specified in
+ this protocol specification. If the length of the challenge is
+ greater than 32 bytes, only the last 32 bytes are used. It is
+ legitimate (but not necessary) for a V3 server to reject a V2
+ ClientHello that has fewer than 16 bytes of challenge data.
+
+ Note: Requests to resume a TLS session should use a TLS client hello.
+
+E.2. Avoiding man-in-the-middle version rollback
+
+ When TLS clients fall back to Version 2.0 compatibility mode, they
+ should use special PKCS #1 block formatting. This is done so that TLS
+ servers will reject Version 2.0 sessions with TLS-capable clients.
+
+ When TLS clients are in Version 2.0 compatibility mode, they set the
+ right-hand (least-significant) 8 random bytes of the PKCS padding
+ (not including the terminal null of the padding) for the RSA
+ encryption of the ENCRYPTED-KEY-DATA field of the CLIENT-MASTER-KEY
+ to 0x03 (the other padding bytes are random). After decrypting the
+ ENCRYPTED-KEY-DATA field, servers that support TLS should issue an
+ error if these eight padding bytes are 0x03. Version 2.0 servers
+ receiving blocks padded in this manner will proceed normally.
+
+
+
+
+
+
+
+Dierks & Allen Standards Track [Page 68]
+
+RFC 2246 The TLS Protocol Version 1.0 January 1999
+
+
+F. Security analysis
+
+ The TLS protocol is designed to establish a secure connection between
+ a client and a server communicating over an insecure channel. This
+ document makes several traditional assumptions, including that
+ attackers have substantial computational resources and cannot obtain
+ secret information from sources outside the protocol. Attackers are
+ assumed to have the ability to capture, modify, delete, replay, and
+ otherwise tamper with messages sent over the communication channel.
+ This appendix outlines how TLS has been designed to resist a variety
+ of attacks.
+
+F.1. Handshake protocol
+
+ The handshake protocol is responsible for selecting a CipherSpec and
+ generating a Master Secret, which together comprise the primary
+ cryptographic parameters associated with a secure session. The
+ handshake protocol can also optionally authenticate parties who have
+ certificates signed by a trusted certificate authority.
+
+F.1.1. Authentication and key exchange
+
+ TLS supports three authentication modes: authentication of both
+ parties, server authentication with an unauthenticated client, and
+ total anonymity. Whenever the server is authenticated, the channel is
+ secure against man-in-the-middle attacks, but completely anonymous
+ sessions are inherently vulnerable to such attacks. Anonymous
+ servers cannot authenticate clients. If the server is authenticated,
+ its certificate message must provide a valid certificate chain
+ leading to an acceptable certificate authority. Similarly,
+ authenticated clients must supply an acceptable certificate to the
+ server. Each party is responsible for verifying that the other's
+ certificate is valid and has not expired or been revoked.
+
+ The general goal of the key exchange process is to create a
+ pre_master_secret known to the communicating parties and not to
+ attackers. The pre_master_secret will be used to generate the
+ master_secret (see Section 8.1). The master_secret is required to
+ generate the certificate verify and finished messages, encryption
+ keys, and MAC secrets (see Sections 7.4.8, 7.4.9 and 6.3). By sending
+ a correct finished message, parties thus prove that they know the
+ correct pre_master_secret.
+
+F.1.1.1. Anonymous key exchange
+
+ Completely anonymous sessions can be established using RSA or
+ Diffie-Hellman for key exchange. With anonymous RSA, the client
+ encrypts a pre_master_secret with the server's uncertified public key
+
+
+
+Dierks & Allen Standards Track [Page 69]
+
+RFC 2246 The TLS Protocol Version 1.0 January 1999
+
+
+ extracted from the server key exchange message. The result is sent in
+ a client key exchange message. Since eavesdroppers do not know the
+ server's private key, it will be infeasible for them to decode the
+ pre_master_secret. (Note that no anonymous RSA Cipher Suites are
+ defined in this document).
+
+ With Diffie-Hellman, the server's public parameters are contained in
+ the server key exchange message and the client's are sent in the
+ client key exchange message. Eavesdroppers who do not know the
+ private values should not be able to find the Diffie-Hellman result
+ (i.e. the pre_master_secret).
+
+ Warning: Completely anonymous connections only provide protection
+ against passive eavesdropping. Unless an independent tamper-
+ proof channel is used to verify that the finished messages
+ were not replaced by an attacker, server authentication is
+ required in environments where active man-in-the-middle
+ attacks are a concern.
+
+F.1.1.2. RSA key exchange and authentication
+
+ With RSA, key exchange and server authentication are combined. The
+ public key may be either contained in the server's certificate or may
+ be a temporary RSA key sent in a server key exchange message. When
+ temporary RSA keys are used, they are signed by the server's RSA or
+ DSS certificate. The signature includes the current
+ ClientHello.random, so old signatures and temporary keys cannot be
+ replayed. Servers may use a single temporary RSA key for multiple
+ negotiation sessions.
+
+ Note: The temporary RSA key option is useful if servers need large
+ certificates but must comply with government-imposed size limits
+ on keys used for key exchange.
+
+ After verifying the server's certificate, the client encrypts a
+ pre_master_secret with the server's public key. By successfully
+ decoding the pre_master_secret and producing a correct finished
+ message, the server demonstrates that it knows the private key
+ corresponding to the server certificate.
+
+ When RSA is used for key exchange, clients are authenticated using
+ the certificate verify message (see Section 7.4.8). The client signs
+ a value derived from the master_secret and all preceding handshake
+ messages. These handshake messages include the server certificate,
+ which binds the signature to the server, and ServerHello.random,
+ which binds the signature to the current handshake process.
+
+
+
+
+
+Dierks & Allen Standards Track [Page 70]
+
+RFC 2246 The TLS Protocol Version 1.0 January 1999
+
+
+F.1.1.3. Diffie-Hellman key exchange with authentication
+
+ When Diffie-Hellman key exchange is used, the server can either
+ supply a certificate containing fixed Diffie-Hellman parameters or
+ can use the server key exchange message to send a set of temporary
+ Diffie-Hellman parameters signed with a DSS or RSA certificate.
+ Temporary parameters are hashed with the hello.random values before
+ signing to ensure that attackers do not replay old parameters. In
+ either case, the client can verify the certificate or signature to
+ ensure that the parameters belong to the server.
+
+ If the client has a certificate containing fixed Diffie-Hellman
+ parameters, its certificate contains the information required to
+ complete the key exchange. Note that in this case the client and
+ server will generate the same Diffie-Hellman result (i.e.,
+ pre_master_secret) every time they communicate. To prevent the
+ pre_master_secret from staying in memory any longer than necessary,
+ it should be converted into the master_secret as soon as possible.
+ Client Diffie-Hellman parameters must be compatible with those
+ supplied by the server for the key exchange to work.
+
+ If the client has a standard DSS or RSA certificate or is
+ unauthenticated, it sends a set of temporary parameters to the server
+ in the client key exchange message, then optionally uses a
+ certificate verify message to authenticate itself.
+
+F.1.2. Version rollback attacks
+
+ Because TLS includes substantial improvements over SSL Version 2.0,
+ attackers may try to make TLS-capable clients and servers fall back
+ to Version 2.0. This attack can occur if (and only if) two TLS-
+ capable parties use an SSL 2.0 handshake.
+
+ Although the solution using non-random PKCS #1 block type 2 message
+ padding is inelegant, it provides a reasonably secure way for Version
+ 3.0 servers to detect the attack. This solution is not secure against
+ attackers who can brute force the key and substitute a new
+ ENCRYPTED-KEY-DATA message containing the same key (but with normal
+ padding) before the application specified wait threshold has expired.
+ Parties concerned about attacks of this scale should not be using
+ 40-bit encryption keys anyway. Altering the padding of the least-
+ significant 8 bytes of the PKCS padding does not impact security for
+ the size of the signed hashes and RSA key lengths used in the
+ protocol, since this is essentially equivalent to increasing the
+ input block size by 8 bytes.
+
+
+
+
+
+
+Dierks & Allen Standards Track [Page 71]
+
+RFC 2246 The TLS Protocol Version 1.0 January 1999
+
+
+F.1.3. Detecting attacks against the handshake protocol
+
+ An attacker might try to influence the handshake exchange to make the
+ parties select different encryption algorithms than they would
+ normally choose. Because many implementations will support 40-bit
+ exportable encryption and some may even support null encryption or
+ MAC algorithms, this attack is of particular concern.
+
+ For this attack, an attacker must actively change one or more
+ handshake messages. If this occurs, the client and server will
+ compute different values for the handshake message hashes. As a
+ result, the parties will not accept each others' finished messages.
+ Without the master_secret, the attacker cannot repair the finished
+ messages, so the attack will be discovered.
+
+F.1.4. Resuming sessions
+
+ When a connection is established by resuming a session, new
+ ClientHello.random and ServerHello.random values are hashed with the
+ session's master_secret. Provided that the master_secret has not been
+ compromised and that the secure hash operations used to produce the
+ encryption keys and MAC secrets are secure, the connection should be
+ secure and effectively independent from previous connections.
+ Attackers cannot use known encryption keys or MAC secrets to
+ compromise the master_secret without breaking the secure hash
+ operations (which use both SHA and MD5).
+
+ Sessions cannot be resumed unless both the client and server agree.
+ If either party suspects that the session may have been compromised,
+ or that certificates may have expired or been revoked, it should
+ force a full handshake. An upper limit of 24 hours is suggested for
+ session ID lifetimes, since an attacker who obtains a master_secret
+ may be able to impersonate the compromised party until the
+ corresponding session ID is retired. Applications that may be run in
+ relatively insecure environments should not write session IDs to
+ stable storage.
+
+F.1.5. MD5 and SHA
+
+ TLS uses hash functions very conservatively. Where possible, both MD5
+ and SHA are used in tandem to ensure that non-catastrophic flaws in
+ one algorithm will not break the overall protocol.
+
+F.2. Protecting application data
+
+ The master_secret is hashed with the ClientHello.random and
+ ServerHello.random to produce unique data encryption keys and MAC
+ secrets for each connection.
+
+
+
+Dierks & Allen Standards Track [Page 72]
+
+RFC 2246 The TLS Protocol Version 1.0 January 1999
+
+
+ Outgoing data is protected with a MAC before transmission. To prevent
+ message replay or modification attacks, the MAC is computed from the
+ MAC secret, the sequence number, the message length, the message
+ contents, and two fixed character strings. The message type field is
+ necessary to ensure that messages intended for one TLS Record Layer
+ client are not redirected to another. The sequence number ensures
+ that attempts to delete or reorder messages will be detected. Since
+ sequence numbers are 64-bits long, they should never overflow.
+ Messages from one party cannot be inserted into the other's output,
+ since they use independent MAC secrets. Similarly, the server-write
+ and client-write keys are independent so stream cipher keys are used
+ only once.
+
+ If an attacker does break an encryption key, all messages encrypted
+ with it can be read. Similarly, compromise of a MAC key can make
+ message modification attacks possible. Because MACs are also
+ encrypted, message-alteration attacks generally require breaking the
+ encryption algorithm as well as the MAC.
+
+ Note: MAC secrets may be larger than encryption keys, so messages can
+ remain tamper resistant even if encryption keys are broken.
+
+F.3. Final notes
+
+ For TLS to be able to provide a secure connection, both the client
+ and server systems, keys, and applications must be secure. In
+ addition, the implementation must be free of security errors.
+
+ The system is only as strong as the weakest key exchange and
+ authentication algorithm supported, and only trustworthy
+ cryptographic functions should be used. Short public keys, 40-bit
+ bulk encryption keys, and anonymous servers should be used with great
+ caution. Implementations and users must be careful when deciding
+ which certificates and certificate authorities are acceptable; a
+ dishonest certificate authority can do tremendous damage.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Dierks & Allen Standards Track [Page 73]
+
+RFC 2246 The TLS Protocol Version 1.0 January 1999
+
+
+G. Patent Statement
+
+ Some of the cryptographic algorithms proposed for use in this
+ protocol have patent claims on them. In addition Netscape
+ Communications Corporation has a patent claim on the Secure Sockets
+ Layer (SSL) work that this standard is based on. The Internet
+ Standards Process as defined in RFC 2026 requests that a statement be
+ obtained from a Patent holder indicating that a license will be made
+ available to applicants under reasonable terms and conditions.
+
+ The Massachusetts Institute of Technology has granted RSA Data
+ Security, Inc., exclusive sub-licensing rights to the following
+ patent issued in the United States:
+
+ Cryptographic Communications System and Method ("RSA"), No.
+ 4,405,829
+
+ Netscape Communications Corporation has been issued the following
+ patent in the United States:
+
+ Secure Socket Layer Application Program Apparatus And Method
+ ("SSL"), No. 5,657,390
+
+ Netscape Communications has issued the following statement:
+
+ Intellectual Property Rights
+
+ Secure Sockets Layer
+
+ The United States Patent and Trademark Office ("the PTO")
+ recently issued U.S. Patent No. 5,657,390 ("the SSL Patent") to
+ Netscape for inventions described as Secure Sockets Layers
+ ("SSL"). The IETF is currently considering adopting SSL as a
+ transport protocol with security features. Netscape encourages
+ the royalty-free adoption and use of the SSL protocol upon the
+ following terms and conditions:
+
+ * If you already have a valid SSL Ref license today which
+ includes source code from Netscape, an additional patent
+ license under the SSL patent is not required.
+
+ * If you don't have an SSL Ref license, you may have a royalty
+ free license to build implementations covered by the SSL
+ Patent Claims or the IETF TLS specification provided that you
+ do not to assert any patent rights against Netscape or other
+ companies for the implementation of SSL or the IETF TLS
+ recommendation.
+
+
+
+
+Dierks & Allen Standards Track [Page 74]
+
+RFC 2246 The TLS Protocol Version 1.0 January 1999
+
+
+ What are "Patent Claims":
+
+ Patent claims are claims in an issued foreign or domestic patent
+ that:
+
+ 1) must be infringed in order to implement methods or build
+ products according to the IETF TLS specification; or
+
+ 2) patent claims which require the elements of the SSL patent
+ claims and/or their equivalents to be infringed.
+
+ The Internet Society, Internet Architecture Board, Internet
+ Engineering Steering Group and the Corporation for National Research
+ Initiatives take no position on the validity or scope of the patents
+ and patent applications, nor on the appropriateness of the terms of
+ the assurance. The Internet Society and other groups mentioned above
+ have not made any determination as to any other intellectual property
+ rights which may apply to the practice of this standard. Any further
+ consideration of these matters is the user's own responsibility.
+
+Security Considerations
+
+ Security issues are discussed throughout this memo.
+
+References
+
+ [3DES] W. Tuchman, "Hellman Presents No Shortcut Solutions To DES,"
+ IEEE Spectrum, v. 16, n. 7, July 1979, pp40-41.
+
+ [BLEI] Bleichenbacher D., "Chosen Ciphertext Attacks against
+ Protocols Based on RSA Encryption Standard PKCS #1" in
+ Advances in Cryptology -- CRYPTO'98, LNCS vol. 1462, pages:
+ 1--12, 1998.
+
+ [DES] ANSI X3.106, "American National Standard for Information
+ Systems-Data Link Encryption," American National Standards
+ Institute, 1983.
+
+ [DH1] W. Diffie and M. E. Hellman, "New Directions in
+ Cryptography," IEEE Transactions on Information Theory, V.
+ IT-22, n. 6, Jun 1977, pp. 74-84.
+
+ [DSS] NIST FIPS PUB 186, "Digital Signature Standard," National
+ Institute of Standards and Technology, U.S. Department of
+ Commerce, May 18, 1994.
+
+ [FTP] Postel J., and J. Reynolds, "File Transfer Protocol", STD 9,
+ RFC 959, October 1985.
+
+
+
+Dierks & Allen Standards Track [Page 75]
+
+RFC 2246 The TLS Protocol Version 1.0 January 1999
+
+
+ [HTTP] Berners-Lee, T., Fielding, R., and H. Frystyk, "Hypertext
+ Transfer Protocol -- HTTP/1.0", RFC 1945, May 1996.
+
+ [HMAC] Krawczyk, H., Bellare, M., and R. Canetti, "HMAC: Keyed-
+ Hashing for Message Authentication," RFC 2104, February
+ 1997.
+
+ [IDEA] X. Lai, "On the Design and Security of Block Ciphers," ETH
+ Series in Information Processing, v. 1, Konstanz: Hartung-
+ Gorre Verlag, 1992.
+
+ [MD2] Kaliski, B., "The MD2 Message Digest Algorithm", RFC 1319,
+ April 1992.
+
+ [MD5] Rivest, R., "The MD5 Message Digest Algorithm", RFC 1321,
+ April 1992.
+
+ [PKCS1] RSA Laboratories, "PKCS #1: RSA Encryption Standard,"
+ version 1.5, November 1993.
+
+ [PKCS6] RSA Laboratories, "PKCS #6: RSA Extended Certificate Syntax
+ Standard," version 1.5, November 1993.
+
+ [PKCS7] RSA Laboratories, "PKCS #7: RSA Cryptographic Message Syntax
+ Standard," version 1.5, November 1993.
+
+ [PKIX] Housley, R., Ford, W., Polk, W. and D. Solo, "Internet
+ Public Key Infrastructure: Part I: X.509 Certificate and CRL
+ Profile", RFC 2459, January 1999.
+
+ [RC2] Rivest, R., "A Description of the RC2(r) Encryption
+ Algorithm", RFC 2268, January 1998.
+
+ [RC4] Thayer, R. and K. Kaukonen, A Stream Cipher Encryption
+ Algorithm, Work in Progress.
+
+ [RSA] R. Rivest, A. Shamir, and L. M. Adleman, "A Method for
+ Obtaining Digital Signatures and Public-Key Cryptosystems,"
+ Communications of the ACM, v. 21, n. 2, Feb 1978, pp. 120-
+ 126.
+
+ [RSADSI] Contact RSA Data Security, Inc., Tel: 415-595-8782
+
+ [SCH] B. Schneier. Applied Cryptography: Protocols, Algorithms,
+ and Source Code in C, Published by John Wiley & Sons, Inc.
+ 1994.
+
+
+
+
+
+Dierks & Allen Standards Track [Page 76]
+
+RFC 2246 The TLS Protocol Version 1.0 January 1999
+
+
+ [SHA] NIST FIPS PUB 180-1, "Secure Hash Standard," National
+ Institute of Standards and Technology, U.S. Department of
+ Commerce, Work in Progress, May 31, 1994.
+
+ [SSL2] Hickman, Kipp, "The SSL Protocol", Netscape Communications
+ Corp., Feb 9, 1995.
+
+ [SSL3] A. Frier, P. Karlton, and P. Kocher, "The SSL 3.0 Protocol",
+ Netscape Communications Corp., Nov 18, 1996.
+
+ [TCP] Postel, J., "Transmission Control Protocol," STD 7, RFC 793,
+ September 1981.
+
+ [TEL] Postel J., and J. Reynolds, "Telnet Protocol
+ Specifications", STD 8, RFC 854, May 1993.
+
+ [TEL] Postel J., and J. Reynolds, "Telnet Option Specifications",
+ STD 8, RFC 855, May 1993.
+
+ [X509] CCITT. Recommendation X.509: "The Directory - Authentication
+ Framework". 1988.
+
+ [XDR] R. Srinivansan, Sun Microsystems, RFC-1832: XDR: External
+ Data Representation Standard, August 1995.
+
+Credits
+
+ Win Treese
+ Open Market
+
+ EMail: treese@openmarket.com
+
+
+ Editors
+
+ Christopher Allen Tim Dierks
+ Certicom Certicom
+
+ EMail: callen@certicom.com EMail: tdierks@certicom.com
+
+
+ Authors' Addresses
+
+ Tim Dierks Philip L. Karlton
+ Certicom Netscape Communications
+
+ EMail: tdierks@certicom.com
+
+
+
+
+Dierks & Allen Standards Track [Page 77]
+
+RFC 2246 The TLS Protocol Version 1.0 January 1999
+
+
+ Alan O. Freier Paul C. Kocher
+ Netscape Communications Independent Consultant
+
+ EMail: freier@netscape.com EMail: pck@netcom.com
+
+
+ Other contributors
+
+ Martin Abadi Robert Relyea
+ Digital Equipment Corporation Netscape Communications
+
+ EMail: ma@pa.dec.com EMail: relyea@netscape.com
+
+ Ran Canetti Jim Roskind
+ IBM Watson Research Center Netscape Communications
+
+ EMail: canetti@watson.ibm.com EMail: jar@netscape.com
+
+
+ Taher Elgamal Micheal J. Sabin, Ph. D.
+ Securify Consulting Engineer
+
+ EMail: elgamal@securify.com EMail: msabin@netcom.com
+
+
+ Anil R. Gangolli Dan Simon
+ Structured Arts Computing Corp. Microsoft
+
+ EMail: gangolli@structuredarts.com EMail: dansimon@microsoft.com
+
+
+ Kipp E.B. Hickman Tom Weinstein
+ Netscape Communications Netscape Communications
+
+ EMail: kipp@netscape.com EMail: tomw@netscape.com
+
+
+ Hugo Krawczyk
+ IBM Watson Research Center
+
+ EMail: hugo@watson.ibm.com
+
+Comments
+
+ The discussion list for the IETF TLS working group is located at the
+ e-mail address . Information on the
+ group and information on how to subscribe to the list is at
+ .
+
+
+
+Dierks & Allen Standards Track [Page 78]
+
+RFC 2246 The TLS Protocol Version 1.0 January 1999
+
+
+ Archives of the list can be found at:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Dierks & Allen Standards Track [Page 79]
+
+RFC 2246 The TLS Protocol Version 1.0 January 1999
+
+
+Full Copyright Statement
+
+ Copyright (C) The Internet Society (1999). All Rights Reserved.
+
+ This document and translations of it may be copied and furnished to
+ others, and derivative works that comment on or otherwise explain it
+ or assist in its implementation may be prepared, copied, published
+ and distributed, in whole or in part, without restriction of any
+ kind, provided that the above copyright notice and this paragraph are
+ included on all such copies and derivative works. However, this
+ document itself may not be modified in any way, such as by removing
+ the copyright notice or references to the Internet Society or other
+ Internet organizations, except as needed for the purpose of
+ developing Internet standards in which case the procedures for
+ copyrights defined in the Internet Standards process must be
+ followed, or as required to translate it into languages other than
+ English.
+
+ The limited permissions granted above are perpetual and will not be
+ revoked by the Internet Society or its successors or assigns.
+
+ This document and the information contained herein is provided on an
+ "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING
+ TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+ HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+ MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Dierks & Allen Standards Track [Page 80]
+
diff -Nur snapshot-20010228-orig/html/ssl/rfc2487.txt snapshot-20010228/html/ssl/rfc2487.txt
--- snapshot-20010228-orig/html/ssl/rfc2487.txt Thu Jan 1 01:00:00 1970
+++ snapshot-20010228/html/ssl/rfc2487.txt Wed Mar 21 13:38:29 2001
@@ -0,0 +1,451 @@
+
+
+
+
+
+
+Network Working Group P. Hoffman
+Request for Comments: 2487 Internet Mail Consortium
+Category: Standards Track January 1999
+
+
+ SMTP Service Extension for Secure SMTP over TLS
+
+Status of this Memo
+
+ This document specifies an Internet standards track protocol for the
+ Internet community, and requests discussion and suggestions for
+ improvements. Please refer to the current edition of the "Internet
+ Official Protocol Standards" (STD 1) for the standardization state
+ and status of this protocol. Distribution of this memo is unlimited.
+
+Copyright Notice
+
+ Copyright (C) The Internet Society (1999). All Rights Reserved.
+
+1. Abstract
+
+ This document describes an extension to the SMTP service that allows
+ an SMTP server and client to use transport-layer security to provide
+ private, authenticated communication over the Internet. This gives
+ SMTP agents the ability to protect some or all of their
+ communications from eavesdroppers and attackers.
+
+2. Introduction
+
+ SMTP [RFC-821] servers and clients normally communicate in the clear
+ over the Internet. In many cases, this communication goes through one
+ or more router that is not controlled or trusted by either entity.
+ Such an untrusted router might allow a third party to monitor or
+ alter the communications between the server and client.
+
+ Further, there is often a desire for two SMTP agents to be able to
+ authenticate each others' identities. For example, a secure SMTP
+ server might only allow communications from other SMTP agents it
+ knows, or it might act differently for messages received from an
+ agent it knows than from one it doesn't know.
+
+ TLS [TLS], more commonly known as SSL, is a popular mechanism for
+ enhancing TCP communications with privacy and authentication. TLS is
+ in wide use with the HTTP protocol, and is also being used for adding
+ security to many other common protocols that run over TCP.
+
+
+
+
+
+
+Hoffman Standards Track [Page 1]
+
+RFC 2487 SMTP Service Extension January 1999
+
+
+2.1 Terminology
+
+ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
+ "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
+ document are to be interpreted as described in [RFC-2119].
+
+3. STARTTLS Extension
+
+ The STARTTLS extension to SMTP is laid out as follows:
+
+ (1) the name of the SMTP service defined here is STARTTLS;
+
+ (2) the EHLO keyword value associated with the extension is STARTTLS;
+
+ (3) the STARTTLS keyword has no parameters;
+
+ (4) a new SMTP verb, "STARTTLS", is defined;
+
+ (5) no additional parameters are added to any SMTP command.
+
+4. The STARTTLS Keyword
+
+ The STARTTLS keyword is used to tell the SMTP client that the SMTP
+ server allows use of TLS. It takes no parameters.
+
+5. The STARTTLS Command
+
+ The format for the STARTTLS command is:
+
+ STARTTLS
+
+ with no parameters.
+
+ After the client gives the STARTTLS command, the server responds with
+ one of the following reply codes:
+
+ 220 Ready to start TLS
+ 501 Syntax error (no parameters allowed)
+ 454 TLS not available due to temporary reason
+
+ A publicly-referenced SMTP server MUST NOT require use of the
+ STARTTLS extension in order to deliver mail locally. This rule
+ prevents the STARTTLS extension from damaging the interoperability of
+ the Internet's SMTP infrastructure. A publicly-referenced SMTP server
+ is an SMTP server which runs on port 25 of an Internet host listed in
+ the MX record (or A record if an MX record is not present) for the
+ domain name on the right hand side of an Internet mail address.
+
+
+
+
+Hoffman Standards Track [Page 2]
+
+RFC 2487 SMTP Service Extension January 1999
+
+
+ Any SMTP server may refuse to accept messages for relay based on
+ authentication supplied during the TLS negotiation. An SMTP server
+ that is not publicly referenced may refuse to accept any messages for
+ relay or local delivery based on authentication supplied during the
+ TLS negotiation.
+
+ A SMTP server that is not publicly referenced may choose to require
+ that the client perform a TLS negotiation before accepting any
+ commands. In this case, the server SHOULD return the reply code:
+
+ 530 Must issue a STARTTLS command first
+
+ to every command other than NOOP, EHLO, STARTTLS, or QUIT. If the
+ client and server are using the ENHANCEDSTATUSCODES ESMTP extension
+ [RFC-2034], the status code to be returned SHOULD be 5.7.0.
+
+ After receiving a 220 response to a STARTTLS command, the client
+ SHOULD start the TLS negotiation before giving any other SMTP
+ commands.
+
+ If the SMTP client is using pipelining as defined in RFC 1854, the
+ STARTTLS command must be the last command in a group.
+
+5.1 Processing After the STARTTLS Command
+
+ After the TLS handshake has been completed, both parties MUST
+ immediately decide whether or not to continue based on the
+ authentication and privacy achieved. The SMTP client and server may
+ decide to move ahead even if the TLS negotiation ended with no
+ authentication and/or no privacy because most SMTP services are
+ performed with no authentication and no privacy, but some SMTP
+ clients or servers may want to continue only if a particular level of
+ authentication and/or privacy was achieved.
+
+ If the SMTP client decides that the level of authentication or
+ privacy is not high enough for it to continue, it SHOULD issue an
+ SMTP QUIT command immediately after the TLS negotiation is complete.
+ If the SMTP server decides that the level of authentication or
+ privacy is not high enough for it to continue, it SHOULD reply to
+ every SMTP command from the client (other than a QUIT command) with
+ the 554 reply code (with a possible text string such as "Command
+ refused due to lack of security").
+
+ The decision of whether or not to believe the authenticity of the
+ other party in a TLS negotiation is a local matter. However, some
+ general rules for the decisions are:
+
+
+
+
+
+Hoffman Standards Track [Page 3]
+
+RFC 2487 SMTP Service Extension January 1999
+
+
+ - A SMTP client would probably only want to authenticate an SMTP
+ server whose server certificate has a domain name that is the
+ domain name that the client thought it was connecting to.
+ - A publicly-referenced SMTP server would probably want to accept
+ any certificate from an SMTP client, and would possibly want to
+ put distinguishing information about the certificate in the
+ Received header of messages that were relayed or submitted from
+ the client.
+
+5.2 Result of the STARTTLS Command
+
+ Upon completion of the TLS handshake, the SMTP protocol is reset to
+ the initial state (the state in SMTP after a server issues a 220
+ service ready greeting). The server MUST discard any knowledge
+ obtained from the client, such as the argument to the EHLO command,
+ which was not obtained from the TLS negotiation itself. The client
+ MUST discard any knowledge obtained from the server, such as the list
+ of SMTP service extensions, which was not obtained from the TLS
+ negotiation itself. The client SHOULD send an EHLO command as the
+ first command after a successful TLS negotiation.
+
+ The list of SMTP service extensions returned in response to an EHLO
+ command received after the TLS handshake MAY be different than the
+ list returned before the TLS handshake. For example, an SMTP server
+ might not want to advertise support for a particular SASL mechanism
+ [SASL] unless a client has sent an appropriate client certificate
+ during a TLS handshake.
+
+ Both the client and the server MUST know if there is a TLS session
+ active. A client MUST NOT attempt to start a TLS session if a TLS
+ session is already active. A server MUST NOT return the TLS extension
+ in response to an EHLO command received after a TLS handshake has
+ completed.
+
+6. Usage Example
+
+ The following dialog illustrates how a client and server can start a
+ TLS session:
+
+ S:
+ C:
+ S: 220 mail.imc.org SMTP service ready
+ C: EHLO mail.ietf.org
+ S: 250-mail.imc.org offers a warm hug of welcome
+ S: 250 STARTTLS
+ C: STARTTLS
+ S: 220 Go ahead
+ C:
+
+
+
+Hoffman Standards Track [Page 4]
+
+RFC 2487 SMTP Service Extension January 1999
+
+
+ C & S:
+ C & S:
+ C:
+ . . .
+
+7. Security Considerations
+
+ It should be noted that SMTP is not an end-to-end mechanism. Thus, if
+ an SMTP client/server pair decide to add TLS privacy, they are not
+ securing the transport from the originating mail user agent to the
+ recipient. Further, because delivery of a single piece of mail may
+ go between more than two SMTP servers, adding TLS privacy to one pair
+ of servers does not mean that the entire SMTP chain has been made
+ private. Further, just because an SMTP server can authenticate an
+ SMTP client, it does not mean that the mail from the SMTP client was
+ authenticated by the SMTP client when the client received it.
+
+ Both the STMP client and server must check the result of the TLS
+ negotiation to see whether acceptable authentication or privacy was
+ achieved. Ignoring this step completely invalidates using TLS for
+ security. The decision about whether acceptable authentication or
+ privacy was achieved is made locally, is implementation-dependant,
+ and is beyond the scope of this document.
+
+ The SMTP client and server should note carefully the result of the
+ TLS negotiation. If the negotiation results in no privacy, or if it
+ results in privacy using algorithms or key lengths that are deemed
+ not strong enough, or if the authentication is not good enough for
+ either party, the client may choose to end the SMTP session with an
+ immediate QUIT command, or the server may choose to not accept any
+ more SMTP commands.
+
+ A server announcing in an EHLO response that it uses a particular TLS
+ protocol should not pose any security issues, since any use of TLS
+ will be at least as secure as no use of TLS.
+
+ A man-in-the-middle attack can be launched by deleting the "250
+ STARTTLS" response from the server. This would cause the client not
+ to try to start a TLS session. An SMTP client can protect against
+ this attack by recording the fact that a particular SMTP server
+ offers TLS during one session and generating an alarm if it does not
+ appear in the EHLO response for a later session. The lack of TLS
+ during a session SHOULD NOT result in the bouncing of email, although
+ it could result in delayed processing.
+
+
+
+
+
+
+
+Hoffman Standards Track [Page 5]
+
+RFC 2487 SMTP Service Extension January 1999
+
+
+ Before the TLS handshake has begun, any protocol interactions are
+ performed in the clear and may be modified by an active attacker. For
+ this reason, clients and servers MUST discard any knowledge obtained
+ prior to the start of the TLS handshake upon completion of the TLS
+ handshake.
+
+ The STARTTLS extension is not suitable for authenticating the author
+ of an email message unless every hop in the delivery chain, including
+ the submission to the first SMTP server, is authenticated. Another
+ proposal [SMTP-AUTH] can be used to authenticate delivery and MIME
+ security multiparts [MIME-SEC] can be used to authenticate the author
+ of an email message. In addition, the [SMTP-AUTH] proposal offers
+ simpler and more flexible options to authenticate an SMTP client and
+ the SASL EXTERNAL mechanism [SASL] MAY be used in conjunction with
+ the STARTTLS command to provide an authorization identity.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Hoffman Standards Track [Page 6]
+
+RFC 2487 SMTP Service Extension January 1999
+
+
+A. References
+
+ [RFC-821] Postel, J., "Simple Mail Transfer Protocol", RFC 821,
+ August 1982.
+
+ [RFC-1869] Klensin, J., Freed, N, Rose, M, Stefferud, E. and D.
+ Crocker, "SMTP Service Extensions", STD 10, RFC 1869,
+ November 1995.
+
+ [RFC-2034] Freed, N., "SMTP Service Extension for Returning Enhanced
+ Error Codes", RFC 2034, October 1996.
+
+ [RFC-2119] Bradner, S., "Key words for use in RFCs to Indicate
+ Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+ [SASL] Myers, J., "Simple Authentication and Security Layer
+ (SASL)", RFC 2222, October 1997.
+
+ [SMTP-AUTH] "SMTP Service Extension for Authentication", Work in
+ Progress.
+
+ [TLS] Dierks, T. and C. Allen, "The TLS Protocol Version 1.0",
+ RFC 2246, January 1999.
+
+B. Author's Address
+
+ Paul Hoffman
+ Internet Mail Consortium
+ 127 Segre Place
+ Santa Cruz, CA 95060
+
+ Phone: (831) 426-9827
+ EMail: phoffman@imc.org
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Hoffman Standards Track [Page 7]
+
+RFC 2487 SMTP Service Extension January 1999
+
+
+C. Full Copyright Statement
+
+ Copyright (C) The Internet Society (1999). All Rights Reserved.
+
+ This document and translations of it may be copied and furnished to
+ others, and derivative works that comment on or otherwise explain it
+ or assist in its implementation may be prepared, copied, published
+ and distributed, in whole or in part, without restriction of any
+ kind, provided that the above copyright notice and this paragraph are
+ included on all such copies and derivative works. However, this
+ document itself may not be modified in any way, such as by removing
+ the copyright notice or references to the Internet Society or other
+ Internet organizations, except as needed for the purpose of
+ developing Internet standards in which case the procedures for
+ copyrights defined in the Internet Standards process must be
+ followed, or as required to translate it into languages other than
+ English.
+
+ The limited permissions granted above are perpetual and will not be
+ revoked by the Internet Society or its successors or assigns.
+
+ This document and the information contained herein is provided on an
+ "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING
+ TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+ HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+ MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Hoffman Standards Track [Page 8]
+
diff -Nur snapshot-20010228-orig/html/ssl/security.html snapshot-20010228/html/ssl/security.html
--- snapshot-20010228-orig/html/ssl/security.html Thu Jan 1 01:00:00 1970
+++ snapshot-20010228/html/ssl/security.html Wed Mar 21 13:38:29 2001
@@ -0,0 +1,78 @@
+
+
+
+
+Postfix/TLS - Security Considerations
+
+
+Postfix/TLS - Security Considerations
+
+The following sections cover some (possible) security issues with
+regard to Postfix/TLS.
+
+Server/Client private key file
+
+Postfix/TLS uses authentication for the server side (mandatory) and
+the client side (optional). In order to authenticate itself, the
+according process (smptd/smtp) must be able to access the private
+key, which must however be kept secret. As these processes are
+started from 'master' without the possibility of user interaction, it is not
+possible to supply a password, so that the private key can not be
+encrypted.
+
+The only protection can therefore come from filesystem access
+rights, which should be set to 'owner root' and 'readable for owner
+only':
+
+
+-rw------- 1 root sys 887 Apr 29 1999 /etc/postfix/key.pem
+
+
+This protection is only as good as your host is protected
+against root exploits.
+
+You also should be aware, that people having physical access to
+your system might be able to 'steal' the private key if they can
+boot into single user mode without password protection or can move
+the disk to another computer, on which they have root rights. (Yes,
+I know there are such things as encrypted filesystems, but they are
+not in wide spread use today.)
+
+Disk based session cache
+
+If you run disk based session caching (the default) people being
+able to get hold of the files might be able to figure out security
+relevant communication parameters. The security situation is
+however not more dramatic than the private key issue explained
+above, so I don't consider any additional danger coming from saving
+session information to stable storage.
+
+As breaking the code with public key cryptography is just a
+matter of time (even though it might be a very long time), sessions
+should not be used for an infinite duration. The default value for
+Postfix/TLS is 1h; RFC2246 (TLSv1) recommends to not use sessions
+for more than 24h.
+
+DNS issues
+
+One weak point in authentication is the use of the DNS to find out
+the MX information. Since we do (E)SMTP, we must use the MX
+information!
+
+As we have to authenticate the server retrieved via MX, somebody
+able to spoof a wrong MX entry might be able to receive the email,
+if his host can present a certificate issued by an acceptable CA.
+The last part is not too difficult if 'standard' CAs like Verisign,
+Thawte,... are included.
+
+The only way to protect against this problem is that for those
+recipients, for which we want to enforce
+encryption and authentication, the MX lookup must be overridden
+with an appropriate entry in the /etc/postfix/transport table:
+
+
+important.dom.ain smtp:[mailserver.important.dom.ain]
+
+
+
+
diff -Nur snapshot-20010228-orig/html/ssl/setup.html snapshot-20010228/html/ssl/setup.html
--- snapshot-20010228-orig/html/ssl/setup.html Thu Jan 1 01:00:00 1970
+++ snapshot-20010228/html/ssl/setup.html Wed Mar 21 13:38:29 2001
@@ -0,0 +1,219 @@
+
+
+
+
+Postfix/TLS - Setting up the certificates
+
+
+Postfix/TLS - Setting up the certificates
+
+This section explains what kind of certificates are needed to run
+postfix with TLS. The certificates (and maybe keys) can be obtained
+from a third party, that might be a commercial certification
+authority or your internet service provider. On the long run you do
+need certificates that are accepted by other Internet parties, so
+you have to agree with them on certification authorities, of which
+type they might be.
+
+Server certificate
+
+To run SMTP with TLS in server mode, your server
+must have a pair of private key and public
+key.
+
+As the public key must be distributed to the client somehow, it
+is sent from the server to the client during the startup
+negotiation. The client however cannot know from just the
+negotiation, that the public key really belongs to the server and
+is not faked. Therefore a third component is necessary, a
+certificate from a certificate authority (CA), that is sent
+combined with the public key. This server certificate
+contains the name.of.your.host
. The client will then
+check the signature of the CA on the public key to decide,
+whether the certificate (and public key) are authentic.
+
+So for the server we do need:
+
+
+- 1 server private key
+
+- 1 server public key signed by a CA, a server
+certificate, certifying that the public key belongs to
+name.of.your.host
.
+
+- 1 CA certificate with the public key of the CA
+
+
+For this list I definitely want point out the number of components
+used to be 1, because you must have
+1, you cannot have less, you cannot have more!
+
+Server certificate policy
+
+At this point you have to decide about policy. The client which is
+going to connect to your host will check the name in the server
+certificate, the CN (Common Name), against the FQDN (Fully
+Qualified Domain Name) of your server. If both agree, your server's
+identity is proved.
+
+To see, whether the certificate itself is authentic, the client
+itself must have the CA certificate. So, if you
+want to make it easily accessible to other, unknown parties, you
+should have your server certificate issued by a well known and well
+trusted CA. Remember, that your server can only have one server
+certificate at a time.
+
+There are commercial providers (Thawte, Verisign, just to name
+some), the CA certificats of which are well distributed. Not
+knowing of other countries, at least in Germany the CERT of the
+Research Network (DFN) has started a program for universities [DFNCERT].
+
+If you do not care about that for know (you can change that
+later), you can just become your own CA and distribute your CA cert
+to those parties who should know it, and you are set. It is not
+difficult to do.
+Lutz's very short course on being your own
+CA.
+
+Using the certificates with Postfix/TLS
+
+To make the key and certificates available to Postfix/TLS, they
+must be in "PEM" format. Then you have to tell postfix in main.cf
+where to find them:
+
+
+- The private key:
+
+
+smtp_tls_key_file = /etc/postfix/key.pem
+
+
+As the public key is public including the certificate (everybody
+can get a copy), everybody who has a copy of the private key can
+fake your identity. It is not too easy, as he must be able to
+redirect or intercept the IP packages sent to your server, but I
+have seen a lot of things happening. So protect this key with:
+
+
+chown root /etc/postfix/key.pem ; chmod 400 /etc/postfix/key.pem
+
+
+One more possibility for protection is a passphrase. This is
+however a problem, as you have to enter it everytime the server has
+to be started. This has to drawbacks: firstly you would have to
+enter it to postfix everytime you restart it, which I find quite
+impractical for an unattended server which might restart
+automatically after a power outage. Secondly the smtpd processes
+are independently started from master, so that master would have to
+pass the passphrase to the clients somehow. Alltogether I think
+this is impractical and so I don't support by software.
+
+- The server certificate: This certificate is not secret, as it
+will be presented to every client anyhow, so you just name it to
+postfix:
+
+
+smtp_tls_cert_file = /etc/postfix/cert.pem
+
+
+If you like, you can put private key and cert into one file.
+
+- The CA certificate: To also have the CA certificate available,
+you put it into a file and name it to Postfix/TLS. We will come
+back to this file later.
+
+
+smtp_tls_CAfile = /etc/postfix/CAcert.pem
+
+
+
+
+With these certificates you should already have enough to get
+Postfix/TLS running.
+
+Postfix/TLS client mode
+
+When connecting to a server offering TLS, postfix can present a
+client certificate of its own. As realized by now, only one
+certificate can be managed, so it should be issued on your own
+hostname. No default is supplied (no certificate is presented),
+unless you explicitly set the certificate in the configuration. You
+can use the same certificate as for the server side:
+
+
+smtp_tls_key_file = /etc/postfix/key.pem
+chown root /etc/postfix/key.pem ; chmod 400 /etc/postfix/key.pem
+
+
+
+smtp_tls_cert_file = /etc/postfix/cert.pem
+
+
+
+smtp_tls_CAfile = /etc/postfix/CAcert.pem
+
+
+Client certificates
+
+One reason to do all of this work is that I want to do relaying
+based on client certificates. The clients present a certificate
+from a CA, that is unique and cannot be faked.
+
+Some clients can have several certificates issued by different
+CAs. Upon connection the server will pass the client the list of
+CAs he knows (has the CA certificates) and the client can then pass
+back a certificate of choice. With Netscape this means, a window is
+opened and only those client certificates compatible with the
+server are listed for selection.
+
+So if your clients already have certificates from trustable
+sources, it is not necessary to create a lot of problems. You just
+have to collect the CA certificates and make them available to
+Postfix/TLS. If that is not enough, you can still become your own
+CA to easily create client certificates for your users (which are
+of course of no use outside your scope).
+
+Listing CA certificates
+
+You have two possibilities to perform this task.
+
+
+- You just add the CA certificates to the
+smtp[d]_tls_CAfile
you already have created, one after the
+other. This file is probably not very readable, but it has the
+advantage that it is read at smtpd before switching to chroot jail
+and hence works in chroot mode.
+
+- You can add the CA certificates in single files with adequate
+names to a certificate directory specified in:
+
+
+smtpd_tls_CApath = /etc/postfix/certs
+
+
+Please don't forget to issue a $OPENSSL_HOME/bin/c_rehash
+/etc/postfix/certs
after you have made changes, as the
+hashes are use to find the right CA certificate. This method should
+not work in chroot mode.
+
+
+Adding client certificates
+
+The client certificates are issued for a DN (Distinguished Name)
+made up of company, department, name, email... As they may contain
+blanks, @ signs and colons, it is quite difficult to handle them
+with standard postfix tools.
+
+A quite practical thing is that every client certificate has a
+"fingerprint" that is extremely difficult to fake (read this: from
+my knowledge, it might take years even on fast computers). I have
+to do some more research about the security of the fingerprint, but
+at least for relaying it should be secure enough. I will much
+easier find a host with worse security to send out my SPAM than to
+fake a client certificate with a matching fingerprint (which I also
+don't know to from the outside, even from the inside you might
+protect the fingerprint data with a chmod 400
).
+
+
+
diff -Nur snapshot-20010228-orig/html/ssl/test.html snapshot-20010228/html/ssl/test.html
--- snapshot-20010228-orig/html/ssl/test.html Thu Jan 1 01:00:00 1970
+++ snapshot-20010228/html/ssl/test.html Wed Mar 21 13:38:29 2001
@@ -0,0 +1,178 @@
+
+
+
+
+Postfix/TLS - Testing
+
+
+Postfix/TLS - Testing
+
+Testing the package is a little bit difficult, as the communication
+is encrypted, so that you cannot "imitate" the conversation just by
+telnetting to the SMTP port. You also cannot capture the packets
+(well, you can, but if everything is working as advertised, it
+won't help you :-).
+
+Included debugging aids
+
+As all of the messages generated by Postfix are sent to the syslog
+facility, debugging must be done using your normal system logfiles.
+Postfix/TLS supports the logging levels 0 (very quiet) up to 4 (a
+dump of the complete conversation, not recommended).
+
+As a first step set smpt[d]_tls_loglevel=2
and
+watch the logfile. Typically you will have problems with the access
+to the keys or certificates, so you will find error messages
+here.
+
+You can always try to send an email to
+postfix_tls-bounce@serv01.aet.tu-cottbus.de with TLS enabled
+at your side and watch, what is going to happen :-)
+
+While testing the interoperability with ZMailer we learned, that
+an incorrect certificate type (must be server for the server :-)
+can lead to connection failures without clear symptoms. It helps to
+use Netscape 4.5x as a client and carefully study the message boxes
+and certificate information. I have yet to find out how to identify
+this problem from postfix to print a suitable warning to the
+logfile. Hopefully it will be possible without changes in the
+OpenSSL library.
+
+Platforms
+
+
+- Development Platform:
+
+
+- OS: HP-UX 10.20
+
+- OS: Linux 2.x (SuSE Linux)
+
+
+
+- Reported Success:
+
+
+- OS: Solaris 2.5 - Walcir Fontanini
+<walcir@densis.fee.unicamp.br>
+
+
+
+- Test Client:
+
+
+- Software: Netscape 4.5x, Netscape 4.6x, Netscape 4.7x
+
+- OS: HP-UX 10.20, Linux 2.x, Win95
+
+
+
+
+Please don't comment on the stability of Netscape, especially not
+on HP-UX...
+
+Interoperability
+
+Besides support by generic wrapper solutions, there exist specially
+crafted extensions for other MTAs:
+
+
+- Qmail There is an OpenSource patch available,
+extending the Qmail [QMAIL] MTA
+to support RFC2487, written by Frederik Vermeulen [QMAILTLS]. Sending and receiving is
+working from both sides.
+
+
Testing: send mail to ping@linux.student.kuleuven.ac.be
+(will send back complete email including headers).
+
+
+- Zmailer The author/maintainer of ZMailer,
+Matti Aarnio, has incorporated both server and client side TLS
+support [ZMAILER].
+
+
Zmailer -> Postfix works fine,
+Postfix -> Zmailer does not work, since ESMTP is not recognized
+(problem reported).
+
+Testing: send mail to autoanswer@mea.tmt.tele.fi (will
+send back headers).
+
+
+- Sendmail The commercial verson of sendmail
+supports client and server TLS, both sides interoperating with
+Postfix/TLS. As of sendmail-8.11, TLS is also included with the
+opensource version [SENDMAIL].
+
+
Testing: send mail to bounce@esmtp.org (will bounce
+error message including old headers).
+
+
+- Postfix Can send emails to itself :-).
+
+
Testing: send mail to
+postfix_tls-bounce@serv01.aet.tu-cottbus.de (will bounce back,
+includes old headers).
+
+
+
+Other reports are welcome.
+
+Known bugs
+
+This software is just at the beginning, so please be patient. By
+now I have these points:
+
+
+
+
+
diff -Nur snapshot-20010228-orig/man/man8/tlsmgr.8 snapshot-20010228/man/man8/tlsmgr.8
--- snapshot-20010228-orig/man/man8/tlsmgr.8 Thu Jan 1 01:00:00 1970
+++ snapshot-20010228/man/man8/tlsmgr.8 Wed Mar 21 13:32:23 2001
@@ -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 -Nur snapshot-20010228-orig/src/global/Makefile.in snapshot-20010228/src/global/Makefile.in
--- snapshot-20010228-orig/src/global/Makefile.in Wed Mar 21 13:26:24 2001
+++ snapshot-20010228/src/global/Makefile.in Wed Mar 21 13:32:23 2001
@@ -18,7 +18,8 @@
sent.c smtp_stream.c split_addr.c string_list.c sys_exits.c \
timed_ipc.c tok822_find.c tok822_node.c tok822_parse.c \
tok822_resolve.c tok822_rewrite.c tok822_tree.c xtext.c bounce_log.c \
- flush_clnt.c mail_conf_time.c mbox_conf.c mbox_open.c abounce.c
+ flush_clnt.c mail_conf_time.c mbox_conf.c mbox_open.c abounce.c \
+ pfixtls.c
OBJS = been_here.o bounce.o canon_addr.o cleanup_strerror.o clnt_stream.o \
debug_peer.o debug_process.o defer.o deliver_completed.o \
deliver_flock.o deliver_pass.o deliver_request.o domain_list.o \
@@ -38,7 +39,8 @@
sent.o smtp_stream.o split_addr.o string_list.o sys_exits.o \
timed_ipc.o tok822_find.o tok822_node.o tok822_parse.o \
tok822_resolve.o tok822_rewrite.o tok822_tree.o xtext.o bounce_log.o \
- flush_clnt.o mail_conf_time.o mbox_conf.o mbox_open.o abounce.o
+ flush_clnt.o mail_conf_time.o mbox_conf.o mbox_open.o abounce.o \
+ pfixtls.o
HDRS = been_here.h bounce.h canon_addr.h cleanup_user.h clnt_stream.h \
config.h debug_peer.h debug_process.h defer.h deliver_completed.h \
deliver_flock.h deliver_pass.h deliver_request.h domain_list.h \
@@ -54,7 +56,7 @@
recipient_list.h record.h resolve_clnt.h resolve_local.h \
rewrite_clnt.h sent.h smtp_stream.h split_addr.h string_list.h \
sys_exits.h timed_ipc.h tok822.h xtext.h bounce_log.h flush_clnt.h \
- mbox_conf.h mbox_open.h abounce.h
+ mbox_conf.h mbox_open.h abounce.h pfixtls.h
TESTSRC = rec2stream.c stream2rec.c recdump.c
WARN = -W -Wformat -Wimplicit -Wmissing-prototypes \
-Wparentheses -Wstrict-prototypes -Wswitch -Wuninitialized \
@@ -1039,3 +1041,14 @@
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: mail_params.h
+pfixtls.o: pfixtls.h
diff -Nur snapshot-20010228-orig/src/global/mail_params.c snapshot-20010228/src/global/mail_params.c
--- snapshot-20010228-orig/src/global/mail_params.c Wed Mar 21 13:26:24 2001
+++ snapshot-20010228/src/global/mail_params.c Wed Mar 21 13:32:23 2001
@@ -174,6 +174,31 @@
char *var_fflush_domains;
char *var_def_transport;
char *var_mynetworks_style;
+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;
@@ -293,6 +318,26 @@
VAR_IMPORT_ENVIRON, DEF_IMPORT_ENVIRON, &var_import_environ, 0, 0,
VAR_DEF_TRANSPORT, DEF_DEF_TRANSPORT, &var_def_transport, 0, 0,
VAR_MYNETWORKS_STYLE, DEF_MYNETWORKS_STYLE, &var_mynetworks_style, 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[] = {
@@ -307,6 +352,9 @@
VAR_HASH_QUEUE_DEPTH, DEF_HASH_QUEUE_DEPTH, &var_hash_queue_depth, 1, 0,
VAR_FORK_TRIES, DEF_FORK_TRIES, &var_fork_tries, 1, 0,
VAR_FLOCK_TRIES, DEF_FLOCK_TRIES, &var_flock_tries, 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[] = {
@@ -317,6 +365,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,
0,
};
diff -Nur snapshot-20010228-orig/src/global/mail_params.h snapshot-20010228/src/global/mail_params.h
--- snapshot-20010228-orig/src/global/mail_params.h Wed Mar 21 13:26:24 2001
+++ snapshot-20010228/src/global/mail_params.h Wed Mar 21 13:32:23 2001
@@ -430,6 +430,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.
*/
@@ -647,6 +675,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;
@@ -699,6 +731,10 @@
#define DEF_SMTP_BIND_ADDR ""
extern char *var_smtp_bind_addr;
+#define VAR_SMTP_ALWAYS_EHLO "smtp_always_send_ehlo"
+#define DEF_SMTP_ALWAYS_EHLO 0
+extern bool var_smtp_always_ehlo;
+
/*
* SMTP server. The soft error limit determines how many errors an SMTP
* client may make before we start to slow down; the hard error limit
@@ -712,6 +748,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;
@@ -732,6 +772,146 @@
#define DEF_SMTPD_JUNK_CMD 1000
extern int var_smtpd_junk_cmd_limit;
+#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_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.
*/
@@ -1007,6 +1187,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;
@@ -1086,6 +1270,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 -Nur snapshot-20010228-orig/src/global/mail_proto.h snapshot-20010228/src/global/mail_proto.h
--- snapshot-20010228-orig/src/global/mail_proto.h Wed Mar 21 13:26:24 2001
+++ snapshot-20010228/src/global/mail_proto.h Wed Mar 21 13:32:23 2001
@@ -33,6 +33,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 -Nur snapshot-20010228-orig/src/global/pfixtls.c snapshot-20010228/src/global/pfixtls.c
--- snapshot-20010228-orig/src/global/pfixtls.c Thu Jan 1 01:00:00 1970
+++ snapshot-20010228/src/global/pfixtls.c Wed Mar 21 13:32:23 2001
@@ -0,0 +1,2786 @@
+/*++
+/* NAME
+/* pfixtls
+/* SUMMARY
+/* interface to openssl routines
+/* SYNOPSIS
+/* #include
+/*
+/* const long scache_db_version;
+/* const long openssl_version;
+/*
+/* int pfixtls_serverengine;
+/* int pfixtls_serveractive;
+/* int pfixtls_peer_verified;
+/* char *pfixtls_peer_subject;
+/* char *pfixtls_peer_issuer;
+/* char *pfixtls_peer_fingerprint;
+/*
+/* int pfixtls_clientengine;
+/* int pfixtls_clientactive;
+/*
+/* char *pfixtls_peer_CN;
+/* char *pfixtls_issuer_CN;
+/* char *pfixtls_protocol;
+/* const char *pfixtls_cipher_name;
+/* int pfixtls_cipher_usebits;
+/* int pfixtls_cipher_algbits;
+/*
+/* int pfixtls_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
+#include
+#include
+#include /* gettimeofday, not in POSIX */
+#include
+#include
+#include
+#include
+
+/* Utility library. */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+/* 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, NULL, 0, 0
+};
+
+#ifdef HAS_SSL
+
+/* OpenSSL library. */
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+/* 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
+ * can be up to 64 bytes long.
+ * The length of the actual session id is however not defined in RFC2246.
+ * OpenSSL defines a SSL_MAX_SSL_SESSION_ID_LENGTH of 32, but nobody
+ * guarantees, that a client might not try to resume a session with a longer
+ * session id. So to make sure, we define an upper bound of 64.
+ */
+static const int 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 do_dump = 0;
+static DH *dh_512 = NULL, *dh_1024 = NULL;
+static SSL_CTX *ctx = NULL;
+
+static rand_exch_fd = -1;
+
+static DICT *scache_db = NULL;
+const long scache_db_version = 0x00000002L;
+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;
+static struct stat seedfile_stat;
+
+/*
+ * 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 *, char *, int),
+ int (*wfunc)(SSL *, const char *, int),
+ char *buf, int num)
+{
+ int status;
+ int err;
+ int retval;
+ 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;
+ X509 *err_cert;
+ int err;
+ int depth;
+ int verify_depth;
+ 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) && TLScontext->enforce_verify_errors
+ && TLScontext->enforce_CN) {
+ X509_NAME_get_text_by_NID(X509_get_subject_name(err_cert),
+ NID_commonName, buf, 256);
+ CN_lowercase = lowercase(buf);
+ if (strcmp(TLScontext->peername_save, CN_lowercase)) {
+ err = X509_V_ERR_CERT_REJECTED;
+ X509_STORE_CTX_set_error(ctx, err);
+ msg_info("Verify failure: CommonName in certificate does not match: %s != %s", CN_lowercase, TLScontext->peername_save);
+ 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 - \n", len + trunc);
+ msg_info("%s", buf);
+ ret += strlen(buf);
+ }
+#endif
+ return (ret);
+}
+
+
+
+/* taken from OpenSSL apps/s_cb.c */
+
+static long bio_dump_cb(BIO * bio, int cmd, const char *argp, int argi,
+ long argl, long ret)
+{
+ if (!do_dump)
+ return (ret);
+
+ if (cmd == (BIO_CB_READ | BIO_CB_RETURN)) {
+ msg_info("read from %08X [%08lX] (%d bytes => %ld (0x%X))", bio, argp,
+ argi, ret, ret);
+ pfixtls_dump(argp, (int) ret);
+ return (ret);
+ } else if (cmd == (BIO_CB_WRITE | BIO_CB_RETURN)) {
+ msg_info("write to %08X [%08lX] (%d bytes => %ld (0x%X))", bio, argp,
+ argi, ret, ret);
+ pfixtls_dump(argp, (int) ret);
+ }
+ return (ret);
+}
+
+
+ /*
+ * Callback to retrieve a session from the external session cache.
+ */
+static SSL_SESSION *get_session_cb(SSL *ssl, unsigned char *SessionID,
+ int length, int *copy)
+{
+ SSL_SESSION *session;
+ char *idstring;
+ 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;
+
+ idstring = (char *)mymalloc(2 * uselength + 1);
+ if (!idstring) {
+ msg_info("could not allocate memory for IDstring");
+ return (NULL);
+ }
+
+ for(n=0 ; n < uselength ; n++)
+ sprintf(idstring + 2 * n, "%02x", SessionID[n]);
+ if (var_smtpd_tls_loglevel >= 3)
+ msg_info("Trying to reload Session from disc: %s", idstring);
+
+ 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");
+ myfree(idstring);
+ 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");
+
+ myfree(idstring);
+ return (session);
+}
+
+
+static SSL_SESSION *load_clnt_session(const char *hostname)
+{
+ SSL_SESSION *session = NULL;
+ char *idstring;
+ 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;
+
+ idstring = (char *)mymalloc(uselength + 1);
+ if (!idstring) {
+ msg_info("could not allocate memory for IDstring");
+ return (NULL);
+ }
+
+ 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");
+ myfree(idstring);
+ 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(time_t));
+ if (!session)
+ pfixtls_print_errors();
+ }
+ myfree((char *)data);
+ }
+
+ myfree(idstring);
+
+ if (session && (var_smtp_tls_loglevel >= 3))
+ msg_info("Successfully reloaded session from disc");
+
+ return (session);
+}
+
+
+static void remove_srvr_session(unsigned char *SessionID, int length)
+{
+ VSTRING *buf;
+ char *idstring;
+ int n;
+ int uselength;
+
+ if (length > id_maxlength)
+ uselength = id_maxlength; /* Limit length of ID */
+ else
+ uselength = length;
+
+ idstring = (char *)mymalloc(2 * uselength + 1);
+ if (!idstring) {
+ msg_info("could not allocate memory for IDstring");
+ return;
+ }
+
+ for(n=0 ; n < uselength ; n++)
+ sprintf(idstring + 2 * n, "%02x", SessionID[n]);
+ if (var_smtpd_tls_loglevel >= 3)
+ msg_info("Trying to remove session from disc: %s", idstring);
+
+ if (scache_db)
+ dict_del(scache_db, idstring);
+
+ myfree(idstring);
+}
+
+
+static void remove_clnt_session(const char *hostname)
+{
+ char *idstring;
+ int n;
+ int uselength;
+ int length;
+
+ length = strlen(hostname);
+ if (length > id_maxlength)
+ uselength = id_maxlength; /* Limit length of ID */
+ else
+ uselength = length;
+
+ idstring = (char *)mymalloc(uselength + 1);
+ if (!idstring) {
+ msg_info("could not allocate memory for IDstring");
+ return;
+ }
+
+ for(n=0 ; n < uselength ; n++)
+ idstring[n] = tolower(hostname[n]);
+ idstring[uselength] = '\0';
+ if (var_smtp_tls_loglevel >= 3)
+ msg_info("Trying to remove session from disc: %s", idstring);
+ if (scache_db)
+ dict_del(scache_db, idstring);
+ myfree(idstring);
+}
+
+
+ /*
+ * Save a new session to the external cache
+ */
+static int new_session_cb(SSL *ssl, SSL_SESSION *session)
+{
+ char *idstring;
+ int n;
+ int uselength;
+ int dsize;
+ int len;
+ unsigned char *data, *sess_data;
+ pfixtls_scache_info_t scache_info;
+ char *hexdata;
+
+ if (session->session_id_length > id_maxlength)
+ uselength = id_maxlength; /* Limit length of ID */
+ else
+ uselength = session->session_id_length;
+
+ idstring = (char *)mymalloc(2 * uselength + 1);
+ if (!idstring) {
+ msg_info("could not allocate memory for IDstring");
+ return 0;
+ }
+
+ for(n=0 ; n < uselength ; n++)
+ sprintf(idstring + 2 * n, "%02x", session->session_id[n]);
+
+ if (var_smtpd_tls_loglevel >= 3)
+ msg_info("Trying to save Session to disc: %s", idstring);
+
+ /*
+ * 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");
+ myfree(idstring);
+ 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);
+ myfree(idstring);
+ 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);
+ myfree(idstring);
+ return (0);
+}
+
+
+ /*
+ * Save the new session to the external cache. As the HostID is given
+ * by the contacted peer, we may have several negotiations going on at
+ * the same time for the same peer. This is not purely hypothetical but
+ * quite likely if several jobs to the same recipient host are in the queue
+ * and a queue run is started. So we have to take care of race conditions.
+ * As I consider the TLS-SessionID to be unique, we will first try to
+ * create a file with the actual SessionID. Once the writing is finished,
+ * the file is closed and moved to its final name. This way we should be
+ * able to deal with race conditions, since rename should be atomic.
+ * If the rename fails for some reason, we will just silently remove
+ * the temporary file and forget about the session.
+ */
+static void save_clnt_session(SSL_SESSION *session, const char *hostname)
+{
+ char *idstring;
+ int length;
+ int uselength;
+ int n;
+ int len;
+ int dsize;
+ unsigned char *data, *sess_data;
+ pfixtls_scache_info_t scache_info;
+ char *hexdata;
+
+ length = strlen(hostname);
+ if (length > id_maxlength)
+ uselength = id_maxlength; /* Limit length of ID */
+ else
+ uselength = length;
+
+ idstring = (char *)mymalloc(uselength + 1);
+ if (!idstring) {
+ msg_info("could not allocate memory for IDstring");
+ return;
+ }
+
+ for(n=0 ; n < uselength ; n++)
+ idstring[n] = tolower(hostname[n]);
+ idstring[uselength] = '\0';
+ if (var_smtp_tls_loglevel >= 3)
+ msg_info("Trying to save session for hostID 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;
+ }
+ data = (unsigned char *)mymalloc(dsize + sizeof(pfixtls_scache_info_t));
+ if (!data) {
+ msg_info("could not allocate memory for SSL session");
+ myfree(idstring);
+ return;
+ }
+
+ /*
+ * 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);
+ myfree(idstring);
+ return;
+ }
+ 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);
+ myfree(idstring);
+}
+
+ /*
+ * 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_sess_set_get_cb(ctx, get_session_cb);
+ SSL_CTX_sess_set_new_cb(ctx, new_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();
+ session = SSL_get_session(TLScontext->con);
+ if (session && scache_db) {
+ remove_srvr_session(session->session_id,
+ session->session_id_length);
+ SSL_CTX_remove_session(ctx, session);
+ if (var_smtpd_tls_loglevel >= 2)
+ msg_info("SSL session removed");
+ }
+ 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;
+ }
+ X509_NAME_get_text_by_NID(X509_get_subject_name(peer),
+ NID_commonName, TLScontext->peer_CN, CCERT_BUFSIZ);
+ tls_info->peer_CN = TLScontext->peer_CN;
+ X509_NAME_get_text_by_NID(X509_get_issuer_name(peer),
+ NID_commonName, TLScontext->issuer_CN, CCERT_BUFSIZ);
+ if (var_smtpd_tls_loglevel >= 1) {
+ if (tls_info->peer_verified)
+ msg_info("Verified: subject_CN=%s, issuer_CN=%s",
+ TLScontext->peer_CN, TLScontext->issuer_CN);
+ else
+ msg_info("Unverified: subject_CN=%s, issuer_CN=%s",
+ TLScontext->peer_CN, TLScontext->issuer_CN);
+ }
+ tls_info->issuer_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");
+ remove_srvr_session(session->session_id,
+ session->session_id_length);
+ 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)
+{
+ SSL_SESSION *session;
+ TLScontext_t *TLScontext;
+
+ if (pfixtls_serveractive) {
+ TLScontext = (TLScontext_t *)vstream_context(stream);
+ session = SSL_get_session(TLScontext->con);
+ do_tls_operation(vstream_fileno(stream), timeout, TLScontext,
+ SSL_shutdown, NULL, NULL, NULL, 0);
+ if (session) {
+ if (failure && scache_db) {
+ remove_srvr_session(session->session_id,
+ session->session_id_length);
+ if (var_smtpd_tls_loglevel >= 2)
+ msg_info("SSL session removed");
+ }
+ SSL_CTX_remove_session(ctx, session);
+ }
+ /*
+ * 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);
+ }
+
+ /*
+ * 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_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;
+ int j;
+ unsigned int n;
+ SSL_SESSION *session, *old_session;
+ SSL_CIPHER *cipher;
+ X509 *peer;
+ int save_session;
+ int length;
+ int verify_flags;
+ char *lowercase_CN;
+ unsigned char *old_session_id;
+ 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_id = NULL; /* make sure no old info is kept */
+ old_session = NULL;
+
+ /*
+ * Find out the hashed HostID for the client cache and try to
+ * load the session from the cache.
+ * "old_session_id" holds the session ID of the reloaded session, so that
+ * we can later check, whether it is really reused.
+ */
+ strncpy(TLScontext->peername_save, peername, 128);
+ TLScontext->peername_save[128] = '\0'; /* in case name is too long */
+ if (scache_db) {
+ old_session = load_clnt_session(peername);
+ if (old_session) {
+ SSL_set_session(TLScontext->con, old_session);
+ old_session_id =
+ (unsigned char *)mymemdup((char *)old_session->session_id,
+ old_session->session_id_length);
+#if (OPENSSL_VERSION_NUMBER < 0x00906011L) || (OPENSSL_VERSION_NUMBER == 0x00907000L)
+ /*
+ * Ugly Hack: OpenSSL before 0.9.6a (if ever released) 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) {
+ if (scache_db)
+ remove_clnt_session(peername);
+ SSL_CTX_remove_session(ctx, session);
+ if (var_smtp_tls_loglevel >= 2)
+ msg_info("SSL session removed");
+ }
+ if ((old_session) && (session != old_session))
+ SSL_SESSION_free(old_session); /* Must also be removed */
+ SSL_free(TLScontext->con);
+ myfree((char *)TLScontext);
+ return (-1);
+ }
+
+ /*
+ * Now we must save the new session to disk, if necessary. If we had
+ * an old session, its ID was saved in "old_session_id" for comparison.
+ */
+ session = SSL_get_session(TLScontext->con);
+ if (session && scache_db) {
+ save_session = 1;
+ if (old_session_id) {
+ if (memcmp(session->session_id, old_session_id,
+ session->session_id_length) == 0) {
+ if (var_smtp_tls_loglevel >= 3)
+ msg_info("Reusing old session");
+ save_session = 0;
+ }
+ myfree((char *)old_session_id);
+ }
+ if (save_session) {
+#if (OPENSSL_VERSION_NUMBER < 0x00906011L) || (OPENSSL_VERSION_NUMBER == 0x00907000L)
+ /*
+ * Ugly Hack: OpenSSL before 0.9.6a (if ever released) 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
+ save_clnt_session(session, peername);
+ }
+ }
+ if ((old_session) && (session != old_session))
+ SSL_SESSION_free(old_session); /* Remove unused 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;
+
+ X509_NAME_get_text_by_NID(X509_get_subject_name(peer),
+ NID_commonName, TLScontext->peer_CN, CCERT_BUFSIZ);
+ tls_info->peer_CN = TLScontext->peer_CN;
+ X509_NAME_get_text_by_NID(X509_get_issuer_name(peer),
+ NID_commonName, TLScontext->issuer_CN, CCERT_BUFSIZ);
+ if (var_smtp_tls_loglevel >= 1) {
+ if (tls_info->peer_verified)
+ msg_info("Verified: subject_CN=%s, issuer_CN=%s",
+ TLScontext->peer_CN, TLScontext->issuer_CN);
+ else
+ msg_info("Unverified: subject_CN=%s, issuer_CN=%s",
+ TLScontext->peer_CN, TLScontext->issuer_CN);
+ }
+ tls_info->issuer_CN = TLScontext->issuer_CN;
+ X509_free(peer);
+ }
+
+ /*
+ * At this point the CN should already match the peername, if enforced.
+ * 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 (enforce_peername) {
+ if (!tls_info->peer_verified || !tls_info->peer_CN) {
+ msg_info("Removed session without verifiable peername");
+ remove_clnt_session(peername);
+ return (-1);
+ }
+ lowercase_CN = lowercase(mystrdup(tls_info->peer_CN));
+ if (strcmp(peername, lowercase_CN)) {
+ msg_info("Removed session without non-matching peername");
+ remove_clnt_session(peername);
+ myfree(lowercase_CN);
+ return (-1);
+ }
+ myfree(lowercase_CN);
+ }
+
+ /*
+ * 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)
+{
+ SSL_SESSION *session;
+ TLScontext_t *TLScontext;
+
+ if (pfixtls_clientactive) {
+ TLScontext = (TLScontext_t *)vstream_context(stream);
+ session = SSL_get_session(TLScontext->con);
+ do_tls_operation(vstream_fileno(stream), timeout, TLScontext,
+ SSL_shutdown, NULL, NULL, NULL, 0);
+ if (session) {
+ if (failure && scache_db) {
+ remove_clnt_session(TLScontext->peername_save);
+ if (var_smtp_tls_loglevel >= 2)
+ msg_info("SSL session removed");
+ }
+ }
+ /*
+ * 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 -Nur snapshot-20010228-orig/src/global/pfixtls.h snapshot-20010228/src/global/pfixtls.h
--- snapshot-20010228-orig/src/global/pfixtls.h Thu Jan 1 01:00:00 1970
+++ snapshot-20010228/src/global/pfixtls.h Wed Mar 21 13:32:23 2001
@@ -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;
+ char **dNSName;
+ 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... */
+} 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 -Nur snapshot-20010228-orig/src/smtp/Makefile.in snapshot-20010228/src/smtp/Makefile.in
--- snapshot-20010228-orig/src/smtp/Makefile.in Wed Mar 21 13:26:25 2001
+++ snapshot-20010228/src/smtp/Makefile.in Wed Mar 21 13:32:23 2001
@@ -81,6 +81,7 @@
smtp.o: ../../include/mail_proto.h
smtp.o: ../../include/iostuff.h
smtp.o: ../../include/mail_server.h
+smtp.o: ../../include/pfixtls.h
smtp.o: smtp.h
smtp.o: smtp_sasl.h
smtp_addr.o: smtp_addr.c
@@ -99,6 +100,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
@@ -119,6 +121,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,6 +142,7 @@
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
@@ -163,6 +167,7 @@
smtp_proto.o: ../../include/off_cvt.h
smtp_proto.o: ../../include/mark_corrupt.h
smtp_proto.o: ../../include/quote_821_local.h
+smtp_proto.o: ../../include/pfixtls.h
smtp_proto.o: smtp.h
smtp_proto.o: ../../include/argv.h
smtp_proto.o: smtp_sasl.h
@@ -206,9 +211,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
@@ -220,6 +228,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
@@ -239,6 +248,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
@@ -251,3 +261,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 -Nur snapshot-20010228-orig/src/smtp/smtp.c snapshot-20010228/src/smtp/smtp.c
--- snapshot-20010228-orig/src/smtp/smtp.c Wed Mar 21 13:26:25 2001
+++ snapshot-20010228/src/smtp/smtp.c Wed Mar 21 13:32:23 2001
@@ -211,6 +211,7 @@
#include
#include
#include
+#include
/* Single server skeleton. */
@@ -227,6 +228,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;
@@ -250,6 +252,12 @@
char *var_smtp_sasl_passwd;
bool var_smtp_sasl_enable;
char *var_smtp_bind_addr;
+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
@@ -367,6 +375,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 */
@@ -402,6 +418,7 @@
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,
+ VAR_SMTP_TLS_PER_SITE, DEF_SMTP_TLS_PER_SITE, &var_smtp_tls_per_site, 0, 0,
0,
};
static CONFIG_TIME_TABLE time_table[] = {
@@ -413,10 +430,12 @@
VAR_SMTP_DATA1_TMOUT, DEF_SMTP_DATA1_TMOUT, &var_smtp_data1_tmout, 1, 0,
VAR_SMTP_DATA2_TMOUT, DEF_SMTP_DATA2_TMOUT, &var_smtp_data2_tmout, 1, 0,
VAR_SMTP_QUIT_TMOUT, DEF_SMTP_QUIT_TMOUT, &var_smtp_quit_tmout, 1, 0,
+ VAR_SMTP_STARTTLS_TMOUT, DEF_SMTP_STARTTLS_TMOUT, &var_smtp_starttls_tmout, 1, 0,
0,
};
static CONFIG_INT_TABLE int_table[] = {
VAR_DEBUG_PEER_LEVEL, DEF_DEBUG_PEER_LEVEL, &var_debug_peer_level, 1, 0,
+ VAR_SMTP_TLS_SCERT_VD, DEF_SMTP_TLS_SCERT_VD, &var_smtp_tls_scert_vd, 0, 0,
0,
};
static CONFIG_BOOL_TABLE bool_table[] = {
@@ -427,6 +446,10 @@
VAR_SMTP_ALWAYS_EHLO, DEF_SMTP_ALWAYS_EHLO, &var_smtp_always_ehlo,
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_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 -Nur snapshot-20010228-orig/src/smtp/smtp.h snapshot-20010228/src/smtp/smtp.h
--- snapshot-20010228-orig/src/smtp/smtp.h Wed Mar 21 13:26:25 2001
+++ snapshot-20010228/src/smtp/smtp.h Wed Mar 21 13:32:23 2001
@@ -27,6 +27,7 @@
* Global library.
*/
#include
+#include
/*
* State information associated with each SMTP delivery. We're bundling the
@@ -75,9 +76,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 -Nur snapshot-20010228-orig/src/smtp/smtp_connect.c snapshot-20010228/src/smtp/smtp_connect.c
--- snapshot-20010228-orig/src/smtp/smtp_connect.c Wed Mar 21 13:26:25 2001
+++ snapshot-20010228/src/smtp/smtp_connect.c Wed Mar 21 13:32:23 2001
@@ -116,6 +116,7 @@
#include
#include
+#include
/* DNS library. */
@@ -128,7 +129,7 @@
/* 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";
@@ -262,7 +263,7 @@
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, inet_ntoa(sin.sin_addr)));
}
/* smtp_connect_host - direct connection to host */
@@ -280,7 +281,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 +310,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;
}
diff -Nur snapshot-20010228-orig/src/smtp/smtp_proto.c snapshot-20010228/src/smtp/smtp_proto.c
--- snapshot-20010228-orig/src/smtp/smtp_proto.c Wed Mar 21 13:26:25 2001
+++ snapshot-20010228/src/smtp/smtp_proto.c Wed Mar 21 13:32:23 2001
@@ -99,6 +99,7 @@
#include
#include
#include
+#include
/* Application-specific. */
@@ -153,6 +154,8 @@
char *words;
char *word;
int n;
+ int oldfeatures;
+ int rval;
/*
* Prepare for disaster.
@@ -206,7 +209,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
@@ -215,6 +219,7 @@
* advertises a really huge message size limit.
*/
lines = resp->str;
+ oldfeatures = state->features; /* remember */
while ((words = mystrtok(&lines, "\n")) != 0) {
if (mystrtok(&words, "- ") && (word = mystrtok(&words, " \t")) != 0) {
if (strcasecmp(word, "8BITMIME") == 0)
@@ -223,6 +228,8 @@
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);
@@ -241,6 +248,129 @@
if (msg_verbose)
msg_info("server features: 0x%x", state->features);
+#ifdef HAS_SSL
+ if ((state->features & SMTP_FEATURE_STARTTLS) &&
+ (var_smtp_tls_note_starttls_offer) &&
+ (!(session->tls_enforce_tls || session->tls_use_tls)))
+ msg_info("Host offered STARTTLS: [%s]", session->host);
+ if ((session->tls_enforce_tls) &&
+ !(state->features & SMTP_FEATURE_STARTTLS))
+ {
+ /*
+ * We are enforced to use TLS but it is not offered, so we will give
+ * up on this host. We won't even try STARTTLS, because we could
+ * receive a "500 command unrecognized" which would bounce the
+ * message. We instead want to delay until STARTTLS becomes
+ * available.
+ */
+ return (smtp_site_fail(state, 450, "Could not start TLS: 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. For logging, we
+ * will check the CommonName against the name of the host.
+ * 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"));
+ }
+ }
+ if (strcasecmp(session->tls_info.peer_CN, session->host)) {
+ msg_info("Hostname/Certificate mismatch: %s != %s",
+ session->host, session->tls_info.peer_CN);
+ } else if (msg_verbose) {
+ msg_info("Match: %s == %s", session->tls_info.peer_CN,
+ session->host);
+ }
+ } 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;
+ 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 -Nur snapshot-20010228-orig/src/smtp/smtp_session.c snapshot-20010228/src/smtp/smtp_session.c
--- snapshot-20010228-orig/src/smtp/smtp_session.c Wed Mar 21 13:26:25 2001
+++ snapshot-20010228/src/smtp/smtp_session.c Wed Mar 21 13:32:23 2001
@@ -42,15 +42,42 @@
#include
#include
+#include
+#include
+#include
+
/* 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) || 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) || 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 -Nur snapshot-20010228-orig/src/smtpd/Makefile.in snapshot-20010228/src/smtpd/Makefile.in
--- snapshot-20010228-orig/src/smtpd/Makefile.in Wed Mar 21 13:26:25 2001
+++ snapshot-20010228/src/smtpd/Makefile.in Wed Mar 21 13:32:23 2001
@@ -120,6 +120,7 @@
smtpd.o: ../../include/tok822.h
smtpd.o: ../../include/resolve_clnt.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
@@ -147,6 +148,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
@@ -177,6 +179,7 @@
smtpd_check.o: ../../include/mail_conf.h
smtpd_check.o: ../../include/maps.h
smtpd_check.o: ../../include/mail_addr_find.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
@@ -193,6 +196,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
@@ -243,6 +247,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
@@ -252,3 +257,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 -Nur snapshot-20010228-orig/src/smtpd/smtpd.c snapshot-20010228/src/smtpd/smtpd.c
--- snapshot-20010228-orig/src/smtpd/smtpd.c Wed Mar 21 13:26:25 2001
+++ snapshot-20010228/src/smtpd/smtpd.c Wed Mar 21 13:32:23 2001
@@ -283,6 +283,7 @@
#include
#include
#include
+#include
/* Single-threaded server skeleton. */
@@ -307,6 +308,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 */
@@ -350,6 +352,14 @@
char *var_smtpd_sasl_realm;
char *var_filter_xport;
bool var_broken_auth_clients;
+int var_smtpd_starttls_tmout;
+int var_smtpd_tls_wrappermode;
+int var_smtpd_use_tls;
+int var_smtpd_enforce_tls;
+int var_smtpd_tls_ask_ccert;
+int var_smtpd_tls_req_ccert;
+int var_smtpd_tls_ccert_vd;
+int var_smtpd_tls_received_header;
/*
* Global state, for stand-alone mode queue file cleanup. When this is
@@ -445,11 +455,21 @@
else
smtpd_chat_reply(state, "250-SIZE");
smtpd_chat_reply(state, "250-ETRN");
+#ifdef HAS_SSL
+ if ((state->tls_use_tls || state->tls_enforce_tls) && (!state->tls_active))
+ smtpd_chat_reply(state, "250-STARTTLS");
+#endif
#ifdef USE_SASL_AUTH
if (var_smtpd_sasl_enable) {
+#ifdef HAS_SSL
+ if (!state->tls_enforce_tls || 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 8BITMIME");
@@ -807,11 +827,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;
@@ -846,6 +931,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 CN \"%s\" (verified OK))",
+ peer_CN, issuer_CN);
+ else
+ rec_fprintf(state->cleanup, REC_TYPE_NORM,
+ "\t(Client CN \"%s\", Issuer CN \"%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",
@@ -1144,6 +1258,77 @@
return (0);
}
+static int starttls_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
+{
+ char *err;
+
+#ifdef HAS_SSL
+ if (argc != 1) {
+ state->error_mask |= MAIL_ERROR_PROTOCOL;
+ smtpd_chat_reply(state, "501 Syntax: STARTTLS");
+ return (-1);
+ }
+ if (state->tls_active != 0) {
+ state->error_mask |= MAIL_ERROR_PROTOCOL;
+ smtpd_chat_reply(state, "554 Error: TLS already active");
+ return (-1);
+ }
+ if (state->tls_use_tls == 0) {
+ state->error_mask |= MAIL_ERROR_PROTOCOL;
+ smtpd_chat_reply(state, "502 Error: command not implemented");
+ return (-1);
+ }
+ if (!pfixtls_serverengine) {
+ smtpd_chat_reply(state, "454 TLS not available due to temporary reason");
+ 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
@@ -1161,6 +1346,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
@@ -1250,9 +1439,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++;
@@ -1289,6 +1497,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)
@@ -1321,6 +1530,36 @@
* machines.
*/
smtpd_state_init(&state, stream);
+#ifdef HAS_SSL
+ 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;
+ }
+#else
+ state.tls_use_tls = 0;
+ state.tls_enforce_tls = 0;
+#endif
+
+ /*
+ * Provide the SMTP service.
+ */
/*
* See if we need to turn on verbose logging for this client.
@@ -1338,10 +1577,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);
/*
@@ -1408,7 +1643,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.
@@ -1424,6 +1658,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 */
@@ -1446,10 +1686,12 @@
VAR_REJECT_CODE, DEF_REJECT_CODE, &var_reject_code, 0, 0,
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_TLS_CCERT_VD, DEF_SMTPD_TLS_CCERT_VD, &var_smtpd_tls_ccert_vd, 0, 0,
0,
};
static CONFIG_TIME_TABLE time_table[] = {
VAR_SMTPD_TMOUT, DEF_SMTPD_TMOUT, &var_smtpd_tmout, 1, 0,
+ VAR_SMTPD_STARTTLS_TMOUT, DEF_SMTPD_STARTTLS_TMOUT, &var_smtpd_starttls_tmout, 1, 0,
VAR_SMTPD_ERR_SLEEP, DEF_SMTPD_ERR_SLEEP, &var_smtpd_err_sleep, 0, 0,
0,
};
@@ -1461,6 +1703,12 @@
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_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[] = {
@@ -1485,6 +1733,7 @@
VAR_SMTPD_SASL_OPTS, DEF_SMTPD_SASL_OPTS, &var_smtpd_sasl_opts, 0, 0,
VAR_SMTPD_SASL_REALM, DEF_SMTPD_SASL_REALM, &var_smtpd_sasl_realm, 1, 0,
VAR_FILTER_XPORT, DEF_FILTER_XPORT, &var_filter_xport, 0, 0,
+ VAR_RELAY_CCERTS, DEF_RELAY_CCERTS, &var_relay_ccerts, 0, 0,
0,
};
@@ -1501,3 +1750,4 @@
MAIL_SERVER_PRE_ACCEPT, pre_accept,
0);
}
+
diff -Nur snapshot-20010228-orig/src/smtpd/smtpd.h snapshot-20010228/src/smtpd/smtpd.h
--- snapshot-20010228-orig/src/smtpd/smtpd.h Wed Mar 21 13:26:25 2001
+++ snapshot-20010228/src/smtpd/smtpd.h Wed Mar 21 13:32:23 2001
@@ -32,6 +32,7 @@
* Global library.
*/
#include
+#include
/*
* Variables that keep track of conversation state. There is only one SMTP
@@ -76,6 +77,10 @@
VSTRING *sasl_encoded;
VSTRING *sasl_decoded;
#endif
+ int tls_active;
+ int tls_use_tls;
+ int tls_enforce_tls;
+ tls_info_t tls_info;
} SMTPD_STATE;
extern void smtpd_state_init(SMTPD_STATE *, VSTREAM *);
diff -Nur snapshot-20010228-orig/src/smtpd/smtpd_check.c snapshot-20010228/src/smtpd/smtpd_check.c
--- snapshot-20010228-orig/src/smtpd/smtpd_check.c Wed Mar 21 13:26:25 2001
+++ snapshot-20010228/src/smtpd/smtpd_check.c Wed Mar 21 13:32:23 2001
@@ -268,6 +268,7 @@
#include
#include
+#include
#include
#include
#include
@@ -320,6 +321,9 @@
*/
static DOMAIN_LIST *relay_domains;
static NAMADR_LIST *mynetworks;
+#ifdef HAS_SSL
+static MAPS *relay_ccerts;
+#endif
/*
* Pre-parsed restriction lists.
@@ -445,6 +449,10 @@
*/
mynetworks = namadr_list_init(var_mynetworks);
relay_domains = domain_list_init(var_relay_domains);
+#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.
@@ -771,6 +779,36 @@
static int permit_auth_destination(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,
@@ -1673,6 +1711,12 @@
status = permit_sasl_auth(state,
SMTPD_CHECK_OK, SMTPD_CHECK_DUNNO);
#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,
@@ -2060,6 +2104,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 -Nur snapshot-20010228-orig/src/smtpd/smtpd_state.c snapshot-20010228/src/smtpd/smtpd_state.c
--- snapshot-20010228-orig/src/smtpd/smtpd_state.c Wed Mar 21 13:26:25 2001
+++ snapshot-20010228/src/smtpd/smtpd_state.c Wed Mar 21 13:32:23 2001
@@ -91,6 +91,10 @@
state->recursion = 0;
state->msg_size = 0;
state->junk_cmds = 0;
+ state->tls_active = 0;
+ state->tls_use_tls = 0;
+ state->tls_enforce_tls = 0;
+ state->tls_info = tls_info_zero;
#ifdef USE_SASL_AUTH
if (SMTPD_STAND_ALONE(state))
diff -Nur snapshot-20010228-orig/src/tlsmgr/Makefile.in snapshot-20010228/src/tlsmgr/Makefile.in
--- snapshot-20010228-orig/src/tlsmgr/Makefile.in Thu Jan 1 01:00:00 1970
+++ snapshot-20010228/src/tlsmgr/Makefile.in Wed Mar 21 13:32:23 2001
@@ -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 -Nur snapshot-20010228-orig/src/tlsmgr/tlsmgr.c snapshot-20010228/src/tlsmgr/tlsmgr.c
--- snapshot-20010228-orig/src/tlsmgr/tlsmgr.c Thu Jan 1 01:00:00 1970
+++ snapshot-20010228/src/tlsmgr/tlsmgr.c Wed Mar 21 13:32:23 2001
@@ -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
+#include
+#include
+#include
+#include
+#include
+#include /* gettimeofday, not POSIX */
+
+/* OpenSSL library. */
+#ifdef HAS_SSL
+#include /* For the PRNG */
+#endif
+
+/* Utility library. */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+/* Global library. */
+
+#include
+#include
+#include
+
+/* Master process interface */
+
+#include
+#include
+
+/* 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 -Nur snapshot-20010228-orig/src/util/Makefile.in snapshot-20010228/src/util/Makefile.in
--- snapshot-20010228-orig/src/util/Makefile.in Wed Mar 21 13:26:25 2001
+++ snapshot-20010228/src/util/Makefile.in Wed Mar 21 13:32:23 2001
@@ -23,7 +23,7 @@
clean_env.c watchdog.c spawn_command.c duplex_pipe.c sane_rename.c \
sane_link.c unescape.c timed_read.c timed_write.c dict_tcp.c \
hex_quote.c dict_alloc.c rand_sleep.c sane_time.c dict_debug.c \
- sane_socketpair.c
+ sane_socketpair.c dict_sdbm.c sdbm.c
OBJS = argv.o argv_split.o attr.o basename.o binhash.o chroot_uid.o \
close_on_exec.o concatenate.o dict.o dict_db.o dict_dbm.o \
dict_env.o dict_ht.o dict_ldap.o dict_mysql.o dict_ni.o dict_nis.o \
@@ -48,7 +48,7 @@
clean_env.o watchdog.o spawn_command.o duplex_pipe.o sane_rename.o \
sane_link.o unescape.o timed_read.o timed_write.o dict_tcp.o \
hex_quote.o dict_alloc.o rand_sleep.o sane_time.o dict_debug.o \
- sane_socketpair.o
+ sane_socketpair.o dict_sdbm.o sdbm.o
HDRS = argv.h attr.h binhash.h chroot_uid.h connect.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 dir_forest.h events.h \
@@ -64,7 +64,7 @@
vbuf.h vbuf_print.h vstream.h vstring.h vstring_vstream.h \
dict_unix.h dict_pcre.h dict_regexp.h mac_expand.h clean_env.h \
watchdog.h spawn_command.h sane_fsops.h dict_tcp.h hex_quote.h \
- sane_time.h sane_socketpair.h
+ sane_time.h sane_socketpair.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 \
@@ -469,6 +469,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
@@ -1051,3 +1052,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 -Nur snapshot-20010228-orig/src/util/dict_open.c snapshot-20010228/src/util/dict_open.c
--- snapshot-20010228-orig/src/util/dict_open.c Wed Mar 21 13:26:25 2001
+++ snapshot-20010228/src/util/dict_open.c Wed Mar 21 13:32:23 2001
@@ -157,6 +157,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -184,6 +185,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 -Nur snapshot-20010228-orig/src/util/dict_sdbm.c snapshot-20010228/src/util/dict_sdbm.c
--- snapshot-20010228-orig/src/util/dict_sdbm.c Thu Jan 1 01:00:00 1970
+++ snapshot-20010228/src/util/dict_sdbm.c Wed Mar 21 13:32:24 2001
@@ -0,0 +1,407 @@
+/*++
+/* NAME
+/* dict_sdbm 3
+/* SUMMARY
+/* dictionary manager interface to SDBM files
+/* SYNOPSIS
+/* #include
+/*
+/* 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
+#include
+#include
+
+/* 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->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->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->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->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->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->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->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->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.fd = sdbm_pagfno(dbm);
+ if (fstat(dict_sdbm->dict.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 -Nur snapshot-20010228-orig/src/util/dict_sdbm.h snapshot-20010228/src/util/dict_sdbm.h
--- snapshot-20010228-orig/src/util/dict_sdbm.h Thu Jan 1 01:00:00 1970
+++ snapshot-20010228/src/util/dict_sdbm.h Wed Mar 21 13:32:24 2001
@@ -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
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * Utility library.
+ */
+#include
+
+ /*
+ * 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 -Nur snapshot-20010228-orig/src/util/sdbm.c snapshot-20010228/src/util/sdbm.c
--- snapshot-20010228-orig/src/util/sdbm.c Thu Jan 1 01:00:00 1970
+++ snapshot-20010228/src/util/sdbm.c Wed Mar 21 13:32:24 2001
@@ -0,0 +1,939 @@
+/*++
+/* NAME
+/* sdbm 3h
+/* SUMMARY
+/* SDBM Simple DBM: ndbm work-alike hashed database library
+/* SYNOPSIS
+/* include "sdbm.h"
+/* DESCRIPTION
+/* .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
+#include
+#ifdef WIN32
+#include
+#include
+#else
+#include
+#endif
+#include
+#include
+#include
+#include
+#include
+#ifdef __STDC__
+#include
+#endif
+
+#include
+
+/*
+ * 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)
+ {
+ if (lseek (db->dirf, OFF_DIR (dirb), SEEK_SET) < 0
+ || read (db->dirf, db->dirbuf, DBLKSIZ) < 0)
+ return 0;
+ 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)
+ {
+ if (lseek (db->dirf, OFF_DIR (dirb), SEEK_SET) < 0
+ || read (db->dirf, db->dirbuf, DBLKSIZ) < 0)
+ return 0;
+ db->dirbno = dirb;
+ }
+
+ db->dirbuf[c % DBLKSIZ] |= (1 << dbit % BYTESIZ);
+
+ if (dbit >= db->maxbno)
+ db->maxbno += DBLKSIZ * BYTESIZ;
+
+ 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 -Nur snapshot-20010228-orig/src/util/sdbm.h snapshot-20010228/src/util/sdbm.h
--- snapshot-20010228-orig/src/util/sdbm.h Thu Jan 1 01:00:00 1970
+++ snapshot-20010228/src/util/sdbm.h Wed Mar 21 13:32:24 2001
@@ -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
+
+#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 -Nur snapshot-20010228-orig/src/util/vstream.c snapshot-20010228/src/util/vstream.c
--- snapshot-20010228-orig/src/util/vstream.c Wed Mar 21 13:26:27 2001
+++ snapshot-20010228/src/util/vstream.c Wed Mar 21 13:32:24 2001
@@ -72,6 +72,9 @@
/* int vstream_fileno(stream)
/* VSTREAM *stream;
/*
+/* void *vstream_context(stream)
+/* VSTREAM *stream;
+/*
/* int vstream_ferror(stream)
/* VSTREAM *stream;
/*
@@ -249,6 +252,9 @@
/* vstream_fileno() gives access to the file handle associated with
/* a buffered stream. With streams that have separate read/write
/* file descriptors, the result is the current descriptor.
+/*
+/* vstream_context() returns the application context that is passed on to
+/* the application-specified read/write routines.
/*
/* VSTREAM_PATH() is an unsafe macro that returns the name stored
/* with vstream_fopen() or with vstream_control(). The macro is
diff -Nur snapshot-20010228-orig/src/util/vstream.h snapshot-20010228/src/util/vstream.h
--- snapshot-20010228-orig/src/util/vstream.h Wed Mar 21 13:26:27 2001
+++ snapshot-20010228/src/util/vstream.h Wed Mar 21 13:32:24 2001
@@ -90,6 +90,7 @@
#define VSTREAM_GETCHAR() VSTREAM_GETC(VSTREAM_IN)
#define vstream_fileno(vp) ((vp)->fd)
+#define vstream_context(vp) ((vp)->context)
#define vstream_ferror(vp) vbuf_error(&(vp)->buf)
#define vstream_feof(vp) vbuf_eof(&(vp)->buf)
#define vstream_ftimeout(vp) vbuf_timeout(&(vp)->buf)