]> git.sur5r.net Git - openldap/blob - libraries/libldap/tls_g.c
ITS#7506 cleanup prev commit
[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-2016 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 /* suites is a string of colon-separated cipher suite names. */
671 static int
672 tlsg_parse_ciphers( tlsg_ctx *ctx, char *suites )
673 {
674         const char *err;
675         int rc = gnutls_priority_init( &ctx->prios, suites, &err );
676         if ( rc )
677                 ctx->prios = NULL;
678         return rc;
679 }
680
681 /*
682  * TLS support for LBER Sockbufs
683  */
684
685 struct tls_data {
686         tlsg_session            *session;
687         Sockbuf_IO_Desc         *sbiod;
688 };
689
690 static ssize_t
691 tlsg_recv( gnutls_transport_ptr_t ptr, void *buf, size_t len )
692 {
693         struct tls_data         *p;
694
695         if ( buf == NULL || len <= 0 ) return 0;
696
697         p = (struct tls_data *)ptr;
698
699         if ( p == NULL || p->sbiod == NULL ) {
700                 return 0;
701         }
702
703         return LBER_SBIOD_READ_NEXT( p->sbiod, buf, len );
704 }
705
706 static ssize_t
707 tlsg_send( gnutls_transport_ptr_t ptr, const void *buf, size_t len )
708 {
709         struct tls_data         *p;
710         
711         if ( buf == NULL || len <= 0 ) return 0;
712         
713         p = (struct tls_data *)ptr;
714
715         if ( p == NULL || p->sbiod == NULL ) {
716                 return 0;
717         }
718
719         return LBER_SBIOD_WRITE_NEXT( p->sbiod, (char *)buf, len );
720 }
721
722 static int
723 tlsg_sb_setup( Sockbuf_IO_Desc *sbiod, void *arg )
724 {
725         struct tls_data         *p;
726         tlsg_session    *session = arg;
727
728         assert( sbiod != NULL );
729
730         p = LBER_MALLOC( sizeof( *p ) );
731         if ( p == NULL ) {
732                 return -1;
733         }
734         
735         gnutls_transport_set_ptr( session->session, (gnutls_transport_ptr)p );
736         gnutls_transport_set_pull_function( session->session, tlsg_recv );
737         gnutls_transport_set_push_function( session->session, tlsg_send );
738         p->session = session;
739         p->sbiod = sbiod;
740         sbiod->sbiod_pvt = p;
741         return 0;
742 }
743
744 static int
745 tlsg_sb_remove( Sockbuf_IO_Desc *sbiod )
746 {
747         struct tls_data         *p;
748         
749         assert( sbiod != NULL );
750         assert( sbiod->sbiod_pvt != NULL );
751
752         p = (struct tls_data *)sbiod->sbiod_pvt;
753         gnutls_deinit ( p->session->session );
754         LBER_FREE( p->session );
755         LBER_FREE( sbiod->sbiod_pvt );
756         sbiod->sbiod_pvt = NULL;
757         return 0;
758 }
759
760 static int
761 tlsg_sb_close( Sockbuf_IO_Desc *sbiod )
762 {
763         struct tls_data         *p;
764         
765         assert( sbiod != NULL );
766         assert( sbiod->sbiod_pvt != NULL );
767
768         p = (struct tls_data *)sbiod->sbiod_pvt;
769         gnutls_bye ( p->session->session, GNUTLS_SHUT_WR );
770         return 0;
771 }
772
773 static int
774 tlsg_sb_ctrl( Sockbuf_IO_Desc *sbiod, int opt, void *arg )
775 {
776         struct tls_data         *p;
777         
778         assert( sbiod != NULL );
779         assert( sbiod->sbiod_pvt != NULL );
780
781         p = (struct tls_data *)sbiod->sbiod_pvt;
782         
783         if ( opt == LBER_SB_OPT_GET_SSL ) {
784                 *((tlsg_session **)arg) = p->session;
785                 return 1;
786                 
787         } else if ( opt == LBER_SB_OPT_DATA_READY ) {
788                 if( gnutls_record_check_pending( p->session->session ) > 0 ) {
789                         return 1;
790                 }
791         }
792         
793         return LBER_SBIOD_CTRL_NEXT( sbiod, opt, arg );
794 }
795
796 static ber_slen_t
797 tlsg_sb_read( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len)
798 {
799         struct tls_data         *p;
800         ber_slen_t              ret;
801
802         assert( sbiod != NULL );
803         assert( SOCKBUF_VALID( sbiod->sbiod_sb ) );
804
805         p = (struct tls_data *)sbiod->sbiod_pvt;
806
807         ret = gnutls_record_recv ( p->session->session, buf, len );
808         switch (ret) {
809         case GNUTLS_E_INTERRUPTED:
810         case GNUTLS_E_AGAIN:
811                 sbiod->sbiod_sb->sb_trans_needs_read = 1;
812                 sock_errset(EWOULDBLOCK);
813                 ret = 0;
814                 break;
815         case GNUTLS_E_REHANDSHAKE:
816                 for ( ret = gnutls_handshake ( p->session->session );
817                       ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN;
818                       ret = gnutls_handshake ( p->session->session ) );
819                 sbiod->sbiod_sb->sb_trans_needs_read = 1;
820                 ret = 0;
821                 break;
822         default:
823                 sbiod->sbiod_sb->sb_trans_needs_read = 0;
824         }
825         return ret;
826 }
827
828 static ber_slen_t
829 tlsg_sb_write( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len)
830 {
831         struct tls_data         *p;
832         ber_slen_t              ret;
833
834         assert( sbiod != NULL );
835         assert( SOCKBUF_VALID( sbiod->sbiod_sb ) );
836
837         p = (struct tls_data *)sbiod->sbiod_pvt;
838
839         ret = gnutls_record_send ( p->session->session, (char *)buf, len );
840
841         if ( ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN ) {
842                 sbiod->sbiod_sb->sb_trans_needs_write = 1;
843                 sock_errset(EWOULDBLOCK);
844                 ret = 0;
845         } else {
846                 sbiod->sbiod_sb->sb_trans_needs_write = 0;
847         }
848         return ret;
849 }
850
851 static Sockbuf_IO tlsg_sbio =
852 {
853         tlsg_sb_setup,          /* sbi_setup */
854         tlsg_sb_remove,         /* sbi_remove */
855         tlsg_sb_ctrl,           /* sbi_ctrl */
856         tlsg_sb_read,           /* sbi_read */
857         tlsg_sb_write,          /* sbi_write */
858         tlsg_sb_close           /* sbi_close */
859 };
860
861 /* Certs are not automatically varified during the handshake */
862 static int
863 tlsg_cert_verify( tlsg_session *ssl )
864 {
865         unsigned int status = 0;
866         int err;
867         time_t now = time(0);
868         time_t peertime;
869
870         err = gnutls_certificate_verify_peers2( ssl->session, &status );
871         if ( err < 0 ) {
872                 Debug( LDAP_DEBUG_ANY,"TLS: gnutls_certificate_verify_peers2 failed %d\n",
873                         err,0,0 );
874                 return -1;
875         }
876         if ( status ) {
877                 Debug( LDAP_DEBUG_TRACE,"TLS: peer cert untrusted or revoked (0x%x)\n",
878                         status, 0,0 );
879                 return -1;
880         }
881         peertime = gnutls_certificate_expiration_time_peers( ssl->session );
882         if ( peertime == (time_t) -1 ) {
883                 Debug( LDAP_DEBUG_ANY, "TLS: gnutls_certificate_expiration_time_peers failed\n",
884                         0, 0, 0 );
885                 return -1;
886         }
887         if ( peertime < now ) {
888                 Debug( LDAP_DEBUG_ANY, "TLS: peer certificate is expired\n",
889                         0, 0, 0 );
890                 return -1;
891         }
892         peertime = gnutls_certificate_activation_time_peers( ssl->session );
893         if ( peertime == (time_t) -1 ) {
894                 Debug( LDAP_DEBUG_ANY, "TLS: gnutls_certificate_activation_time_peers failed\n",
895                         0, 0, 0 );
896                 return -1;
897         }
898         if ( peertime > now ) {
899                 Debug( LDAP_DEBUG_ANY, "TLS: peer certificate not yet active\n",
900                         0, 0, 0 );
901                 return -1;
902         }
903         return 0;
904 }
905
906 tls_impl ldap_int_tls_impl = {
907         "GnuTLS",
908
909         tlsg_init,
910         tlsg_destroy,
911
912         tlsg_ctx_new,
913         tlsg_ctx_ref,
914         tlsg_ctx_free,
915         tlsg_ctx_init,
916
917         tlsg_session_new,
918         tlsg_session_connect,
919         tlsg_session_accept,
920         tlsg_session_upflags,
921         tlsg_session_errmsg,
922         tlsg_session_my_dn,
923         tlsg_session_peer_dn,
924         tlsg_session_chkhost,
925         tlsg_session_strength,
926
927         &tlsg_sbio,
928
929 #ifdef LDAP_R_COMPILE
930         tlsg_thr_init,
931 #else
932         NULL,
933 #endif
934
935         0
936 };
937
938 #endif /* HAVE_GNUTLS */