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