1/*++
2/* NAME
3/*	tls_fprint 3
4/* SUMMARY
5/*	Digests fingerprints and all that.
6/* SYNOPSIS
7/*	#include <tls.h>
8/*
9/*	char	*tls_serverid_digest(props, protomask, ciphers)
10/*	const TLS_CLIENT_START_PROPS *props;
11/*	long	protomask;
12/*	const char *ciphers;
13/*
14/*	char	*tls_digest_encode(md_buf, md_len)
15/*	const unsigned char *md_buf;
16/*	const char *md_len;
17/*
18/*	char	*tls_data_fprint(buf, len, mdalg)
19/*	const char *buf;
20/*	int	len;
21/*	const char *mdalg;
22/*
23/*	char	*tls_cert_fprint(peercert, mdalg)
24/*	X509	*peercert;
25/*	const char *mdalg;
26/*
27/*	char	*tls_pkey_fprint(peercert, mdalg)
28/*	X509	*peercert;
29/*	const char *mdalg;
30/* DESCRIPTION
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().
35/*
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
39/*	with myfree().
40/*
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().
47/*
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().
54/*
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().
62/*
63/*	Arguments:
64/* .IP peercert
65/*	Server or client X.509 certificate.
66/* .IP md_buf
67/*	The raw binary digest.
68/* .IP md_len
69/*	The digest length in bytes.
70/* .IP mdalg
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.
74/* .IP buf
75/*	Input data for the message digest algorithm mdalg.
76/* .IP len
77/*	The length of the input data.
78/* .IP props
79/*	The client start properties for the session, which contains the
80/*	initial serverid from the SMTP client and the DANE verification
81/*	parameters.
82/* .IP protomask
83/*	The mask of protocol exclusions.
84/* .IP ciphers
85/*	The SSL client cipherlist.
86/* LICENSE
87/* .ad
88/* .fi
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.
92/* AUTHOR(S)
93/*	Wietse Venema
94/*	IBM T.J. Watson Research
95/*	P.O. Box 704
96/*	Yorktown Heights, NY 10598, USA
97/*
98/*	Viktor Dukhovni
99/*--*/
100
101/* System library. */
102
103#include <sys_defs.h>
104#include <ctype.h>
105
106#ifdef USE_TLS
107#include <string.h>
108
109/* Utility library. */
110
111#include <msg.h>
112#include <mymalloc.h>
113#include <stringops.h>
114
115/* Global library. */
116
117#include <mail_params.h>
118
119/* TLS library. */
120
121#define TLS_INTERNAL
122#include <tls.h>
123
124/* Application-specific. */
125
126static const char hexcodes[] = "0123456789ABCDEF";
127
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)
132
133#define digest_dane(dane, memb) do { \
134	if ((dane)->memb != 0) \
135	    checkok(digest_tlsa_usage(mdctx, (dane)->memb, #memb)); \
136    } while (0)
137
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); \
143	} \
144    } while (0)
145
146/* digest_tlsa_usage - digest TA or EE match list sorted by alg and value */
147
148static int digest_tlsa_usage(EVP_MD_CTX * mdctx, TLS_TLSA *tlsa,
149			             const char *usage)
150{
151    char  **dgst;
152    int     ok = 1;
153
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);
158    }
159    return (ok);
160}
161
162/* tls_serverid_digest - suffix props->serverid with parameter digest */
163
164char   *tls_serverid_digest(const TLS_CLIENT_START_PROPS *props, long protomask,
165			            const char *ciphers)
166{
167    EVP_MD_CTX *mdctx;
168    const EVP_MD *md;
169    const char *mdalg;
170    unsigned char md_buf[EVP_MAX_MD_SIZE];
171    unsigned int md_len;
172    int     ok = 1;
173    int     i;
174    long    sslversion;
175    VSTRING *result;
176
177    /*
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.
185     */
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);
189
190    /* Salt the session lookup key with the OpenSSL runtime version. */
191    sslversion = SSLeay();
192
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);
199
200    /*
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.
209     *
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
214     * reused.
215     *
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.
219     *
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.
224     */
225    if (props->dane) {
226	digest_dane(props->dane, ta);
227#if 0
228	digest_dane(props->dane, ee);		/* See above */
229#endif
230	digest_string(props->tls_level == TLS_LEV_DANE ? props->host : "");
231    }
232    checkok(EVP_DigestFinal_ex(mdctx, md_buf, &md_len));
233    EVP_MD_CTX_destroy(mdctx);
234    if (!ok)
235	msg_fatal("error computing %s message digest", mdalg);
236
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);
240
241    /*
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.
246     *
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
250     * result.
251     */
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)]);
258    }
259    VSTRING_TERMINATE(result);
260    return (vstring_export(result));
261}
262
263/* tls_digest_encode - encode message digest binary blob as xx:xx:... */
264
265char   *tls_digest_encode(const unsigned char *md_buf, int md_len)
266{
267    int     i;
268    char   *result = mymalloc(md_len * 3);
269
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);
273
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';
279    }
280    return (result);
281}
282
283/* tls_data_fprint - compute and encode digest of binary object */
284
285char   *tls_data_fprint(const char *buf, int len, const char *mdalg)
286{
287    EVP_MD_CTX *mdctx;
288    const EVP_MD *md;
289    unsigned char md_buf[EVP_MAX_MD_SIZE];
290    unsigned int md_len;
291    int     ok = 1;
292
293    /* Previously available in "init" routine. */
294    if ((md = EVP_get_digestbyname(mdalg)) == 0)
295	msg_panic("digest algorithm \"%s\" not found", mdalg);
296
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);
302    if (!ok)
303	msg_fatal("error computing %s message digest", mdalg);
304
305    return (tls_digest_encode(md_buf, md_len));
306}
307
308/* tls_cert_fprint - extract certificate fingerprint */
309
310char   *tls_cert_fprint(X509 *peercert, const char *mdalg)
311{
312    int     len;
313    char   *buf;
314    char   *buf2;
315    char   *result;
316
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");
322
323    result = tls_data_fprint(buf, len, mdalg);
324    myfree(buf);
325
326    return (result);
327}
328
329/* tls_pkey_fprint - extract public key fingerprint from certificate */
330
331char   *tls_pkey_fprint(X509 *peercert, const char *mdalg)
332{
333    if (var_tls_bc_pkey_fprint) {
334	const char *myname = "tls_pkey_fprint";
335	ASN1_BIT_STRING *key;
336	char   *result;
337
338	key = X509_get0_pubkey_bitstr(peercert);
339	if (key == 0)
340	    msg_fatal("%s: error extracting legacy public-key fingerprint: %m",
341		      myname);
342
343	result = tls_data_fprint((char *) key->data, key->length, mdalg);
344	return (result);
345    } else {
346	int     len;
347	char   *buf;
348	char   *buf2;
349	char   *result;
350
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");
356
357	result = tls_data_fprint(buf, len, mdalg);
358	myfree(buf);
359	return (result);
360    }
361}
362
363#endif
364