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