postfix-tls-script revision 1.6 1 1.1 christos #!/bin/sh
2 1.1 christos # $NetBSD: postfix-tls-script,v 1.6 2026/05/09 18:49:13 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.6 christos # .IP "\fBsmtp_tls_security_level (Postfix >= 3.11: may; Postfix < 3.11: 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