From bff7316e6d172fac813eb9070d68ddace9619050 Mon Sep 17 00:00:00 2001 From: Jan Vcelak Date: Thu, 18 Nov 2010 12:00:08 +0100 Subject: [PATCH] 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 deletion(-) create mode 100644 openldap-nss-non-blocking.patch 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@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 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 2.4.23-3 - add support for multiple prefixed Mozilla NSS database files in TLS_CACERTDIR - reject non-file keyfiles in TLS_CACERTDIR (#652315)