]> git.sur5r.net Git - openldap/blob - libraries/libldap/tls_m.c
Silence warning in print_deref(): cast lutil_b64_ntop() arg to unsigned char*
[openldap] / libraries / libldap / tls_m.c
1 /* tls_m.c - Handle tls/ssl using Mozilla NSS. */
2 /* $OpenLDAP$ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4  *
5  * Copyright 2008-2009 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: written by Howard Chu.
17  */
18
19 #include "portable.h"
20
21 #ifdef HAVE_MOZNSS
22
23 #include "ldap_config.h"
24
25 #include <stdio.h>
26
27 #include <ac/stdlib.h>
28 #include <ac/errno.h>
29 #include <ac/socket.h>
30 #include <ac/string.h>
31 #include <ac/ctype.h>
32 #include <ac/time.h>
33 #include <ac/unistd.h>
34 #include <ac/param.h>
35 #include <ac/dirent.h>
36
37 #include "ldap-int.h"
38 #include "ldap-tls.h"
39
40 #ifdef LDAP_R_COMPILE
41 #include <ldap_pvt_thread.h>
42 #endif
43
44 #include <nspr.h>
45 #include <nss.h>
46 #include <ssl.h>
47
48 typedef struct tlsm_ctx {
49         PRFileDesc *tc_model;
50         int tc_refcnt;
51 #ifdef LDAP_R_COMPILE
52         ldap_pvt_thread_mutex_t tc_refmutex;
53 #endif
54 } tlsm_ctx;
55
56 typedef PRFileDesc tlsm_session;
57
58 static PRDescIdentity   tlsm_layer_id;
59
60 static const PRIOMethods tlsm_PR_methods;
61
62 extern tls_impl ldap_int_tls_impl;
63
64 static int tslm_did_init;
65
66 #ifdef LDAP_R_COMPILE
67
68 static void
69 tlsm_thr_init( void )
70 {
71 }
72
73 #endif /* LDAP_R_COMPILE */
74
75 /*
76  * Initialize TLS subsystem. Should be called only once.
77  */
78 static int
79 tlsm_init( void )
80 {
81         PR_Init(0, 0, 0);
82
83         tlsm_layer_id = PR_GetUniqueIdentity("OpenLDAP");
84
85         if ( !NSS_IsInitialized() ) {
86                 tlsm_did_init = 1;
87
88                 NSS_NoDB_Init("");
89
90                 NSS_SetDomesticPolicy();
91         }
92
93         /* No cipher suite handling for now */
94
95         return 0;
96 }
97
98 /*
99  * Tear down the TLS subsystem. Should only be called once.
100  */
101 static void
102 tlsm_destroy( void )
103 {
104         /* Only if we did the actual initialization */
105         if ( tlsm_did_init ) {
106                 tlsm_did_init = 0;
107
108                 NSS_Shutdown();
109         }
110
111         PR_Cleanup();
112 }
113
114 static tls_ctx *
115 tlsm_ctx_new ( struct ldapoptions *lo )
116 {
117         tlsm_ctx *ctx;
118
119         ctx = LDAP_MALLOC( sizeof (*ctx) );
120         if ( ctx ) {
121                 PRFileDesc *fd = PR_CreateIOLayerStub(tlsm_layer_id, &tlsm_PR_methods);
122                 if ( fd ) {
123                         ctx->tc_model = SSL_ImportFD( NULL, fd );
124                         if ( ctx->tc_model ) {
125                                 ctx->tc_refcnt = 1;
126 #ifdef LDAP_R_COMPILE
127                                 ldap_pvt_thread_mutex_init( &ctx->tc_refmutex );
128 #endif
129                         } else {
130                                 PR_DELETE( fd );
131                                 LDAP_FREE( ctx );
132                                 ctx = NULL;
133                         }
134                 } else {
135                         LDAP_FREE( ctx );
136                         ctx = NULL;
137                 }
138         }
139         return (tls_ctx *)ctx;
140 }
141
142 static void
143 tlsm_ctx_ref( tls_ctx *ctx )
144 {
145         tlsm_ctx *c = (tlsm_ctx *)ctx;
146 #ifdef LDAP_R_COMPILE
147         ldap_pvt_thread_mutex_lock( &c->tc_refmutex );
148 #endif
149         c->tc_refcnt++;
150 #ifdef LDAP_R_COMPILE
151         ldap_pvt_thread_mutex_unlock( &c->tc_refmutex );
152 #endif
153 }
154
155 static void
156 tlsm_ctx_free ( tls_ctx *ctx )
157 {
158         tlsm_ctx *c = (tlsm_ctx *)ctx;
159         int refcount;
160
161         if ( !c ) return;
162
163 #ifdef LDAP_R_COMPILE
164         ldap_pvt_thread_mutex_lock( &c->tc_refmutex );
165 #endif
166         refcount = --c->tc_refcnt;
167 #ifdef LDAP_R_COMPILE
168         ldap_pvt_thread_mutex_unlock( &c->tc_refmutex );
169 #endif
170         if ( refcount )
171                 return;
172         PR_Close( c->tc_model );
173 #ifdef LDAP_R_COMPILE
174         ldap_pvt_thread_mutex_destroy( &c->tc_refmutex );
175 #endif
176         LDAP_FREE( c );
177 }
178
179 /*
180  * initialize a new TLS context
181  */
182 static int
183 tlsm_ctx_init( struct ldapoptions *lo, struct ldaptls *lt, int is_server )
184 {
185         tlsm_ctx *ctx = lo->ldo_tls_ctx;
186         int rc;
187
188         SSL_OptionSet( ctx->tc_model, SSL_SECURITY, PR_TRUE );
189         SSL_OptionSet( ctx->tc_model, SSL_HANDSHAKE_AS_CLIENT, !is_server );
190         SSL_OptionSet( ctx->tc_model, SSL_HANDSHAKE_AS_SERVER, is_server );
191
192         /* See SECMOD_OpenUserDB() */
193 #if 0
194         if ( lo->ldo_tls_ciphersuite &&
195                 tlsm_parse_ciphers( ctx, lt->lt_ciphersuite )) {
196                 Debug( LDAP_DEBUG_ANY,
197                            "TLS: could not set cipher list %s.\n",
198                            lo->ldo_tls_ciphersuite, 0, 0 );
199                 return -1;
200         }
201
202         if (lo->ldo_tls_cacertdir != NULL) {
203                 Debug( LDAP_DEBUG_ANY, 
204                        "TLS: warning: cacertdir not implemented for gnutls\n",
205                        NULL, NULL, NULL );
206         }
207
208         if (lo->ldo_tls_cacertfile != NULL) {
209                 rc = gnutls_certificate_set_x509_trust_file( 
210                         ctx->cred,
211                         lt->lt_cacertfile,
212                         GNUTLS_X509_FMT_PEM );
213                 if ( rc < 0 ) return -1;
214         }
215
216         if ( lo->ldo_tls_certfile && lo->ldo_tls_keyfile ) {
217                 rc = gnutls_certificate_set_x509_key_file( 
218                         ctx->cred,
219                         lt->lt_certfile,
220                         lt->lt_keyfile,
221                         GNUTLS_X509_FMT_PEM );
222                 if ( rc ) return -1;
223         } else if ( lo->ldo_tls_certfile || lo->ldo_tls_keyfile ) {
224                 Debug( LDAP_DEBUG_ANY, 
225                        "TLS: only one of certfile and keyfile specified\n",
226                        NULL, NULL, NULL );
227                 return -1;
228         }
229
230         if ( lo->ldo_tls_dhfile ) {
231                 Debug( LDAP_DEBUG_ANY, 
232                        "TLS: warning: ignoring dhfile\n", 
233                        NULL, NULL, NULL );
234         }
235
236         if ( lo->ldo_tls_crlfile ) {
237                 rc = gnutls_certificate_set_x509_crl_file( 
238                         ctx->cred,
239                         lt->lt_crlfile,
240                         GNUTLS_X509_FMT_PEM );
241                 if ( rc < 0 ) return -1;
242                 rc = 0;
243         }
244         if ( is_server ) {
245                 gnutls_dh_params_init(&ctx->dh_params);
246                 gnutls_dh_params_generate2(ctx->dh_params, DH_BITS);
247         }
248 #endif
249         return 0;
250 }
251
252 static tls_session *
253 tlsm_session_new ( tls_ctx * ctx, int is_server )
254 {
255         tlsm_ctx *c = (tlsm_ctx *)ctx;
256         tlsm_session *session;
257         PRFileDesc *fd;
258
259         fd = PR_CreateIOLayerStub(tlsm_layer_id, &tlsm_PR_methods);
260         if ( !fd ) {
261                 return NULL;
262         }
263
264         session = SSL_ImportFD( c->tc_model, fd );
265         if ( !session ) {
266                 PR_DELETE( fd );
267                 return NULL;
268         }
269
270         SSL_ResetHandshake( session, is_server );
271
272         return (tls_session *)session;
273
274
275 static int
276 tlsm_session_accept( tls_session *session )
277 {
278         tlsm_session *s = (tlsm_session *)session;
279
280         return SSL_ForceHandshake( s );
281 }
282
283 static int
284 tlsm_session_connect( LDAP *ld, tls_session *session )
285 {
286         tlsm_session *s = (tlsm_session *)session;
287         int rc;
288
289         /* By default, NSS checks the cert hostname for us */
290         rc = SSL_SetURL( s, ld->ld_options.ldo_defludp->lud_host );
291         return SSL_ForceHandshake( s );
292 }
293
294 static int
295 tlsm_session_upflags( Sockbuf *sb, tls_session *session, int rc )
296 {
297         /* Should never happen */
298         rc = PR_GetError();
299
300         if ( rc != PR_PENDING_INTERRUPT_ERROR && rc != PR_WOULD_BLOCK_ERROR )
301                 return 0;
302         return 0;
303 }
304
305 static char *
306 tlsm_session_errmsg( int rc, char *buf, size_t len )
307 {
308         int i;
309
310         rc = PR_GetError();
311         i = PR_GetErrorTextLength();
312         if ( i > len ) {
313                 char *msg = LDAP_MALLOC( i+1 );
314                 PR_GetErrorText( msg );
315                 memcpy( buf, msg, len );
316                 LDAP_FREE( msg );
317         } else if ( i ) {
318                 PR_GetErrorText( buf );
319         }
320
321         return i ? buf : NULL;
322 }
323
324 static int
325 tlsm_session_my_dn( tls_session *session, struct berval *der_dn )
326 {
327         tlsm_session *s = (tlsm_session *)session;
328         CERTCertificate *cert;
329
330         cert = SSL_LocalCertificate( s );
331         if (!cert) return LDAP_INVALID_CREDENTIALS;
332
333         der_dn->bv_val = cert->derSubject.data;
334         der_dn->bv_len = cert->derSubject.len;
335         CERT_DestroyCertificate( cert );
336         return 0;
337 }
338
339 static int
340 tlsm_session_peer_dn( tls_session *session, struct berval *der_dn )
341 {
342         tlsm_session *s = (tlsm_session *)session;
343         CERTCertificate *cert;
344
345         cert = SSL_PeerCertificate( s );
346         if (!cert) return LDAP_INVALID_CREDENTIALS;
347         
348         der_dn->bv_val = cert->derSubject.data;
349         der_dn->bv_len = cert->derSubject.len;
350         CERT_DestroyCertificate( cert );
351         return 0;
352 }
353
354 /* what kind of hostname were we given? */
355 #define IS_DNS  0
356 #define IS_IP4  1
357 #define IS_IP6  2
358
359 static int
360 tlsm_session_chkhost( LDAP *ld, tls_session *session, const char *name_in )
361 {
362 /* NSS already does a hostname check */
363 #if 0
364         int i, ret;
365         const gnutls_datum_t *peer_cert_list;
366         int list_size;
367         struct berval bv;
368         char altname[NI_MAXHOST];
369         size_t altnamesize;
370
371         gnutls_x509_crt_t cert;
372         gnutls_datum_t *x;
373         const char *name;
374         char *ptr;
375         char *domain = NULL;
376 #ifdef LDAP_PF_INET6
377         struct in6_addr addr;
378 #else
379         struct in_addr addr;
380 #endif
381         int n, len1 = 0, len2 = 0;
382         int ntype = IS_DNS;
383         time_t now = time(0);
384
385         if( ldap_int_hostname &&
386                 ( !name_in || !strcasecmp( name_in, "localhost" ) ) )
387         {
388                 name = ldap_int_hostname;
389         } else {
390                 name = name_in;
391         }
392
393         peer_cert_list = gnutls_certificate_get_peers( session->session, 
394                                                 &list_size );
395         if ( !peer_cert_list ) {
396                 Debug( LDAP_DEBUG_ANY,
397                         "TLS: unable to get peer certificate.\n",
398                         0, 0, 0 );
399                 /* If this was a fatal condition, things would have
400                  * aborted long before now.
401                  */
402                 return LDAP_SUCCESS;
403         }
404         ret = gnutls_x509_crt_init( &cert );
405         if ( ret < 0 )
406                 return LDAP_LOCAL_ERROR;
407         ret = gnutls_x509_crt_import( cert, peer_cert_list, GNUTLS_X509_FMT_DER );
408         if ( ret ) {
409                 gnutls_x509_crt_deinit( cert );
410                 return LDAP_LOCAL_ERROR;
411         }
412
413 #ifdef LDAP_PF_INET6
414         if (name[0] == '[' && strchr(name, ']')) {
415                 char *n2 = ldap_strdup(name+1);
416                 *strchr(n2, ']') = 0;
417                 if (inet_pton(AF_INET6, n2, &addr))
418                         ntype = IS_IP6;
419                 LDAP_FREE(n2);
420         } else 
421 #endif
422         if ((ptr = strrchr(name, '.')) && isdigit((unsigned char)ptr[1])) {
423                 if (inet_aton(name, (struct in_addr *)&addr)) ntype = IS_IP4;
424         }
425         
426         if (ntype == IS_DNS) {
427                 len1 = strlen(name);
428                 domain = strchr(name, '.');
429                 if (domain) {
430                         len2 = len1 - (domain-name);
431                 }
432         }
433
434         for ( i=0, ret=0; ret >= 0; i++ ) {
435                 altnamesize = sizeof(altname);
436                 ret = gnutls_x509_crt_get_subject_alt_name( cert, i, 
437                         altname, &altnamesize, NULL );
438                 if ( ret < 0 ) break;
439
440                 /* ignore empty */
441                 if ( altnamesize == 0 ) continue;
442
443                 if ( ret == GNUTLS_SAN_DNSNAME ) {
444                         if (ntype != IS_DNS) continue;
445         
446                         /* Is this an exact match? */
447                         if ((len1 == altnamesize) && !strncasecmp(name, altname, len1)) {
448                                 break;
449                         }
450
451                         /* Is this a wildcard match? */
452                         if (domain && (altname[0] == '*') && (altname[1] == '.') &&
453                                 (len2 == altnamesize-1) && !strncasecmp(domain, &altname[1], len2))
454                         {
455                                 break;
456                         }
457                 } else if ( ret == GNUTLS_SAN_IPADDRESS ) {
458                         if (ntype == IS_DNS) continue;
459
460 #ifdef LDAP_PF_INET6
461                         if (ntype == IS_IP6 && altnamesize != sizeof(struct in6_addr)) {
462                                 continue;
463                         } else
464 #endif
465                         if (ntype == IS_IP4 && altnamesize != sizeof(struct in_addr)) {
466                                 continue;
467                         }
468                         if (!memcmp(altname, &addr, altnamesize)) {
469                                 break;
470                         }
471                 }
472         }
473         if ( ret >= 0 ) {
474                 ret = LDAP_SUCCESS;
475         } else {
476                 altnamesize = sizeof(altname);
477                 ret = gnutls_x509_crt_get_dn_by_oid( cert, CN_OID,
478                         0, 0, altname, &altnamesize );
479                 if ( ret < 0 ) {
480                         Debug( LDAP_DEBUG_ANY,
481                                 "TLS: unable to get common name from peer certificate.\n",
482                                 0, 0, 0 );
483                         ret = LDAP_CONNECT_ERROR;
484                         if ( ld->ld_error ) {
485                                 LDAP_FREE( ld->ld_error );
486                         }
487                         ld->ld_error = LDAP_STRDUP(
488                                 _("TLS: unable to get CN from peer certificate"));
489
490                 } else {
491                         ret = LDAP_LOCAL_ERROR;
492                         if ( len1 == altnamesize && strncasecmp(name, altname, altnamesize) == 0 ) {
493                                 ret = LDAP_SUCCESS;
494
495                         } else if (( altname[0] == '*' ) && ( altname[1] == '.' )) {
496                                         /* Is this a wildcard match? */
497                                 if( domain &&
498                                         (len2 == altnamesize-1) && !strncasecmp(domain, &altname[1], len2)) {
499                                         ret = LDAP_SUCCESS;
500                                 }
501                         }
502                 }
503
504                 if( ret == LDAP_LOCAL_ERROR ) {
505                         altname[altnamesize] = '\0';
506                         Debug( LDAP_DEBUG_ANY, "TLS: hostname (%s) does not match "
507                                 "common name in certificate (%s).\n", 
508                                 name, altname, 0 );
509                         ret = LDAP_CONNECT_ERROR;
510                         if ( ld->ld_error ) {
511                                 LDAP_FREE( ld->ld_error );
512                         }
513                         ld->ld_error = LDAP_STRDUP(
514                                 _("TLS: hostname does not match CN in peer certificate"));
515                 }
516         }
517         gnutls_x509_crt_deinit( cert );
518         return ret;
519 #endif
520 }
521
522 static int
523 tlsm_session_strength( tls_session *session )
524 {
525         tlsm_session *s = (tlsm_session *)session;
526         int rc, keySize;
527
528         rc = SSL_SecurityStatus( s, NULL, NULL, NULL, &keySize,
529                 NULL, NULL );
530         return rc ? 0 : keySize;
531 }
532
533 /*
534  * TLS support for LBER Sockbufs
535  */
536
537 struct tls_data {
538         tlsm_session            *session;
539         Sockbuf_IO_Desc         *sbiod;
540 };
541
542
543 static PRStatus PR_CALLBACK
544 tlsm_PR_Close(PRFileDesc *fd)
545 {
546         return PR_SUCCESS;
547 }
548
549 static int PR_CALLBACK
550 tlsm_PR_Recv(PRFileDesc *fd, void *buf, PRInt32 len, PRIntn flags,
551          PRIntervalTime timeout)
552 {
553         struct tls_data         *p;
554
555         if ( buf == NULL || len <= 0 ) return 0;
556
557         p = (struct tls_data *)fd->secret;
558
559         if ( p == NULL || p->sbiod == NULL ) {
560                 return 0;
561         }
562
563         return LBER_SBIOD_READ_NEXT( p->sbiod, buf, len );
564 }
565
566 static int PR_CALLBACK
567 tlsm_PR_Send(PRFileDesc *fd, const void *buf, PRInt32 len, PRIntn flags,
568          PRIntervalTime timeout)
569 {
570         struct tls_data         *p;
571
572         if ( buf == NULL || len <= 0 ) return 0;
573
574         p = (struct tls_data *)fd->secret;
575
576         if ( p == NULL || p->sbiod == NULL ) {
577                 return 0;
578         }
579
580         return LBER_SBIOD_WRITE_NEXT( p->sbiod, (char *)buf, len );
581 }
582
583 static int PR_CALLBACK
584 tlsm_PR_Read(PRFileDesc *fd, void *buf, PRInt32 len)
585 {
586         return tlsm_PR_Recv( fd, buf, len, 0, PR_INTERVAL_NO_TIMEOUT );
587 }
588
589 static int PR_CALLBACK
590 tlsm_PR_Write(PRFileDesc *fd, const void *buf, PRInt32 len)
591 {
592         return tlsm_PR_Send( fd, buf, len, 0, PR_INTERVAL_NO_TIMEOUT );
593 }
594
595 static PRStatus PR_CALLBACK
596 tlsm_PR_GetPeerName(PRFileDesc *fd, PRNetAddr *addr)
597 {
598         struct tls_data         *p;
599         int rc;
600         ber_socklen_t len;
601
602         p = (struct tls_data *)fd->secret;
603
604         if ( p == NULL || p->sbiod == NULL ) {
605                 return PR_FAILURE;
606         }
607         len = sizeof(PRNetAddr);
608         return getpeername( p->sbiod->sbiod_sb->sb_fd, (struct sockaddr *)addr, &len );
609 }
610
611 static PRStatus PR_CALLBACK
612 tlsm_PR_prs_unimp()
613 {
614     PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
615     return PR_FAILURE;
616 }
617
618 static PRFileDesc * PR_CALLBACK
619 tlsm_PR_pfd_unimp()
620 {
621     PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
622     return NULL;
623 }
624
625 static PRInt16 PR_CALLBACK
626 tlsm_PR_i16_unimp()
627 {
628     PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
629     return SECFailure;
630 }
631
632 static PRInt32 PR_CALLBACK
633 tlsm_PR_i32_unimp()
634 {
635     PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
636     return SECFailure;
637 }
638
639 static PRInt64 PR_CALLBACK
640 tlsm_PR_i64_unimp()
641 {
642     PRInt64 res;
643
644     PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
645     LL_I2L(res, -1L);
646     return res;
647 }
648
649 static const PRIOMethods tlsm_PR_methods = {
650     PR_DESC_LAYERED,
651     tlsm_PR_Close,                      /* close        */
652     tlsm_PR_Read,                       /* read         */
653     tlsm_PR_Write,                      /* write        */
654     tlsm_PR_i32_unimp,          /* available    */
655     tlsm_PR_i64_unimp,          /* available64  */
656     tlsm_PR_prs_unimp,          /* fsync        */
657     tlsm_PR_i32_unimp,          /* seek         */
658     tlsm_PR_i64_unimp,          /* seek64       */
659     tlsm_PR_prs_unimp,          /* fileInfo     */
660     tlsm_PR_prs_unimp,          /* fileInfo64   */
661     tlsm_PR_i32_unimp,          /* writev       */
662     tlsm_PR_prs_unimp,          /* connect      */
663     tlsm_PR_pfd_unimp,          /* accept       */
664     tlsm_PR_prs_unimp,          /* bind         */
665     tlsm_PR_prs_unimp,          /* listen       */
666     (PRShutdownFN)tlsm_PR_Close,                        /* shutdown     */
667     tlsm_PR_Recv,                       /* recv         */
668     tlsm_PR_Send,                       /* send         */
669     tlsm_PR_i32_unimp,          /* recvfrom     */
670     tlsm_PR_i32_unimp,          /* sendto       */
671     (PRPollFN)tlsm_PR_i16_unimp,        /* poll         */
672     tlsm_PR_i32_unimp,          /* acceptread   */
673     tlsm_PR_i32_unimp,          /* transmitfile */
674     tlsm_PR_prs_unimp,          /* getsockname  */
675     tlsm_PR_GetPeerName,        /* getpeername  */
676     tlsm_PR_i32_unimp,          /* getsockopt   OBSOLETE */
677     tlsm_PR_i32_unimp,          /* setsockopt   OBSOLETE */
678     tlsm_PR_i32_unimp,          /* getsocketoption   */
679     tlsm_PR_i32_unimp,          /* setsocketoption   */
680     tlsm_PR_i32_unimp,          /* Send a (partial) file with header/trailer*/
681     (PRConnectcontinueFN)tlsm_PR_prs_unimp,             /* connectcontinue */
682     tlsm_PR_i32_unimp,          /* reserved for future use */
683     tlsm_PR_i32_unimp,          /* reserved for future use */
684     tlsm_PR_i32_unimp,          /* reserved for future use */
685     tlsm_PR_i32_unimp           /* reserved for future use */
686 };
687
688 static int
689 tlsm_sb_setup( Sockbuf_IO_Desc *sbiod, void *arg )
690 {
691         struct tls_data         *p;
692         tlsm_session    *session = arg;
693         PRFileDesc *fd;
694
695         assert( sbiod != NULL );
696
697         p = LBER_MALLOC( sizeof( *p ) );
698         if ( p == NULL ) {
699                 return -1;
700         }
701
702         fd = PR_GetIdentitiesLayer( session, tlsm_layer_id );
703         if ( !fd ) {
704                 LBER_FREE( p );
705                 return -1;
706         }
707
708         fd->secret = (PRFilePrivate *)p;
709         p->session = session;
710         p->sbiod = sbiod;
711         sbiod->sbiod_pvt = p;
712         return 0;
713 }
714
715 static int
716 tlsm_sb_remove( Sockbuf_IO_Desc *sbiod )
717 {
718         struct tls_data         *p;
719         
720         assert( sbiod != NULL );
721         assert( sbiod->sbiod_pvt != NULL );
722
723         p = (struct tls_data *)sbiod->sbiod_pvt;
724         PR_Close( p->session );
725         LBER_FREE( sbiod->sbiod_pvt );
726         sbiod->sbiod_pvt = NULL;
727         return 0;
728 }
729
730 static int
731 tlsm_sb_close( Sockbuf_IO_Desc *sbiod )
732 {
733         struct tls_data         *p;
734         
735         assert( sbiod != NULL );
736         assert( sbiod->sbiod_pvt != NULL );
737
738         p = (struct tls_data *)sbiod->sbiod_pvt;
739         PR_Shutdown( p->session, PR_SHUTDOWN_BOTH );
740         return 0;
741 }
742
743 static int
744 tlsm_sb_ctrl( Sockbuf_IO_Desc *sbiod, int opt, void *arg )
745 {
746         struct tls_data         *p;
747         
748         assert( sbiod != NULL );
749         assert( sbiod->sbiod_pvt != NULL );
750
751         p = (struct tls_data *)sbiod->sbiod_pvt;
752         
753         if ( opt == LBER_SB_OPT_GET_SSL ) {
754                 *((tlsm_session **)arg) = p->session;
755                 return 1;
756                 
757         } else if ( opt == LBER_SB_OPT_DATA_READY ) {
758         PRPollDesc pd = { p->session, PR_POLL_READ, 0 };
759         if( PR_Poll( &pd, 1, 1 ) > 0 ) {
760             return 1;
761                 }
762         }
763         
764         return LBER_SBIOD_CTRL_NEXT( sbiod, opt, arg );
765 }
766
767 static ber_slen_t
768 tlsm_sb_read( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len)
769 {
770         struct tls_data         *p;
771         ber_slen_t              ret;
772         int                     err;
773
774         assert( sbiod != NULL );
775         assert( SOCKBUF_VALID( sbiod->sbiod_sb ) );
776
777         p = (struct tls_data *)sbiod->sbiod_pvt;
778
779         ret = PR_Recv( p->session, buf, len, 0, PR_INTERVAL_NO_TIMEOUT );
780         if ( ret < 0 ) {
781                 err = PR_GetError();
782                 if ( err == PR_PENDING_INTERRUPT_ERROR || err == PR_WOULD_BLOCK_ERROR ) {
783                         sbiod->sbiod_sb->sb_trans_needs_read = 1;
784                         sock_errset(EWOULDBLOCK);
785                 }
786         } else {
787                 sbiod->sbiod_sb->sb_trans_needs_read = 0;
788         }
789         return ret;
790 }
791
792 static ber_slen_t
793 tlsm_sb_write( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len)
794 {
795         struct tls_data         *p;
796         ber_slen_t              ret;
797         int                     err;
798
799         assert( sbiod != NULL );
800         assert( SOCKBUF_VALID( sbiod->sbiod_sb ) );
801
802         p = (struct tls_data *)sbiod->sbiod_pvt;
803
804         ret = PR_Send( p->session, (char *)buf, len, 0, PR_INTERVAL_NO_TIMEOUT );
805         if ( ret < 0 ) {
806                 err = PR_GetError();
807                 if ( err == PR_PENDING_INTERRUPT_ERROR || err == PR_WOULD_BLOCK_ERROR ) {
808                         sbiod->sbiod_sb->sb_trans_needs_write = 1;
809                         sock_errset(EWOULDBLOCK);
810                         ret = 0;
811                 }
812         } else {
813                 sbiod->sbiod_sb->sb_trans_needs_write = 0;
814         }
815         return ret;
816 }
817
818 static Sockbuf_IO tlsm_sbio =
819 {
820         tlsm_sb_setup,          /* sbi_setup */
821         tlsm_sb_remove,         /* sbi_remove */
822         tlsm_sb_ctrl,           /* sbi_ctrl */
823         tlsm_sb_read,           /* sbi_read */
824         tlsm_sb_write,          /* sbi_write */
825         tlsm_sb_close           /* sbi_close */
826 };
827
828 tls_impl ldap_int_moznss_impl = {
829         "MozNSS",
830
831         tlsm_init,
832         tlsm_destroy,
833
834         tlsm_ctx_new,
835         tlsm_ctx_ref,
836         tlsm_ctx_free,
837         tlsm_ctx_init,
838
839         tlsm_session_new,
840         tlsm_session_connect,
841         tlsm_session_accept,
842         tlsm_session_upflags,
843         tlsm_session_errmsg,
844         tlsm_session_my_dn,
845         tlsm_session_peer_dn,
846         tlsm_session_chkhost,
847         tlsm_session_strength,
848
849         &tlsm_sbio,
850
851 #ifdef LDAP_R_COMPILE
852         tlsm_thr_init,
853 #else
854         NULL,
855 #endif
856
857         0
858 };
859
860 #endif /* HAVE_MOZNSS */