xref: /netbsd-src/external/bsd/openldap/dist/libraries/libldap/tls_g.c (revision 181254a7b1bdde6873432bffef2d2decc4b5c22f)
1 /*	$NetBSD: tls_g.c,v 1.2 2020/08/11 13:15:38 christos Exp $	*/
2 
3 /* tls_g.c - Handle tls/ssl using GNUTLS. */
4 /* $OpenLDAP$ */
5 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
6  *
7  * Copyright 2008-2020 The OpenLDAP Foundation.
8  * All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted only as authorized by the OpenLDAP
12  * Public License.
13  *
14  * A copy of this license is available in the file LICENSE in the
15  * top-level directory of the distribution or, alternatively, at
16  * <http://www.OpenLDAP.org/license.html>.
17  */
18 /* ACKNOWLEDGEMENTS: GNUTLS support written by Howard Chu and
19  * Emily Backes; sponsored by The Written Word (thewrittenword.com)
20  * and Stanford University (stanford.edu).
21  */
22 
23 #include <sys/cdefs.h>
24 __RCSID("$NetBSD: tls_g.c,v 1.2 2020/08/11 13:15:38 christos Exp $");
25 
26 #include "portable.h"
27 
28 #ifdef HAVE_GNUTLS
29 
30 #include "ldap_config.h"
31 
32 #include <stdio.h>
33 
34 #include <ac/stdlib.h>
35 #include <ac/errno.h>
36 #include <ac/socket.h>
37 #include <ac/string.h>
38 #include <ac/ctype.h>
39 #include <ac/time.h>
40 #include <ac/unistd.h>
41 #include <ac/param.h>
42 #include <ac/dirent.h>
43 #include <sys/stat.h>
44 #include <fcntl.h>
45 
46 #include "ldap-int.h"
47 #include "ldap-tls.h"
48 
49 #include <gnutls/gnutls.h>
50 #include <gnutls/x509.h>
51 
52 typedef struct tlsg_ctx {
53 	gnutls_certificate_credentials_t cred;
54 	gnutls_dh_params_t dh_params;
55 	unsigned long verify_depth;
56 	int refcount;
57 	int reqcert;
58 	gnutls_priority_t prios;
59 #ifdef LDAP_R_COMPILE
60 	ldap_pvt_thread_mutex_t ref_mutex;
61 #endif
62 } tlsg_ctx;
63 
64 typedef struct tlsg_session {
65 	gnutls_session_t session;
66 	tlsg_ctx *ctx;
67 	struct berval peer_der_dn;
68 } tlsg_session;
69 
70 static int tlsg_parse_ciphers( tlsg_ctx *ctx, char *suites );
71 static int tlsg_cert_verify( tlsg_session *s );
72 
73 #ifdef LDAP_R_COMPILE
74 
75 static void
76 tlsg_thr_init( void )
77 {
78 	/* do nothing */
79 }
80 #endif /* LDAP_R_COMPILE */
81 
82 /*
83  * Initialize TLS subsystem. Should be called only once.
84  */
85 static int
86 tlsg_init( void )
87 {
88 	gnutls_global_init();
89 	return 0;
90 }
91 
92 /*
93  * Tear down the TLS subsystem. Should only be called once.
94  */
95 static void
96 tlsg_destroy( void )
97 {
98 	gnutls_global_deinit();
99 }
100 
101 static tls_ctx *
102 tlsg_ctx_new ( struct ldapoptions *lo )
103 {
104 	tlsg_ctx *ctx;
105 
106 	ctx = ber_memcalloc ( 1, sizeof (*ctx) );
107 	if ( ctx ) {
108 		if ( gnutls_certificate_allocate_credentials( &ctx->cred )) {
109 			ber_memfree( ctx );
110 			return NULL;
111 		}
112 		ctx->refcount = 1;
113 		gnutls_priority_init( &ctx->prios, "NORMAL", NULL );
114 #ifdef LDAP_R_COMPILE
115 		ldap_pvt_thread_mutex_init( &ctx->ref_mutex );
116 #endif
117 	}
118 	return (tls_ctx *)ctx;
119 }
120 
121 static void
122 tlsg_ctx_ref( tls_ctx *ctx )
123 {
124 	tlsg_ctx *c = (tlsg_ctx *)ctx;
125 	LDAP_MUTEX_LOCK( &c->ref_mutex );
126 	c->refcount++;
127 	LDAP_MUTEX_UNLOCK( &c->ref_mutex );
128 }
129 
130 static void
131 tlsg_ctx_free ( tls_ctx *ctx )
132 {
133 	tlsg_ctx *c = (tlsg_ctx *)ctx;
134 	int refcount;
135 
136 	if ( !c ) return;
137 
138 	LDAP_MUTEX_LOCK( &c->ref_mutex );
139 	refcount = --c->refcount;
140 	LDAP_MUTEX_UNLOCK( &c->ref_mutex );
141 	if ( refcount )
142 		return;
143 	gnutls_priority_deinit( c->prios );
144 	gnutls_certificate_free_credentials( c->cred );
145 	if ( c->dh_params )
146 		gnutls_dh_params_deinit( c->dh_params );
147 	ber_memfree ( c );
148 }
149 
150 static int
151 tlsg_getfile( const char *path, gnutls_datum_t *buf )
152 {
153 	int rc = -1, fd;
154 	struct stat st;
155 
156 	fd = open( path, O_RDONLY );
157 	if ( fd >= 0 && fstat( fd, &st ) == 0 ) {
158 		buf->size = st.st_size;
159 		buf->data = LDAP_MALLOC( st.st_size + 1 );
160 		if ( buf->data ) {
161 			rc = read( fd, buf->data, st.st_size );
162 			close( fd );
163 			if ( rc < st.st_size )
164 				rc = -1;
165 			else
166 				rc = 0;
167 		}
168 	}
169 	return rc;
170 }
171 
172 /* This is the GnuTLS default */
173 #define	VERIFY_DEPTH	6
174 
175 /*
176  * initialize a new TLS context
177  */
178 static int
179 tlsg_ctx_init( struct ldapoptions *lo, struct ldaptls *lt, int is_server )
180 {
181 	tlsg_ctx *ctx = lo->ldo_tls_ctx;
182 	int rc;
183 
184  	if ( lo->ldo_tls_ciphersuite &&
185 		tlsg_parse_ciphers( ctx, lt->lt_ciphersuite )) {
186  		Debug( LDAP_DEBUG_ANY,
187  			   "TLS: could not set cipher list %s.\n",
188  			   lo->ldo_tls_ciphersuite, 0, 0 );
189 		return -1;
190  	}
191 
192 	if (lo->ldo_tls_cacertdir != NULL) {
193 		Debug( LDAP_DEBUG_ANY,
194 		       "TLS: warning: cacertdir not implemented for gnutls\n",
195 		       NULL, NULL, NULL );
196 	}
197 
198 	if (lo->ldo_tls_cacertfile != NULL) {
199 		rc = gnutls_certificate_set_x509_trust_file(
200 			ctx->cred,
201 			lt->lt_cacertfile,
202 			GNUTLS_X509_FMT_PEM );
203 		if ( rc < 0 ) return -1;
204 	}
205 
206 	if ( lo->ldo_tls_certfile && lo->ldo_tls_keyfile ) {
207 		gnutls_x509_privkey_t key;
208 		gnutls_datum_t buf;
209 		gnutls_x509_crt_t certs[VERIFY_DEPTH];
210 		unsigned int max = VERIFY_DEPTH;
211 
212 		rc = gnutls_x509_privkey_init( &key );
213 		if ( rc ) return -1;
214 
215 		/* OpenSSL builds the cert chain for us, but GnuTLS
216 		 * expects it to be present in the certfile. If it's
217 		 * not, we have to build it ourselves. So we have to
218 		 * do some special checks here...
219 		 */
220 		rc = tlsg_getfile( lt->lt_keyfile, &buf );
221 		if ( rc ) return -1;
222 		rc = gnutls_x509_privkey_import( key, &buf,
223 			GNUTLS_X509_FMT_PEM );
224 		LDAP_FREE( buf.data );
225 		if ( rc < 0 ) return rc;
226 
227 		rc = tlsg_getfile( lt->lt_certfile, &buf );
228 		if ( rc ) return -1;
229 		rc = gnutls_x509_crt_list_import( certs, &max, &buf,
230 			GNUTLS_X509_FMT_PEM, 0 );
231 		LDAP_FREE( buf.data );
232 		if ( rc < 0 ) return rc;
233 
234 		/* If there's only one cert and it's not self-signed,
235 		 * then we have to build the cert chain.
236 		 */
237 		if ( max == 1 && !gnutls_x509_crt_check_issuer( certs[0], certs[0] )) {
238 			unsigned int i;
239 			for ( i = 1; i<VERIFY_DEPTH; i++ ) {
240 				if ( gnutls_certificate_get_issuer( ctx->cred, certs[i-1], &certs[i], 0 ))
241 					break;
242 				max++;
243 				/* If this CA is self-signed, we're done */
244 				if ( gnutls_x509_crt_check_issuer( certs[i], certs[i] ))
245 					break;
246 			}
247 		}
248 		rc = gnutls_certificate_set_x509_key( ctx->cred, certs, max, key );
249 		if ( rc ) return -1;
250 	} else if ( lo->ldo_tls_certfile || lo->ldo_tls_keyfile ) {
251 		Debug( LDAP_DEBUG_ANY,
252 		       "TLS: only one of certfile and keyfile specified\n",
253 		       NULL, NULL, NULL );
254 		return -1;
255 	}
256 
257 	if ( lo->ldo_tls_crlfile ) {
258 		rc = gnutls_certificate_set_x509_crl_file(
259 			ctx->cred,
260 			lt->lt_crlfile,
261 			GNUTLS_X509_FMT_PEM );
262 		if ( rc < 0 ) return -1;
263 		rc = 0;
264 	}
265 
266 	/* FIXME: ITS#5992 - this should be configurable,
267 	 * and V1 CA certs should be phased out ASAP.
268 	 */
269 	gnutls_certificate_set_verify_flags( ctx->cred,
270 		GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT );
271 
272 	if ( is_server && lo->ldo_tls_dhfile ) {
273 		gnutls_datum_t buf;
274 		rc = tlsg_getfile( lo->ldo_tls_dhfile, &buf );
275 		if ( rc ) return -1;
276 		rc = gnutls_dh_params_init( &ctx->dh_params );
277 		if ( rc == 0 )
278 			rc = gnutls_dh_params_import_pkcs3( ctx->dh_params, &buf,
279 				GNUTLS_X509_FMT_PEM );
280 		LDAP_FREE( buf.data );
281 		if ( rc ) return -1;
282 		gnutls_certificate_set_dh_params( ctx->cred, ctx->dh_params );
283 	}
284 
285 	ctx->reqcert = lo->ldo_tls_require_cert;
286 
287 	return 0;
288 }
289 
290 static tls_session *
291 tlsg_session_new ( tls_ctx * ctx, int is_server )
292 {
293 	tlsg_ctx *c = (tlsg_ctx *)ctx;
294 	tlsg_session *session;
295 
296 	session = ber_memcalloc ( 1, sizeof (*session) );
297 	if ( !session )
298 		return NULL;
299 
300 	session->ctx = c;
301 	gnutls_init( &session->session, is_server ? GNUTLS_SERVER : GNUTLS_CLIENT );
302 	gnutls_priority_set( session->session, c->prios );
303 	if ( c->cred )
304 		gnutls_credentials_set( session->session, GNUTLS_CRD_CERTIFICATE, c->cred );
305 
306 	if ( is_server ) {
307 		int flag = 0;
308 		if ( c->reqcert ) {
309 			flag = GNUTLS_CERT_REQUEST;
310 			if ( c->reqcert == LDAP_OPT_X_TLS_DEMAND ||
311 				c->reqcert == LDAP_OPT_X_TLS_HARD )
312 				flag = GNUTLS_CERT_REQUIRE;
313 			gnutls_certificate_server_set_request( session->session, flag );
314 		}
315 	}
316 	return (tls_session *)session;
317 }
318 
319 static int
320 tlsg_session_accept( tls_session *session )
321 {
322 	tlsg_session *s = (tlsg_session *)session;
323 	int rc;
324 
325 	rc = gnutls_handshake( s->session );
326 	if ( rc == 0 && s->ctx->reqcert != LDAP_OPT_X_TLS_NEVER ) {
327 		const gnutls_datum_t *peer_cert_list;
328 		unsigned int list_size;
329 
330 		peer_cert_list = gnutls_certificate_get_peers( s->session,
331 						&list_size );
332 		if ( !peer_cert_list && s->ctx->reqcert == LDAP_OPT_X_TLS_TRY )
333 			rc = 0;
334 		else {
335 			rc = tlsg_cert_verify( s );
336 			if ( rc && s->ctx->reqcert == LDAP_OPT_X_TLS_ALLOW )
337 				rc = 0;
338 		}
339 	}
340 	return rc;
341 }
342 
343 static int
344 tlsg_session_connect( LDAP *ld, tls_session *session )
345 {
346 	return tlsg_session_accept( session);
347 }
348 
349 static int
350 tlsg_session_upflags( Sockbuf *sb, tls_session *session, int rc )
351 {
352 	tlsg_session *s = (tlsg_session *)session;
353 
354 	if ( rc != GNUTLS_E_INTERRUPTED && rc != GNUTLS_E_AGAIN )
355 		return 0;
356 
357 	switch (gnutls_record_get_direction (s->session)) {
358 	case 0:
359 		sb->sb_trans_needs_read = 1;
360 		return 1;
361 	case 1:
362 		sb->sb_trans_needs_write = 1;
363 		return 1;
364 	}
365 	return 0;
366 }
367 
368 static char *
369 tlsg_session_errmsg( tls_session *sess, int rc, char *buf, size_t len )
370 {
371 	return (char *)gnutls_strerror( rc );
372 }
373 
374 static void
375 tlsg_x509_cert_dn( struct berval *cert, struct berval *dn, int get_subject )
376 {
377 	BerElementBuffer berbuf;
378 	BerElement *ber = (BerElement *)&berbuf;
379 	ber_tag_t tag;
380 	ber_len_t len;
381 	ber_int_t i;
382 
383 	ber_init2( ber, cert, LBER_USE_DER );
384 	tag = ber_skip_tag( ber, &len );	/* Sequence */
385 	tag = ber_skip_tag( ber, &len );	/* Sequence */
386 	tag = ber_peek_tag( ber, &len );	/* Context + Constructed (version) */
387 	if ( tag == 0xa0 ) {	/* Version is optional */
388 		tag = ber_skip_tag( ber, &len );
389 		tag = ber_get_int( ber, &i );	/* Int: Version */
390 	}
391 	tag = ber_skip_tag( ber, &len );	/* Int: Serial (can be longer than ber_int_t) */
392 	ber_skip_data( ber, len );
393 	tag = ber_skip_tag( ber, &len );	/* Sequence: Signature */
394 	ber_skip_data( ber, len );
395 	if ( !get_subject ) {
396 		tag = ber_peek_tag( ber, &len );	/* Sequence: Issuer DN */
397 	} else {
398 		tag = ber_skip_tag( ber, &len );
399 		ber_skip_data( ber, len );
400 		tag = ber_skip_tag( ber, &len );	/* Sequence: Validity */
401 		ber_skip_data( ber, len );
402 		tag = ber_peek_tag( ber, &len );	/* Sequence: Subject DN */
403 	}
404 	len = ber_ptrlen( ber );
405 	dn->bv_val = cert->bv_val + len;
406 	dn->bv_len = cert->bv_len - len;
407 }
408 
409 static int
410 tlsg_session_my_dn( tls_session *session, struct berval *der_dn )
411 {
412 	tlsg_session *s = (tlsg_session *)session;
413 	const gnutls_datum_t *x;
414 	struct berval bv;
415 
416 	x = gnutls_certificate_get_ours( s->session );
417 
418 	if (!x) return LDAP_INVALID_CREDENTIALS;
419 
420 	bv.bv_val = (char *) x->data;
421 	bv.bv_len = x->size;
422 
423 	tlsg_x509_cert_dn( &bv, der_dn, 1 );
424 	return 0;
425 }
426 
427 static int
428 tlsg_session_peer_dn( tls_session *session, struct berval *der_dn )
429 {
430 	tlsg_session *s = (tlsg_session *)session;
431 	if ( !s->peer_der_dn.bv_val ) {
432 		const gnutls_datum_t *peer_cert_list;
433 		unsigned int list_size;
434 		struct berval bv;
435 
436 		peer_cert_list = gnutls_certificate_get_peers( s->session,
437 							&list_size );
438 		if ( !peer_cert_list ) return LDAP_INVALID_CREDENTIALS;
439 
440 		bv.bv_len = peer_cert_list->size;
441 		bv.bv_val = (char *) peer_cert_list->data;
442 
443 		tlsg_x509_cert_dn( &bv, &s->peer_der_dn, 1 );
444 	}
445 	*der_dn = s->peer_der_dn;
446 	return 0;
447 }
448 
449 /* what kind of hostname were we given? */
450 #define	IS_DNS	0
451 #define	IS_IP4	1
452 #define	IS_IP6	2
453 
454 #define	CN_OID	"2.5.4.3"
455 
456 static int
457 tlsg_session_chkhost( LDAP *ld, tls_session *session, const char *name_in )
458 {
459 	tlsg_session *s = (tlsg_session *)session;
460 	int i, ret;
461 	const gnutls_datum_t *peer_cert_list;
462 	unsigned int list_size;
463 	char altname[NI_MAXHOST];
464 	size_t altnamesize;
465 
466 	gnutls_x509_crt_t cert;
467 	const char *name;
468 	char *ptr;
469 	char *domain = NULL;
470 #ifdef LDAP_PF_INET6
471 	struct in6_addr addr;
472 #else
473 	struct in_addr addr;
474 #endif
475 	int len1 = 0, len2 = 0;
476 	int ntype = IS_DNS;
477 
478 	if( ldap_int_hostname &&
479 		( !name_in || !strcasecmp( name_in, "localhost" ) ) )
480 	{
481 		name = ldap_int_hostname;
482 	} else {
483 		name = name_in;
484 	}
485 
486 	peer_cert_list = gnutls_certificate_get_peers( s->session,
487 						&list_size );
488 	if ( !peer_cert_list ) {
489 		Debug( LDAP_DEBUG_ANY,
490 			"TLS: unable to get peer certificate.\n",
491 			0, 0, 0 );
492 		/* If this was a fatal condition, things would have
493 		 * aborted long before now.
494 		 */
495 		return LDAP_SUCCESS;
496 	}
497 	ret = gnutls_x509_crt_init( &cert );
498 	if ( ret < 0 )
499 		return LDAP_LOCAL_ERROR;
500 	ret = gnutls_x509_crt_import( cert, peer_cert_list, GNUTLS_X509_FMT_DER );
501 	if ( ret ) {
502 		gnutls_x509_crt_deinit( cert );
503 		return LDAP_LOCAL_ERROR;
504 	}
505 
506 #ifdef LDAP_PF_INET6
507 	if (inet_pton(AF_INET6, name, &addr)) {
508 		ntype = IS_IP6;
509 	} else
510 #endif
511 	if ((ptr = strrchr(name, '.')) && isdigit((unsigned char)ptr[1])) {
512 		if (inet_aton(name, (struct in_addr *)&addr)) ntype = IS_IP4;
513 	}
514 
515 	if (ntype == IS_DNS) {
516 		len1 = strlen(name);
517 		domain = strchr(name, '.');
518 		if (domain) {
519 			len2 = len1 - (domain-name);
520 		}
521 	}
522 
523 	for ( i=0, ret=0; ret >= 0; i++ ) {
524 		altnamesize = sizeof(altname);
525 		ret = gnutls_x509_crt_get_subject_alt_name( cert, i,
526 			altname, &altnamesize, NULL );
527 		if ( ret < 0 ) break;
528 
529 		/* ignore empty */
530 		if ( altnamesize == 0 ) continue;
531 
532 		if ( ret == GNUTLS_SAN_DNSNAME ) {
533 			if (ntype != IS_DNS) continue;
534 
535 			/* Is this an exact match? */
536 			if ((len1 == altnamesize) && !strncasecmp(name, altname, len1)) {
537 				break;
538 			}
539 
540 			/* Is this a wildcard match? */
541 			if (domain && (altname[0] == '*') && (altname[1] == '.') &&
542 				(len2 == altnamesize-1) && !strncasecmp(domain, &altname[1], len2))
543 			{
544 				break;
545 			}
546 		} else if ( ret == GNUTLS_SAN_IPADDRESS ) {
547 			if (ntype == IS_DNS) continue;
548 
549 #ifdef LDAP_PF_INET6
550 			if (ntype == IS_IP6 && altnamesize != sizeof(struct in6_addr)) {
551 				continue;
552 			} else
553 #endif
554 			if (ntype == IS_IP4 && altnamesize != sizeof(struct in_addr)) {
555 				continue;
556 			}
557 			if (!memcmp(altname, &addr, altnamesize)) {
558 				break;
559 			}
560 		}
561 	}
562 	if ( ret >= 0 ) {
563 		ret = LDAP_SUCCESS;
564 	} else {
565 		/* find the last CN */
566 		i=0;
567 		do {
568 			altnamesize = 0;
569 			ret = gnutls_x509_crt_get_dn_by_oid( cert, CN_OID,
570 				i, 1, altname, &altnamesize );
571 			if ( ret == GNUTLS_E_SHORT_MEMORY_BUFFER )
572 				i++;
573 			else
574 				break;
575 		} while ( 1 );
576 
577 		if ( i ) {
578 			altnamesize = sizeof(altname);
579 			ret = gnutls_x509_crt_get_dn_by_oid( cert, CN_OID,
580 				i-1, 0, altname, &altnamesize );
581 		}
582 
583 		if ( ret < 0 ) {
584 			Debug( LDAP_DEBUG_ANY,
585 				"TLS: unable to get common name from peer certificate.\n",
586 				0, 0, 0 );
587 			ret = LDAP_CONNECT_ERROR;
588 			if ( ld->ld_error ) {
589 				LDAP_FREE( ld->ld_error );
590 			}
591 			ld->ld_error = LDAP_STRDUP(
592 				_("TLS: unable to get CN from peer certificate"));
593 
594 		} else {
595 			ret = LDAP_LOCAL_ERROR;
596 			if ( !len1 ) len1 = strlen( name );
597 			if ( len1 == altnamesize && strncasecmp(name, altname, altnamesize) == 0 ) {
598 				ret = LDAP_SUCCESS;
599 
600 			} else if (( altname[0] == '*' ) && ( altname[1] == '.' )) {
601 					/* Is this a wildcard match? */
602 				if( domain &&
603 					(len2 == altnamesize-1) && !strncasecmp(domain, &altname[1], len2)) {
604 					ret = LDAP_SUCCESS;
605 				}
606 			}
607 		}
608 
609 		if( ret == LDAP_LOCAL_ERROR ) {
610 			altname[altnamesize] = '\0';
611 			Debug( LDAP_DEBUG_ANY, "TLS: hostname (%s) does not match "
612 				"common name in certificate (%s).\n",
613 				name, altname, 0 );
614 			ret = LDAP_CONNECT_ERROR;
615 			if ( ld->ld_error ) {
616 				LDAP_FREE( ld->ld_error );
617 			}
618 			ld->ld_error = LDAP_STRDUP(
619 				_("TLS: hostname does not match CN in peer certificate"));
620 		}
621 	}
622 	gnutls_x509_crt_deinit( cert );
623 	return ret;
624 }
625 
626 static int
627 tlsg_session_strength( tls_session *session )
628 {
629 	tlsg_session *s = (tlsg_session *)session;
630 	gnutls_cipher_algorithm_t c;
631 
632 	c = gnutls_cipher_get( s->session );
633 	return gnutls_cipher_get_key_size( c ) * 8;
634 }
635 
636 /* suites is a string of colon-separated cipher suite names. */
637 static int
638 tlsg_parse_ciphers( tlsg_ctx *ctx, char *suites )
639 {
640 	const char *err;
641 	int rc = gnutls_priority_init( &ctx->prios, suites, &err );
642 	if ( rc )
643 		ctx->prios = NULL;
644 	return rc;
645 }
646 
647 /*
648  * TLS support for LBER Sockbufs
649  */
650 
651 struct tls_data {
652 	tlsg_session		*session;
653 	Sockbuf_IO_Desc		*sbiod;
654 };
655 
656 static ssize_t
657 tlsg_recv( gnutls_transport_ptr_t ptr, void *buf, size_t len )
658 {
659 	struct tls_data		*p;
660 
661 	if ( buf == NULL || len <= 0 ) return 0;
662 
663 	p = (struct tls_data *)ptr;
664 
665 	if ( p == NULL || p->sbiod == NULL ) {
666 		return 0;
667 	}
668 
669 	return LBER_SBIOD_READ_NEXT( p->sbiod, buf, len );
670 }
671 
672 static ssize_t
673 tlsg_send( gnutls_transport_ptr_t ptr, const void *buf, size_t len )
674 {
675 	struct tls_data		*p;
676 
677 	if ( buf == NULL || len <= 0 ) return 0;
678 
679 	p = (struct tls_data *)ptr;
680 
681 	if ( p == NULL || p->sbiod == NULL ) {
682 		return 0;
683 	}
684 
685 	return LBER_SBIOD_WRITE_NEXT( p->sbiod, (char *)buf, len );
686 }
687 
688 static int
689 tlsg_sb_setup( Sockbuf_IO_Desc *sbiod, void *arg )
690 {
691 	struct tls_data		*p;
692 	tlsg_session	*session = arg;
693 
694 	assert( sbiod != NULL );
695 
696 	p = LBER_MALLOC( sizeof( *p ) );
697 	if ( p == NULL ) {
698 		return -1;
699 	}
700 
701 	gnutls_transport_set_ptr( session->session, (gnutls_transport_ptr)p );
702 	gnutls_transport_set_pull_function( session->session, tlsg_recv );
703 	gnutls_transport_set_push_function( session->session, tlsg_send );
704 	p->session = session;
705 	p->sbiod = sbiod;
706 	sbiod->sbiod_pvt = p;
707 	return 0;
708 }
709 
710 static int
711 tlsg_sb_remove( Sockbuf_IO_Desc *sbiod )
712 {
713 	struct tls_data		*p;
714 
715 	assert( sbiod != NULL );
716 	assert( sbiod->sbiod_pvt != NULL );
717 
718 	p = (struct tls_data *)sbiod->sbiod_pvt;
719 	gnutls_deinit ( p->session->session );
720 	LBER_FREE( p->session );
721 	LBER_FREE( sbiod->sbiod_pvt );
722 	sbiod->sbiod_pvt = NULL;
723 	return 0;
724 }
725 
726 static int
727 tlsg_sb_close( Sockbuf_IO_Desc *sbiod )
728 {
729 	struct tls_data		*p;
730 
731 	assert( sbiod != NULL );
732 	assert( sbiod->sbiod_pvt != NULL );
733 
734 	p = (struct tls_data *)sbiod->sbiod_pvt;
735 	gnutls_bye ( p->session->session, GNUTLS_SHUT_WR );
736 	return 0;
737 }
738 
739 static int
740 tlsg_sb_ctrl( Sockbuf_IO_Desc *sbiod, int opt, void *arg )
741 {
742 	struct tls_data		*p;
743 
744 	assert( sbiod != NULL );
745 	assert( sbiod->sbiod_pvt != NULL );
746 
747 	p = (struct tls_data *)sbiod->sbiod_pvt;
748 
749 	if ( opt == LBER_SB_OPT_GET_SSL ) {
750 		*((tlsg_session **)arg) = p->session;
751 		return 1;
752 
753 	} else if ( opt == LBER_SB_OPT_DATA_READY ) {
754 		if( gnutls_record_check_pending( p->session->session ) > 0 ) {
755 			return 1;
756 		}
757 	}
758 
759 	return LBER_SBIOD_CTRL_NEXT( sbiod, opt, arg );
760 }
761 
762 static ber_slen_t
763 tlsg_sb_read( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len)
764 {
765 	struct tls_data		*p;
766 	ber_slen_t		ret;
767 
768 	assert( sbiod != NULL );
769 	assert( SOCKBUF_VALID( sbiod->sbiod_sb ) );
770 
771 	p = (struct tls_data *)sbiod->sbiod_pvt;
772 
773 	ret = gnutls_record_recv ( p->session->session, buf, len );
774 	switch (ret) {
775 	case GNUTLS_E_INTERRUPTED:
776 	case GNUTLS_E_AGAIN:
777 		sbiod->sbiod_sb->sb_trans_needs_read = 1;
778 		sock_errset(EWOULDBLOCK);
779 		ret = 0;
780 		break;
781 	case GNUTLS_E_REHANDSHAKE:
782 		for ( ret = gnutls_handshake ( p->session->session );
783 		      ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN;
784 		      ret = gnutls_handshake ( p->session->session ) );
785 		sbiod->sbiod_sb->sb_trans_needs_read = 1;
786 		ret = 0;
787 		break;
788 	default:
789 		sbiod->sbiod_sb->sb_trans_needs_read = 0;
790 	}
791 	return ret;
792 }
793 
794 static ber_slen_t
795 tlsg_sb_write( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len)
796 {
797 	struct tls_data		*p;
798 	ber_slen_t		ret;
799 
800 	assert( sbiod != NULL );
801 	assert( SOCKBUF_VALID( sbiod->sbiod_sb ) );
802 
803 	p = (struct tls_data *)sbiod->sbiod_pvt;
804 
805 	ret = gnutls_record_send ( p->session->session, (char *)buf, len );
806 
807 	if ( ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN ) {
808 		sbiod->sbiod_sb->sb_trans_needs_write = 1;
809 		sock_errset(EWOULDBLOCK);
810 		ret = 0;
811 	} else {
812 		sbiod->sbiod_sb->sb_trans_needs_write = 0;
813 	}
814 	return ret;
815 }
816 
817 static Sockbuf_IO tlsg_sbio =
818 {
819 	tlsg_sb_setup,		/* sbi_setup */
820 	tlsg_sb_remove,		/* sbi_remove */
821 	tlsg_sb_ctrl,		/* sbi_ctrl */
822 	tlsg_sb_read,		/* sbi_read */
823 	tlsg_sb_write,		/* sbi_write */
824 	tlsg_sb_close		/* sbi_close */
825 };
826 
827 /* Certs are not automatically varified during the handshake */
828 static int
829 tlsg_cert_verify( tlsg_session *ssl )
830 {
831 	unsigned int status = 0;
832 	int err;
833 	time_t now = time(0);
834 	time_t peertime;
835 
836 	err = gnutls_certificate_verify_peers2( ssl->session, &status );
837 	if ( err < 0 ) {
838 		Debug( LDAP_DEBUG_ANY,"TLS: gnutls_certificate_verify_peers2 failed %d\n",
839 			err,0,0 );
840 		return -1;
841 	}
842 	if ( status ) {
843 		Debug( LDAP_DEBUG_TRACE,"TLS: peer cert untrusted or revoked (0x%x)\n",
844 			status, 0,0 );
845 		return -1;
846 	}
847 	peertime = gnutls_certificate_expiration_time_peers( ssl->session );
848 	if ( peertime == (time_t) -1 ) {
849 		Debug( LDAP_DEBUG_ANY, "TLS: gnutls_certificate_expiration_time_peers failed\n",
850 			0, 0, 0 );
851 		return -1;
852 	}
853 	if ( peertime < now ) {
854 		Debug( LDAP_DEBUG_ANY, "TLS: peer certificate is expired\n",
855 			0, 0, 0 );
856 		return -1;
857 	}
858 	peertime = gnutls_certificate_activation_time_peers( ssl->session );
859 	if ( peertime == (time_t) -1 ) {
860 		Debug( LDAP_DEBUG_ANY, "TLS: gnutls_certificate_activation_time_peers failed\n",
861 			0, 0, 0 );
862 		return -1;
863 	}
864 	if ( peertime > now ) {
865 		Debug( LDAP_DEBUG_ANY, "TLS: peer certificate not yet active\n",
866 			0, 0, 0 );
867 		return -1;
868 	}
869 	return 0;
870 }
871 
872 tls_impl ldap_int_tls_impl = {
873 	"GnuTLS",
874 
875 	tlsg_init,
876 	tlsg_destroy,
877 
878 	tlsg_ctx_new,
879 	tlsg_ctx_ref,
880 	tlsg_ctx_free,
881 	tlsg_ctx_init,
882 
883 	tlsg_session_new,
884 	tlsg_session_connect,
885 	tlsg_session_accept,
886 	tlsg_session_upflags,
887 	tlsg_session_errmsg,
888 	tlsg_session_my_dn,
889 	tlsg_session_peer_dn,
890 	tlsg_session_chkhost,
891 	tlsg_session_strength,
892 
893 	&tlsg_sbio,
894 
895 #ifdef LDAP_R_COMPILE
896 	tlsg_thr_init,
897 #else
898 	NULL,
899 #endif
900 
901 	0
902 };
903 
904 #endif /* HAVE_GNUTLS */
905