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