Home | History | Annotate | Line # | Download | only in conf
postfix-tls-script revision 1.1
      1  1.1  christos #!/bin/sh
      2  1.1  christos #	$NetBSD: postfix-tls-script,v 1.1 2017/02/14 01:13:34 christos Exp $
      3  1.1  christos #
      4  1.1  christos 
      5  1.1  christos #++
      6  1.1  christos # NAME
      7  1.1  christos #	postfix-tls 1
      8  1.1  christos # SUMMARY
      9  1.1  christos #	Postfix TLS management
     10  1.1  christos # SYNOPSIS
     11  1.1  christos #	\fBpostfix tls\fR \fIsubcommand\fR
     12  1.1  christos # DESCRIPTION
     13  1.1  christos #	The "\fBpostfix tls \fIsubcommand\fR" feature enables
     14  1.1  christos #	opportunistic TLS in the Postfix SMTP client or server, and
     15  1.1  christos #	manages Postfix SMTP server private keys and certificates.
     16  1.1  christos #
     17  1.1  christos #	The following subcommands are available:
     18  1.1  christos # .IP "\fBenable-client\fR [\fB-r \fIrandsource\fR]"
     19  1.1  christos #	Enable opportunistic TLS in the Postfix SMTP client, if all
     20  1.1  christos #	SMTP client TLS settings are at their default values.
     21  1.1  christos #	Otherwise, suggest parameter settings without making any
     22  1.1  christos #	changes.
     23  1.1  christos # .sp
     24  1.1  christos #	Specify \fIrandsource\fR to update the value of the
     25  1.1  christos #	\fBtls_random_source\fR configuration parameter (typically,
     26  1.1  christos #	/dev/urandom).  Prepend \fBdev:\fR to device paths or
     27  1.1  christos #	\fBegd:\fR to EGD socket paths.
     28  1.1  christos # .sp
     29  1.1  christos #	See also the \fBall-default-client\fR subcommand.
     30  1.1  christos # .IP "\fBenable-server\fR [\fB-r \fIrandsource\fR] [\fB-a \fIalgorithm\fR] [\fB-b \fIbits\fR] [\fIhostname\fB...\fR]"
     31  1.1  christos #	Create a new private key and self-signed server certificate
     32  1.1  christos #	and enable opportunistic TLS in the Postfix SMTP server,
     33  1.1  christos #	if all SMTP server TLS settings are at their default values.
     34  1.1  christos #	Otherwise, suggest parameter settings without making any
     35  1.1  christos #	changes.
     36  1.1  christos # .sp
     37  1.1  christos #	The \fIrandsource\fR parameter is as with \fBenable-client\fR
     38  1.1  christos #	above, and the remaining options are as with \fBnew-server-key\fR
     39  1.1  christos #	below.
     40  1.1  christos # .sp
     41  1.1  christos #	See also the \fBall-default-server\fR subcommand.
     42  1.1  christos # .IP "\fBnew-server-key\fR [\fB-a \fIalgorithm\fR] [\fB-b \fIbits\fR] [\fIhostname\fB...\fR]"
     43  1.1  christos #	Create a new private key and self-signed server certificate,
     44  1.1  christos #	but do not deploy them. Log and display commands to deploy
     45  1.1  christos #	the new key and corresponding certificate.  Also log and
     46  1.1  christos #	display commands to output a corresponding CSR or TLSA
     47  1.1  christos #	records which may be needed to obtain a CA certificate or
     48  1.1  christos #	to update DNS before the new key can be deployed.
     49  1.1  christos # .sp
     50  1.1  christos #	The \fIalgorithm\fR defaults to \fBrsa\fR, and \fIbits\fR
     51  1.1  christos #	defaults to 2048.  If you choose the \fBecdsa\fR  \fIalgorithm\fR
     52  1.1  christos #	then \fIbits\fR will be an EC curve name (by default
     53  1.1  christos #	\fBsecp256r1\fR, also known as prime256v1).  Curves other
     54  1.1  christos #	than \fBsecp256r1\fR, \fBsecp384r1\fR or \fBsecp521r1\fR
     55  1.1  christos #	are unlikely to be widely interoperable.  When generating
     56  1.1  christos #	EC keys, use one of these three.  DSA keys are obsolete and
     57  1.1  christos #	are not supported.
     58  1.1  christos # .sp
     59  1.1  christos #	Note: ECDSA support requires OpenSSL 1.0.0 or later and may
     60  1.1  christos #	not be available on your system.  Not all client systems
     61  1.1  christos #	will support ECDSA, so you'll generally want to deploy both
     62  1.1  christos #	RSA and ECDSA certificates to make use of ECDSA with
     63  1.1  christos #	compatible clients and RSA with the rest. If you want to
     64  1.1  christos #	deploy certificate chains with intermediate CAs for both
     65  1.1  christos #	RSA and ECDSA, you'll want at least OpenSSL 1.0.2, as earlier
     66  1.1  christos #	versions may not handle multiple chain files correctly.
     67  1.1  christos # .sp
     68  1.1  christos #	The first \fIhostname\fR argument will be the \fBCommonName\fR
     69  1.1  christos #	of both the subject and issuer of the self-signed certificate.
     70  1.1  christos #	It, and any additional \fIhostname\fR arguments, will also
     71  1.1  christos #	be listed as DNS alternative names in the certificate.  If
     72  1.1  christos #	no \fIhostname\fR is provided the value of the \fBmyhostname\fR
     73  1.1  christos #	main.cf parameter will be used.
     74  1.1  christos # .sp
     75  1.1  christos #	For RSA, the generated private key and certificate files
     76  1.1  christos #	are named \fBkey-\fIyyyymmdd-hhmmss\fB.pem\fR and
     77  1.1  christos #	\fBcert-\fIyyyymmdd-hhmmss\fB.pem\fR, where \fIyyyymmdd\fR
     78  1.1  christos #	is the calendar date and \fIhhmmss\fR is the time of day
     79  1.1  christos #	in UTC.  For ECDSA, the file names start with \fBeckey-\fR
     80  1.1  christos #	and \fBeccert-\fR instead of \fBkey-\fR and \fBcert-\fR
     81  1.1  christos #	respectively.
     82  1.1  christos # .sp
     83  1.1  christos #	Before deploying the new key and certificate with DANE,
     84  1.1  christos #	update the DNS with new DANE TLSA records, then wait for
     85  1.1  christos #	secondary nameservers to update and then for stale records
     86  1.1  christos #	in remote DNS caches to expire.
     87  1.1  christos # .sp
     88  1.1  christos #	Before deploying a new CA certificate make sure to include
     89  1.1  christos #	all the required intermediate issuing CA certificates in
     90  1.1  christos #	the certificate chain file.  The server certificate must
     91  1.1  christos #	be the first certificate in the chain file.  Overwrite and
     92  1.1  christos #	deploy the file with the original self-signed certificate
     93  1.1  christos #	that was generated together with the key.
     94  1.1  christos # .IP "\fBnew-server-cert\fR [\fB-a \fIalgorithm\fR] [\fB-b \fIbits\fR] [\fIhostname\fB...\fR]"
     95  1.1  christos #	This is just like \fBnew-server-key\fR except that, rather
     96  1.1  christos #	than generating a new private key, any currently deployed
     97  1.1  christos #	private key is copied to the new key file.  Thus if you're
     98  1.1  christos #	publishing DANE TLSA "3 1 1" or "3 1 2" records, there is
     99  1.1  christos #	no need to update DNS records.  The \fIalgorithm\fR and
    100  1.1  christos #	\fIbits\fR arguments are used only if no key of the same
    101  1.1  christos #	algorithm is already configured.
    102  1.1  christos # .sp
    103  1.1  christos #	This command is rarely needed, because the self-signed
    104  1.1  christos #	certificates generated have a 100-year nominal expiration
    105  1.1  christos #	time.  The underlying public key algorithms may well be
    106  1.1  christos #	obsoleted by quantum computers long before then.
    107  1.1  christos # .sp
    108  1.1  christos #	The most plausible reason for using this command is when
    109  1.1  christos #	the system hostname changes, and you'd like the name in the
    110  1.1  christos #	certificate to match the new hostname (not required for
    111  1.1  christos #	DANE "3 1 1", but some needlessly picky non-DANE opportunistic
    112  1.1  christos #	TLS clients may log warnings or even refuse to communicate).
    113  1.1  christos # .IP "\fBdeploy-server-cert \fIcertfile\fB \fIkeyfile\fR"
    114  1.1  christos #	This subcommand deploys the certificates in \fIcertfile\fR
    115  1.1  christos #	and private key in \fIkeyfile\fR (which are typically
    116  1.1  christos #	generated by the commands above, which will also log and
    117  1.1  christos #	display the full command needed to deploy the generated key
    118  1.1  christos #	and certificate).  After the new certificate and key are
    119  1.1  christos #	deployed any obsolete keys and certificates may be removed
    120  1.1  christos #	by hand.   The \fIkeyfile\fR and \fIcertfile\fR filenames
    121  1.1  christos #	may be relative to the Postfix configuration directory.
    122  1.1  christos # .IP "\fBoutput-server-csr\fR [\fB-k \fIkeyfile\fR] [\fIhostname\fB...\fR]"
    123  1.1  christos #	Write to stdout a certificate signing request (CSR) for the
    124  1.1  christos #	specified \fIkeyfile\fR.
    125  1.1  christos # .sp
    126  1.1  christos #	Instead of an absolute pathname or a pathname relative to
    127  1.1  christos #	$config_directory, \fIkeyfile\fR may specify one of the
    128  1.1  christos #	supported key algorithm names (see "\fBpostconf -T
    129  1.1  christos #	public-key-algorithms\fR"). In that case, the corresponding
    130  1.1  christos #	setting from main.cf is used to locate the \fIkeyfile\fR.
    131  1.1  christos #	The default \fIkeyfile\fR value is \fBrsa\fR.
    132  1.1  christos # .sp
    133  1.1  christos #	Zero or more \fIhostname\fR values can be specified.  The
    134  1.1  christos #	default \fIhostname\fR is the value of \fBmyhostname\fR
    135  1.1  christos #	main.cf parameter.
    136  1.1  christos # .IP "\fBoutput-server-tlsa\fR [\fB-h \fIhostname\fR] [\fIkeyfile\fB...\fR]"
    137  1.1  christos #	Write to stdout a DANE TLSA RRset suitable for a port 25
    138  1.1  christos #	SMTP server on host \fIhostname\fR with keys from any of
    139  1.1  christos #	the specified \fIkeyfile\fR values.  The default \fIhostname\fR
    140  1.1  christos #	is the value of the \fBmyhostname\fR main.cf parameter.
    141  1.1  christos # .sp
    142  1.1  christos #	Instead of absolute pathnames or pathnames relative to
    143  1.1  christos #	$config_directory, the \fIkeyfile\fR list may specify
    144  1.1  christos #	names of supported public key algorithms (see "\fBpostconf
    145  1.1  christos #	-T public-key-algorithms\fR").  In that case, the actual
    146  1.1  christos #	\fIkeyfile\fR list uses the values of the corresponding
    147  1.1  christos #	Postfix server TLS key file parameters.  If a parameter
    148  1.1  christos #	value is empty or equal to \fBnone\fR, then no TLSA record
    149  1.1  christos #	is output for that algorithm.
    150  1.1  christos # .sp
    151  1.1  christos #	The default \fIkeyfile\fR list consists of the two supported
    152  1.1  christos #	algorithms \fBrsa\fR and \fBecdsa\fR.
    153  1.1  christos # AUXILIARY COMMANDS
    154  1.1  christos # .IP "\fBall-default-client\fR"
    155  1.1  christos #	Exit with status 0 (success) if all SMTP client TLS settings are
    156  1.1  christos #	at their default values.  Otherwise, exit with a non-zero status.
    157  1.1  christos #	This is typically used as follows:
    158  1.1  christos # .sp
    159  1.1  christos #	\fBpostfix tls all-default-client &&
    160  1.1  christos #		postfix tls enable-client\fR
    161  1.1  christos # .IP "\fBall-default-server\fR"
    162  1.1  christos #	Exit with status 0 (success) if all SMTP server TLS settings are
    163  1.1  christos #	at their default values.  Otherwise, exit with a non-zero status.
    164  1.1  christos #	This is typically used as follows:
    165  1.1  christos # .sp
    166  1.1  christos #	\fBpostfix tls all-default-server &&
    167  1.1  christos #		postfix tls enable-server\fR
    168  1.1  christos # CONFIGURATION PARAMETERS
    169  1.1  christos # .ad 
    170  1.1  christos # .fi
    171  1.1  christos #	The "\fBpostfix tls \fIsubcommand\fR" feature reads
    172  1.1  christos #	or updates the following configuration parameters.
    173  1.1  christos # .IP "\fBcommand_directory (see 'postconf -d' output)\fR"
    174  1.1  christos #	The location of all postfix administrative commands.
    175  1.1  christos # .IP "\fBconfig_directory (see 'postconf -d' output)\fR"
    176  1.1  christos #	The default location of the Postfix main.cf and master.cf
    177  1.1  christos #	configuration files.
    178  1.1  christos # .IP "\fBopenssl_path (openssl)\fR"
    179  1.1  christos #	The location of the OpenSSL command line program \fBopenssl\fR(1).
    180  1.1  christos # .IP "\fBsmtp_tls_loglevel (0)\fR"
    181  1.1  christos #	Enable additional Postfix SMTP client logging of TLS activity.
    182  1.1  christos # .IP "\fBsmtp_tls_security_level (empty)\fR"
    183  1.1  christos #	The default SMTP TLS security level for the Postfix SMTP client;
    184  1.1  christos #	when a non-empty value is specified, this overrides the obsolete
    185  1.1  christos #	parameters smtp_use_tls, smtp_enforce_tls, and smtp_tls_enforce_peername.
    186  1.1  christos # .IP "\fBsmtp_tls_session_cache_database (empty)\fR"
    187  1.1  christos #	Name of the file containing the optional Postfix SMTP client
    188  1.1  christos #	TLS session cache.
    189  1.1  christos # .IP "\fBsmtpd_tls_cert_file (empty)\fR"
    190  1.1  christos #	File with the Postfix SMTP server RSA certificate in PEM format.
    191  1.1  christos # .IP "\fBsmtpd_tls_eccert_file (empty)\fR"
    192  1.1  christos #	File with the Postfix SMTP server ECDSA certificate in PEM format.
    193  1.1  christos # .IP "\fBsmtpd_tls_eckey_file ($smtpd_tls_eccert_file)\fR"
    194  1.1  christos #	File with the Postfix SMTP server ECDSA private key in PEM format.
    195  1.1  christos # .IP "\fBsmtpd_tls_key_file ($smtpd_tls_cert_file)\fR"
    196  1.1  christos #	File with the Postfix SMTP server RSA private key in PEM format.
    197  1.1  christos # .IP "\fBsmtpd_tls_loglevel (0)\fR"
    198  1.1  christos #	Enable additional Postfix SMTP server logging of TLS activity.
    199  1.1  christos # .IP "\fBsmtpd_tls_received_header (no)\fR"
    200  1.1  christos #	Request that the Postfix SMTP server produces Received:  message
    201  1.1  christos #	headers that include information about the protocol and cipher used,
    202  1.1  christos #	as well as the remote SMTP client CommonName and client certificate issuer
    203  1.1  christos #	CommonName.
    204  1.1  christos # .IP "\fBsmtpd_tls_security_level (empty)\fR"
    205  1.1  christos #	The SMTP TLS security level for the Postfix SMTP server; when
    206  1.1  christos #	a non-empty value is specified, this overrides the obsolete parameters
    207  1.1  christos #	smtpd_use_tls and smtpd_enforce_tls.
    208  1.1  christos # .IP "\fBtls_random_source (see 'postconf -d' output)\fR"
    209  1.1  christos #	The external entropy source for the in-memory \fBtlsmgr\fR(8) pseudo
    210  1.1  christos #	random number generator (PRNG) pool.
    211  1.1  christos # SEE ALSO
    212  1.1  christos #	master(8) Postfix master program
    213  1.1  christos #	postfix(1) Postfix administrative interface
    214  1.1  christos # README FILES
    215  1.1  christos # .ad
    216  1.1  christos # .fi
    217  1.1  christos #	Use "\fBpostconf readme_directory\fR" or
    218  1.1  christos #	"\fBpostconf html_directory\fR" to locate this information.
    219  1.1  christos # .na
    220  1.1  christos # .nf
    221  1.1  christos #	TLS_README, Postfix TLS configuration and operation
    222  1.1  christos # LICENSE
    223  1.1  christos # .ad
    224  1.1  christos # .fi
    225  1.1  christos #	The Secure Mailer license must be distributed with this software.
    226  1.1  christos # HISTORY
    227  1.1  christos #	The "\fBpostfix tls\fR" command was introduced with Postfix
    228  1.1  christos #	version 3.1.
    229  1.1  christos # AUTHOR(S)
    230  1.1  christos #	Viktor Dukhovni
    231  1.1  christos #--
    232  1.1  christos 
    233  1.1  christos RSA_BITS=2048		# default
    234  1.1  christos EC_CURVE=secp256r1	# default
    235  1.1  christos 
    236  1.1  christos case $daemon_directory in
    237  1.1  christos "") echo This script must be run by the postfix command. 1>&2
    238  1.1  christos     echo Do not run directly. 1>&2
    239  1.1  christos     exit 1;;
    240  1.1  christos esac
    241  1.1  christos 
    242  1.1  christos umask 022
    243  1.1  christos SHELL=/bin/sh
    244  1.1  christos 
    245  1.1  christos postconf=$command_directory/postconf
    246  1.1  christos LOGGER="$command_directory/postlog -t $MAIL_LOGTAG/postfix-tls-script"
    247  1.1  christos INFO="$LOGGER -p info"
    248  1.1  christos WARN="$LOGGER -p warn"
    249  1.1  christos ERROR="$LOGGER -p error"
    250  1.1  christos FATAL="$LOGGER -p fatal"
    251  1.1  christos 
    252  1.1  christos # Overwrite SMTP client and server settings only when these are at defaults.
    253  1.1  christos client_settings="
    254  1.1  christos     smtp_use_tls 
    255  1.1  christos     smtp_enforce_tls 
    256  1.1  christos     smtp_tls_enforce_peername 
    257  1.1  christos     smtp_tls_security_level 
    258  1.1  christos     smtp_tls_cert_file
    259  1.1  christos     smtp_tls_dcert_file
    260  1.1  christos     smtp_tls_eccert_file
    261  1.1  christos "
    262  1.1  christos 
    263  1.1  christos server_settings="
    264  1.1  christos     smtpd_use_tls 
    265  1.1  christos     smtpd_enforce_tls 
    266  1.1  christos     smtpd_tls_security_level 
    267  1.1  christos     smtpd_tls_cert_file
    268  1.1  christos     smtpd_tls_dcert_file
    269  1.1  christos     smtpd_tls_eccert_file
    270  1.1  christos "
    271  1.1  christos 
    272  1.1  christos #
    273  1.1  christos # Can't do much without these in place.
    274  1.1  christos #
    275  1.1  christos cd $command_directory || {
    276  1.1  christos     # Let's hope there's a "postlog" somewhere else on the PATH
    277  1.1  christos     FATAL="postlog -p fatal -t $MAIL_LOGTAG/postfix-tls-script"
    278  1.1  christos     msg="no Postfix command directory '${command_directory}'"
    279  1.1  christos     $FATAL "$msg" || { echo "$msg" >&2; sleep 1; }
    280  1.1  christos     exit 1
    281  1.1  christos }
    282  1.1  christos 
    283  1.1  christos check_getopt() {
    284  1.1  christos     OPTIND=1
    285  1.1  christos     a=
    286  1.1  christos     b=
    287  1.1  christos     c=
    288  1.1  christos     set -- -a 1 -b 2 -c -- -pos
    289  1.1  christos     while getopts :a:b:c o
    290  1.1  christos     do
    291  1.1  christos 	case $o in
    292  1.1  christos 	a) a="${OPTARG}";;
    293  1.1  christos 	b) b="${OPTARG}";;
    294  1.1  christos 	c) c=3;;
    295  1.1  christos 	*) return 1;;
    296  1.1  christos 	esac
    297  1.1  christos     done
    298  1.1  christos     shift `expr ${OPTIND} - 1`
    299  1.1  christos     if [ "${a}" != "1" -o "${b}" != 2 -o "${c}" != 3 \
    300  1.1  christos 	 -o "${OPTIND}" -ne 7 -o "$1" != "-pos" ]; then
    301  1.1  christos 	return 1
    302  1.1  christos     fi
    303  1.1  christos }
    304  1.1  christos 
    305  1.1  christos check_getopt || {
    306  1.1  christos     $FATAL "/bin/sh does not implement a compatible 'getopts' built-in"
    307  1.1  christos     exit 1
    308  1.1  christos }
    309  1.1  christos 
    310  1.1  christos # ----- BEGIN OpenSSL-specific -----
    311  1.1  christos 
    312  1.1  christos # No need to set the location of the OpenSSL command in each Postfix instance,
    313  1.1  christos # the value from the default instance is used for all instances.
    314  1.1  christos #
    315  1.1  christos default_config_directory=`$postconf -dh config_directory`
    316  1.1  christos openssl=`$postconf -c $default_config_directory -xh openssl_path`
    317  1.1  christos "$openssl" version >/dev/null 2>&1 || {
    318  1.1  christos     $FATAL "No working openssl(1) command found with 'openssl_path = $openssl'"
    319  1.1  christos     exit 1
    320  1.1  christos }
    321  1.1  christos 
    322  1.1  christos # ----- END OpenSSL-specific -----
    323  1.1  christos 
    324  1.1  christos test -n "$config_directory" -a -d "$config_directory" || {
    325  1.1  christos     $FATAL no Postfix configuration directory $config_directory!
    326  1.1  christos     exit 1
    327  1.1  christos }
    328  1.1  christos 
    329  1.1  christos # Do we support TLS and if so which algorithms?
    330  1.1  christos #
    331  1.1  christos $postconf -T compile-version | grep . >/dev/null || {
    332  1.1  christos     mail_version=`$postconf -dh mail_version`
    333  1.1  christos     $FATAL "Postfix $mail_version is not compiled with TLS support"
    334  1.1  christos     exit 1
    335  1.1  christos }
    336  1.1  christos rsa=
    337  1.1  christos ecdsa=
    338  1.1  christos for _algo in `$postconf -T public-key-algorithms | egrep '^(rsa|ecdsa)$'`
    339  1.1  christos do
    340  1.1  christos     eval $_algo=$_algo
    341  1.1  christos done
    342  1.1  christos 
    343  1.1  christos # ----- BEGIN OpenSSL-specific -----
    344  1.1  christos 
    345  1.1  christos if [ -n "${ecdsa}" ]; then
    346  1.1  christos     $openssl ecparam -name secp256r1 >/dev/null 2>&1 || {
    347  1.1  christos 	cat <<-EOM | $WARN
    348  1.1  christos 	Postfix supports ECDSA, but the $openssl command does not. Consider
    349  1.1  christos 	setting the openssl_path parameter to a more capable version of the
    350  1.1  christos 	command-line utility than $openssl (with PATH=$PATH).
    351  1.1  christos 	EOM
    352  1.1  christos 	ecdsa=
    353  1.1  christos     }
    354  1.1  christos fi
    355  1.1  christos if [ -n "${rsa}" ]; then
    356  1.1  christos    DEFALG=rsa
    357  1.1  christos elif [ -n "${ecdsa}" ]; then
    358  1.1  christos    DEFALG=ecdsa
    359  1.1  christos else
    360  1.1  christos     mail_version=`$postconf -dh mail_version`
    361  1.1  christos     $FATAL "Postfix $mail_version does not support either RSA or ECDSA"
    362  1.1  christos     exit 1
    363  1.1  christos fi
    364  1.1  christos 
    365  1.1  christos # Make sure stdin is open when testing
    366  1.1  christos if [ -r /dev/stdin ] < /dev/null; then
    367  1.1  christos     stdin=/dev/stdin
    368  1.1  christos elif [ -r /dev/fd/0 ] </dev/null; then
    369  1.1  christos     stdin=/dev/fd/0
    370  1.1  christos else
    371  1.1  christos     $FATAL No /dev/fd/0 or /dev/stdin found
    372  1.1  christos     exit 1
    373  1.1  christos fi
    374  1.1  christos 
    375  1.1  christos hex_sha256() {
    376  1.1  christos     $openssl dgst -binary -sha256 | od -An -vtx1 | tr -d ' \012'
    377  1.1  christos }
    378  1.1  christos 
    379  1.1  christos # We require SHA2-256 support from openssl(1)
    380  1.1  christos #
    381  1.1  christos null256=e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
    382  1.1  christos tmp=`hex_sha256 </dev/null 2>/dev/null`
    383  1.1  christos if [ "${tmp}" != "${null256}" ]; then
    384  1.1  christos     cat <<EOF >&2
    385  1.1  christos Your $openssl does not support the SHA2-256 digest algorithm. To enable
    386  1.1  christos 'postfix tls', install an OpenSSL that does. Install its openssl(1) command
    387  1.1  christos at /usr/local/bin/openssl or other suitable location, and set the
    388  1.1  christos 'openssl_path' parameter in $default_config_directory/main.cf accordingly.
    389  1.1  christos EOF
    390  1.1  christos     $FATAL "No 'postfix tls' support when openssl(1) is obsolete"
    391  1.1  christos     exit 1
    392  1.1  christos fi
    393  1.1  christos 
    394  1.1  christos read_key() {
    395  1.1  christos     [ -n "$1" -a -f "$1" ] || return 1
    396  1.1  christos 
    397  1.1  christos     # Old OpenSSL versions return success even for unsupported sub-commands!
    398  1.1  christos     # So we inspect the output instead.	 Don't prompt if the key is password
    399  1.1  christos     # protected.
    400  1.1  christos     #
    401  1.1  christos     while read cmd key_algo key_param cert_param; do
    402  1.1  christos 	$openssl $cmd -passin "pass:umask 077" -in "$1" |
    403  1.1  christos 	    grep . && return 0
    404  1.1  christos     done 2>/dev/null <<-EOF
    405  1.1  christos 	rsa rsa smtpd_tls_key_file smtpd_tls_cert_file
    406  1.1  christos 	ec ecdsa smtpd_tls_eckey_file smtpd_tls_eccert_file
    407  1.1  christos 	EOF
    408  1.1  christos     return 1
    409  1.1  christos }
    410  1.1  christos 
    411  1.1  christos pubkey_dgst() {
    412  1.1  christos     [ -n "$1" -a -f "$1" ] || return 1
    413  1.1  christos 
    414  1.1  christos     # Old OpenSSL versions return success even for unsupported sub-commands!
    415  1.1  christos     # So we inspect the output instead.
    416  1.1  christos     #
    417  1.1  christos     for cmd in ec rsa; do
    418  1.1  christos 	$openssl $cmd -passin "pass:umask 077" -in "$1" -pubout |
    419  1.1  christos 	$openssl $cmd -pubin -outform DER |
    420  1.1  christos 	hex_sha256 | egrep -v "${null256}" && return 0
    421  1.1  christos     done 2>/dev/null
    422  1.1  christos     return 1
    423  1.1  christos }
    424  1.1  christos 
    425  1.1  christos cert_pubkey_dgst() {
    426  1.1  christos     [ -n "$1" -a -f "$1" ] || return 1
    427  1.1  christos 
    428  1.1  christos     # Old OpenSSL versions return success even for unsupported sub-commands!
    429  1.1  christos     # So we inspect the output instead.
    430  1.1  christos     #
    431  1.1  christos     for cmd in ec rsa; do
    432  1.1  christos 	$openssl x509 -pubkey -noout -in "$1" |
    433  1.1  christos 	$openssl $cmd -pubin -outform DER |
    434  1.1  christos 	hex_sha256 | egrep -v "${null256}" && return 0
    435  1.1  christos     done 2>/dev/null
    436  1.1  christos     return 1
    437  1.1  christos }
    438  1.1  christos 
    439  1.1  christos copy_key() {
    440  1.1  christos     _algo=$1; shift
    441  1.1  christos     _bits=$1; shift
    442  1.1  christos     _fold=$1; shift
    443  1.1  christos     _fnew=$1; shift
    444  1.1  christos     _umask=`umask`
    445  1.1  christos 
    446  1.1  christos     umask 077
    447  1.1  christos     read_key "${_fold}" > "${_fnew}"	# sets key_algo of current key
    448  1.1  christos     _ret=$?
    449  1.1  christos     umask "${_umask}"
    450  1.1  christos 
    451  1.1  christos     if [ "${_ret}" -ne 0 ]; then
    452  1.1  christos 	$FATAL "Error copying private key from '${_fold}' to '${_fnew}'"
    453  1.1  christos 	return 1
    454  1.1  christos     fi
    455  1.1  christos     if [ "${key_algo}" != "${_algo}" ]; then
    456  1.1  christos 	$FATAL "Key algorithm '$key_algo' of '${_fold}' is not '${_algo}'"
    457  1.1  christos 	return 1
    458  1.1  christos     fi
    459  1.1  christos     # XXX: We'd need C-code in postconf to portably check for compatible "bits"
    460  1.1  christos }
    461  1.1  christos 
    462  1.1  christos create_key() {
    463  1.1  christos     _algo=$1
    464  1.1  christos     _bits=$2
    465  1.1  christos     _fnew=$3
    466  1.1  christos     _umask=`umask`
    467  1.1  christos 
    468  1.1  christos     case $_algo in
    469  1.1  christos         "") $FATAL "Internal error: empty algorithm"; return 1;;
    470  1.1  christos       $rsa) set -- "${openssl}" genrsa -out "${_fnew}" "${_bits}";;
    471  1.1  christos     $ecdsa) set -- "${openssl}" ecparam -param_enc named_curve -genkey \
    472  1.1  christos 		-out "${_fnew}" -name "${_bits}";;
    473  1.1  christos 	 *) $FATAL "Internal error: bad algorithm '${_algo}'"
    474  1.1  christos 	    return 1;;
    475  1.1  christos     esac
    476  1.1  christos 
    477  1.1  christos     umask 077
    478  1.1  christos     _err=`"$@" 2>&1`
    479  1.1  christos     _ret=$?
    480  1.1  christos     umask "${_umask}"
    481  1.1  christos 
    482  1.1  christos     if [ "${_ret}" -ne 0 ]; then
    483  1.1  christos 	echo "${_err}" | $WARN
    484  1.1  christos 	$FATAL "error generating new ${_algo} ${_bits} private key"
    485  1.1  christos 	return 1
    486  1.1  christos     fi
    487  1.1  christos }
    488  1.1  christos 
    489  1.1  christos create_cert() {
    490  1.1  christos     _k=$1; shift
    491  1.1  christos     _c=$1; shift
    492  1.1  christos     set_fqdn "$1"
    493  1.1  christos     if [ $# -gt 0 ]; then shift; fi
    494  1.1  christos     set -- "$fqdn" "$@"
    495  1.1  christos 
    496  1.1  christos     if [ -r "${_c}" ]; then
    497  1.1  christos 	$FATAL "New certificate file already exists: ${_c}"
    498  1.1  christos 	return 1
    499  1.1  christos     fi
    500  1.1  christos 
    501  1.1  christos     # Generate a new self-signed (~100 year) certificate
    502  1.1  christos     #
    503  1.1  christos     (
    504  1.1  christos 	echo "default_md = sha256"
    505  1.1  christos 	echo "x509_extensions = v3"
    506  1.1  christos 	echo "prompt = yes"
    507  1.1  christos 	echo "distinguished_name = dn"
    508  1.1  christos 	echo "[dn]"
    509  1.1  christos 	echo "[v3]"
    510  1.1  christos 	echo "basicConstraints = CA:false"
    511  1.1  christos 	echo "subjectKeyIdentifier = hash"
    512  1.1  christos 	echo "extendedKeyUsage = serverAuth, clientAuth"
    513  1.1  christos 	echo "subjectAltName = @alts"
    514  1.1  christos 	echo "[alts]"
    515  1.1  christos 	i=1; for dns in "$@"; do
    516  1.1  christos 	    # XXX map empty to $myhostname
    517  1.1  christos 	    echo "DNS.$i = $dns"
    518  1.1  christos 	    i=`expr $i + 1`
    519  1.1  christos 	done
    520  1.1  christos     ) | $openssl req -x509 -config $stdin -new -key "${_k}" \
    521  1.1  christos 	    -subj "/CN=$fqdn" -days 36525 -out "${_c}" || {
    522  1.1  christos 	rm -f "${_c}" "${_k}"
    523  1.1  christos 	$FATAL "error generating self-signed SSL certificate"
    524  1.1  christos 	return 1
    525  1.1  christos     }
    526  1.1  christos }
    527  1.1  christos 
    528  1.1  christos output_server_csr() {
    529  1.1  christos     set_keyfile "$1" || return 1
    530  1.1  christos     shift
    531  1.1  christos     set_fqdn "$1" || return 1
    532  1.1  christos     shift
    533  1.1  christos     set -- "$fqdn" "$@"
    534  1.1  christos     (
    535  1.1  christos 	echo "default_md = sha256"
    536  1.1  christos 	echo "req_extensions = v3"
    537  1.1  christos 	echo "prompt = yes"
    538  1.1  christos 	echo "distinguished_name = dn"
    539  1.1  christos 	echo "[dn]"
    540  1.1  christos 	echo "[v3]"
    541  1.1  christos 	echo "subjectKeyIdentifier = hash"
    542  1.1  christos 	echo "extendedKeyUsage = serverAuth, clientAuth"
    543  1.1  christos 	echo "subjectAltName = @alts"
    544  1.1  christos 	echo "[alts]"
    545  1.1  christos 	i=1; for dns in "$@"; do
    546  1.1  christos 	    echo "DNS.$i = $dns"
    547  1.1  christos 	    i=`expr $i + 1`
    548  1.1  christos 	done
    549  1.1  christos     ) | $openssl req -config $stdin -new -key "$keyfile" -subj /
    550  1.1  christos }
    551  1.1  christos 
    552  1.1  christos # ----- END OpenSSL-specific -----
    553  1.1  christos 
    554  1.1  christos info_enable_client() {
    555  1.1  christos 	cat <<-EOM
    556  1.1  christos 	*** Non-default SMTP client TLS settings detected, no changes made.
    557  1.1  christos 	For opportunistic TLS in the Postfix SMTP client, the below settings
    558  1.1  christos 	are typical:
    559  1.1  christos 	  smtp_tls_security_level = may
    560  1.1  christos 	  smtp_tls_loglevel = 1
    561  1.1  christos 	EOM
    562  1.1  christos 	if get_cache_db_type dbtype
    563  1.1  christos 	then
    564  1.1  christos 	    echo "  smtp_tls_session_cache_database = ${dbtype}:\${data_directory}/smtp_scache"
    565  1.1  christos 	fi
    566  1.1  christos }
    567  1.1  christos 
    568  1.1  christos info_client_deployed() {
    569  1.1  christos 	cat <<-EOM
    570  1.1  christos 	Enabled opportunistic TLS in the Postfix SMTP client.
    571  1.1  christos 	Run the command:
    572  1.1  christos 	  # postfix reload
    573  1.1  christos 	if you want the new settings to take effect immediately.
    574  1.1  christos 	EOM
    575  1.1  christos }
    576  1.1  christos 
    577  1.1  christos info_enable_server() {
    578  1.1  christos 	cat <<-EOM
    579  1.1  christos 	*** Non-default SMTP server TLS settings detected, no changes made.
    580  1.1  christos 	For opportunistic TLS in the Postfix SMTP server, the below settings
    581  1.1  christos 	are typical:
    582  1.1  christos 	  smtpd_tls_security_level = may
    583  1.1  christos 	  smtpd_tls_loglevel = 1
    584  1.1  christos 	You can use "postfix tls new-server-cert" to create a new certificate.
    585  1.1  christos 	Or, "postfix tls new-server-key" to also force a new private key.
    586  1.1  christos 	If you publish DANE TLSA records, see:
    587  1.1  christos 	  https://tools.ietf.org/html/rfc7671#section-8
    588  1.1  christos 	  https://tools.ietf.org/html/rfc7671#section-5.1
    589  1.1  christos 	  https://tools.ietf.org/html/rfc7671#section-5.2
    590  1.1  christos 	  https://community.letsencrypt.org/t/please-avoid-3-0-1-and-3-0-2-dane-tlsa-records-with-le-certificates/7022
    591  1.1  christos 	EOM
    592  1.1  christos }
    593  1.1  christos 
    594  1.1  christos # args: certfile keyfile deploy
    595  1.1  christos info_created() {
    596  1.1  christos 	cat <<-EOM
    597  1.1  christos 	New private key and self-signed certificate created. To deploy run:
    598  1.1  christos 	  # postfix tls deploy-server-cert $1 $2
    599  1.1  christos 	EOM
    600  1.1  christos }
    601  1.1  christos 
    602  1.1  christos # args: certfile keyfile deploy
    603  1.1  christos info_server_deployed() {
    604  1.1  christos 	if [ "$3" = "enable" ]; then
    605  1.1  christos 	    echo "Enabled opportunistic TLS in the Postfix SMTP server"
    606  1.1  christos 	fi
    607  1.1  christos 	cat <<-EOM
    608  1.1  christos 	New TLS private key and certificate deployed.
    609  1.1  christos 	Run the command:
    610  1.1  christos 	  # postfix reload
    611  1.1  christos 	if you want the new settings to take effect immediately.
    612  1.1  christos 	EOM
    613  1.1  christos }
    614  1.1  christos 
    615  1.1  christos # args: certfile keyfile deploy
    616  1.1  christos info_csr() {
    617  1.1  christos 	cat <<-EOM
    618  1.1  christos 	To generate a CSR run:
    619  1.1  christos 	  # postfix tls output-server-csr -k $2 [<hostname> ...]
    620  1.1  christos 	EOM
    621  1.1  christos 	if [ -z "$3" ]; then
    622  1.1  christos 	    echo "Save the signed certificate chain in $1, and deploy as above."
    623  1.1  christos 	else
    624  1.1  christos 	    echo "Save the signed certificate chain in $1."
    625  1.1  christos 	fi
    626  1.1  christos }
    627  1.1  christos 
    628  1.1  christos # args: certfile keyfile deploy
    629  1.1  christos info_tlsa() {
    630  1.1  christos 	# If already deployed, info for how to show all the deployed keys.
    631  1.1  christos 	# Otherwise, just the new keys, so that TLSA records can be updated
    632  1.1  christos 	# first.
    633  1.1  christos 	if [ -n "$3" ]; then shift $#; fi
    634  1.1  christos 	cat <<-EOM
    635  1.1  christos 	To generate TLSA records run:
    636  1.1  christos 	  # postfix tls output-server-tlsa [-h <hostname>] $2
    637  1.1  christos 	EOM
    638  1.1  christos }
    639  1.1  christos 
    640  1.1  christos # args: certfile keyfile deploy
    641  1.1  christos info_dane_dns() {
    642  1.1  christos 	# If already deployed, too late to wait, otherwise advise updating TLSA
    643  1.1  christos 	# RRs before deployment.
    644  1.1  christos 	if [ -n "$3" ]; then
    645  1.1  christos 	    cat <<-EOM
    646  1.1  christos 	(If you have DANE TLSA RRs, update them as soon as possible to match
    647  1.1  christos 	the newly deployed keys).
    648  1.1  christos 	EOM
    649  1.1  christos 	else
    650  1.1  christos 	    cat <<-EOM
    651  1.1  christos 	(deploy after updating the DNS and waiting for stale RRs to expire).
    652  1.1  christos 	EOM
    653  1.1  christos 	fi
    654  1.1  christos }
    655  1.1  christos 
    656  1.1  christos set_fqdn() {
    657  1.1  christos     if [ -n "$1" ]; then fqdn=$1; return 0; fi
    658  1.1  christos     fqdn=`$postconf -xh myhostname` || return 1
    659  1.1  christos     case $fqdn in /*) fqdn=`cat "${fqdn}"` || return 1;; esac
    660  1.1  christos }
    661  1.1  christos 
    662  1.1  christos set_keyfile() {
    663  1.1  christos     keyfile=$1
    664  1.1  christos     case $keyfile in
    665  1.1  christos        rsa) if [ -n "${rsa}" ]; then
    666  1.1  christos 		keyfile=`$postconf -nxh smtpd_tls_key_file`
    667  1.1  christos 	    else
    668  1.1  christos 		keyfile=
    669  1.1  christos 	    fi
    670  1.1  christos 	    ;;
    671  1.1  christos      ecdsa) if [ -n "${ecdsa}" ]; then
    672  1.1  christos 		keyfile=`$postconf -nxh smtpd_tls_eckey_file`
    673  1.1  christos 	    else
    674  1.1  christos 		keyfile=
    675  1.1  christos 	    fi
    676  1.1  christos 	    ;;
    677  1.1  christos         "") : empty ok;;
    678  1.1  christos       none) : see below;;
    679  1.1  christos         /*) ;;
    680  1.1  christos          *) # User-specified key pathnames are relative to the configuration
    681  1.1  christos 	    # directory
    682  1.1  christos 	    keyfile="${config_directory}/${keyfile}";;
    683  1.1  christos     esac
    684  1.1  christos     if [ "${keyfile}" = "none" ]; then keyfile= ; fi
    685  1.1  christos }
    686  1.1  christos 
    687  1.1  christos check_key() {
    688  1.1  christos     read_key "$1" >/dev/null && return 0
    689  1.1  christos     $FATAL "no private key found in file: $1"
    690  1.1  christos     return 1
    691  1.1  christos }
    692  1.1  christos 
    693  1.1  christos # Create new key or copy existing if specified.
    694  1.1  christos #
    695  1.1  christos ensure_key() {
    696  1.1  christos     _algo=$1; shift
    697  1.1  christos     _bits=$1; shift
    698  1.1  christos     stamp=`TZ=UTC date +%Y%m%d-%H%M%S`
    699  1.1  christos 
    700  1.1  christos     case $_algo in
    701  1.1  christos 	"") $FATAL "Internal error: empty algorithm "; return 1;;
    702  1.1  christos       $rsa) keyfile="${config_directory}/key-${stamp}.pem"
    703  1.1  christos 	    certfile="${config_directory}/cert-${stamp}.pem";;
    704  1.1  christos     $ecdsa) keyfile="${config_directory}/eckey-${stamp}.pem"
    705  1.1  christos 	    certfile="${config_directory}/eccert-${stamp}.pem";;
    706  1.1  christos 	 *) $FATAL "Internal error: bad algorithm '${_algo}'"
    707  1.1  christos 	    return 1;;
    708  1.1  christos     esac
    709  1.1  christos 
    710  1.1  christos     if [ -r "${keyfile}" ]; then
    711  1.1  christos 	$FATAL "New private key file already exists: ${keyfile}"
    712  1.1  christos 	return 1
    713  1.1  christos     fi
    714  1.1  christos     if [ -r "${certfile}" ]; then
    715  1.1  christos 	$FATAL "New certificate file already exists: ${certfile}"
    716  1.1  christos 	return 1
    717  1.1  christos     fi
    718  1.1  christos 
    719  1.1  christos     if [ -n "$1" ]; then
    720  1.1  christos 	copy_key "${_algo}" "${_bits}" "$1" "${keyfile}" && return 0
    721  1.1  christos     else
    722  1.1  christos 	create_key "${_algo}" "${_bits}" "${keyfile}" && return 0
    723  1.1  christos     fi
    724  1.1  christos     rm -f "${keyfile}"
    725  1.1  christos     return 1
    726  1.1  christos }
    727  1.1  christos 
    728  1.1  christos init_random_source() {
    729  1.1  christos     tls_random_source=$1
    730  1.1  christos 
    731  1.1  christos     if [ -z "${tls_random_source}" ]; then
    732  1.1  christos 	tls_random_source=`$postconf -xh tls_random_source`
    733  1.1  christos     fi
    734  1.1  christos     if [ -n "${tls_random_source}" ]; then
    735  1.1  christos 	return 0
    736  1.1  christos     fi
    737  1.1  christos     if [ -r /dev/urandom ]
    738  1.1  christos     then
    739  1.1  christos 	tls_random_source=dev:/dev/urandom
    740  1.1  christos     else
    741  1.1  christos 	$FATAL no default TLS random source defined and no /dev/urandom
    742  1.1  christos 	return 1
    743  1.1  christos     fi
    744  1.1  christos }
    745  1.1  christos 
    746  1.1  christos # Don't be too clever by half.
    747  1.1  christos all_default() {
    748  1.1  christos     for var in "$@"
    749  1.1  christos     do
    750  1.1  christos 	val=`$postconf -nh "${var}"`
    751  1.1  christos 	if [ -n "$val" ]; then return 1; fi
    752  1.1  christos     done
    753  1.1  christos     return 0
    754  1.1  christos }
    755  1.1  christos 
    756  1.1  christos # Select read-write database type for TLS session caches.
    757  1.1  christos #
    758  1.1  christos get_cache_db_type() {
    759  1.1  christos     var=$1; shift
    760  1.1  christos     prio=0
    761  1.1  christos     ret=1
    762  1.1  christos     for _dbtype in `$postconf -m`
    763  1.1  christos     do
    764  1.1  christos 	_prio=0
    765  1.1  christos 	case $_dbtype in
    766  1.1  christos 	 lmdb) _prio=2;;
    767  1.1  christos 	btree) _prio=1;;
    768  1.1  christos 	esac
    769  1.1  christos 	if [ "$_prio" -gt "$prio" ]
    770  1.1  christos 	then
    771  1.1  christos 	    eval "$var=\$_dbtype"
    772  1.1  christos 	    prio=$_prio
    773  1.1  christos 	    ret=0
    774  1.1  christos 	fi
    775  1.1  christos     done
    776  1.1  christos     return $ret
    777  1.1  christos }
    778  1.1  christos 
    779  1.1  christos deploy_server_cert() {
    780  1.1  christos     certfile=$1; shift
    781  1.1  christos     keyfile=$1; shift
    782  1.1  christos     deploy=$1; shift
    783  1.1  christos 
    784  1.1  christos     # Sets key_algo, key_param and cert_param
    785  1.1  christos     check_key "$keyfile" || return 1
    786  1.1  christos 
    787  1.1  christos     cd=`cert_pubkey_dgst "${certfile}"` || {
    788  1.1  christos 	$FATAL "error computing certificate public key digest"
    789  1.1  christos 	return 1
    790  1.1  christos     }
    791  1.1  christos     kd=`pubkey_dgst "$keyfile"` || {
    792  1.1  christos 	$FATAL "error computing public key digest"
    793  1.1  christos 	return 1
    794  1.1  christos     }
    795  1.1  christos 
    796  1.1  christos     if [ "$cd" != "$kd" ]; then
    797  1.1  christos 	$FATAL "Certificate in ${certfile} does not match key in ${keyfile}"
    798  1.1  christos 	return 1
    799  1.1  christos     fi
    800  1.1  christos 
    801  1.1  christos     set -- \
    802  1.1  christos 	"${key_param} = ${keyfile}" \
    803  1.1  christos 	"${cert_param} = ${certfile}"
    804  1.1  christos 
    805  1.1  christos     if [ "${deploy}" = "enable" ]; then
    806  1.1  christos 	set -- "$@" \
    807  1.1  christos 	    "smtpd_tls_security_level = may" \
    808  1.1  christos 	    "smtpd_tls_received_header = yes" \
    809  1.1  christos 	    "smtpd_tls_loglevel = 1"
    810  1.1  christos     fi
    811  1.1  christos 
    812  1.1  christos     if [ -n "${tls_random_source}" ]; then
    813  1.1  christos 	set -- "$@" "tls_random_source = ${tls_random_source}"
    814  1.1  christos     fi
    815  1.1  christos 
    816  1.1  christos     # All in one shot, since postconf delays modifying "hot" main.cf files.
    817  1.1  christos     $postconf -e "$@" || return 1
    818  1.1  christos }
    819  1.1  christos 
    820  1.1  christos # Prepare a new cert and perhaps re-use any existing private key.
    821  1.1  christos #
    822  1.1  christos new_server_cert() {
    823  1.1  christos     algo=$1; shift
    824  1.1  christos     bits=$1; shift
    825  1.1  christos     oldkey=$1; shift
    826  1.1  christos     deploy=$1; shift
    827  1.1  christos 
    828  1.1  christos     # resets keyfile (copy or else new) and new certfile
    829  1.1  christos     ensure_key "$algo" "$bits" "${oldkey}" || return 1
    830  1.1  christos     create_cert "${keyfile}" "${certfile}" "$@" || return 1
    831  1.1  christos     if [ -n "${deploy}" ]; then
    832  1.1  christos 	deploy_server_cert "${certfile}" "${keyfile}" "${deploy}" || return 1
    833  1.1  christos     fi
    834  1.1  christos 
    835  1.1  christos     (
    836  1.1  christos 	if [ -z "${deploy}" ]; then
    837  1.1  christos 	    info_created "${certfile}" "${keyfile}" "${deploy}"
    838  1.1  christos 	else
    839  1.1  christos 	    info_server_deployed "${certfile}" "${keyfile}" "${deploy}"
    840  1.1  christos 	fi
    841  1.1  christos 	info_csr "${certfile}" "${keyfile}" "${deploy}"
    842  1.1  christos 	info_tlsa "${certfile}" "${keyfile}" "${deploy}"
    843  1.1  christos 	if [ -z "${oldkey}" ]; then
    844  1.1  christos 	    info_dane_dns "${certfile}" "${keyfile}" "${deploy}"
    845  1.1  christos 	fi
    846  1.1  christos     ) | $INFO
    847  1.1  christos }
    848  1.1  christos 
    849  1.1  christos enable_client() {
    850  1.1  christos     if all_default ${client_settings}
    851  1.1  christos     then
    852  1.1  christos 	set -- \
    853  1.1  christos 	    "smtp_tls_security_level = may" \
    854  1.1  christos 	    "smtp_tls_loglevel = 1"
    855  1.1  christos 
    856  1.1  christos 	if get_cache_db_type dbtype
    857  1.1  christos 	then
    858  1.1  christos 	    set -- "$@" \
    859  1.1  christos 		"smtp_tls_session_cache_database = ${dbtype}:${data_directory}/smtp_scache"
    860  1.1  christos 	fi
    861  1.1  christos 
    862  1.1  christos 	if [ -n "${tls_random_source}" ]; then
    863  1.1  christos 	    set -- "$@" "tls_random_source = ${tls_random_source}"
    864  1.1  christos 	fi
    865  1.1  christos 
    866  1.1  christos 	# All in one shot, since postconf delays modifying "hot" main.cf files.
    867  1.1  christos 	$postconf -e "$@" || return 1
    868  1.1  christos 	info_client_deployed
    869  1.1  christos     else
    870  1.1  christos 	info_enable_client
    871  1.1  christos     fi | $INFO
    872  1.1  christos }
    873  1.1  christos 
    874  1.1  christos enable_server() {
    875  1.1  christos     algo=$1; shift
    876  1.1  christos     bits=$1; shift
    877  1.1  christos 
    878  1.1  christos     if all_default ${server_settings}
    879  1.1  christos     then
    880  1.1  christos 	# algo bits keyfile deploy [hostnames ...]
    881  1.1  christos 	new_server_cert "${algo}" "${bits}" "" "enable" "$@" || return 1
    882  1.1  christos     else
    883  1.1  christos 	info_enable_server | $INFO
    884  1.1  christos     fi
    885  1.1  christos }
    886  1.1  christos 
    887  1.1  christos output_server_tlsa() {
    888  1.1  christos     hostname=$1
    889  1.1  christos     check_key "$2" || return 1
    890  1.1  christos     data=`pubkey_dgst "$2"` || return 1
    891  1.1  christos     if [ -z "$data" ]
    892  1.1  christos     then
    893  1.1  christos 	$FATAL error computing SHA2-256 SPKI digest of "$key"
    894  1.1  christos 	return 1
    895  1.1  christos     fi
    896  1.1  christos     echo "_25._tcp.$hostname. IN TLSA 3 1 1 $data"
    897  1.1  christos }
    898  1.1  christos 
    899  1.1  christos #
    900  1.1  christos # Parse JCL
    901  1.1  christos #
    902  1.1  christos case $1 in
    903  1.1  christos enable-client)
    904  1.1  christos 	cmd=$1; shift; OPTIND=1
    905  1.1  christos 	rand=
    906  1.1  christos 	while getopts :r: _opt
    907  1.1  christos 	do
    908  1.1  christos 	    case $_opt in
    909  1.1  christos 	    r) rand="${OPTARG}";;
    910  1.1  christos 	    *) $FATAL "usage: postfix tls $cmd [-r devrandom]"
    911  1.1  christos 	       exit 1;;
    912  1.1  christos 	    esac
    913  1.1  christos 	done
    914  1.1  christos 
    915  1.1  christos 	# No positional arguments supported with enable-client
    916  1.1  christos 	if [ $# -ge "${OPTIND}" ]; then
    917  1.1  christos 	    $FATAL "usage: postfix tls $cmd [-r devrandom]"
    918  1.1  christos 	    exit 1
    919  1.1  christos 	fi
    920  1.1  christos 	# But, shift anyway
    921  1.1  christos 	shift `expr $OPTIND - 1`
    922  1.1  christos 
    923  1.1  christos 	init_random_source "${rand}" || exit 1
    924  1.1  christos 	enable_client || exit 1
    925  1.1  christos 	;;
    926  1.1  christos 
    927  1.1  christos enable-server)
    928  1.1  christos 	cmd=$1; shift; OPTIND=1
    929  1.1  christos 	algo=$DEFALG
    930  1.1  christos 	bits=
    931  1.1  christos 	rand=
    932  1.1  christos 	while getopts :a:b:r: _opt
    933  1.1  christos 	do
    934  1.1  christos 	    case $_opt in
    935  1.1  christos 	    a) algo="${OPTARG}";;
    936  1.1  christos 	    b) bits="${OPTARG}";;
    937  1.1  christos 	    r) rand="${OPTARG}";;
    938  1.1  christos 	    *) $FATAL "usage: postfix tls $cmd [-a algorithm] [-b bits ] [-r devrandom] [hostname ...]"
    939  1.1  christos 	       exit 1;;
    940  1.1  christos 	    esac
    941  1.1  christos 	done
    942  1.1  christos 
    943  1.1  christos 	# Here positional arguments are hostnames for the new certificate, as
    944  1.1  christos 	# many as the user wants
    945  1.1  christos 	shift `expr $OPTIND - 1`
    946  1.1  christos 
    947  1.1  christos 	case $algo in
    948  1.1  christos 	    "") $FATAL "Internal error: empty algorithm "; return 1;;
    949  1.1  christos 	  $rsa) : ${bits:=${RSA_BITS}};;
    950  1.1  christos 	$ecdsa) : ${bits:=${EC_CURVE}};;
    951  1.1  christos 	     *) $FATAL "Unsupported private key algorithm: $algo"
    952  1.1  christos 		exit 1;;
    953  1.1  christos 	esac
    954  1.1  christos 
    955  1.1  christos 	init_random_source "${rand}" || exit 1
    956  1.1  christos 	enable_server "${algo}" "${bits}" "$@" || exit 1
    957  1.1  christos 	;;
    958  1.1  christos 
    959  1.1  christos new-server-key)
    960  1.1  christos 	cmd=$1; shift; OPTIND=1
    961  1.1  christos 	algo=$DEFALG
    962  1.1  christos 	while getopts :a:b: _opt
    963  1.1  christos 	do
    964  1.1  christos 	    case $_opt in
    965  1.1  christos 	    a) algo="${OPTARG}";;
    966  1.1  christos 	    b) bits="${OPTARG}";;
    967  1.1  christos 	    *) $FATAL "usage: postfix tls $cmd [-a algorithm] [-b bits ] [hostname ...]"
    968  1.1  christos 	       exit 1;;
    969  1.1  christos 	    esac
    970  1.1  christos 	done
    971  1.1  christos 
    972  1.1  christos 	# Here positional arguments are hostnames for the new certificate, as
    973  1.1  christos 	# many as the user wants
    974  1.1  christos 	shift `expr $OPTIND - 1`
    975  1.1  christos 
    976  1.1  christos 	case $algo in
    977  1.1  christos 	    "") $FATAL "Internal error: empty algorithm "; return 1;;
    978  1.1  christos 	  $rsa) : ${bits:=${RSA_BITS}};;
    979  1.1  christos 	$ecdsa) : ${bits:=${EC_CURVE}};;
    980  1.1  christos 	     *) $FATAL "Unsupported public key algorithm: $algo"
    981  1.1  christos 		exit 1;;
    982  1.1  christos 	esac
    983  1.1  christos 
    984  1.1  christos 	# Force new key
    985  1.1  christos 	new_server_cert "${algo}" "${bits}" "" "" "$@" || exit 1
    986  1.1  christos 	;;
    987  1.1  christos 
    988  1.1  christos new-server-cert)
    989  1.1  christos 	cmd=$1; shift; OPTIND=1
    990  1.1  christos 	algo=$DEFALG
    991  1.1  christos 	while getopts :a:b: _opt
    992  1.1  christos 	do
    993  1.1  christos 	    case $_opt in
    994  1.1  christos 	    a) algo="${OPTARG}";;
    995  1.1  christos 	    b) bits="${OPTARG}";;
    996  1.1  christos 	    *) $FATAL "usage: postfix tls $cmd [-a algorithm] [-b bits ] [hostname ...]"
    997  1.1  christos 	       exit 1;;
    998  1.1  christos 	    esac
    999  1.1  christos 	done
   1000  1.1  christos 
   1001  1.1  christos 	# Here positional arguments are hostnames for the new certificate, as
   1002  1.1  christos 	# many as the user wants
   1003  1.1  christos 	shift `expr $OPTIND - 1`
   1004  1.1  christos 
   1005  1.1  christos 	case $algo in
   1006  1.1  christos 	    "") $FATAL "Invalid empty key algorithm"; exit 1;;
   1007  1.1  christos 	  $rsa) : ${bits:=${RSA_BITS}};;
   1008  1.1  christos 	$ecdsa) : ${bits:=${EC_CURVE}};;
   1009  1.1  christos 	     *) $FATAL "Unsupported private key algorithm: $algo"
   1010  1.1  christos 		exit 1;;
   1011  1.1  christos 	esac
   1012  1.1  christos 
   1013  1.1  christos 	# Existing keyfile or empty
   1014  1.1  christos 	set_keyfile "${algo}"
   1015  1.1  christos 
   1016  1.1  christos 	if [ -n "${keyfile}" -a ! -f "${keyfile}" ]; then
   1017  1.1  christos 	    echo "Key file: ${keyfile} not found, creating new keys" | $WARN
   1018  1.1  christos 	    keyfile=
   1019  1.1  christos 	fi
   1020  1.1  christos 
   1021  1.1  christos 	# Try to re-use (copy) existing key.
   1022  1.1  christos 	new_server_cert "${algo}" "${bits}" "${keyfile}" "" "$@" || exit 1
   1023  1.1  christos 	;;
   1024  1.1  christos 
   1025  1.1  christos deploy-server-cert)
   1026  1.1  christos 	if [ $# -ne 3 ]; then
   1027  1.1  christos 	    $FATAL "usage: postfix tls $1 certfile keyfile"
   1028  1.1  christos 	    exit 1
   1029  1.1  christos 	fi
   1030  1.1  christos 	shift
   1031  1.1  christos 
   1032  1.1  christos 	# User-specified key and cert pathnames are relative to the
   1033  1.1  christos 	# configuration directory
   1034  1.1  christos 	#
   1035  1.1  christos 	case "${1}" in
   1036  1.1  christos 	/*) certfile="${1}" ;;
   1037  1.1  christos 	 *) certfile="${config_directory}/${1}" ;;
   1038  1.1  christos 	esac
   1039  1.1  christos 	case "${2}" in
   1040  1.1  christos 	/*) keyfile="${2}" ;;
   1041  1.1  christos 	 *) keyfile="${config_directory}/${2}" ;;
   1042  1.1  christos 	esac
   1043  1.1  christos 
   1044  1.1  christos 	deploy_server_cert "${certfile}" "${keyfile}" || exit 1
   1045  1.1  christos 	info_server_deployed "${certfile}" "${keyfile}" "deploy" | $INFO
   1046  1.1  christos 	;;
   1047  1.1  christos 
   1048  1.1  christos output-server-csr)
   1049  1.1  christos 	cmd=$1; shift; OPTIND=1
   1050  1.1  christos 	k=
   1051  1.1  christos 	while getopts :k: _opt
   1052  1.1  christos 	do
   1053  1.1  christos 	    case $_opt in
   1054  1.1  christos 	    k) k="${OPTARG}";;
   1055  1.1  christos 	    *) $FATAL "usage: postfix tls $cmd [-k keyfile] [hostname ...]"
   1056  1.1  christos 	       exit 1;;
   1057  1.1  christos 	    esac
   1058  1.1  christos 	done
   1059  1.1  christos 
   1060  1.1  christos 	# Here positional arguments are hostnames for the new certificate, as
   1061  1.1  christos 	# many as the user wants
   1062  1.1  christos 	shift `expr $OPTIND - 1`
   1063  1.1  christos 
   1064  1.1  christos 	if [ -n "${k}" ]; then
   1065  1.1  christos 	    set_keyfile "${k}"
   1066  1.1  christos 	else
   1067  1.1  christos 	    for _algo in $rsa $ecdsa
   1068  1.1  christos 	    do
   1069  1.1  christos 		set_keyfile "${_algo}"
   1070  1.1  christos 		if [ -n "${keyfile}" ]; then
   1071  1.1  christos 		    break
   1072  1.1  christos 		fi
   1073  1.1  christos 	    done
   1074  1.1  christos 	fi
   1075  1.1  christos 
   1076  1.1  christos 	if [ -z "${keyfile}" -o ! -r "${keyfile}" ]; then
   1077  1.1  christos 	    $FATAL "No usable keyfile specified or configured"
   1078  1.1  christos 	    exit 1
   1079  1.1  christos 	fi
   1080  1.1  christos 
   1081  1.1  christos 	# Default <hostname> from $myhostname
   1082  1.1  christos 	if [ $# -eq 0 ]; then
   1083  1.1  christos 	    set_fqdn
   1084  1.1  christos 	    set -- "$fqdn"
   1085  1.1  christos 	fi
   1086  1.1  christos 
   1087  1.1  christos 	# Output a CSR for the requested names
   1088  1.1  christos 	output_server_csr "$keyfile" "$@" || exit 1
   1089  1.1  christos 	;;
   1090  1.1  christos 
   1091  1.1  christos output-server-tlsa)
   1092  1.1  christos 	cmd=$1; shift; OPTIND=1
   1093  1.1  christos 	hostname=
   1094  1.1  christos 	while getopts :h: _opt
   1095  1.1  christos 	do
   1096  1.1  christos 	    case $_opt in
   1097  1.1  christos 	    h) hostname="${OPTARG}";;
   1098  1.1  christos 	    *) $FATAL "usage: postfix tls $cmd [-h hostname] [keyfile ...]"
   1099  1.1  christos 	       exit 1;;
   1100  1.1  christos 	    esac
   1101  1.1  christos 	done
   1102  1.1  christos 	set_fqdn "${hostname}"
   1103  1.1  christos 
   1104  1.1  christos 	# Here positional arguments are keyfiles for which we ouput "3 1 1"
   1105  1.1  christos 	# TLSA RRs, as many keyfiles as the user wants.  By default the live
   1106  1.1  christos 	# RSA and/or ECDSA keys.
   1107  1.1  christos 	shift `expr $OPTIND - 1`
   1108  1.1  christos 
   1109  1.1  christos 	if [ $# -eq 0 ]; then set -- $rsa $ecdsa; fi
   1110  1.1  christos 
   1111  1.1  christos 	found=
   1112  1.1  christos 	for _k in "$@"
   1113  1.1  christos 	do
   1114  1.1  christos 	    set_keyfile "${_k}"
   1115  1.1  christos 	    if [ -z "${keyfile}" ]; then continue; fi
   1116  1.1  christos 	    echo "; ${keyfile}"
   1117  1.1  christos 	    output_server_tlsa "${fqdn}" "${keyfile}" || exit 1
   1118  1.1  christos 	    found=1
   1119  1.1  christos 	done
   1120  1.1  christos 	if [ -z "${found}" ]; then
   1121  1.1  christos 	    $FATAL "No usable keyfiles specified or configured"
   1122  1.1  christos 	    exit 1
   1123  1.1  christos 	fi
   1124  1.1  christos 	;;
   1125  1.1  christos 
   1126  1.1  christos all-default-client)
   1127  1.1  christos 	cmd=$1; shift; OPTIND=1
   1128  1.1  christos 
   1129  1.1  christos 	# No arguments for all-default-client
   1130  1.1  christos 	if [ $# -ge "${OPTIND}" ]; then
   1131  1.1  christos 	    $FATAL "usage: postfix tls $cmd"
   1132  1.1  christos 	    exit 1
   1133  1.1  christos 	fi
   1134  1.1  christos 
   1135  1.1  christos 	all_default ${client_settings} || exit 1
   1136  1.1  christos 	;;
   1137  1.1  christos 
   1138  1.1  christos all-default-server)
   1139  1.1  christos 	cmd=$1; shift; OPTIND=1
   1140  1.1  christos 
   1141  1.1  christos 	# No arguments for all-default-server
   1142  1.1  christos 	if [ $# -ge "${OPTIND}" ]; then
   1143  1.1  christos 	    $FATAL "usage: postfix tls $cmd"
   1144  1.1  christos 	    exit 1
   1145  1.1  christos 	fi
   1146  1.1  christos 
   1147  1.1  christos 	all_default ${server_settings} || exit 1
   1148  1.1  christos 	;;
   1149  1.1  christos 
   1150  1.1  christos *)
   1151  1.1  christos 	$ERROR "unknown tls command: '$1'"
   1152  1.1  christos 	$FATAL "usage: postfix tls enable-client (or enable-server, new-server-key, new-server-cert, deploy-server-cert, output-server-csr, output-server-tlsa, all-default-client, all-default-server)"
   1153  1.1  christos 	exit 1
   1154  1.1  christos 	;;
   1155  1.1  christos 
   1156  1.1  christos esac
   1157