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