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