![]() ![]() |
10.10 STARTTLSEncryption can improve the security of sendmail. Ordinarily, mail is sent between two machines in the clear. That is, if you were to watch the transmission of bytes over the network[33] you would see what is actually being sent or received. This includes passwords, which are also sent in the clear.
To reduce the likelihood that someone watching the network will find something that can harm you, you can encrypt the stream of data. Three forms of encryption are available as of this writing:
In this section we show you:
10.10.1 The SSL LibraryAn SSL library might already exist on your machine. If so, and if it is a secure version, you can skip this section. The SSL library is available from the OpenSSL organization at the following sites:
Be sure you download V0.9.6g or higher (earlier versions had security problems). After you have downloaded and unpacked the source, look in the file called INSTALL. That file contains all the information you need to build and install the package. Throughout the rest of this section we will assume you have successfully installed this package in its default location /usr/local/ssl. 10.10.1.1 A proper random number sourceIf your system lacks the device /dev/urandom you will need to perform additional steps before you can use TLS. If your system supports /dev/urandom you can skip this section. For TLS (and thus STARTTLS) to work in a reliable and secure manner, you need to set up a way for sendmail to acquire high-quality pseudorandom numbers. There are a few alternatives to /dev/urandom that you can use, some more suitable than others. They are, in order of preference:
10.10.1.2 SUNWskiSun Microsystems provides an equivalent to /dev/urandom, called /dev/random, as part of its SUNWski package for Solaris. If it is not already installed on your system you can install it from a variety of sources. First, look on your Solaris Server Intranet Extension CD (if you have one). Lacking that, look at the web site http://freeware4sun.wroc.pl/. Look under Solaris 2.6 for patch number 106754, 106755, or 106756, which contain the SUNWski package. 10.10.1.3 EGDEGD is a persistent daemon that provides excellent pseudorandom numbers via a Unix domain socket. It is available as perl(1) source from http://egd.sourceforge.net/. If you choose to download and install this daemon, you can advise sendmail of that fact by defining the RandFile option (RandFile) in your mc configuration file: define(`confRAND_FILE', `egd:/etc/entropy') Here, a decision was made to run the EGD daemon at the system level, and to have it create its socket as /etc/entropy.[34] If you place that socket in a different location, you should replace /etc/entropy in the confRAND_FILE line (as discussed earlier) with the new location. The egd: prefix is required and constant.
Note that, to include support inside sendmail for use with this daemon, you must build sendmail with the EGD compile-time macro defined (EGD): APPENDDEF(`confENVDEF', `-DEGD') 10.10.1.4 PRNGDPRNGD is an EGD-compatible daemon available from http://www.aet.tu-cottbus.de/personen/jaenicke/postfix_tls/prngd.html. You download and install it, then use it in the same manner described for EGD. 10.10.1.5 Roll your ownIt is possible to use a file created by you that contains random numbers. To do this, first define the location of that file with sendmail's RandFile option (RandFile). Such a declaration might look like this: define(`confRAND_FILE', `file:/var/run/randfile') Note that the file: prefix is literal and must be present. The file, here named /var/run/randfile, contains at least 128 bytes of random data. For such a file to work, you need to update its contents more often than once every 10 minutes. If you update it less often, sendmail might refuse to use it upon startup (as a daemon or simply to send an email message). That is, the modification time of the file must always be within 10 minutes of any envocation of sendmail. 10.10.2 Digital CertificatesDigital certificates—and especially the acronyms and abbreviated terms used by sendmail to describe digital certificates—are central to the understanding and operation of TLS. A digital certificate is simply a block of bytes issued to a machine or a business by an authority. The block of bytes is unique to the person, machine, or business that acquired it, and can serve to identify that machine or business. Buried within that block of bytes is the identity of the authority that issued the block. When a machine or business offers the block of bytes as its identification, the receiver can connect to the authority to verify that the block of bytes is in fact authentic. In actual practice, such blocks of bytes or digital certificates can be more complex. For example, a master authority can issue a certificate to a regional authority. That regional authority can then issue individual certificates to individual machines. Each machine's certificate will contain embedded information about both authorities: the regional and the master authority. Digital certificates are supplied in two parts: a public part and a private part. The private part is kept private on the local machine and never given out. The public part is given to others to establish an identity. To ensure that digital certificates are valid, a hash is also stored inside each certificate. When a certificate is offered as identification, the receiver fetches the public part to ascertain the issuing authority, then uses that public part as a key to decrypt the authority's signature inside the certificate. A hash is computed on the decrypted signature, and if that hash matches the hash stored inside the certificate, the certificate is considered good. Each digital certificate is embedded with a time that allows an authority to expire the certificate. This feature is used by commercial sites that issue top-level certificates, as a way to force the annual renewal (and payment for) those certificates. The sendmail program uses a number of acronyms and abbreviations to refer to the various components of digital certificates. They are listed in Table 10-3.
For example, you might see a reference to "install a CA cert" in this book or in the sendmail documentation. This phrase means to install a digital certificate issued by a certificate authority. When you install the Certs of the issuing CA, you are generally installing only the public parts. You are encouraged to refer to Table 10-3 while reading the next few sections, where these acronyms, abbreviations, and terms are frequently used. 10.10.3 Enable TLS with BuildTo enable TLS in sendmail you need to add two new lines to your Build m4 file: APPENDDEF(`conf_sendmail_ENVDEF', `-DSTARTTLS') APPENDDEF(`conf_sendmail_LIBS', `-lssl -lcrypto') With these two lines in place, build a new sendmail. If you get an error such as the following: tls.c:16: openssl/err.h: No such file or directory you will need to let Build know where you installed the ssl components: APPENDDEF(`conf_sendmail_INCDIRS', `-I/opt/packages/openssl/include') APPENDDEF(`conf_sendmail_LIBDIRS', `-L/opt/packages/openssl/lib') Here, we installed OpenSSL in the nonstandard path /opt/packages/openssl. 10.10.4 Set Up Your CertificatesThere are two ways to set up your site's certificates: create your own and sign them yourself; or create your own and have a commercial site sign them. Commercial signatures generally require the payment of an annual fee. Table 10-4 shows a few of the commercial sites that sign certificates. There are many more than we show here. Use your favorite search engine to find more.
We don't cover the creation of certificates here, nor how to sign them. Instead we refer you to:
10.10.5 Add STARTTLS Support to Your mc FileAfter you have built sendmail with STARTTLS support (Section 10.10.3), and after you have created a certificate for use with sendmail, you must set up your configuration file to use STARTTLS. There are seven mc configuration file macros that you can use to do this. Based on what we have shown in the previous sections, one way to define them might look like this: define(`CERT_DIR', `/etc/mail/certs') define(`confCACERT_PATH', `CERT_DIR') define(`confCACERT', `CERT_DIR`'/cacert.pem') define(`confSERVER_CERT', `CERT_DIR`'/client.cert.pem') define(`confSERVER_KEY', `CERT_DIR`'/client.key.pem') define(`confCLIENT_CERT', `CERT_DIR`'/client.cert.pem') define(`confCLIENT_KEY', `CERT_DIR`'/client.key.pem') Here, we have set the settings for the server and client certificate and key files to be the same. At your site, you might want to have separate certificates and keys for the two roles of server (accept connections) and client (make connections). You might also prefer to keep the key outside the certificate file, as was done with the cacert.pem. 10.10.6 Test STARTTLSOnce you have built sendmail with STARTTLS support, and before you install it, you should test to see if STARTTLS is working. One way to perform such a test is like this: # obj.*/sendmail/sendmail -bs -Am Here, we run the newly built sendmail relative to the source directory. The -bs tells sendmail to speak SMTP on its standard input. The -Am tells sendmail to use its server configuration file (not submit.cf), even though it is running in mail-submission mode. Such a test session might look like this: 220 your.host.domain ESMTP Sendmail 8.12.7/8.12.7; Mon, 14 Jan 2002 11:43:02 -0700 (PST) ehlo your.host.domain 250-your.host.domain Hello root@localhost, pleased to meet you 250-ENHANCEDSTATUSCODES 250-PIPELINING 250-8BITMIME 250-SIZE 250-DSN 250-ETRN 250-STARTTLS Here, the STARTTLS SMTP keyword appears, revealing that this site supports SSL encryptions of connections. If STARTTLS doesn't appear, rerun the command with extra debugging, like this: # obj.*/sendmail/sendmail -O LogLevel=14 -bs -Am Look in your syslog log files for sendmail messages. Look for messages such as warnings about unsafe files, or warnings about the validity of X.509 certificates. If this fails, and you need additional help, you can connect to http://www.sendmail.org/tips/. If STARTTLS does appear, run sendmail as usual. Then examine Received: header lines for mail you received from other sites that support STARTTLS, and look for indications that TLS encryption worked: Received: from other.host.domain (other.host.domain [123.45.67.89]) by your.host.domain (8.12.5/8.12.3) with ESMTP id g75FlHR4038187 (version=TLSv1/SSLv3 cipher=EDH-RSA-DES-CBC3-SHA bits=168 verify=NO) Note that, even though the Received: header shows verify=NO, the message was still encrypted because the cipher= and bits= are present with values. 10.10.7 Macros for Use with STARTTLSIf you decide to use STARTTLS with sendmail, be aware that a number of related sendmail macros are useful in rule sets and database maps. These are shown in Table 10-5, and described in detail in Chapter 21.
10.10.8 STARTTLS and the access DatabaseBeginning with V8.11, four new prefixes in the access database are available for use with STARTTLS connection encryption (Section 10.10). CERTISSUER: and CERTSUBJECT: are for use with the Local_Relay_Auth rule set. TLS_Srv: and TLS_Clt: are for use with the tls_server and tls_client rule sets. 10.10.8.1 The access database and Local_Relay_AuthIn the rule set Local_Relay_Auth, the STARTTLS-related sendmail macro ${verify} (which contains the result of connection verification) is compared to the literal value OK. If it is not OK, the other relaying checks are performed. If ${verify} is OK, the value in the sendmail macro ${cert_issuer} (${cert_issuer}) is prefixed with CERTISSUER:, and the result looked up in the access database. That macro contains as its value the distinguished name of the authority that signed the presented certificate. The value undergoes special translation before the lookup. Specifically, all nonprinting characters, the space and tab characters, and the special characters: < > ( ) " + are replaced with the hexadecimal value of the character prefixed with a plus. For example, Sendmail CA becomes Sendmail+20CA. Therefore, if the issuer has the following distinguished name: /C=US/ST=California/L=Berkeley/O=Sendmail.org/CN=Sendmail CA/ that value undergoes special translation, and is prefixed with the special prefix CERTISSUER: just before the lookup. So the following is looked up: CERTISSUER:/C=US/ST=California/L=Berkeley/O=Sendmail.org/CN=Sendmail+20CA/ If that prefix and distinguished name are found in the database, and if the value returned is the keyword RELAY, relaying is allowed. If the value returned is the keyword SUBJECT instead of RELAY, the value of the sendmail macro ${cert_subject} (${cert_subject}) is looked up in the access database. That macro contains as its value the distinguished name of the connecting site. That value also undergoes translation, and is prefixed with the special prefix CERTSUBJECT: just before the lookup. For example, if the distinguished name of the certificate for the connecting site is: /C=US/ST=California/L=Berkeley/O=Sendmail.org/CN=Eric Allman/ the following is looked up: CERTSUBJECT:/C=US/ST=California/L=Berkeley/O=Sendmail.org/CN=Eric+20Allman/ If the prefixed macro's value is found, and if the value returned is the keyword RELAY, relaying is allowed. 10.10.8.2 The access database with tls_server and tls_clientThe tls_server rule set is called after the local sendmail issued (or should have issued) the STARTTLS SMTP command. This rule set handles outbound connections. The tls_client rule set is called at two possible points: just after the connecting host's STARTTLS SMTP command is offered; and from the check_mail rule set (which is called just after the connecting host issues the MAIL FROM: command). This tls_client rule set handles inbound connections. Both rule sets are given the value of the ${verify} sendmail macro in their workspaces. The tls_client rule set is given that value, followed by a $| operator, and a literal string that is MAIL when tls_client is called from the check_mail rule set, or STARTTLS otherwise. If the access database is not used, the connection is allowed in all cases, both inbound and outbound, unless the value in ${verify} is SOFTWARE, in which instance the connection is not allowed. If the access database is used, the tls_server rule set looks up the hostname of the destination host in the access database using the TLS_Srv: prefix. For example, if the local sendmail connected to the server insecure.host.domain, and if the negotiation for the TLS connection was good, the following lookup is performed: TLS_Srv:insecure.host.domain The tls_client rule set looks up the hostname of the inbound connecting host in the access database using the TLS_Clt: prefix. For example, if the local sendmail accepts a connection from ssl.host.domain, and if the negotiation for TLS connection was good, the following lookup is performed: TLS_Clt:ssl.host.domain For both rule sets, if the host or domain is not found, the host.domain, then the domain, are looked up, and if neither is found, a bare prefix is looked up to determine the default behavior: TLS_Clt: VERIFY TLS_Srv: VERIFY Here, the default for inbound and outbound connections is to require that they all be verified. The access database righthand-side string VERIFY means that the value in the ${verify} macro must be OK. In addition to the VERIFY value keyword, a number of bits (key length) can also be specified as: VERIFY:bits
In addition to requiring that the certificate be verified, the number of bits in the ${cipher_bits} sendmail macro must be at least as wide as the number of bits specified in bits. If the number of bits is the only item of concern, and if certificate verification is not of concern, the VERIFY in VERIFY:bits can be changed into ENCR: ENCR:bits
Here, no certificate verification is required, but the number of bits in the ${cipher_bits} sendmail macro must be at least as wide as the number of bits specified in bits. If the certificate is verified, and/or the number of bits is sufficient, the connection is allowed. Otherwise, it is rejected. When rejected, the rejection is temporary by default. You can prefix the VERIFY or ENCR with a TEMP+ to make a particular failure temporary, or with a PERM+ to make it permanent: TEMP+VERIFY You can also define the TLS_PERM_ERR macro in your mc configuration file to redefine the default to be a permanent failure: define(`TLS_PERM_ERR') If you wish to add your own rule to the tls_client or tls_server rule sets, you can do so with an appropriate mc configuration command: LOCAL_TLS_CLIENT Your rules, if any, will be called first. That is, for example, if you add rules to tls_client, those rules will be called before those that were already in the tls_client rule set. You do not need to restore the workspace at the end of your rules, however, because that restoration is taken care of for you. 10.10.8.3 The tls_rcpt rule setIn the previous section you learned that the tls_server rule set could be used to require that all mail to a particular site always be encrypted. For example, an access database entry such as the following does just that for the hostA.domain site: TLS_Srv:hostA.domain ENCR:128 However, because of MX records, mail might not always be sent to the hostA.domain's mail server. Consider these two MX records: hostA.domain. IN MX 10 mail.hostA.domain. hostA.domain. IN MX 50 mail.someother.domain. When the server mail.hostA.domain is down or heavily loaded, your local sendmail will likely connect to the backup MX site mail.someother.domain. When this happens, the requirement that all mail be encrypted (as set in the access database) will not be honored. Because you have no way of knowing ahead of time what host will serve as an MX backup, you probably won't have that backup host listed in you access database: TLS_Srv:hostA.domain ENCR:128 When sendmail connects to mail.someother.domain (and when mail.someother.domain does not support STARTTLS) the message will be transmitted in plain text (unencrypted). The tls_rcpt rule set was created specifically to deal with this problem. It is called just before a RCPT TO: command is sent to the other site. The workspace supplied to tls_rcpt is the current recipient (the one that will be given in the RCPT TO: command when it is issued). This rule set is allowed to require encryption or verification of the recipient's MTA, even if the message was redirected with MX records to another site. The tls_rcpt rule set looks up the recipient in four different ways, where the format of the recipient address is user@host.domain. Each lookup is prefixed with a literal TLS_Rcpt:. The lookups are: TLS_Rcpt:user@host.domain TLS_Rcpt:user@ TLS_Rcpt:host.domain TLS_Rcpt:domain TLS_Rcpt: The tls_rcpt rule set accepts the righthand-side value from the first matched lookup. If there is no match, the recipient address is considered good and the RCPT TO: command is allowed to be issued. The allowable righthand-side values are the same as those described for the tls_server rule set in the previous section. The requirements in the righthand side are compared to the ${verify} and ${cipher_bits} macros, as appropriate, and the connection is either allowed to continue, or not, based on the result. To illustrate, consider the MX example given earlier. If the access database contains the following entry: TLS_Rcpt:hostA.domain ENCR:128 encryption is required for any recipient at hostA.domain, even if delivery is redirected with an MX record to another site, such as mail.someother.domain. In addition to the righthand-side values described earlier, the tls_rcpt rule set allows four righthand-side suffixes. Each starts with a plus, and when two or more are listed, each is separated from the others with two plus signs: TLS_Rcpt:hostA.domain ENCR:128+CN:smtp.hostA.domain++CI:hostB.domain The suffixes allow further checks to be applied to the connection in addition to those required by the existing righthand-side value. The suffixes and their meanings are:
If you wish to add your own rules to the tls_rcpt rule set, you can do so with the following mc configuration command: LOCAL_TLS_RCPT If your rules return a #error or #discard delivery agent, the connection is rejected. If they return a $#OK,[35] the connection is accepted and subsequent tls_rcpt rule set rules are skipped (the access database lookups are not performed):
R $* $# OK skip subsequent tls_rcpt rule set rules But if they return a $@OK, further tls_rcpt rule set rules are allowed, and the access database lookups are performed, which might subsequently reject the connection: R $* $@ OK allow subsequent tls_rcpt rule set rules Your rules, if any, will be called first. That is, for example, if you add rules to tls_rcpt, those rules will be called before those that were already in the tls_rcpt rule set. You need not restore the workspace at the end of your rules, however, because that restoration is taken care of for you. 10.10.8.4 Disable STARTTLS with the try_tls rule setBy default STARTTLS is used whenever possible. Unfortunately, some hosts on the Internet do not properly implement STARTTLS, so even though they offer STARTTLS, they don't use it properly and the connection fails. If you know ahead of time which hosts have this problem you can list them in the access database and cause STARTTLS to be skipped for them. The try_tls rule set allows you to exempt specific connecting hosts and domains from STARTTLS support. This rule set simply looks up the connecting host's hostname and address in the access database. Each lookup is prefixed with a literal Try_TLS:. If the lookup finds the host or address (if either is in the access database) the use of STARTTLS is suppressed: Try_TLS:broken.server NO The righthand-side value for this lookup can be anything. All the try_tls rule set cares about is whether the lookup succeeds. If you wish to add your own rule to the try_tls rule set, you can do so with the following mc configuration command: LOCAL_TRY_TLS If your rules return a #error or #discard delivery agent, STARTTLS is suppressed. If they return a $#OK,[36] STARTTLS is offered and subsequent try_tls rule set rules are skipped (the access database lookups are not performed):
R $* $# OK skip subsequent try_tls rule set rules But if they return a $@OK, STARTTLS might be offered. We say might because further try_tls rule set rules are allowed, and access database lookups are performed, which, in turn, can subsequently disallow STARTTLS: R $* $@ OK allow subsequent try_tls rule set rules Your rules, if any, will be called first. That is, for example, if you add rules to try_tls, those rules will be called before those that were already in the try_tls rule set. You need not restore the workspace at the end of your rules, however, because that restoration is taken care of for you. |
![]() ![]() |