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