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
update_error_state(TLS_SESS_STATE * TLScontext,int depth,X509 * errorcert,int errorcode)117 static 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
tls_verify_certificate_callback(int ok,X509_STORE_CTX * ctx)141 int 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
tls_log_verify_error(TLS_SESS_STATE * TLScontext)191 void 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
tls_text_name(X509_NAME * name,int nid,const char * label,const TLS_SESS_STATE * TLScontext,int gripe)269 static 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
tls_peer_CN(X509 * peercert,const TLS_SESS_STATE * TLScontext)394 char *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
tls_issuer_CN(X509 * peer,const TLS_SESS_STATE * TLScontext)409 char *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