[openldap] MozNSS - implement full non-blocking semantics
jvcelak
jvcelak at fedoraproject.org
Mon Nov 22 17:50:24 UTC 2010
commit bff7316e6d172fac813eb9070d68ddace9619050
Author: Jan Vcelak <jvcelak at redhat.com>
Date: Thu Nov 18 12:00:08 2010 +0100
MozNSS - implement full non-blocking semantics
fix: ldapsearch -Z hangs server if starttls fails (#652822)
Resolves: #652822
openldap-nss-non-blocking.patch | 361 +++++++++++++++++++++++++++++++++++++++
openldap.spec | 9 +-
2 files changed, 369 insertions(+), 1 deletions(-)
---
diff --git a/openldap-nss-non-blocking.patch b/openldap-nss-non-blocking.patch
new file mode 100644
index 0000000..38508f3
--- /dev/null
+++ b/openldap-nss-non-blocking.patch
@@ -0,0 +1,361 @@
+Mozilla NSS - implement full non-blocking semantics
+
+Resolves: #652822
+Upstream ITS: #6714
+Author: Rich Megginson (rmeggins at redhat.com)
+
+diff -u -uNPrp openldap-2.4.23/libraries/libldap/tls_m.c openldap-2.4.23.new/libraries/libldap/tls_m.c
+--- openldap-2.4.23/libraries/libldap/tls_m.c 2010-11-22 15:50:48.752386500 +0100
++++ openldap-2.4.23.new/libraries/libldap/tls_m.c 2010-11-22 15:53:44.936512466 +0100
+@@ -2105,49 +2105,74 @@ struct tls_data {
+ we will just see if the IO op returns EAGAIN or EWOULDBLOCK,
+ and just set this flag */
+ PRBool nonblock;
++ /*
++ * NSS tries hard to be backwards compatible with SSLv2 clients, or
++ * clients that send an SSLv2 client hello. This message is not
++ * tagged in any way, so NSS has no way to know if the incoming
++ * message is a valid SSLv2 client hello or just some bogus data
++ * (or cleartext LDAP). We store the first byte read from the
++ * client here. The most common case will be a client sending
++ * LDAP data instead of SSL encrypted LDAP data. This can happen,
++ * for example, if using ldapsearch -Z - if the starttls fails,
++ * the client will fallback to plain cleartext LDAP. So if we
++ * see that the firstbyte is a valid LDAP tag, we can be
++ * pretty sure this is happening.
++ */
++ ber_tag_t firsttag;
++ /*
++ * NSS doesn't return SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE, etc.
++ * when it is blocked, so we have to set a flag in the wrapped send
++ * and recv calls that tells us what operation NSS was last blocked
++ * on
++ */
++#define TLSM_READ 1
++#define TLSM_WRITE 2
++ int io_flag;
+ };
+
+-static int
+-tlsm_is_io_ready( PRFileDesc *fd, PRInt16 in_flags, PRInt16 *out_flags )
++static struct tls_data *
++tlsm_get_pvt_tls_data( PRFileDesc *fd )
+ {
+ struct tls_data *p;
+- PRFileDesc *pollfd = NULL;
+ PRFileDesc *myfd;
+- PRPollDesc polldesc;
+- int rc;
++
++ if ( !fd ) {
++ return NULL;
++ }
+
+ myfd = PR_GetIdentitiesLayer( fd, tlsm_layer_id );
+
+ if ( !myfd ) {
+- return 0;
++ return NULL;
+ }
+
+ p = (struct tls_data *)myfd->secret;
+
++ return p;
++}
++
++static int
++tlsm_is_non_ssl_message( PRFileDesc *fd, ber_tag_t *thebyte )
++{
++ struct tls_data *p;
++
++ if ( thebyte ) {
++ *thebyte = LBER_DEFAULT;
++ }
++
++ p = tlsm_get_pvt_tls_data( fd );
+ if ( p == NULL || p->sbiod == NULL ) {
+ return 0;
+ }
+
+- /* wrap the sockbuf fd with a NSPR FD created especially
+- for use with polling, and only with polling */
+- pollfd = PR_CreateSocketPollFd( p->sbiod->sbiod_sb->sb_fd );
+- polldesc.fd = pollfd;
+- polldesc.in_flags = in_flags;
+- polldesc.out_flags = 0;
+-
+- /* do the poll - no waiting, no blocking */
+- rc = PR_Poll( &polldesc, 1, PR_INTERVAL_NO_WAIT );
+-
+- /* unwrap the socket */
+- PR_DestroySocketPollFd( pollfd );
+-
+- /* rc will be either 1 if IO is ready, 0 if IO is not
+- ready, or -1 if there was some error (and the caller
+- should use PR_GetError() to figure out what */
+- if (out_flags) {
+- *out_flags = polldesc.out_flags;
++ if ( p->firsttag == LBER_SEQUENCE ) {
++ if ( *thebyte ) {
++ *thebyte = p->firsttag;
++ }
++ return 1;
+ }
+- return rc;
++
++ return 0;
+ }
+
+ static tls_session *
+@@ -2157,6 +2182,7 @@ tlsm_session_new ( tls_ctx * ctx, int is
+ tlsm_session *session;
+ PRFileDesc *fd;
+ PRStatus status;
++ int rc;
+
+ c->tc_is_server = is_server;
+ status = PR_CallOnceWithArg( &c->tc_callonce, tlsm_deferred_ctx_init, c );
+@@ -2184,121 +2210,80 @@ tlsm_session_new ( tls_ctx * ctx, int is
+ SSL_ConfigServerSessionIDCache( 0, 0, 0, NULL );
+ }
+
++ rc = SSL_ResetHandshake( session, is_server );
++ if ( rc ) {
++ PRErrorCode err = PR_GetError();
++ Debug( LDAP_DEBUG_TRACE,
++ "TLS: error: new session - reset handshake failure %d - error %d:%s\n",
++ rc, err,
++ err ? PR_ErrorToString( err, PR_LANGUAGE_I_DEFAULT ) : "unknown" );
++ PR_DELETE( fd );
++ PR_Close( session );
++ session = NULL;
++ }
++
+ return (tls_session *)session;
+ }
+
+ static int
+-tlsm_session_accept( tls_session *session )
++tlsm_session_accept_or_connect( tls_session *session, int is_accept )
+ {
+ tlsm_session *s = (tlsm_session *)session;
+- int rc;
+- PRErrorCode err;
+- int waitcounter = 0;
+-
+- rc = SSL_ResetHandshake( s, PR_TRUE /* server */ );
+- if (rc) {
+- err = PR_GetError();
+- Debug( LDAP_DEBUG_TRACE,
+- "TLS: error: accept - reset handshake failure %d - error %d:%s\n",
+- rc, err,
+- err ? PR_ErrorToString( err, PR_LANGUAGE_I_DEFAULT ) : "unknown" );
+- }
++ int rc = SSL_ForceHandshake( s );
++ const char *op = is_accept ? "accept" : "connect";
+
+- do {
+- PRInt32 filesReady;
+- PRInt16 in_flags;
+- PRInt16 out_flags;
+-
+- errno = 0;
+- rc = SSL_ForceHandshake( s );
+- if (rc == SECSuccess) {
+- rc = 0;
+- break; /* done */
+- }
+- err = PR_GetError();
+- if ( errno == EAGAIN || errno == EWOULDBLOCK ) {
+- waitcounter++;
+- in_flags = PR_POLL_READ | PR_POLL_EXCEPT;
+- out_flags = 0;
+- errno = 0;
+- filesReady = tlsm_is_io_ready( s, in_flags, &out_flags );
+- if ( filesReady < 0 ) {
+- err = PR_GetError();
+- Debug( LDAP_DEBUG_ANY,
+- "TLS: error: accept - error waiting for socket to be ready: %d - error %d:%s\n",
+- errno, err,
+- err ? PR_ErrorToString( err, PR_LANGUAGE_I_DEFAULT ) : "unknown" );
+- rc = -1;
+- break; /* hard error */
+- } else if ( out_flags & PR_POLL_NVAL ) {
+- PR_SetError(PR_BAD_DESCRIPTOR_ERROR, 0);
+- Debug( LDAP_DEBUG_ANY,
+- "TLS: error: accept failure - invalid socket\n",
+- NULL, NULL, NULL );
+- rc = -1;
+- break;
+- } else if ( out_flags & PR_POLL_EXCEPT ) {
+- err = PR_GetError();
++ if ( rc ) {
++ PRErrorCode err = PR_GetError();
++ rc = -1;
++ if ( err == PR_WOULD_BLOCK_ERROR ) {
++ ber_tag_t thetag = LBER_DEFAULT;
++ /* see if we are blocked because of a bogus packet */
++ if ( tlsm_is_non_ssl_message( s, &thetag ) ) { /* see if we received a non-SSL message */
+ Debug( LDAP_DEBUG_ANY,
+- "TLS: error: accept - error waiting for socket to be ready: %d - error %d:%s\n",
+- errno, err,
+- err ? PR_ErrorToString( err, PR_LANGUAGE_I_DEFAULT ) : "unknown" );
+- rc = -1;
+- break; /* hard error */
++ "TLS: error: %s - error - received non-SSL message [0x%x]\n",
++ op, (unsigned int)thetag, 0 );
++ /* reset error to something more descriptive */
++ PR_SetError( SSL_ERROR_RX_MALFORMED_HELLO_REQUEST, EPROTO );
+ }
+- } else { /* hard error */
+- err = PR_GetError();
++ } else {
+ Debug( LDAP_DEBUG_ANY,
+- "TLS: error: accept - force handshake failure: %d - error %d:%s\n",
+- errno, err,
+- err ? PR_ErrorToString( err, PR_LANGUAGE_I_DEFAULT ) : "unknown" );
+- rc = -1;
+- break; /* hard error */
++ "TLS: error: %s - force handshake failure: errno %d - moznss error %d\n",
++ op, errno, err );
+ }
+- } while (rc == SECFailure);
+-
+- Debug( LDAP_DEBUG_TRACE,
+- "TLS: accept completed after %d waits\n", waitcounter, NULL, NULL );
++ }
+
+ return rc;
+ }
++static int
++tlsm_session_accept( tls_session *session )
++{
++ return tlsm_session_accept_or_connect( session, 1 );
++}
+
+ static int
+ tlsm_session_connect( LDAP *ld, tls_session *session )
+ {
+- tlsm_session *s = (tlsm_session *)session;
+- int rc;
+- PRErrorCode err;
+-
+- rc = SSL_ResetHandshake( s, PR_FALSE /* server */ );
+- if (rc) {
+- err = PR_GetError();
+- Debug( LDAP_DEBUG_TRACE,
+- "TLS: error: connect - reset handshake failure %d - error %d:%s\n",
+- rc, err,
+- err ? PR_ErrorToString( err, PR_LANGUAGE_I_DEFAULT ) : "unknown" );
+- }
+-
+- rc = SSL_ForceHandshake( s );
+- if (rc) {
+- err = PR_GetError();
+- Debug( LDAP_DEBUG_TRACE,
+- "TLS: error: connect - force handshake failure %d - error %d:%s\n",
+- rc, err,
+- err ? PR_ErrorToString( err, PR_LANGUAGE_I_DEFAULT ) : "unknown" );
+- }
+-
+- return rc;
++ return tlsm_session_accept_or_connect( session, 0 );
+ }
+
+ static int
+ tlsm_session_upflags( Sockbuf *sb, tls_session *session, int rc )
+ {
+- /* Should never happen */
+- rc = PR_GetError();
++ int prerror = PR_GetError();
++
++ if ( ( prerror == PR_PENDING_INTERRUPT_ERROR ) || ( prerror == PR_WOULD_BLOCK_ERROR ) ) {
++ tlsm_session *s = (tlsm_session *)session;
++ struct tls_data *p = tlsm_get_pvt_tls_data( s );
++
++ if ( p && ( p->io_flag == TLSM_READ ) ) {
++ sb->sb_trans_needs_read = 1;
++ return 1;
++ } else if ( p && ( p->io_flag == TLSM_WRITE ) ) {
++ sb->sb_trans_needs_write = 1;
++ return 1;
++ }
++ }
+
+- if ( rc != PR_PENDING_INTERRUPT_ERROR && rc != PR_WOULD_BLOCK_ERROR )
+- return 0;
+ return 0;
+ }
+
+@@ -2587,7 +2572,7 @@ tlsm_PR_Recv(PRFileDesc *fd, void *buf,
+
+ if ( buf == NULL || len <= 0 ) return 0;
+
+- p = (struct tls_data *)fd->secret;
++ p = tlsm_get_pvt_tls_data( fd );
+
+ if ( p == NULL || p->sbiod == NULL ) {
+ return 0;
+@@ -2603,7 +2588,10 @@ tlsm_PR_Recv(PRFileDesc *fd, void *buf,
+ "TLS: error: tlsm_PR_Recv returned %d - error %d:%s\n",
+ rc, errno, STRERROR(errno) );
+ }
++ } else if ( ( rc > 0 ) && ( len > 0 ) && ( p->firsttag == LBER_DEFAULT ) ) {
++ p->firsttag = (ber_tag_t)*((char *)buf);
+ }
++ p->io_flag = TLSM_READ;
+
+ return rc;
+ }
+@@ -2617,7 +2605,7 @@ tlsm_PR_Send(PRFileDesc *fd, const void
+
+ if ( buf == NULL || len <= 0 ) return 0;
+
+- p = (struct tls_data *)fd->secret;
++ p = tlsm_get_pvt_tls_data( fd );
+
+ if ( p == NULL || p->sbiod == NULL ) {
+ return 0;
+@@ -2634,6 +2622,7 @@ tlsm_PR_Send(PRFileDesc *fd, const void
+ rc, errno, STRERROR(errno) );
+ }
+ }
++ p->io_flag = TLSM_WRITE;
+
+ return rc;
+ }
+@@ -2656,7 +2645,7 @@ tlsm_PR_GetPeerName(PRFileDesc *fd, PRNe
+ struct tls_data *p;
+ ber_socklen_t len;
+
+- p = (struct tls_data *)fd->secret;
++ p = tlsm_get_pvt_tls_data( fd );
+
+ if ( p == NULL || p->sbiod == NULL ) {
+ return PR_FAILURE;
+@@ -2669,7 +2658,7 @@ static PRStatus PR_CALLBACK
+ tlsm_PR_GetSocketOption(PRFileDesc *fd, PRSocketOptionData *data)
+ {
+ struct tls_data *p;
+- p = (struct tls_data *)fd->secret;
++ p = tlsm_get_pvt_tls_data( fd );
+
+ if ( !data ) {
+ return PR_FAILURE;
+@@ -2804,6 +2793,7 @@ tlsm_sb_setup( Sockbuf_IO_Desc *sbiod, v
+ fd->secret = (PRFilePrivate *)p;
+ p->session = session;
+ p->sbiod = sbiod;
++ p->firsttag = LBER_DEFAULT;
+ sbiod->sbiod_pvt = p;
+ return 0;
+ }
+@@ -2851,7 +2841,7 @@ tlsm_sb_ctrl( Sockbuf_IO_Desc *sbiod, in
+ return 1;
+
+ } else if ( opt == LBER_SB_OPT_DATA_READY ) {
+- if ( tlsm_is_io_ready( p->session, PR_POLL_READ, NULL ) > 0 ) {
++ if ( p && ( SSL_DataPending( p->session ) > 0 ) ) {
+ return 1;
+ }
+
diff --git a/openldap.spec b/openldap.spec
index c0ec47b..387c269 100644
--- a/openldap.spec
+++ b/openldap.spec
@@ -7,7 +7,7 @@
Name: openldap
Version: 2.4.23
-Release: 3%{?dist}
+Release: 4%{?dist}
Summary: LDAP support libraries
Group: System Environment/Daemons
License: OpenLDAP
@@ -36,6 +36,8 @@ Patch103: openldap-reject-non-file-keyfiles.patch
Patch104: openldap-use-cacert-dir-and-file.patch
Patch105: openldap-cacertdir-hash-only.patch
Patch106: openldap-improve-trace-messages.patch
+# patches sent upstream
+Patch107: openldap-nss-non-blocking.patch
# patches for the evolution library (see README.evolution)
Patch200: openldap-evolution-ntlm.patch
@@ -144,6 +146,7 @@ pushd openldap-%{version}
%patch104 -p1 -b .use-cacert-dir-and-file-dir
%patch105 -p1 -b .cacertdir-hash-only
%patch106 -p1 -b .improve-trace-messages
+%patch107 -p1 -b .nss-non-blocking
cp %{_datadir}/libtool/config/config.{sub,guess} build/
@@ -651,6 +654,10 @@ exit 0
%attr(0644,root,root) %{evolution_connector_libdir}/*.a
%changelog
+* Mon Nov 22 2010 Jan Vcelak <jvcelak at redhat.com> 2.4.23-4
+- Mozilla NSS - implement full non-blocking semantics
+ ldapsearch -Z hangs server if starttls fails (#652822)
+
* Thu Nov 18 2010 Jan Vcelak <jvcelak at redhat.com> 2.4.23-3
- add support for multiple prefixed Mozilla NSS database files in TLS_CACERTDIR
- reject non-file keyfiles in TLS_CACERTDIR (#652315)
More information about the scm-commits
mailing list