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