xref: /netbsd-src/external/ibm-public/postfix/dist/src/tls/tls_verify.c (revision c38e7cc395b1472a774ff828e46123de44c628e9)
1 /*	$NetBSD: tls_verify.c,v 1.2 2017/02/14 01:16:48 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 /*
27 /*	const char *tls_dns_name(gn, TLScontext)
28 /*	const GENERAL_NAME *gn;
29 /*	TLS_SESS_STATE *TLScontext;
30 /* DESCRIPTION
31 /*	tls_verify_certificate_callback() is called several times (directly
32 /*	or indirectly) from crypto/x509/x509_vfy.c. It collects errors
33 /*	and trust information at each element of the trust chain.
34 /*	The last call at depth 0 sets the verification status based
35 /*	on the cumulative winner (lowest depth) of errors vs. trust.
36 /*	We always return 1 (continue the handshake) and handle trust
37 /*	and peer-name verification problems at the application level.
38 /*
39 /*	tls_log_verify_error() (called only when we care about the
40 /*	peer certificate, that is not when opportunistic) logs the
41 /*	reason why the certificate failed to be verified.
42 /*
43 /*	tls_peer_CN() returns the text CommonName for the peer
44 /*	certificate subject, or an empty string if no CommonName was
45 /*	found. The result is allocated with mymalloc() and must be
46 /*	freed by the caller; it contains UTF-8 without non-printable
47 /*	ASCII characters.
48 /*
49 /*	tls_issuer_CN() returns the text CommonName for the peer
50 /*	certificate issuer, or an empty string if no CommonName was
51 /*	found. The result is allocated with mymalloc() and must be
52 /*	freed by the caller; it contains UTF-8 without non-printable
53 /*	ASCII characters.
54 /*
55 /*	tls_dns_name() returns the string value of a GENERAL_NAME
56 /*	from a DNS subjectAltName extension. If non-printable characters
57 /*	are found, a null string is returned instead. Further sanity
58 /*	checks may be added if the need arises.
59 /*
60 /*	Arguments:
61 /* .IP ok
62 /*	Result of prior verification: non-zero means success.  In
63 /*	order to reduce the noise level, some tests or error reports
64 /*	are disabled when verification failed because of some
65 /*	earlier problem.
66 /* .IP ctx
67 /*	SSL application context. This links to the Postfix TLScontext
68 /*	with enforcement and logging options.
69 /* .IP gn
70 /*	An OpenSSL GENERAL_NAME structure holding a DNS subjectAltName
71 /*	to be decoded and checked for validity.
72 /* .IP peercert
73 /*	Server or client X.509 certificate.
74 /* .IP TLScontext
75 /*	Server or client context for warning messages.
76 /* DIAGNOSTICS
77 /*	tls_peer_CN(), tls_issuer_CN() and tls_dns_name() log a warning
78 /*	when 1) the requested information is not available in the specified
79 /*	certificate, 2) the result exceeds a fixed limit, 3) the result
80 /*	contains NUL characters or the result contains non-printable or
81 /*	non-ASCII characters.
82 /* LICENSE
83 /* .ad
84 /* .fi
85 /*	This software is free. You can do with it whatever you want.
86 /*	The original author kindly requests that you acknowledge
87 /*	the use of his software.
88 /* AUTHOR(S)
89 /*	Originally written by:
90 /*	Lutz Jaenicke
91 /*	BTU Cottbus
92 /*	Allgemeine Elektrotechnik
93 /*	Universitaetsplatz 3-4
94 /*	D-03044 Cottbus, Germany
95 /*
96 /*	Updated by:
97 /*	Wietse Venema
98 /*	IBM T.J. Watson Research
99 /*	P.O. Box 704
100 /*	Yorktown Heights, NY 10598, USA
101 /*
102 /*	Victor Duchovni
103 /*	Morgan Stanley
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 /* TLS library. */
121 
122 #define TLS_INTERNAL
123 #include <tls.h>
124 
125 /* update_error_state - safely stash away error state */
126 
127 static void update_error_state(TLS_SESS_STATE *TLScontext, int depth,
128 			               X509 *errorcert, int errorcode)
129 {
130     /* No news is good news */
131     if (TLScontext->errordepth >= 0 && TLScontext->errordepth <= depth)
132 	return;
133 
134     /*
135      * The certificate pointer is stable during the verification callback,
136      * but may be freed after the callback returns.  Since we delay error
137      * reporting till later, we bump the refcount so we can rely on it still
138      * being there until later.
139      */
140     if (TLScontext->errorcert != 0)
141 	X509_free(TLScontext->errorcert);
142     if (errorcert != 0)
143 	X509_up_ref(errorcert);
144     TLScontext->errorcert = errorcert;
145     TLScontext->errorcode = errorcode;
146     TLScontext->errordepth = depth;
147 }
148 
149 /* tls_verify_certificate_callback - verify peer certificate info */
150 
151 int     tls_verify_certificate_callback(int ok, X509_STORE_CTX *ctx)
152 {
153     char    buf[CCERT_BUFSIZ];
154     X509   *cert;
155     int     err;
156     int     depth;
157     int     max_depth;
158     SSL    *con;
159     TLS_SESS_STATE *TLScontext;
160 
161     /* May be NULL as of OpenSSL 1.0, thanks for the API change! */
162     cert = X509_STORE_CTX_get_current_cert(ctx);
163     err = X509_STORE_CTX_get_error(ctx);
164     con = X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
165     TLScontext = SSL_get_ex_data(con, TLScontext_index);
166     depth = X509_STORE_CTX_get_error_depth(ctx);
167 
168     /* Don't log the internal root CA unless there's an unexpected error. */
169     if (ok && TLScontext->tadepth > 0 && depth > TLScontext->tadepth)
170 	return (1);
171 
172     /*
173      * Certificate chain depth limit violations are mis-reported by the
174      * OpenSSL library, from SSL_CTX_set_verify(3):
175      *
176      * The certificate verification depth set with SSL[_CTX]_verify_depth()
177      * stops the verification at a certain depth. The error message produced
178      * will be that of an incomplete certificate chain and not
179      * X509_V_ERR_CERT_CHAIN_TOO_LONG as may be expected.
180      *
181      * We set a limit that is one higher than the user requested limit. If this
182      * higher limit is reached, we raise an error even a trusted root CA is
183      * present at this depth. This disambiguates trust chain truncation from
184      * an incomplete trust chain.
185      */
186     max_depth = SSL_get_verify_depth(con) - 1;
187 
188     /*
189      * We never terminate the SSL handshake in the verification callback,
190      * rather we allow the TLS handshake to continue, but mark the session as
191      * unverified. The application is responsible for closing any sessions
192      * with unverified credentials.
193      */
194     if (max_depth >= 0 && depth > max_depth) {
195 	X509_STORE_CTX_set_error(ctx, err = X509_V_ERR_CERT_CHAIN_TOO_LONG);
196 	ok = 0;
197     }
198     if (ok == 0)
199 	update_error_state(TLScontext, depth, cert, err);
200 
201     if (TLScontext->log_mask & TLS_LOG_VERBOSE) {
202 	if (cert)
203 	    X509_NAME_oneline(X509_get_subject_name(cert), buf, sizeof(buf));
204 	else
205 	    strcpy(buf, "<unknown>");
206 	msg_info("%s: depth=%d verify=%d subject=%s",
207 		 TLScontext->namaddr, depth, ok, printable(buf, '?'));
208     }
209     return (1);
210 }
211 
212 /* tls_log_verify_error - Report final verification error status */
213 
214 void    tls_log_verify_error(TLS_SESS_STATE *TLScontext)
215 {
216     char    buf[CCERT_BUFSIZ];
217     int     err = TLScontext->errorcode;
218     X509   *cert = TLScontext->errorcert;
219     int     depth = TLScontext->errordepth;
220 
221 #define PURPOSE ((depth>0) ? "CA": TLScontext->am_server ? "client": "server")
222 
223     if (err == X509_V_OK)
224 	return;
225 
226     /*
227      * Specific causes for verification failure.
228      */
229     switch (err) {
230     case X509_V_ERR_CERT_UNTRUSTED:
231 
232 	/*
233 	 * We expect the error cert to be the leaf, but it is likely
234 	 * sufficient to omit it from the log, even less user confusion.
235 	 */
236 	msg_info("certificate verification failed for %s: "
237 		 "not trusted by local or TLSA policy", TLScontext->namaddr);
238 	break;
239     case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
240 	msg_info("certificate verification failed for %s: "
241 		 "self-signed certificate", TLScontext->namaddr);
242 	break;
243     case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
244     case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
245 
246 	/*
247 	 * There is no difference between issuing cert not provided and
248 	 * provided, but not found in CAfile/CApath. Either way, we don't
249 	 * trust it.
250 	 */
251 	if (cert)
252 	    X509_NAME_oneline(X509_get_issuer_name(cert), buf, sizeof(buf));
253 	else
254 	    strcpy(buf, "<unknown>");
255 	msg_info("certificate verification failed for %s: untrusted issuer %s",
256 		 TLScontext->namaddr, printable(buf, '?'));
257 	break;
258     case X509_V_ERR_CERT_NOT_YET_VALID:
259     case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
260 	msg_info("%s certificate verification failed for %s: certificate not"
261 		 " yet valid", PURPOSE, TLScontext->namaddr);
262 	break;
263     case X509_V_ERR_CERT_HAS_EXPIRED:
264     case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
265 	msg_info("%s certificate verification failed for %s: certificate has"
266 		 " expired", PURPOSE, TLScontext->namaddr);
267 	break;
268     case X509_V_ERR_INVALID_PURPOSE:
269 	msg_info("certificate verification failed for %s: not designated for "
270 		 "use as a %s certificate", TLScontext->namaddr, PURPOSE);
271 	break;
272     case X509_V_ERR_CERT_CHAIN_TOO_LONG:
273 	msg_info("certificate verification failed for %s: "
274 		 "certificate chain longer than limit(%d)",
275 		 TLScontext->namaddr, depth - 1);
276 	break;
277     default:
278 	msg_info("%s certificate verification failed for %s: num=%d:%s",
279 		 PURPOSE, TLScontext->namaddr, err,
280 		 X509_verify_cert_error_string(err));
281 	break;
282     }
283 }
284 
285 #ifndef DONT_GRIPE
286 #define DONT_GRIPE 0
287 #define DO_GRIPE 1
288 #endif
289 
290 /* tls_text_name - extract certificate property value by name */
291 
292 static char *tls_text_name(X509_NAME *name, int nid, const char *label,
293 			        const TLS_SESS_STATE *TLScontext, int gripe)
294 {
295     const char *myname = "tls_text_name";
296     int     pos;
297     X509_NAME_ENTRY *entry;
298     ASN1_STRING *entry_str;
299     int     asn1_type;
300     int     utf8_length;
301     unsigned char *utf8_value;
302     int     ch;
303     unsigned char *cp;
304 
305     if (name == 0 || (pos = X509_NAME_get_index_by_NID(name, nid, -1)) < 0) {
306 	if (gripe != DONT_GRIPE) {
307 	    msg_warn("%s: %s: peer certificate has no %s",
308 		     myname, TLScontext->namaddr, label);
309 	    tls_print_errors();
310 	}
311 	return (0);
312     }
313 #if 0
314 
315     /*
316      * If the match is required unambiguous, insist that that no other values
317      * be present.
318      */
319     if (X509_NAME_get_index_by_NID(name, nid, pos) >= 0) {
320 	msg_warn("%s: %s: multiple %ss in peer certificate",
321 		 myname, TLScontext->namaddr, label);
322 	return (0);
323     }
324 #endif
325 
326     if ((entry = X509_NAME_get_entry(name, pos)) == 0) {
327 	/* This should not happen */
328 	msg_warn("%s: %s: error reading peer certificate %s entry",
329 		 myname, TLScontext->namaddr, label);
330 	tls_print_errors();
331 	return (0);
332     }
333     if ((entry_str = X509_NAME_ENTRY_get_data(entry)) == 0) {
334 	/* This should not happen */
335 	msg_warn("%s: %s: error reading peer certificate %s data",
336 		 myname, TLScontext->namaddr, label);
337 	tls_print_errors();
338 	return (0);
339     }
340 
341     /*
342      * XXX Convert everything into UTF-8. This is a super-set of ASCII, so we
343      * don't have to bother with separate code paths for ASCII-like content.
344      * If the payload is ASCII then we won't waste lots of CPU cycles
345      * converting it into UTF-8. It's up to OpenSSL to do something
346      * reasonable when converting ASCII formats that contain non-ASCII
347      * content.
348      *
349      * XXX Don't bother optimizing the string length error check. It is not
350      * worth the complexity.
351      */
352     asn1_type = ASN1_STRING_type(entry_str);
353     if ((utf8_length = ASN1_STRING_to_UTF8(&utf8_value, entry_str)) < 0) {
354 	msg_warn("%s: %s: error decoding peer %s of ASN.1 type=%d",
355 		 myname, TLScontext->namaddr, label, asn1_type);
356 	tls_print_errors();
357 	return (0);
358     }
359 
360     /*
361      * No returns without cleaning up. A good optimizer will replace multiple
362      * blocks of identical code by jumps to just one such block.
363      */
364 #define TLS_TEXT_NAME_RETURN(x) do { \
365 	char *__tls_text_name_temp = (x); \
366 	OPENSSL_free(utf8_value); \
367 	return (__tls_text_name_temp); \
368     } while (0)
369 
370     /*
371      * Remove trailing null characters. They would give false alarms with the
372      * length check and with the embedded null check.
373      */
374 #define TRIM0(s, l) do { while ((l) > 0 && (s)[(l)-1] == 0) --(l); } while (0)
375 
376     TRIM0(utf8_value, utf8_length);
377 
378     /*
379      * Enforce the length limit, because the caller will copy the result into
380      * a fixed-length buffer.
381      */
382     if (utf8_length >= CCERT_BUFSIZ) {
383 	msg_warn("%s: %s: peer %s too long: %d",
384 		 myname, TLScontext->namaddr, label, utf8_length);
385 	TLS_TEXT_NAME_RETURN(0);
386     }
387 
388     /*
389      * Reject embedded nulls in ASCII or UTF-8 names. OpenSSL is responsible
390      * for producing properly-formatted UTF-8.
391      */
392     if (utf8_length != strlen((char *) utf8_value)) {
393 	msg_warn("%s: %s: NULL character in peer %s",
394 		 myname, TLScontext->namaddr, label);
395 	TLS_TEXT_NAME_RETURN(0);
396     }
397 
398     /*
399      * Reject non-printable ASCII characters in UTF-8 content.
400      *
401      * Note: the code below does not find control characters in illegal UTF-8
402      * sequences. It's OpenSSL's job to produce valid UTF-8, and reportedly,
403      * it does validation.
404      */
405     for (cp = utf8_value; (ch = *cp) != 0; cp++) {
406 	if (ISASCII(ch) && !ISPRINT(ch)) {
407 	    msg_warn("%s: %s: non-printable content in peer %s",
408 		     myname, TLScontext->namaddr, label);
409 	    TLS_TEXT_NAME_RETURN(0);
410 	}
411     }
412     TLS_TEXT_NAME_RETURN(mystrdup((char *) utf8_value));
413 }
414 
415 /* tls_dns_name - Extract valid DNS name from subjectAltName value */
416 
417 const char *tls_dns_name(const GENERAL_NAME * gn,
418 			         const TLS_SESS_STATE *TLScontext)
419 {
420     const char *myname = "tls_dns_name";
421     char   *cp;
422     const char *dnsname;
423     int     len;
424 
425     /*
426      * Peername checks are security sensitive, carefully scrutinize the
427      * input!
428      */
429     if (gn->type != GEN_DNS)
430 	msg_panic("%s: Non DNS input argument", myname);
431 
432     /*
433      * We expect the OpenSSL library to construct GEN_DNS extesion objects as
434      * ASN1_IA5STRING values. Check we got the right union member.
435      */
436     if (ASN1_STRING_type(gn->d.ia5) != V_ASN1_IA5STRING) {
437 	msg_warn("%s: %s: invalid ASN1 value type in subjectAltName",
438 		 myname, TLScontext->namaddr);
439 	return (0);
440     }
441 
442     /*
443      * Safe to treat as an ASCII string possibly holding a DNS name
444      */
445     dnsname = (const char *) ASN1_STRING_get0_data(gn->d.ia5);
446     len = ASN1_STRING_length(gn->d.ia5);
447     TRIM0(dnsname, len);
448 
449     /*
450      * Per Dr. Steven Henson of the OpenSSL development team, ASN1_IA5STRING
451      * values can have internal ASCII NUL values in this context because
452      * their length is taken from the decoded ASN1 buffer, a trailing NUL is
453      * always appended to make sure that the string is terminated, but the
454      * ASN.1 length may differ from strlen().
455      */
456     if (len != strlen(dnsname)) {
457 	msg_warn("%s: %s: internal NUL in subjectAltName",
458 		 myname, TLScontext->namaddr);
459 	return 0;
460     }
461 
462     /*
463      * XXX: Should we be more strict and call valid_hostname()? So long as
464      * the name is safe to handle, if it is not a valid hostname, it will not
465      * compare equal to the expected peername, so being more strict than
466      * "printable" is likely excessive...
467      */
468     if (*dnsname && !allprint(dnsname)) {
469 	cp = mystrdup(dnsname);
470 	msg_warn("%s: %s: non-printable characters in subjectAltName: %.100s",
471 		 myname, TLScontext->namaddr, printable(cp, '?'));
472 	myfree(cp);
473 	return 0;
474     }
475     return (dnsname);
476 }
477 
478 /* tls_peer_CN - extract peer common name from certificate */
479 
480 char   *tls_peer_CN(X509 *peercert, const TLS_SESS_STATE *TLScontext)
481 {
482     char   *cn;
483 
484     cn = tls_text_name(X509_get_subject_name(peercert), NID_commonName,
485 		       "subject CN", TLScontext, DONT_GRIPE);
486     return (cn ? cn : mystrdup(""));
487 }
488 
489 /* tls_issuer_CN - extract issuer common name from certificate */
490 
491 char   *tls_issuer_CN(X509 *peer, const TLS_SESS_STATE *TLScontext)
492 {
493     X509_NAME *name;
494     char   *cn;
495 
496     name = X509_get_issuer_name(peer);
497 
498     /*
499      * If no issuer CN field, use Organization instead. CA certs without a CN
500      * are common, so we only complain if the organization is also missing.
501      */
502     if ((cn = tls_text_name(name, NID_commonName,
503 			    "issuer CN", TLScontext, DONT_GRIPE)) == 0)
504 	cn = tls_text_name(name, NID_organizationName,
505 			   "issuer Organization", TLScontext, DONT_GRIPE);
506     return (cn ? cn : mystrdup(""));
507 }
508 
509 #endif
510