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