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