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