1 /* $NetBSD: tls_fprint.c,v 1.5 2025/02/25 19:15:50 christos Exp $ */
2
3 /*++
4 /* NAME
5 /* tls_fprint 3
6 /* SUMMARY
7 /* Digests fingerprints and all that.
8 /* SYNOPSIS
9 /* #include <tls.h>
10 /*
11 /* EVP_MD *tls_digest_byname(const char *mdalg, EVP_MD_CTX **mdctxPtr)
12 /* const char *mdalg;
13 /* EVP_MD_CTX **mdctxPtr;
14 /*
15 /* char *tls_serverid_digest(TLScontext, props, ciphers)
16 /* TLS_SESS_STATE *TLScontext;
17 /* const TLS_CLIENT_START_PROPS *props;
18 /* const char *ciphers;
19 /*
20 /* char *tls_digest_encode(md_buf, md_len)
21 /* const unsigned char *md_buf;
22 /* const char *md_len;
23 /*
24 /* char *tls_cert_fprint(peercert, mdalg)
25 /* X509 *peercert;
26 /* const char *mdalg;
27 /*
28 /* char *tls_pkey_fprint(peercert, mdalg)
29 /* EVP_PKEY *peerpkey;
30 /* const char *mdalg;
31 /* DESCRIPTION
32 /* tls_digest_byname() constructs, and optionally returns, an EVP_MD_CTX
33 /* handle for performing digest operations with the algorithm named by the
34 /* mdalg parameter. The return value is non-null on success, and holds a
35 /* digest algorithm handle. If the mdctxPtr argument is non-null the
36 /* created context is returned to the caller, who is then responsible for
37 /* deleting it by calling EVP_MD_ctx_free() once it is no longer needed.
38 /*
39 /* tls_digest_encode() converts a binary message digest to a hex ASCII
40 /* format with ':' separators between each pair of hex digits.
41 /* The return value is dynamically allocated with mymalloc(),
42 /* and the caller must eventually free it with myfree().
43 /*
44 /* tls_cert_fprint() returns a fingerprint of the given
45 /* certificate using the requested message digest, formatted
46 /* with tls_digest_encode(). Panics if the
47 /* (previously verified) digest algorithm is not found. The return
48 /* value is dynamically allocated with mymalloc(), and the caller
49 /* must eventually free it with myfree().
50 /*
51 /* tls_pkey_fprint() returns a public-key fingerprint; in all
52 /* other respects the function behaves as tls_cert_fprint().
53 /* The return value is dynamically allocated with mymalloc(),
54 /* and the caller must eventually free it with myfree().
55 /*
56 /* tls_serverid_digest() suffixes props->serverid computed by the SMTP
57 /* client with "&" plus a digest of additional parameters needed to ensure
58 /* that re-used sessions are more likely to be reused and that they will
59 /* satisfy all protocol and security requirements. The return value is
60 /* dynamically allocated with mymalloc(), and the caller must eventually
61 /* free it with myfree().
62 /*
63 /* Arguments:
64 /* .IP mdalg
65 /* A digest algorithm name, such as "sha256".
66 /* .IP peercert
67 /* Server or client X.509 certificate.
68 /* .IP md_buf
69 /* The raw binary digest.
70 /* .IP md_len
71 /* The digest length in bytes.
72 /* .IP mdalg
73 /* Name of a message digest algorithm suitable for computing secure
74 /* (1st pre-image resistant) message digests of certificates. For now,
75 /* md5, sha1, or member of SHA-2 family if supported by OpenSSL.
76 /* .IP mdctxPtr
77 /* Pointer to an (EVP_MD_CTX *) handle, or NULL if only probing for
78 /* algorithm support without immediate use in mind.
79 /* .IP buf
80 /* Input data for the message digest algorithm mdalg.
81 /* .IP len
82 /* The length of the input data.
83 /* .IP props
84 /* The client start properties for the session, which contains the
85 /* initial serverid from the SMTP client and the DANE verification
86 /* parameters.
87 /* .IP protomask
88 /* The mask of protocol exclusions.
89 /* .IP ciphers
90 /* The SSL client cipherlist.
91 /* LICENSE
92 /* .ad
93 /* .fi
94 /* This software is free. You can do with it whatever you want.
95 /* The original author kindly requests that you acknowledge
96 /* the use of his software.
97 /* AUTHOR(S)
98 /* Wietse Venema
99 /* IBM T.J. Watson Research
100 /* P.O. Box 704
101 /* Yorktown Heights, NY 10598, USA
102 /*
103 /* Viktor Dukhovni
104 /*--*/
105
106 /* System library. */
107
108 #include <sys_defs.h>
109 #include <ctype.h>
110
111 #ifdef USE_TLS
112 #include <string.h>
113
114 /* Utility library. */
115
116 #include <msg.h>
117 #include <mymalloc.h>
118 #include <stringops.h>
119
120 /* Global library. */
121
122 #include <mail_params.h>
123
124 /* TLS library. */
125
126 #define TLS_INTERNAL
127 #include <tls.h>
128
129 /* Application-specific. */
130
131 static const char hexcodes[] = "0123456789ABCDEF";
132
133 #define CHECK_OK_AND(stillok) (ok = ok && (stillok))
134 #define CHECK_OK_AND_DIGEST_OBJECT(m, p) \
135 CHECK_OK_AND_DIGEST_DATA((m), (unsigned char *)(p), sizeof(*(p)))
136 #define CHECK_OK_AND_DIGEST_DATA(m, p, l) CHECK_OK_AND(digest_bytes((m), (p), (l)))
137 #define CHECK_OK_AND_DIGEST_CHARS(m, s) CHECK_OK_AND(digest_chars((m), (s)))
138
139 /* digest_bytes - hash octet string of given length */
140
digest_bytes(EVP_MD_CTX * ctx,const unsigned char * buf,size_t len)141 static int digest_bytes(EVP_MD_CTX *ctx, const unsigned char *buf, size_t len)
142 {
143 return (EVP_DigestUpdate(ctx, buf, len));
144 }
145
146 /* digest_chars - hash string including trailing NUL */
147
digest_chars(EVP_MD_CTX * ctx,const char * s)148 static int digest_chars(EVP_MD_CTX *ctx, const char *s)
149 {
150 return (EVP_DigestUpdate(ctx, s, strlen(s) + 1));
151 }
152
153 /* tlsa_cmp - compare TLSA RRs for sorting to canonical order */
154
tlsa_cmp(const void * a,const void * b)155 static int tlsa_cmp(const void *a, const void *b)
156 {
157 TLS_TLSA *p = *(TLS_TLSA **) a;
158 TLS_TLSA *q = *(TLS_TLSA **) b;
159 int d;
160
161 if ((d = (int) p->usage - (int) q->usage) != 0)
162 return d;
163 if ((d = (int) p->selector - (int) q->selector) != 0)
164 return d;
165 if ((d = (int) p->mtype - (int) q->mtype) != 0)
166 return d;
167 if ((d = (int) p->length - (int) q->length) != 0)
168 return d;
169 return (memcmp(p->data, q->data, p->length));
170 }
171
172 /* tls_digest_tlsa - fold in digest of TLSA records */
173
tls_digest_tlsa(EVP_MD_CTX * mdctx,TLS_TLSA * tlsa)174 static int tls_digest_tlsa(EVP_MD_CTX *mdctx, TLS_TLSA *tlsa)
175 {
176 TLS_TLSA *p;
177 TLS_TLSA **arr;
178 int ok = 1;
179 int n;
180 int i;
181
182 for (n = 0, p = tlsa; p != 0; p = p->next)
183 ++n;
184 arr = (TLS_TLSA **) mymalloc(n * sizeof(*arr));
185 for (i = 0, p = tlsa; p; p = p->next)
186 arr[i++] = (void *) p;
187 qsort(arr, n, sizeof(arr[0]), tlsa_cmp);
188
189 CHECK_OK_AND_DIGEST_OBJECT(mdctx, &n);
190 for (i = 0; i < n; ++i) {
191 CHECK_OK_AND_DIGEST_OBJECT(mdctx, &arr[i]->usage);
192 CHECK_OK_AND_DIGEST_OBJECT(mdctx, &arr[i]->selector);
193 CHECK_OK_AND_DIGEST_OBJECT(mdctx, &arr[i]->mtype);
194 CHECK_OK_AND_DIGEST_OBJECT(mdctx, &arr[i]->length);
195 CHECK_OK_AND_DIGEST_DATA(mdctx, arr[i]->data, arr[i]->length);
196 }
197 myfree((void *) arr);
198 return (ok);
199 }
200
201 /* tls_digest_byname - test availability or prepare to use digest */
202
tls_digest_byname(const char * mdalg,EVP_MD_CTX ** mdctxPtr)203 const EVP_MD *tls_digest_byname(const char *mdalg, EVP_MD_CTX **mdctxPtr)
204 {
205 const EVP_MD *md;
206 EVP_MD_CTX *mdctx = NULL;
207 int ok = 1;
208
209 /*
210 * In OpenSSL 3.0, because of dynamically variable algorithm providers,
211 * there is a time-of-check/time-of-use issue that means that abstract
212 * algorithm handles returned by EVP_get_digestbyname() can (and not
213 * infrequently do) return ultimately unusable algorithms, to check for
214 * actual availability, one needs to use the new EVP_MD_fetch() API, or
215 * indirectly check usability by creating a concrete context. We take the
216 * latter approach here (works for 1.1.1 without #ifdef).
217 *
218 * Note that EVP_MD_CTX_{create,destroy} were renamed to, respectively,
219 * EVP_MD_CTX_{new,free} in OpenSSL 1.1.0.
220 */
221 CHECK_OK_AND(md = EVP_get_digestbyname(mdalg));
222
223 /*
224 * Sanity check: Newer shared libraries could (hypothetical ABI break)
225 * allow larger digests, we avoid such poison algorithms.
226 */
227 CHECK_OK_AND(EVP_MD_size(md) <= EVP_MAX_MD_SIZE);
228 CHECK_OK_AND(mdctx = EVP_MD_CTX_new());
229 CHECK_OK_AND(EVP_DigestInit_ex(mdctx, md, NULL));
230
231
232 if (ok && mdctxPtr != 0)
233 *mdctxPtr = mdctx;
234 else
235 EVP_MD_CTX_free(mdctx);
236 return (ok ? md : 0);
237 }
238
239 /* tls_serverid_digest - suffix props->serverid with parameter digest */
240
tls_serverid_digest(TLS_SESS_STATE * TLScontext,const TLS_CLIENT_START_PROPS * props,const char * ciphers)241 char *tls_serverid_digest(TLS_SESS_STATE *TLScontext,
242 const TLS_CLIENT_START_PROPS *props,
243 const char *ciphers)
244 {
245 EVP_MD_CTX *mdctx;
246 const char *mdalg;
247 unsigned char md_buf[EVP_MAX_MD_SIZE];
248 unsigned int md_len;
249 int ok = 1;
250 int i;
251 long sslversion;
252 VSTRING *result;
253
254 /*
255 * Try to use sha256: our serverid choice should be strong enough to
256 * resist 2nd-preimage attacks with a difficulty comparable to that of
257 * DANE TLSA digests. Failing that, we compute serverid digests with the
258 * default digest, but DANE requires sha256 and sha512, so if we must
259 * fall back to our default digest, DANE support won't be available. We
260 * panic if the fallback algorithm is not available, as it was verified
261 * available in tls_client_init() and must not simply vanish. Our
262 * provider set is not expected to change once the OpenSSL library is
263 * initialized.
264 */
265 if (tls_digest_byname(mdalg = LN_sha256, &mdctx) == 0
266 && tls_digest_byname(mdalg = props->mdalg, &mdctx) == 0)
267 msg_panic("digest algorithm \"%s\" not found", props->mdalg);
268
269 /* Salt the session lookup key with the OpenSSL runtime version. */
270 sslversion = OpenSSL_version_num();
271
272 CHECK_OK_AND_DIGEST_CHARS(mdctx, props->helo ? props->helo : "");
273 CHECK_OK_AND_DIGEST_OBJECT(mdctx, &sslversion);
274 CHECK_OK_AND_DIGEST_CHARS(mdctx, props->protocols);
275 CHECK_OK_AND_DIGEST_CHARS(mdctx, ciphers);
276
277 /* Just in case we make this destination-policy specific */
278 CHECK_OK_AND_DIGEST_OBJECT(mdctx, &props->enable_rpk);
279
280 /*
281 * Ensure separation of caches for sessions where DANE trust
282 * configuration succeeded from those where it did not. The latter
283 * should always see a certificate validation failure, both on initial
284 * handshake and on resumption.
285 */
286 CHECK_OK_AND_DIGEST_OBJECT(mdctx, &TLScontext->must_fail);
287
288 /*
289 * DNS-based or synthetic DANE trust settings are potentially used at all
290 * levels above "encrypt".
291 */
292 if (TLScontext->level > TLS_LEV_ENCRYPT
293 && props->dane && props->dane->tlsa) {
294 CHECK_OK_AND(tls_digest_tlsa(mdctx, props->dane->tlsa));
295 } else {
296 int none = 0; /* Record a TLSA RR count of zero */
297
298 CHECK_OK_AND_DIGEST_OBJECT(mdctx, &none);
299 }
300
301 /*
302 * Include the chosen SNI name, which can affect server certificate
303 * selection.
304 */
305 if (TLScontext->level > TLS_LEV_ENCRYPT && TLScontext->peer_sni)
306 CHECK_OK_AND_DIGEST_CHARS(mdctx, TLScontext->peer_sni);
307 else
308 CHECK_OK_AND_DIGEST_CHARS(mdctx, "");
309
310 CHECK_OK_AND(EVP_DigestFinal_ex(mdctx, md_buf, &md_len));
311 EVP_MD_CTX_destroy(mdctx);
312 if (!ok)
313 msg_fatal("error computing %s message digest", mdalg);
314
315 /* Check for OpenSSL contract violation */
316 if (md_len > EVP_MAX_MD_SIZE)
317 msg_panic("unexpectedly large %s digest size: %u", mdalg, md_len);
318
319 /*
320 * Append the digest to the serverid. We don't compare this digest to
321 * any user-specified fingerprints. Therefore, we don't need to use a
322 * colon-separated format, which saves space in the TLS session cache and
323 * makes logging of session cache lookup keys more readable.
324 *
325 * This does however duplicate a few lines of code from the digest encoder
326 * for colon-separated cert and pkey fingerprints. If that is a
327 * compelling reason to consolidate, we could use that and append the
328 * result.
329 */
330 result = vstring_alloc(strlen(props->serverid) + 1 + 2 * md_len);
331 vstring_strcpy(result, props->serverid);
332 VSTRING_ADDCH(result, '&');
333 for (i = 0; i < md_len; i++) {
334 VSTRING_ADDCH(result, hexcodes[(md_buf[i] & 0xf0) >> 4U]);
335 VSTRING_ADDCH(result, hexcodes[(md_buf[i] & 0x0f)]);
336 }
337 VSTRING_TERMINATE(result);
338 return (vstring_export(result));
339 }
340
341 /* tls_digest_encode - encode message digest binary blob as xx:xx:... */
342
tls_digest_encode(const unsigned char * md_buf,int md_len)343 char *tls_digest_encode(const unsigned char *md_buf, int md_len)
344 {
345 int i;
346 char *result = mymalloc(md_len * 3);
347
348 /* Check for contract violation */
349 if (md_len > EVP_MAX_MD_SIZE || md_len >= INT_MAX / 3)
350 msg_panic("unexpectedly large message digest size: %u", md_len);
351
352 /* No risk of overruns, len is bounded by OpenSSL digest length */
353 for (i = 0; i < md_len; i++) {
354 result[i * 3] = hexcodes[(md_buf[i] & 0xf0) >> 4U];
355 result[(i * 3) + 1] = hexcodes[(md_buf[i] & 0x0f)];
356 result[(i * 3) + 2] = (i + 1 != md_len) ? ':' : '\0';
357 }
358 return (result);
359 }
360
361 /* tls_data_fprint - compute and encode digest of binary object */
362
tls_data_fprint(const unsigned char * buf,int len,const char * mdalg)363 static char *tls_data_fprint(const unsigned char *buf, int len, const char *mdalg)
364 {
365 EVP_MD_CTX *mdctx = NULL;
366 unsigned char md_buf[EVP_MAX_MD_SIZE];
367 unsigned int md_len;
368 int ok = 1;
369
370 /* Previously available in "init" routine. */
371 if (tls_digest_byname(mdalg, &mdctx) == 0)
372 msg_panic("digest algorithm \"%s\" not found", mdalg);
373
374 CHECK_OK_AND_DIGEST_DATA(mdctx, buf, len);
375 CHECK_OK_AND(EVP_DigestFinal_ex(mdctx, md_buf, &md_len));
376 EVP_MD_CTX_destroy(mdctx);
377 if (!ok)
378 msg_fatal("error computing %s message digest", mdalg);
379
380 return (tls_digest_encode(md_buf, md_len));
381 }
382
383 /* tls_cert_fprint - extract certificate fingerprint */
384
tls_cert_fprint(X509 * peercert,const char * mdalg)385 char *tls_cert_fprint(X509 *peercert, const char *mdalg)
386 {
387 int len;
388 unsigned char *buf;
389 unsigned char *buf2;
390 char *result;
391
392 len = i2d_X509(peercert, NULL);
393 buf2 = buf = mymalloc(len);
394 i2d_X509(peercert, &buf2);
395 if (buf2 - buf != len)
396 msg_panic("i2d_X509 invalid result length");
397
398 result = tls_data_fprint(buf, len, mdalg);
399 myfree(buf);
400
401 return (result);
402 }
403
404 /* tls_pkey_fprint - extract public key fingerprint */
405
tls_pkey_fprint(EVP_PKEY * peerpkey,const char * mdalg)406 char *tls_pkey_fprint(EVP_PKEY *peerpkey, const char *mdalg)
407 {
408 int len;
409 unsigned char *buf;
410 unsigned char *buf2;
411 char *result;
412
413 len = i2d_PUBKEY(peerpkey, NULL);
414 buf2 = buf = mymalloc(len);
415 i2d_PUBKEY(peerpkey, &buf2);
416 if (buf2 - buf != len)
417 msg_panic("i2d_PUBKEY invalid result length");
418
419 result = tls_data_fprint(buf, len, mdalg);
420 myfree(buf);
421 return (result);
422 }
423
424 #endif
425