5
/* Digests fingerprints and all that.
9
/* char *tls_serverid_digest(props, protomask, ciphers)
10
/* const TLS_CLIENT_START_PROPS *props;
12
/* const char *ciphers;
14
/* char *tls_digest_encode(md_buf, md_len)
15
/* const unsigned char *md_buf;
16
/* const char *md_len;
18
/* char *tls_data_fprint(buf, len, mdalg)
23
/* char *tls_cert_fprint(peercert, mdalg)
27
/* char *tls_pkey_fprint(peercert, mdalg)
31
/* tls_digest_encode() converts a binary message digest to a hex ASCII
32
/* format with ':' separators between each pair of hex digits.
33
/* The return value is dynamically allocated with mymalloc(),
34
/* and the caller must eventually free it with myfree().
36
/* tls_data_fprint() digests unstructured data, and encodes the digested
37
/* result via tls_digest_encode(). The return value is dynamically
38
/* allocated with mymalloc(), and the caller must eventually free it
41
/* tls_cert_fprint() returns a fingerprint of the the given
42
/* certificate using the requested message digest, formatted
43
/* with tls_digest_encode(). Panics if the
44
/* (previously verified) digest algorithm is not found. The return
45
/* value is dynamically allocated with mymalloc(), and the caller
46
/* must eventually free it with myfree().
48
/* tls_pkey_fprint() returns a public-key fingerprint; in all
49
/* other respects the function behaves as tls_cert_fprint().
50
/* The var_tls_bc_pkey_fprint variable enables an incorrect
51
/* algorithm that was used in Postfix versions 2.9.[0-5].
52
/* The return value is dynamically allocated with mymalloc(),
53
/* and the caller must eventually free it with myfree().
55
/* tls_serverid_digest() suffixes props->serverid computed by the SMTP
56
/* client with "&" plus a digest of additional parameters
57
/* needed to ensure that re-used sessions are more likely to
58
/* be reused and that they will satisfy all protocol and
59
/* security requirements.
60
/* The return value is dynamically allocated with mymalloc(),
61
/* and the caller must eventually free it with myfree().
65
/* Server or client X.509 certificate.
67
/* The raw binary digest.
69
/* The digest length in bytes.
71
/* Name of a message digest algorithm suitable for computing secure
72
/* (1st pre-image resistant) message digests of certificates. For now,
73
/* md5, sha1, or member of SHA-2 family if supported by OpenSSL.
75
/* Input data for the message digest algorithm mdalg.
77
/* The length of the input data.
79
/* The client start properties for the session, which contains the
80
/* initial serverid from the SMTP client and the DANE verification
83
/* The mask of protocol exclusions.
85
/* The SSL client cipherlist.
89
/* This software is free. You can do with it whatever you want.
90
/* The original author kindly requests that you acknowledge
91
/* the use of his software.
94
/* IBM T.J. Watson Research
96
/* Yorktown Heights, NY 10598, USA
101
/* System library. */
103
#include <sys_defs.h>
109
/* Utility library. */
112
#include <mymalloc.h>
113
#include <stringops.h>
115
/* Global library. */
117
#include <mail_params.h>
124
/* Application-specific. */
126
static const char hexcodes[] = "0123456789ABCDEF";
128
#define checkok(ret) (ok &= ((ret) ? 1 : 0))
129
#define digest_data(p, l) checkok(EVP_DigestUpdate(mdctx, (char *)(p), (l)))
130
#define digest_object(p) digest_data((p), sizeof(*(p)))
131
#define digest_string(s) digest_data((s), strlen(s)+1)
133
#define digest_dane(dane, memb) do { \
134
if ((dane)->memb != 0) \
135
checkok(digest_tlsa_usage(mdctx, (dane)->memb, #memb)); \
138
#define digest_tlsa_argv(tlsa, memb) do { \
139
if ((tlsa)->memb) { \
140
digest_string(#memb); \
141
for (dgst = (tlsa)->memb->argv; *dgst; ++dgst) \
142
digest_string(*dgst); \
146
/* digest_tlsa_usage - digest TA or EE match list sorted by alg and value */
148
static int digest_tlsa_usage(EVP_MD_CTX * mdctx, TLS_TLSA *tlsa,
154
for (digest_string(usage); tlsa; tlsa = tlsa->next) {
155
digest_string(tlsa->mdalg);
156
digest_tlsa_argv(tlsa, pkeys);
157
digest_tlsa_argv(tlsa, certs);
162
/* tls_serverid_digest - suffix props->serverid with parameter digest */
164
char *tls_serverid_digest(const TLS_CLIENT_START_PROPS *props, long protomask,
170
unsigned char md_buf[EVP_MAX_MD_SIZE];
178
* Try to use sha256: our serverid choice should be strong enough to
179
* resist 2nd-preimage attacks with a difficulty comparable to that of
180
* DANE TLSA digests. Failing that, we compute serverid digests with the
181
* default digest, but DANE requires sha256 and sha512, so if we must
182
* fall back to our default digest, DANE support won't be available. We
183
* panic if the fallback algorithm is not available, as it was verified
184
* available in tls_client_init() and must not simply vanish.
186
if ((md = EVP_get_digestbyname(mdalg = "sha256")) == 0
187
&& (md = EVP_get_digestbyname(mdalg = props->mdalg)) == 0)
188
msg_panic("digest algorithm \"%s\" not found", mdalg);
190
/* Salt the session lookup key with the OpenSSL runtime version. */
191
sslversion = SSLeay();
193
mdctx = EVP_MD_CTX_create();
194
checkok(EVP_DigestInit_ex(mdctx, md, NULL));
195
digest_string(props->helo ? props->helo : "");
196
digest_object(&sslversion);
197
digest_object(&protomask);
198
digest_string(ciphers);
201
* All we get from the session cache is a single bit telling us whether
202
* the certificate is trusted or not, but we need to know whether the
203
* trust is CA-based (in that case we must do name checks) or whether it
204
* is a direct end-point match. We mustn't confuse the two, so it is
205
* best to process only TA trust in the verify callback and check the EE
206
* trust after. This works since re-used sessions always have access to
207
* the leaf certificate, while only the original session has the leaf and
208
* the full trust chain.
210
* Only the trust anchor matchlist is hashed into the session key. The end
211
* entity certs are not used to determine whether a certificate is
212
* trusted or not, rather these are rechecked against the leaf cert
213
* outside the verification callback, each time a session is created or
216
* Therefore, the security context of the session does not depend on the EE
217
* matching data, which is checked separately each time. So we exclude
218
* the EE part of the DANE structure from the serverid digest.
220
* If the security level is "dane", we send SNI information to the peer.
221
* This may cause it to respond with a non-default certificate. Since
222
* certificates for sessions with no or different SNI data may not match,
223
* we must include the SNI name in the session id.
226
digest_dane(props->dane, ta);
228
digest_dane(props->dane, ee); /* See above */
230
digest_string(props->tls_level == TLS_LEV_DANE ? props->host : "");
232
checkok(EVP_DigestFinal_ex(mdctx, md_buf, &md_len));
233
EVP_MD_CTX_destroy(mdctx);
235
msg_fatal("error computing %s message digest", mdalg);
237
/* Check for OpenSSL contract violation */
238
if (md_len > EVP_MAX_MD_SIZE)
239
msg_panic("unexpectedly large %s digest size: %u", mdalg, md_len);
242
* Append the digest to the serverid. We don't compare this digest to
243
* any user-specified fingerprints. Therefore, we don't need to use a
244
* colon-separated format, which saves space in the TLS session cache and
245
* makes logging of session cache lookup keys more readable.
247
* This does however duplicate a few lines of code from the digest encoder
248
* for colon-separated cert and pkey fingerprints. If that is a
249
* compelling reason to consolidate, we could use that and append the
252
result = vstring_alloc(strlen(props->serverid) + 1 + 2 * md_len);
253
vstring_strcpy(result, props->serverid);
254
VSTRING_ADDCH(result, '&');
255
for (i = 0; i < md_len; i++) {
256
VSTRING_ADDCH(result, hexcodes[(md_buf[i] & 0xf0) >> 4U]);
257
VSTRING_ADDCH(result, hexcodes[(md_buf[i] & 0x0f)]);
259
VSTRING_TERMINATE(result);
260
return (vstring_export(result));
263
/* tls_digest_encode - encode message digest binary blob as xx:xx:... */
265
char *tls_digest_encode(const unsigned char *md_buf, int md_len)
268
char *result = mymalloc(md_len * 3);
270
/* Check for contract violation */
271
if (md_len > EVP_MAX_MD_SIZE || md_len >= INT_MAX / 3)
272
msg_panic("unexpectedly large message digest size: %u", md_len);
274
/* No risk of overrunes, len is bounded by OpenSSL digest length */
275
for (i = 0; i < md_len; i++) {
276
result[i * 3] = hexcodes[(md_buf[i] & 0xf0) >> 4U];
277
result[(i * 3) + 1] = hexcodes[(md_buf[i] & 0x0f)];
278
result[(i * 3) + 2] = (i + 1 != md_len) ? ':' : '\0';
283
/* tls_data_fprint - compute and encode digest of binary object */
285
char *tls_data_fprint(const char *buf, int len, const char *mdalg)
289
unsigned char md_buf[EVP_MAX_MD_SIZE];
293
/* Previously available in "init" routine. */
294
if ((md = EVP_get_digestbyname(mdalg)) == 0)
295
msg_panic("digest algorithm \"%s\" not found", mdalg);
297
mdctx = EVP_MD_CTX_create();
298
checkok(EVP_DigestInit_ex(mdctx, md, NULL));
299
digest_data(buf, len);
300
checkok(EVP_DigestFinal_ex(mdctx, md_buf, &md_len));
301
EVP_MD_CTX_destroy(mdctx);
303
msg_fatal("error computing %s message digest", mdalg);
305
return (tls_digest_encode(md_buf, md_len));
308
/* tls_cert_fprint - extract certificate fingerprint */
310
char *tls_cert_fprint(X509 *peercert, const char *mdalg)
317
len = i2d_X509(peercert, NULL);
318
buf2 = buf = mymalloc(len);
319
i2d_X509(peercert, (unsigned char **) &buf2);
320
if (buf2 - buf != len)
321
msg_panic("i2d_X509 invalid result length");
323
result = tls_data_fprint(buf, len, mdalg);
329
/* tls_pkey_fprint - extract public key fingerprint from certificate */
331
char *tls_pkey_fprint(X509 *peercert, const char *mdalg)
333
if (var_tls_bc_pkey_fprint) {
334
const char *myname = "tls_pkey_fprint";
335
ASN1_BIT_STRING *key;
338
key = X509_get0_pubkey_bitstr(peercert);
340
msg_fatal("%s: error extracting legacy public-key fingerprint: %m",
343
result = tls_data_fprint((char *) key->data, key->length, mdalg);
351
len = i2d_X509_PUBKEY(X509_get_X509_PUBKEY(peercert), NULL);
352
buf2 = buf = mymalloc(len);
353
i2d_X509_PUBKEY(X509_get_X509_PUBKEY(peercert), (unsigned char **) &buf2);
354
if (buf2 - buf != len)
355
msg_panic("i2d_X509_PUBKEY invalid result length");
357
result = tls_data_fprint(buf, len, mdalg);