1/*	$NetBSD: tls_verify.c,v 1.4 2022/10/08 16:12:50 christos Exp $	*/
2
3/*++
4/* NAME
5/*	tls_verify 3
6/* SUMMARY
7/*	peer name and peer certificate verification
8/* SYNOPSIS
9/*	#define TLS_INTERNAL
10/*	#include <tls.h>
11/*
12/*	int	tls_verify_certificate_callback(ok, ctx)
13/*	int	ok;
14/*	X509_STORE_CTX *ctx;
15/*
16/*	int     tls_log_verify_error(TLScontext)
17/*	TLS_SESS_STATE *TLScontext;
18/*
19/*	char *tls_peer_CN(peercert, TLScontext)
20/*	X509   *peercert;
21/*	TLS_SESS_STATE *TLScontext;
22/*
23/*	char *tls_issuer_CN(peercert, TLScontext)
24/*	X509   *peercert;
25/*	TLS_SESS_STATE *TLScontext;
26/* DESCRIPTION
27/*	tls_verify_certificate_callback() is called several times (directly
28/*	or indirectly) from crypto/x509/x509_vfy.c. It collects errors
29/*	and trust information at each element of the trust chain.
30/*	The last call at depth 0 sets the verification status based
31/*	on the cumulative winner (lowest depth) of errors vs. trust.
32/*	We always return 1 (continue the handshake) and handle trust
33/*	and peer-name verification problems at the application level.
34/*
35/*	tls_log_verify_error() (called only when we care about the
36/*	peer certificate, that is not when opportunistic) logs the
37/*	reason why the certificate failed to be verified.
38/*
39/*	tls_peer_CN() returns the text CommonName for the peer
40/*	certificate subject, or an empty string if no CommonName was
41/*	found. The result is allocated with mymalloc() and must be
42/*	freed by the caller; it contains UTF-8 without non-printable
43/*	ASCII characters.
44/*
45/*	tls_issuer_CN() returns the text CommonName for the peer
46/*	certificate issuer, or an empty string if no CommonName was
47/*	found. The result is allocated with mymalloc() and must be
48/*	freed by the caller; it contains UTF-8 without non-printable
49/*	ASCII characters.
50/*
51/*	Arguments:
52/* .IP ok
53/*	Result of prior verification: non-zero means success.  In
54/*	order to reduce the noise level, some tests or error reports
55/*	are disabled when verification failed because of some
56/*	earlier problem.
57/* .IP ctx
58/*	SSL application context. This links to the Postfix TLScontext
59/*	with enforcement and logging options.
60/* .IP gn
61/*	An OpenSSL GENERAL_NAME structure holding a DNS subjectAltName
62/*	to be decoded and checked for validity.
63/* .IP peercert
64/*	Server or client X.509 certificate.
65/* .IP TLScontext
66/*	Server or client context for warning messages.
67/* DIAGNOSTICS
68/*	tls_peer_CN() and tls_issuer_CN() log a warning when 1) the requested
69/*	information is not available in the specified certificate, 2) the
70/*	result exceeds a fixed limit, 3) the result contains NUL characters or
71/*	the result contains non-printable or non-ASCII characters.
72/* LICENSE
73/* .ad
74/* .fi
75/*	This software is free. You can do with it whatever you want.
76/*	The original author kindly requests that you acknowledge
77/*	the use of his software.
78/* AUTHOR(S)
79/*	Originally written by:
80/*	Lutz Jaenicke
81/*	BTU Cottbus
82/*	Allgemeine Elektrotechnik
83/*	Universitaetsplatz 3-4
84/*	D-03044 Cottbus, Germany
85/*
86/*	Updated by:
87/*	Wietse Venema
88/*	IBM T.J. Watson Research
89/*	P.O. Box 704
90/*	Yorktown Heights, NY 10598, USA
91/*
92/*	Victor Duchovni
93/*	Morgan Stanley
94/*--*/
95
96/* System library. */
97
98#include <sys_defs.h>
99#include <ctype.h>
100
101#ifdef USE_TLS
102#include <string.h>
103
104/* Utility library. */
105
106#include <msg.h>
107#include <mymalloc.h>
108#include <stringops.h>
109
110/* TLS library. */
111
112#define TLS_INTERNAL
113#include <tls.h>
114
115/* update_error_state - safely stash away error state */
116
117static void update_error_state(TLS_SESS_STATE *TLScontext, int depth,
118			               X509 *errorcert, int errorcode)
119{
120    /* No news is good news */
121    if (TLScontext->errordepth >= 0 && TLScontext->errordepth <= depth)
122	return;
123
124    /*
125     * The certificate pointer is stable during the verification callback,
126     * but may be freed after the callback returns.  Since we delay error
127     * reporting till later, we bump the refcount so we can rely on it still
128     * being there until later.
129     */
130    if (TLScontext->errorcert != 0)
131	X509_free(TLScontext->errorcert);
132    if (errorcert != 0)
133	X509_up_ref(errorcert);
134    TLScontext->errorcert = errorcert;
135    TLScontext->errorcode = errorcode;
136    TLScontext->errordepth = depth;
137}
138
139/* tls_verify_certificate_callback - verify peer certificate info */
140
141int     tls_verify_certificate_callback(int ok, X509_STORE_CTX *ctx)
142{
143    char    buf[CCERT_BUFSIZ];
144    X509   *cert;
145    int     err;
146    int     depth;
147    SSL    *con;
148    TLS_SESS_STATE *TLScontext;
149
150    /* May be NULL as of OpenSSL 1.0, thanks for the API change! */
151    cert = X509_STORE_CTX_get_current_cert(ctx);
152    err = X509_STORE_CTX_get_error(ctx);
153    con = X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
154    TLScontext = SSL_get_ex_data(con, TLScontext_index);
155    depth = X509_STORE_CTX_get_error_depth(ctx);
156
157    /*
158     * Transient failures to load the (DNS or synthetic TLSA) trust settings
159     * must poison certificate verification, since otherwise the default
160     * trust store may bless a certificate that would have failed
161     * verification with the preferred trust anchors (or fingerprints).
162     *
163     * Since we unconditionally continue, or in any case if verification is
164     * about to succeed, there is eventually a final depth 0 callback, at
165     * which point we force an "unspecified" error.  The failure to load the
166     * trust settings was logged earlier.
167     */
168    if (TLScontext->must_fail) {
169	if (depth == 0) {
170	    X509_STORE_CTX_set_error(ctx, err = X509_V_ERR_UNSPECIFIED);
171	    update_error_state(TLScontext, depth, cert, err);
172	}
173	return (1);
174    }
175    if (ok == 0)
176	update_error_state(TLScontext, depth, cert, err);
177
178    if (TLScontext->log_mask & TLS_LOG_VERBOSE) {
179	if (cert)
180	    X509_NAME_oneline(X509_get_subject_name(cert), buf, sizeof(buf));
181	else
182	    strcpy(buf, "<unknown>");
183	msg_info("%s: depth=%d verify=%d subject=%s",
184		 TLScontext->namaddr, depth, ok, printable(buf, '?'));
185    }
186    return (1);
187}
188
189/* tls_log_verify_error - Report final verification error status */
190
191void    tls_log_verify_error(TLS_SESS_STATE *TLScontext)
192{
193    char    buf[CCERT_BUFSIZ];
194    int     err = TLScontext->errorcode;
195    X509   *cert = TLScontext->errorcert;
196    int     depth = TLScontext->errordepth;
197
198#define PURPOSE ((depth>0) ? "CA": TLScontext->am_server ? "client": "server")
199
200    if (err == X509_V_OK)
201	return;
202
203    /*
204     * Specific causes for verification failure.
205     */
206    switch (err) {
207    case X509_V_ERR_CERT_UNTRUSTED:
208
209	/*
210	 * We expect the error cert to be the leaf, but it is likely
211	 * sufficient to omit it from the log, even less user confusion.
212	 */
213	msg_info("certificate verification failed for %s: "
214		 "not trusted by local or TLSA policy", TLScontext->namaddr);
215	break;
216    case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
217	msg_info("certificate verification failed for %s: "
218		 "self-signed certificate", TLScontext->namaddr);
219	break;
220    case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
221    case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
222
223	/*
224	 * There is no difference between issuing cert not provided and
225	 * provided, but not found in CAfile/CApath. Either way, we don't
226	 * trust it.
227	 */
228	if (cert)
229	    X509_NAME_oneline(X509_get_issuer_name(cert), buf, sizeof(buf));
230	else
231	    strcpy(buf, "<unknown>");
232	msg_info("certificate verification failed for %s: untrusted issuer %s",
233		 TLScontext->namaddr, printable(buf, '?'));
234	break;
235    case X509_V_ERR_CERT_NOT_YET_VALID:
236    case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
237	msg_info("%s certificate verification failed for %s: certificate not"
238		 " yet valid", PURPOSE, TLScontext->namaddr);
239	break;
240    case X509_V_ERR_CERT_HAS_EXPIRED:
241    case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
242	msg_info("%s certificate verification failed for %s: certificate has"
243		 " expired", PURPOSE, TLScontext->namaddr);
244	break;
245    case X509_V_ERR_INVALID_PURPOSE:
246	msg_info("certificate verification failed for %s: not designated for "
247		 "use as a %s certificate", TLScontext->namaddr, PURPOSE);
248	break;
249    case X509_V_ERR_CERT_CHAIN_TOO_LONG:
250	msg_info("certificate verification failed for %s: "
251		 "certificate chain longer than limit(%d)",
252		 TLScontext->namaddr, depth - 1);
253	break;
254    default:
255	msg_info("%s certificate verification failed for %s: num=%d:%s",
256		 PURPOSE, TLScontext->namaddr, err,
257		 X509_verify_cert_error_string(err));
258	break;
259    }
260}
261
262#ifndef DONT_GRIPE
263#define DONT_GRIPE 0
264#define DO_GRIPE 1
265#endif
266
267/* tls_text_name - extract certificate property value by name */
268
269static char *tls_text_name(X509_NAME *name, int nid, const char *label,
270			        const TLS_SESS_STATE *TLScontext, int gripe)
271{
272    const char *myname = "tls_text_name";
273    int     pos;
274    X509_NAME_ENTRY *entry;
275    ASN1_STRING *entry_str;
276    int     asn1_type;
277    int     utf8_length;
278    unsigned char *utf8_value;
279    int     ch;
280    unsigned char *cp;
281
282    if (name == 0 || (pos = X509_NAME_get_index_by_NID(name, nid, -1)) < 0) {
283	if (gripe != DONT_GRIPE) {
284	    msg_warn("%s: %s: peer certificate has no %s",
285		     myname, TLScontext->namaddr, label);
286	    tls_print_errors();
287	}
288	return (0);
289    }
290#if 0
291
292    /*
293     * If the match is required unambiguous, insist that that no other values
294     * be present.
295     */
296    if (X509_NAME_get_index_by_NID(name, nid, pos) >= 0) {
297	msg_warn("%s: %s: multiple %ss in peer certificate",
298		 myname, TLScontext->namaddr, label);
299	return (0);
300    }
301#endif
302
303    if ((entry = X509_NAME_get_entry(name, pos)) == 0) {
304	/* This should not happen */
305	msg_warn("%s: %s: error reading peer certificate %s entry",
306		 myname, TLScontext->namaddr, label);
307	tls_print_errors();
308	return (0);
309    }
310    if ((entry_str = X509_NAME_ENTRY_get_data(entry)) == 0) {
311	/* This should not happen */
312	msg_warn("%s: %s: error reading peer certificate %s data",
313		 myname, TLScontext->namaddr, label);
314	tls_print_errors();
315	return (0);
316    }
317
318    /*
319     * XXX Convert everything into UTF-8. This is a super-set of ASCII, so we
320     * don't have to bother with separate code paths for ASCII-like content.
321     * If the payload is ASCII then we won't waste lots of CPU cycles
322     * converting it into UTF-8. It's up to OpenSSL to do something
323     * reasonable when converting ASCII formats that contain non-ASCII
324     * content.
325     *
326     * XXX Don't bother optimizing the string length error check. It is not
327     * worth the complexity.
328     */
329    asn1_type = ASN1_STRING_type(entry_str);
330    if ((utf8_length = ASN1_STRING_to_UTF8(&utf8_value, entry_str)) < 0) {
331	msg_warn("%s: %s: error decoding peer %s of ASN.1 type=%d",
332		 myname, TLScontext->namaddr, label, asn1_type);
333	tls_print_errors();
334	return (0);
335    }
336
337    /*
338     * No returns without cleaning up. A good optimizer will replace multiple
339     * blocks of identical code by jumps to just one such block.
340     */
341#define TLS_TEXT_NAME_RETURN(x) do { \
342	char *__tls_text_name_temp = (x); \
343	OPENSSL_free(utf8_value); \
344	return (__tls_text_name_temp); \
345    } while (0)
346
347    /*
348     * Remove trailing null characters. They would give false alarms with the
349     * length check and with the embedded null check.
350     */
351#define TRIM0(s, l) do { while ((l) > 0 && (s)[(l)-1] == 0) --(l); } while (0)
352
353    TRIM0(utf8_value, utf8_length);
354
355    /*
356     * Enforce the length limit, because the caller will copy the result into
357     * a fixed-length buffer.
358     */
359    if (utf8_length >= CCERT_BUFSIZ) {
360	msg_warn("%s: %s: peer %s too long: %d",
361		 myname, TLScontext->namaddr, label, utf8_length);
362	TLS_TEXT_NAME_RETURN(0);
363    }
364
365    /*
366     * Reject embedded nulls in ASCII or UTF-8 names. OpenSSL is responsible
367     * for producing properly-formatted UTF-8.
368     */
369    if (utf8_length != strlen((char *) utf8_value)) {
370	msg_warn("%s: %s: NULL character in peer %s",
371		 myname, TLScontext->namaddr, label);
372	TLS_TEXT_NAME_RETURN(0);
373    }
374
375    /*
376     * Reject non-printable ASCII characters in UTF-8 content.
377     *
378     * Note: the code below does not find control characters in illegal UTF-8
379     * sequences. It's OpenSSL's job to produce valid UTF-8, and reportedly,
380     * it does validation.
381     */
382    for (cp = utf8_value; (ch = *cp) != 0; cp++) {
383	if (ISASCII(ch) && !ISPRINT(ch)) {
384	    msg_warn("%s: %s: non-printable content in peer %s",
385		     myname, TLScontext->namaddr, label);
386	    TLS_TEXT_NAME_RETURN(0);
387	}
388    }
389    TLS_TEXT_NAME_RETURN(mystrdup((char *) utf8_value));
390}
391
392/* tls_peer_CN - extract peer common name from certificate */
393
394char   *tls_peer_CN(X509 *peercert, const TLS_SESS_STATE *TLScontext)
395{
396    char   *cn;
397    const char *san;
398
399    /* Absent a commonName, return a validated DNS-ID SAN */
400    cn = tls_text_name(X509_get_subject_name(peercert), NID_commonName,
401		       "subject CN", TLScontext, DONT_GRIPE);
402    if (cn == 0 && (san = SSL_get0_peername(TLScontext->con)) != 0)
403	cn = mystrdup(san);
404    return (cn ? cn : mystrdup(""));
405}
406
407/* tls_issuer_CN - extract issuer common name from certificate */
408
409char   *tls_issuer_CN(X509 *peer, const TLS_SESS_STATE *TLScontext)
410{
411    X509_NAME *name;
412    char   *cn;
413
414    name = X509_get_issuer_name(peer);
415
416    /*
417     * If no issuer CN field, use Organization instead. CA certs without a CN
418     * are common, so we only complain if the organization is also missing.
419     */
420    if ((cn = tls_text_name(name, NID_commonName,
421			    "issuer CN", TLScontext, DONT_GRIPE)) == 0)
422	cn = tls_text_name(name, NID_organizationName,
423			   "issuer Organization", TLScontext, DONT_GRIPE);
424    return (cn ? cn : mystrdup(""));
425}
426
427#endif
428