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