summaryrefslogtreecommitdiffstats
path: root/ldap/servers/slapd/result.c
diff options
context:
space:
mode:
Diffstat (limited to 'ldap/servers/slapd/result.c')
-rw-r--r--ldap/servers/slapd/result.c1794
1 files changed, 1794 insertions, 0 deletions
diff --git a/ldap/servers/slapd/result.c b/ldap/servers/slapd/result.c
new file mode 100644
index 00000000..b8be99f7
--- /dev/null
+++ b/ldap/servers/slapd/result.c
@@ -0,0 +1,1794 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* result.c - routines to send ldap results, errors, and referrals */
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#ifndef _WIN32
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <unistd.h>
+#endif
+#include <errno.h>
+#include <signal.h>
+#include "slap.h"
+#include "pratom.h"
+#include "fe.h"
+#include "vattr_spi.h"
+
+
+#if defined( NET_SSL )
+#include <ssl.h>
+#endif
+
+PRUint64 num_entries_sent;
+PRUint64 num_bytes_sent;
+PRLock *num_sent_mutex;
+
+static long current_conn_count;
+static PRLock *current_conn_count_mutex;
+
+static int flush_ber( Slapi_PBlock *pb, Connection *conn,
+ Operation *op, BerElement *ber, int type );
+static char *notes2str( unsigned int notes, char *buf, size_t buflen );
+static void log_result( Slapi_PBlock *pb, Operation *op, int err,
+ unsigned long tag, int nentries );
+static void log_entry( Operation *op, Slapi_Entry *e );
+static void log_referral( Operation *op );
+
+#define _LDAP_SEND_RESULT 0
+#define _LDAP_SEND_REFERRAL 1
+#define _LDAP_SEND_ENTRY 2
+
+#define SLAPI_SEND_VATTR_FLAG_REALONLY 0x01
+#define SLAPI_SEND_VATTR_FLAG_VIRTUALONLY 0x02
+
+void g_set_num_entries_sent( PRUint64 val )
+{
+ num_entries_sent = val;
+}
+
+PRUint64 g_get_num_entries_sent()
+{
+ return( num_entries_sent );
+}
+
+void g_set_num_bytes_sent( PRUint64 val )
+{
+ num_bytes_sent = val;
+}
+
+PRUint64 g_get_num_bytes_sent()
+{
+ return( num_bytes_sent );
+}
+
+void g_set_num_sent_mutex( PRLock *plock )
+{
+ num_sent_mutex = plock;
+}
+
+PRLock *g_get_num_sent_mutex()
+{
+ return( num_sent_mutex );
+}
+
+static void
+delete_default_referral(struct berval **referrals)
+{
+ if (referrals)
+ {
+ int ii = 0;
+ for (ii = 0; referrals[ii]; ++ii)
+ ber_bvfree(referrals[ii]);
+ slapi_ch_free((void**)&referrals);
+ }
+}
+
+void
+g_set_default_referral( struct berval **ldap_url ) {
+ struct berval **default_referral;
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+ int nReferrals;
+
+ /* check to see if we want to delete all referrals */
+ if ( ldap_url && ldap_url[0] &&
+ strcasecmp ( (char *)ldap_url[0]->bv_val, REFERRAL_REMOVE_CMD) == 0 ) {
+ delete_default_referral(slapdFrontendConfig->defaultreferral);
+ slapdFrontendConfig->defaultreferral = NULL;
+ return;
+ }
+
+ /* count the number of referrals */
+ for ( nReferrals = 0; ldap_url && ldap_url[nReferrals]; nReferrals++ )
+ ;
+
+ default_referral = (struct berval **)
+ slapi_ch_malloc( (nReferrals + 1) * sizeof(struct berval *) );
+
+ /* terminate the end, and add the referrals backwards */
+ default_referral[nReferrals--] = NULL;
+
+ while ( nReferrals >= 0 ) {
+ default_referral[nReferrals] = ber_bvdup(ldap_url[nReferrals]);
+ nReferrals--;
+ }
+
+ delete_default_referral(slapdFrontendConfig->defaultreferral);
+ slapdFrontendConfig->defaultreferral = default_referral;
+}
+
+struct berval**
+g_get_default_referral() {
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+ return slapdFrontendConfig->defaultreferral;
+}
+
+/*
+ * routines to manage keeping track of the current number of connections
+ * to the server. this information is used by the listener thread to
+ * determine when to stop listening for new connections, which it does
+ * when the total number of descriptors available minus the number of
+ * current connections drops below the reservedescriptors mark.
+ */
+
+void g_set_current_conn_count_mutex( PRLock *plock )
+{
+ PR_ASSERT( NULL != plock );
+
+ current_conn_count_mutex = plock;
+}
+
+PRLock *g_get_current_conn_count_mutex()
+{
+ return( current_conn_count_mutex );
+}
+
+long g_get_current_conn_count()
+{
+ long tmp;
+
+ PR_ASSERT( NULL != current_conn_count_mutex );
+
+ PR_Lock( current_conn_count_mutex );
+ tmp = current_conn_count;
+ PR_Unlock( current_conn_count_mutex );
+
+ return( tmp );
+}
+
+void g_increment_current_conn_count()
+{
+ PR_ASSERT( NULL != current_conn_count_mutex );
+
+ PR_Lock( current_conn_count_mutex );
+ current_conn_count++;
+ PR_Unlock( current_conn_count_mutex );
+}
+
+void g_decrement_current_conn_count()
+{
+ PR_ASSERT( NULL != current_conn_count_mutex );
+
+ PR_Lock( current_conn_count_mutex );
+ current_conn_count--;
+/* PR_ASSERT( current_conn_count >= 0 ); JCM BASTARD */
+ PR_Unlock( current_conn_count_mutex );
+}
+
+
+void
+send_ldap_result(
+ Slapi_PBlock *pb,
+ int err,
+ char *matched,
+ char *text,
+ int nentries,
+ struct berval **urls
+)
+{
+ send_ldap_result_ext(pb, err, matched, text, nentries, urls, NULL);
+}
+
+
+static int
+check_and_send_extended_result(Slapi_PBlock *pb, unsigned long tag, BerElement *ber)
+{
+ /*
+ * if this is an LDAPv3 ExtendedResponse to an ExtendedRequest,
+ * check to see if the optional responseName and response OCTET
+ * STRING need to be appended.
+ */
+ int rc= 0;
+ char *exop_oid;
+ struct berval *exop_value;
+ slapi_pblock_get(pb, SLAPI_EXT_OP_RET_OID, &exop_oid);
+ slapi_pblock_get(pb, SLAPI_EXT_OP_RET_VALUE, &exop_value);
+ if ( LDAP_RES_EXTENDED == tag ) {
+ if (exop_oid != NULL) {
+ rc = ber_printf( ber, "ts",
+ LDAP_TAG_EXOP_RES_OID, exop_oid);
+ }
+ if (rc != LBER_ERROR && exop_value != NULL) {
+ rc = ber_printf( ber, "to",
+ LDAP_TAG_EXOP_RES_VALUE,
+ exop_value->bv_val,
+ exop_value->bv_len );
+ }
+ }
+ return rc;
+}
+
+static int
+check_and_send_SASL_response(Slapi_PBlock *pb, unsigned long tag, BerElement *ber, Connection *conn)
+{
+ /*
+ * if this is an LDAPv3 BindResponse, check to see if the
+ * optional serverSaslCreds OCTET STRING is present and needs
+ * to be appended.
+ */
+ int rc= 0;
+ if ( LDAP_RES_BIND == tag && conn->c_ldapversion >= LDAP_VERSION3 )
+ {
+ struct berval *bind_ret_saslcreds; /* v3 serverSaslCreds */
+ slapi_pblock_get(pb, SLAPI_BIND_RET_SASLCREDS, &bind_ret_saslcreds);
+ if ( bind_ret_saslcreds != NULL ) {
+ rc = ber_printf( ber, "to",
+ LDAP_TAG_SASL_RES_CREDS,
+ bind_ret_saslcreds->bv_val,
+ bind_ret_saslcreds->bv_len );
+ }
+ }
+ return rc;
+}
+
+
+/*
+ * the input ber, if present, is not consumed
+ */
+void
+send_ldap_result_ext(
+ Slapi_PBlock *pb,
+ int err,
+ char *matched,
+ char *text,
+ int nentries,
+ struct berval **urls,
+ BerElement *ber
+)
+{
+ Connection *conn = pb->pb_conn;
+ int i, rc, logit = 0;
+ unsigned long tag;
+ int flush_ber_element = 1;
+ Slapi_Operation *operation;
+ char *dn;
+ passwdPolicy *pwpolicy = NULL;
+
+
+ slapi_pblock_get( pb, SLAPI_TARGET_DN, &dn );
+ pwpolicy = new_passwdPolicy(pb, dn);
+
+ slapi_pblock_get (pb, SLAPI_OPERATION, &operation);
+
+ if ( ber != NULL ) {
+ flush_ber_element = 0;
+ }
+
+
+ if(err != LDAP_SUCCESS){
+ /* count the error for snmp */
+ /* first check for security errors */
+ if( err == LDAP_INVALID_CREDENTIALS
+ || err == LDAP_INAPPROPRIATE_AUTH
+ || err == LDAP_AUTH_METHOD_NOT_SUPPORTED
+ || err == LDAP_STRONG_AUTH_NOT_SUPPORTED
+ || err == LDAP_STRONG_AUTH_REQUIRED
+ || err == LDAP_CONFIDENTIALITY_REQUIRED
+ || err == LDAP_INSUFFICIENT_ACCESS
+ || err == LDAP_AUTH_UNKNOWN )
+ {
+ if(g_get_global_snmp_vars()->ops_tbl.dsSecurityErrors!=NULL)
+ PR_AtomicIncrement(g_get_global_snmp_vars()->ops_tbl.dsSecurityErrors);
+ }else if( err != LDAP_REFERRAL
+ && err != LDAP_OPT_REFERRALS
+ && err != LDAP_PARTIAL_RESULTS)
+ {
+ /*madman man spec says not to count as normal errors
+ --security errors
+ --referrals
+ -- partially seviced operations will not be conted as an error
+ */
+ if(g_get_global_snmp_vars()->ops_tbl.dsErrors!=NULL)
+ PR_AtomicIncrement(g_get_global_snmp_vars()->ops_tbl.dsErrors);
+ }
+
+ }
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "=> send_ldap_result %d:%s:%s\n", err,
+ matched ? matched : "", text ? text : "" );
+
+ switch ( operation->o_tag ) {
+ case LBER_DEFAULT:
+ tag = LBER_SEQUENCE;
+ break;
+
+ case LDAP_REQ_SEARCH:
+ tag = LDAP_RES_SEARCH_RESULT;
+ break;
+
+ case LDAP_REQ_DELETE:
+ tag = LDAP_RES_DELETE;
+ break;
+
+ case LDAP_REFERRAL:
+ if ( conn->c_ldapversion > LDAP_VERSION2 ) {
+ tag = LDAP_TAG_REFERRAL;
+ break;
+ }
+ /* fallthru */
+
+ default:
+ tag = operation->o_tag + 1;
+ break;
+ }
+
+ if ( conn == NULL ) {
+ if ( operation->o_result_handler != NULL ) {
+ operation->o_result_handler( conn, operation, err,
+ matched, text, nentries, urls );
+ logit = 1;
+ }
+ goto log_and_return;
+ }
+
+ /* invalid password. Update the password retry here */
+ /* put this here for now. It could be a send_result pre-op plugin. */
+ if ( err == LDAP_INVALID_CREDENTIALS &&
+ pwpolicy->pw_lockout == 1 ) {
+
+ update_pw_retry ( pb );
+ }
+
+ if ( ber == NULL ) {
+ if ( (ber = der_alloc()) == NULL ) {
+ LDAPDebug( LDAP_DEBUG_ANY, "ber_alloc failed\n", 0, 0, 0 );
+ goto log_and_return;
+ }
+ }
+
+ /* there is no admin limit exceeded in v2 - change to size limit XXX */
+ if ( err == LDAP_ADMINLIMIT_EXCEEDED &&
+ conn->c_ldapversion < LDAP_VERSION3 ) {
+ err = LDAP_SIZELIMIT_EXCEEDED;
+ }
+
+ if ( conn->c_ldapversion < LDAP_VERSION3 || urls == NULL ) {
+ char *save, *buf = NULL;
+
+ /*
+ * if there are v2 referrals to send, construct
+ * the v2 referral string.
+ */
+ if ( urls != NULL ) {
+ int len;
+
+ /* count the referral */
+ PR_AtomicIncrement(g_get_global_snmp_vars()->ops_tbl.dsReferrals);
+
+ /*
+ * figure out how much space we need
+ */
+ len = 10; /* strlen("Referral:") + NULL */
+ for ( i = 0; urls[i] != NULL; i++ ) {
+ len += urls[i]->bv_len + 1; /* newline + ref */
+ }
+ if ( text != NULL ) {
+ len += strlen( text ) + 1; /* text + newline */
+ }
+ /*
+ * allocate buffer and fill it in with the error
+ * message plus v2-style referrals.
+ */
+ buf = slapi_ch_malloc( len );
+ *buf = '\0';
+ if ( text != NULL ) {
+ strcpy( buf, text );
+ strcat( buf, "\n" );
+ }
+ strcat( buf, "Referral:" );
+ for ( i = 0; urls[i] != NULL; i++ ) {
+ strcat( buf, "\n" );
+ strcat( buf, urls[i]->bv_val );
+ }
+ save = text;
+ text = buf;
+ }
+
+ if ( (conn->c_ldapversion < LDAP_VERSION3 &&
+ err == LDAP_REFERRAL) || urls != NULL ) {
+ err = LDAP_PARTIAL_RESULTS;
+ }
+ rc = ber_printf( ber, "{it{ess", operation->o_msgid, tag, err,
+ matched ? matched : "", text ? text : "" );
+
+ /*
+ * if this is an LDAPv3 ExtendedResponse to an ExtendedRequest,
+ * check to see if the optional responseName and response OCTET
+ * STRING need to be appended.
+ */
+ if ( rc != LBER_ERROR )
+ {
+ rc= check_and_send_extended_result(pb, tag, ber);
+ }
+
+ /*
+ * if this is an LDAPv3 BindResponse, check to see if the
+ * optional serverSaslCreds OCTET STRING is present and needs
+ * to be appended.
+ */
+ if ( rc != LBER_ERROR )
+ {
+ rc= check_and_send_SASL_response(pb, tag, ber, conn);
+/* XXXmcs: should we also check for a missing auth response control? */
+ }
+
+ if ( rc != LBER_ERROR ) {
+ rc = ber_printf( ber, "}" ); /* one more } to come */
+ }
+
+ if ( buf != NULL ) {
+ text = save;
+ slapi_ch_free( (void**)&buf );
+ }
+ } else {
+ /*
+ * there are v3 referrals to add to the result
+ */
+ /* count the referral */
+ if (! config_check_referral_mode())
+ PR_AtomicIncrement(g_get_global_snmp_vars()->ops_tbl.dsReferrals);
+ rc = ber_printf( ber, "{it{esst{s", operation->o_msgid, tag, err,
+ matched ? matched : "", text ? text : "", LDAP_TAG_REFERRAL,
+ urls[0]->bv_val );
+ for ( i = 1 ; urls[i] != NULL && rc != LBER_ERROR; i++ ) {
+ rc = ber_printf( ber, "s", urls[i]->bv_val );
+ }
+ if ( rc != LBER_ERROR ) {
+ rc = ber_printf( ber, "}" ); /* two more } to come */
+ }
+
+ /*
+ * if this is an LDAPv3 ExtendedResponse to an ExtendedRequest,
+ * check to see if the optional responseName and response OCTET
+ * STRING need to be appended.
+ */
+ if ( rc != LBER_ERROR )
+ {
+ rc= check_and_send_extended_result(pb, tag, ber);
+ }
+
+ /*
+ * if this is an LDAPv3 BindResponse, check to see if the
+ * optional serverSaslCreds OCTET STRING is present and needs
+ * to be appended.
+ */
+ if ( rc != LBER_ERROR )
+ {
+ rc= check_and_send_SASL_response(pb, tag, ber, conn);
+ }
+
+ if ( rc != LBER_ERROR ) {
+ rc = ber_printf( ber, "}" ); /* one more } to come */
+ }
+ }
+ if ( operation->o_results.result_controls != NULL
+ && conn->c_ldapversion >= LDAP_VERSION3
+ && write_controls( ber, operation->o_results.result_controls ) != 0 ) {
+ rc = LBER_ERROR;
+ }
+
+ if ( rc != LBER_ERROR ) { /* end the LDAPMessage sequence */
+ rc = ber_put_seq( ber );
+ }
+
+ if ( rc == LBER_ERROR ) {
+ LDAPDebug( LDAP_DEBUG_ANY, "ber_printf failed\n", 0, 0, 0 );
+ if (flush_ber_element == 1) {
+ /* we alloced the ber */
+ ber_free( ber, 1 /* freebuf */ );
+ }
+ goto log_and_return;
+ }
+
+ if ( flush_ber_element ) {
+ /* write only one pdu at a time - wait til it's our turn */
+ if ( flush_ber( pb, conn, operation, ber, _LDAP_SEND_RESULT ) == 0 ) {
+ logit = 1;
+ }
+ }
+
+log_and_return:
+ operation->o_status = SLAPI_OP_STATUS_RESULT_SENT; /* in case this has not yet been set */
+
+ if ( logit && operation_is_flag_set( operation,
+ OP_FLAG_ACTION_LOG_ACCESS )) {
+ log_result( pb, operation, err, tag, nentries );
+ }
+
+ delete_passwdPolicy (&pwpolicy);
+ LDAPDebug( LDAP_DEBUG_TRACE, "<= send_ldap_result\n", 0, 0, 0 );
+}
+
+
+void
+send_nobackend_ldap_result( Slapi_PBlock *pb )
+{
+ struct berval **refurls;
+ int err;
+
+ refurls = g_get_default_referral();
+ err = ( refurls == NULL ) ? LDAP_NO_SUCH_OBJECT : LDAP_REFERRAL;
+ /* richm 20010831 - bug 556992 - the post op code needs to know what the
+ ldap error sent to the client was - slapi_send_ldap_result sets the
+ err in the pblock, so this function needs to also */
+ slapi_pblock_set(pb, SLAPI_RESULT_CODE, &err);
+
+ send_ldap_result( pb, err, NULL, NULL, 0, refurls );
+}
+
+
+int
+send_ldapv3_referral(
+ Slapi_PBlock *pb,
+ struct berval **urls
+)
+{
+ Connection *conn = pb->pb_conn;
+ BerElement *ber;
+ int i, rc, logit = 0;
+ Slapi_Operation *operation;
+
+ slapi_pblock_get (pb, SLAPI_OPERATION, &operation);
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "=> send_ldapv3_referral\n", 0, 0, 0 );
+
+ if ( conn == NULL ) {
+ if ( operation->o_search_referral_handler != NULL ) {
+ if (( rc = (*operation->o_search_referral_handler)(
+ pb->pb_backend, conn, operation, urls )) == 0 ) {
+ logit = 1;
+ }
+ goto log_and_return;
+ }
+ return( 0 );
+ }
+ if ( urls == NULL ) {
+ return( 0 );
+ }
+
+ if ( (ber = der_alloc()) == NULL ) {
+ LDAPDebug( LDAP_DEBUG_ANY, "ber_alloc failed\n", 0, 0, 0 );
+ send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL,
+ "ber_alloc", 0, NULL );
+ return( -1 );
+ }
+
+ /*
+ * send the ldapv3 SearchResultReference. it looks like this:
+ *
+ * SearchResultReference ::= [APPLICATION 19] SEQUENCE OF LDAPURL
+ *
+ * all wrapped up in an LDAPMessage sequence which looks like this:
+ * LDAPMessage ::= SEQUENCE {
+ * messageID MessageID,
+ * SearchResultReference
+ * controls [0] Controls OPTIONAL
+ * }
+ */
+
+ for ( i = 0, rc = ber_printf( ber, "{it{", operation->o_msgid,
+ LDAP_RES_SEARCH_REFERENCE );
+ rc != LBER_ERROR && urls[i] != NULL; i++ ) {
+ rc = ber_printf( ber, "s", urls[i]->bv_val );
+ }
+ if ( rc == LBER_ERROR ) {
+ LDAPDebug( LDAP_DEBUG_ANY, "ber_printf failed\n", 0, 0, 0 );
+ send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL,
+ "ber_printf", 0, NULL );
+ return( -1 );
+ }
+ if ( ber_printf( ber, "}}" ) == LBER_ERROR ) {
+ LDAPDebug( LDAP_DEBUG_ANY, "ber_printf failed\n", 0, 0, 0 );
+ send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL,
+ "ber_printf", 0, NULL );
+ return( -1 );
+ }
+
+ /* write only one pdu at a time - wait til it's our turn */
+ if ( (rc = flush_ber( pb, conn, operation, ber, _LDAP_SEND_REFERRAL
+ )) == 0 ) {
+ logit = 1;
+ }
+
+log_and_return:
+ if ( logit && operation_is_flag_set( operation,
+ OP_FLAG_ACTION_LOG_ACCESS)){
+ log_referral( operation );
+ }
+
+ return( rc );
+}
+
+/*
+ * send_ldap_referral - called to send a referral (SearchResultReference)
+ * to a v3 client during a search. for v2 clients, it just adds the
+ * referral(s) to the url list passed in the third parameter. this list
+ * is then returned to v2 clients when it is passed to send_ldap_result().
+ */
+int
+send_ldap_referral (
+ Slapi_PBlock *pb,
+ Slapi_Entry *e,
+ struct berval **refs,
+ struct berval ***urls
+)
+{
+ char *refAttr = "ref";
+ char *attrs[2] = { NULL, NULL };
+
+ /* count the referral */
+ PR_AtomicIncrement(g_get_global_snmp_vars()->ops_tbl.dsReferrals);
+
+ attrs[0] = refAttr;
+ if ( e != NULL &&
+ plugin_call_acl_plugin (pb, e, attrs, NULL,
+ SLAPI_ACL_READ, ACLPLUGIN_ACCESS_DEFAULT, NULL)
+ != LDAP_SUCCESS ) {
+ return( 0 );
+ }
+ if ( pb->pb_conn && pb->pb_conn->c_ldapversion > LDAP_VERSION2 ) {
+ /*
+ * v3 connection - send the referral(s) in a
+ * SearchResultReference packet right now.
+ */
+ return( send_ldapv3_referral( pb, refs ) );
+ } else {
+ /*
+ * v2 connection - add the referral(s) to the
+ * list being maintained in urls. they will be
+ * sent to the client later when send_ldap_result()
+ * is called.
+ */
+ int i, need, have;
+
+ if ( refs == NULL && urls == NULL ) {
+ return( 0 );
+ }
+
+ for ( have = 0; *urls != NULL && (*urls)[have] != NULL;
+ have++ ) {
+ ; /* NULL */
+ }
+ for ( need = 0; refs != NULL && refs[need] != NULL; need++ ) {
+ ; /* NULL */
+ }
+
+ *urls = (struct berval **) slapi_ch_realloc( (char *) *urls,
+ (need + have + 1) * sizeof(struct berval *) );
+ for ( i = have; i < have + need; i++ ) {
+ (*urls)[i] = ber_bvdup( refs[i - have] );
+ }
+ (*urls)[i] = NULL;
+ }
+
+ return( 0 );
+}
+
+int
+encode_attr_2(
+ Slapi_PBlock *pb,
+ BerElement *ber,
+ Slapi_Entry *e,
+ Slapi_ValueSet *vs,
+ int attrsonly,
+ const char *attribute_type,
+ const char *returned_type
+)
+{
+
+ char *attrs[2] = { NULL, NULL };
+
+ attrs[0] = (char*)attribute_type;
+
+#if !defined(DISABLE_ACL_CHECK)
+ if ( plugin_call_acl_plugin (pb, e, attrs, NULL, SLAPI_ACL_READ,
+ ACLPLUGIN_ACCESS_READ_ON_ATTR, NULL ) != LDAP_SUCCESS ) {
+ return( 0 );
+ }
+#endif
+
+ if ( ber_printf( ber, "{s[", returned_type ) == -1 ) {
+ LDAPDebug( LDAP_DEBUG_ANY, "ber_printf failed\n", 0, 0, 0 );
+ ber_free( ber, 1 );
+ send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL,
+ "ber_printf type", 0, NULL );
+ return( -1 );
+ }
+
+ if ( ! attrsonly )
+ {
+ Slapi_Value *v;
+ int i= slapi_valueset_first_value(vs,&v);
+ while(i!=-1)
+ {
+ if ( ber_printf( ber, "o", v->bv.bv_val,v->bv.bv_len ) == -1 )
+ {
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "ber_printf failed\n", 0, 0, 0 );
+ ber_free( ber, 1 );
+ send_ldap_result( pb, LDAP_OPERATIONS_ERROR,
+ NULL, "ber_printf value", 0, NULL );
+ return( -1 );
+ }
+ i= slapi_valueset_next_value(vs,i,&v);
+ }
+ }
+
+ if ( ber_printf( ber, "]}" ) == -1 ) {
+ LDAPDebug( LDAP_DEBUG_ANY, "ber_printf failed\n", 0, 0, 0 );
+ ber_free( ber, 1 );
+ send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL,
+ "ber_printf type end", 0, NULL );
+ return( -1 );
+ }
+
+ return( 0 );
+}
+
+int
+encode_attr(
+ Slapi_PBlock *pb,
+ BerElement *ber,
+ Slapi_Entry *e,
+ Slapi_Attr *a,
+ int attrsonly,
+ char *type
+)
+{
+ return encode_attr_2(pb,ber,e,&(a->a_present_values),attrsonly,a->a_type,type);
+}
+
+#define LASTMODATTR( x ) (strcasecmp( x, "modifytimestamp" ) == 0 \
+ || strcasecmp( x, "modifiersname" ) == 0 \
+ || strcasecmp( x, "createtimestamp" ) == 0 \
+ || strcasecmp( x, "creatorsname" ) == 0)
+
+/*
+ * send_ldap_search_entry:
+ * return 0 if OK
+ * return 1 if this entry not sent
+ * return -1 if error result sent or fatal error
+ */
+int
+send_ldap_search_entry(
+ Slapi_PBlock *pb,
+ Slapi_Entry *e,
+ LDAPControl **ectrls,
+ char **attrs,
+ int attrsonly
+)
+{
+ return send_ldap_search_entry_ext(pb, e, ectrls, attrs, attrsonly, 0, 0, NULL);
+}
+
+/*
+ * LDAPv2 attr names from RFC1274 and their LDAPv3 equivalent.
+ *
+ * The ;binary attrs are deliberately reversed.
+ */
+static const char *idds_v2_attrt[][2] = {
+ {"commonName","cn"},
+ {"surname","sn"},
+ {"userCertificate;binary","userCertificate"},
+ {"caCertificate;binary","caCertificate"},
+ {"countryName","c"},
+ {"localityName","l"},
+ {"stateOrProvinceName","st"},
+ {"streetAddress","street"},
+ {"organizationName","o"},
+ {"organizationalUnitName","ou"},
+ {"userid","uid"},
+ {"rfc822Mailbox","mail"},
+ {"domainComponent","dc"},
+ {"mobileTelephoneNumber","mobile"},
+ {"pagerTelephoneNumber","pager"},
+ {"friendlyCountryName","co"},
+ {NULL,NULL}
+};
+
+/*
+ * Map an LDAPv3 attribute name to its LDAPv2 equivalent.
+ */
+static const char *idds_map_attrt_v3(
+ const char *atin
+)
+{
+ int i;
+
+ for (i = 0; idds_v2_attrt[i][0] != NULL; i++) {
+ if (strcasecmp(atin, idds_v2_attrt[i][1]) == 0) {
+ return (idds_v2_attrt[i][0]);
+ }
+ }
+
+ return NULL;
+}
+
+/*
+ * RFC: 2251 Page: 29
+ *
+ * attributes: A list of the attributes to be returned from each entry
+ * which matches the search filter. There are two special values which
+ * may be used: an empty list with no attributes, and the attribute
+ * description string "*". Both of these signify that all user
+ * attributes are to be returned. (The "*" allows the client to
+ * request all user attributes in addition to specific operational
+ * attributes).
+ *
+ * Attributes MUST be named at most once in the list, and are returned
+ * at most once in an entry. If there are attribute descriptions in
+ * the list which are not recognized, they are ignored by the server.
+ *
+ * If the client does not want any attributes returned, it can specify
+ * a list containing only the attribute with OID "1.1". This OID was
+ * chosen arbitrarily and does not correspond to any attribute in use.
+ */
+
+
+/* Helper functions */
+
+static int send_all_attrs(Slapi_Entry *e,char **attrs,Slapi_Operation *op,Slapi_PBlock *pb,BerElement *ber,int attrsonly,int ldapversion,int *dontsendattr, int real_attrs_only, int some_named_attrs)
+{
+ int i = 0;
+ int rc = 0;
+
+ int typelist_flags = 0;
+ vattr_type_thang *typelist = NULL;
+ vattr_type_thang *current_type = NULL;
+ char *current_type_name = NULL;
+ int rewrite_rfc1274 = 0;
+ int vattr_flags = 0;
+
+ if(real_attrs_only == SLAPI_SEND_VATTR_FLAG_REALONLY)
+ vattr_flags = SLAPI_REALATTRS_ONLY;
+ else
+ {
+ vattr_flags = SLAPI_VIRTUALATTRS_REQUEST_POINTERS;
+ if(real_attrs_only == SLAPI_SEND_VATTR_FLAG_VIRTUALONLY)
+ vattr_flags |= SLAPI_VIRTUALATTRS_ONLY;
+ }
+
+ if (some_named_attrs) {
+ /*
+ * If the client listed some attribute types by name, one or
+ * more of the requested types MAY be operational. Inform the
+ * virtual attributes subsystem (certain optimizations are done
+ * by the vattrs code and vattr service providers if operational
+ * attributes are NOT requested).
+ */
+ vattr_flags |= SLAPI_VIRTUALATTRS_LIST_OPERATIONAL_ATTRS;
+ }
+
+ rc = slapi_vattr_list_attrs(e,&typelist,vattr_flags,&typelist_flags);
+ if (0 != rc) {
+ goto exit;
+ }
+
+ if (typelist_flags & SLAPI_VIRTUALATTRS_REALATTRS_ONLY) {
+ /*
+ * There is no point in consulting the vattr service providers
+ * for every attr if they didn't contribute to the attr list.
+ */
+ vattr_flags |= SLAPI_REALATTRS_ONLY;
+ }
+
+ rewrite_rfc1274 = config_get_rewrite_rfc1274();
+
+ /* Send the attrs back to the client */
+ for (current_type = vattr_typethang_first(typelist); current_type; current_type = vattr_typethang_next(current_type) ) {
+
+ Slapi_ValueSet **values = NULL;
+ int attr_free_flags = 0;
+ unsigned long current_type_flags = 0;
+ int sendit = 0;
+ char *name_to_return = NULL;
+ int *type_name_disposition = 0;
+ char **actual_type_name = NULL;
+ const char *v2name = NULL;
+
+ current_type_name = vattr_typethang_get_name(current_type);
+ current_type_flags = vattr_typethang_get_flags(current_type);
+
+ name_to_return = current_type_name;
+ /* We only return operational attributes if the client is LDAPv2 and the attribute is one of a special set,
+ OR if the client also requested the attribute by name. If it did, we use the specified name rather than
+ the base name.
+ */
+ if ( current_type_flags & SLAPI_ATTR_FLAG_OPATTR ) {
+ if ( LDAP_VERSION2 == ldapversion && LASTMODATTR( current_type_name) ) {
+ sendit = 1;
+ } else {
+ for ( i = 0; attrs != NULL && attrs[i] != NULL; i++ ) {
+ if ( slapi_attr_type_cmp( attrs[i], current_type_name, SLAPI_TYPE_CMP_SUBTYPE ) == 0 ) {
+ sendit = 1;
+ name_to_return = op->o_searchattrs[i];
+ break;
+ }
+ }
+ }
+ /*
+ * it's a user attribute. send it.
+ */
+ } else {
+ sendit = 1;
+ }
+ /* Now send to the client */
+ if (sendit) {
+ /**********************************************/
+ int item_count = 0;
+ int iter = 0;
+ int j = 0;
+ Slapi_DN *namespace_dn;
+ Slapi_Backend *backend=0;
+ vattr_context *ctx;
+
+ /* get the namespace dn */
+ slapi_pblock_get( pb, SLAPI_BACKEND, (void *)&backend);
+ namespace_dn = (Slapi_DN*)slapi_be_getsuffix(backend, 0);
+
+ /* Get the attribute value from the vattr service */
+ /* ctx will be freed by attr_context_ungrok() */
+ ctx = vattr_context_new ( pb );
+ rc = slapi_vattr_namespace_values_get_sp(
+ ctx,
+ e,
+ namespace_dn,
+ current_type_name,
+ &values,
+ &type_name_disposition,
+ &actual_type_name,
+ vattr_flags | SLAPI_VIRTUALATTRS_SUPPRESS_SUBTYPES,
+ &attr_free_flags,
+ &item_count
+ );
+ if (0 == rc && item_count > 0) {
+
+ for(iter=0; iter<item_count; iter++)
+ {
+ if (SLAPI_VIRTUALATTRS_TYPE_NAME_MATCHED_SUBTYPE == type_name_disposition[iter]) {
+ name_to_return = actual_type_name[iter];
+ }
+
+ /*
+ * The dontsendattr array is used to track whether attributes
+ * that were explicitly requested by the client have been
+ * returned. Check here to see if the attribute we just
+ * arranged to send back was explicitly requested, and if so,
+ * set its dontsendattr flag so the send_specific_attrs()
+ * function does not return it a second time.
+ */
+ for ( i = 0; attrs != NULL && attrs[i] != NULL; i++ ) {
+ if ( !dontsendattr[i] && slapi_attr_type_cmp( current_type_name, attrs[i], SLAPI_TYPE_CMP_SUBTYPE ) == 0 ) {
+ /* Client is also asking for an attr which is in '*', zap it. */
+ dontsendattr[i]= 1;
+ }
+ }
+
+ rc = encode_attr_2( pb, ber, e, values[iter], attrsonly, current_type_name, name_to_return );
+
+ if (rewrite_rfc1274 != 0) {
+ v2name = idds_map_attrt_v3(current_type_name);
+ if (v2name != NULL) {
+ /* also return values with RFC1274 attr name */
+ rc = encode_attr_2(pb, ber, e, values[iter],
+ attrsonly,
+ current_type_name,
+ v2name);
+ }
+ }
+
+ slapi_vattr_values_free(&(values[iter]), &(actual_type_name[iter]), attr_free_flags);
+ if ( rc != 0 ) {
+ goto exit;
+ }
+ }
+
+ slapi_ch_free((void**)&actual_type_name);
+ slapi_ch_free((void**)&type_name_disposition);
+ slapi_ch_free((void**)&values);
+
+ } else {
+ rc = 0;
+ }
+ }
+ }
+exit:
+ if (NULL != typelist) {
+ slapi_vattr_attrs_free(&typelist,typelist_flags);
+ }
+ return rc;
+}
+
+int send_specific_attrs(Slapi_Entry *e,char **attrs,Slapi_Operation *op,Slapi_PBlock *pb,BerElement *ber,int attrsonly,int ldapversion,int *dontsendattr, int real_attrs_only)
+{
+ int i,j = 0;
+ int rc = 0;
+ int vattr_flags = 0;
+ vattr_context *ctx;
+
+ if(real_attrs_only == SLAPI_SEND_VATTR_FLAG_REALONLY)
+ vattr_flags = SLAPI_REALATTRS_ONLY;
+ else
+ {
+ vattr_flags = SLAPI_VIRTUALATTRS_REQUEST_POINTERS;
+ if(real_attrs_only == SLAPI_SEND_VATTR_FLAG_VIRTUALONLY)
+ vattr_flags |= SLAPI_VIRTUALATTRS_ONLY;
+ }
+
+ for ( i = 0; attrs[i] != NULL; i++ )
+ {
+ char *current_type_name = attrs[i];
+ if(!dontsendattr[i]) {
+ Slapi_ValueSet **values = NULL;
+ int attr_free_flags = 0;
+ char *name_to_return = NULL;
+ char **actual_type_name= NULL;
+ int *type_name_disposition = 0;
+ int item_count = 0;
+ int iter = 0;
+ Slapi_DN *namespace_dn;
+ Slapi_Backend *backend=0;
+
+ /*
+ * Here we call the computed attribute code to see whether
+ * the requested attribute is to be computed.
+ * The subroutine compute_attribute calls encode_attr on our behalf, in order
+ * to avoid the inefficiency of returning a complex structure
+ * which we'd have to free
+ */
+ rc = compute_attribute(attrs[i],pb,ber,e,attrsonly,op->o_searchattrs[i]);
+ if (0 == rc) {
+ continue; /* Means this was a computed attr and we prcessed it OK. */
+ }
+ if (-1 != rc) {
+ /* Means that some error happened */
+ return rc;
+ }
+ else {
+ rc = 0; /* Means that we just didn't recognize this as a computed attr */
+ }
+
+ /* get the namespace dn */
+ slapi_pblock_get( pb, SLAPI_BACKEND, (void *)&backend);
+ namespace_dn = (Slapi_DN*)slapi_be_getsuffix(backend, 0);
+
+ /* Get the attribute value from the vattr service */
+ /* ctx will be freed by attr_context_ungrok() */
+ ctx = vattr_context_new ( pb );
+ rc = slapi_vattr_namespace_values_get_sp(
+ ctx,
+ e,
+ namespace_dn,
+ current_type_name,
+ &values,
+ &type_name_disposition,
+ &actual_type_name,
+ vattr_flags,
+ &attr_free_flags,
+ &item_count
+ );
+ if (0 == rc && item_count > 0) {
+
+ for(iter=0; iter<item_count; iter++)
+ {
+ if (SLAPI_VIRTUALATTRS_TYPE_NAME_MATCHED_SUBTYPE == type_name_disposition[iter]) {
+ name_to_return = actual_type_name[iter];
+ } else {
+ name_to_return = op->o_searchattrs[i];
+ }
+
+ /*
+ * The client may have specified a list of attributes
+ * with duplicates, 'cn cn cn'.
+ * We need to determine which of any duplicates take precedence
+ * For subtypes, the attribute which is most generic should be
+ * returned (since it will also trigger the return of the less
+ * generic attribute subtypes.
+ */
+ for ( j = i+1; attrs != NULL && attrs[j] != NULL && dontsendattr[i]==0; j++ )
+ {
+ if ( !dontsendattr[j] && slapi_attr_type_cmp( attrs[j], actual_type_name[iter], SLAPI_TYPE_CMP_SUBTYPE ) == 0 )
+ {
+ /* discover which is the more generic attribute and cancel the other*/
+ int attrbase = slapi_attr_type_cmp( attrs[j], current_type_name, SLAPI_TYPE_CMP_EXACT );
+
+ if(attrbase >= 0)
+ dontsendattr[j]= 1;
+ else
+ dontsendattr[i]= 1; /* the current value is superceeded later */
+ }
+ }
+
+ /* we may have just cancelled ourselves so check */
+ if(!dontsendattr[i])
+ rc = encode_attr_2( pb, ber, e, values[iter], attrsonly, current_type_name, name_to_return );
+
+ slapi_vattr_values_free(&(values[iter]), &(actual_type_name[iter]), attr_free_flags);
+ if ( rc != 0 ) {
+ goto exit;
+ }
+ }
+
+ slapi_ch_free((void**)&actual_type_name);
+ slapi_ch_free((void**)&type_name_disposition);
+ slapi_ch_free((void**)&values);
+
+ } else {
+ rc = 0;
+ }
+ }
+ }
+exit:
+ return rc;
+
+}
+
+
+int
+send_ldap_search_entry_ext(
+ Slapi_PBlock *pb,
+ Slapi_Entry *e,
+ LDAPControl **ectrls,
+ char **attrs,
+ int attrsonly,
+ int send_result,
+ int nentries,
+ struct berval **urls
+)
+{
+ Connection *conn = pb->pb_conn;
+ Operation *op = pb->pb_op;
+ BerElement *ber;
+ int i, rc = 0, logit = 0;
+ int alluserattrs, noattrs, some_named_attrs;
+ int *dontsendattr= NULL;
+ Slapi_Operation *operation;
+ int real_attrs_only = 0;
+ LDAPControl **ctrlp = 0;
+
+ slapi_pblock_get (pb, SLAPI_OPERATION, &operation);
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "=> send_ldap_search_entry (%s)\n",
+ slapi_entry_get_dn_const(e), 0, 0 );
+
+ if ( conn == NULL ) {
+ if ( op->o_search_entry_handler != NULL ) {
+ if (( rc = (*op->o_search_entry_handler)(
+ pb->pb_backend, conn, op, e )) == 0 ) {
+ logit = 1;
+ goto log_and_return;
+ } else {
+ return rc;
+ }
+ }
+ return 0;
+ }
+
+#if !defined(DISABLE_ACL_CHECK)
+ if ( plugin_call_acl_plugin (pb, e, attrs, NULL,
+ SLAPI_ACL_READ, ACLPLUGIN_ACCESS_READ_ON_ENTRY, NULL ) != LDAP_SUCCESS ) {
+ LDAPDebug( LDAP_DEBUG_ACL, "acl: access to entry not allowed\n",
+ 0, 0, 0 );
+ return( 1 );
+ }
+#endif
+
+ /* Check for possible get_effective_rights control */
+ if ( operation->o_flags & OP_FLAG_GET_EFFECTIVE_RIGHTS ) {
+ char *errbuf = NULL;
+ rc = plugin_call_acl_plugin (pb, e, attrs, NULL, SLAPI_ACL_ALL,
+ ACLPLUGIN_ACCESS_GET_EFFECTIVE_RIGHTS, &errbuf);
+ if ( rc != LDAP_SUCCESS ) {
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "Failed to get effective rights for entry (%s), rc=%d\n",
+ slapi_entry_get_dn_const(e), rc, 0 );
+ /* Send error result and abort op if the control is critical */
+ send_ldap_result( pb, rc, NULL, errbuf, 0, NULL );
+ slapi_ch_free ( (void**)&errbuf );
+ return( -1 );
+ }
+ slapi_ch_free ( (void**)&errbuf );
+ }
+
+ if ( (ber = der_alloc()) == NULL ) {
+ LDAPDebug( LDAP_DEBUG_ANY, "ber_alloc failed\n", 0, 0, 0 );
+ send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL,
+ "ber_alloc", 0, NULL );
+ return( -1 );
+ }
+
+ rc = ber_printf( ber, "{it{s{", op->o_msgid,
+ LDAP_RES_SEARCH_ENTRY, slapi_entry_get_dn_const(e) );
+
+ if ( rc == -1 ) {
+ LDAPDebug( LDAP_DEBUG_ANY, "ber_printf failed\n", 0, 0, 0 );
+ ber_free( ber, 1 );
+ send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL,
+ "ber_printf dn", 0, NULL );
+ return( -1 );
+ }
+
+ /*
+ * in ldapv3, the special attribute "*" means all user attributes,
+ * NULL means all user attributes, and "1.1" means no attributes.
+ * operational attributes are only retrieved if they are named
+ * specifically.
+ */
+
+ /* figure out if we want all user attributes or no attributes at all */
+ alluserattrs = 0;
+ noattrs = 0;
+ some_named_attrs = 0;
+ if ( attrs == NULL ) {
+ alluserattrs = 1;
+ } else {
+ for ( i = 0; attrs[i] != NULL; i++ ) {
+ if ( strcmp( LDAP_ALL_USER_ATTRS, attrs[i] ) == 0 ) {
+ alluserattrs = 1;
+ } else if ( strcmp( LDAP_NO_ATTRS, attrs[i] ) == 0 ) {
+ noattrs = 1;
+ } else {
+ some_named_attrs = 1;
+ }
+ }
+ if ( i > 1 && noattrs ) {
+ /*
+ * user has specified the special "1.1" noattrs attr
+ * and some other stuff. this is not allowed, but
+ * what should we do? we'll allow them to keep going.
+ */
+ LDAPDebug( LDAP_DEBUG_TRACE,
+ "Accepting illegal other attributes specified with "
+ "special \"1.1\" attribute\n", 0, 0, 0 );
+ }
+ /*
+ * We maintain a flag array so that we can remove requests
+ * for duplicate attributes.
+ */
+ dontsendattr= (int*) slapi_ch_calloc( i+1, sizeof(int) );
+ }
+
+
+ /* determine whether we are to return virtual attributes */
+ slapi_pblock_get(pb, SLAPI_REQCONTROLS, &ctrlp);
+ if(slapi_control_present(ctrlp, LDAP_CONTROL_REAL_ATTRS_ONLY, NULL, NULL))
+ real_attrs_only = SLAPI_SEND_VATTR_FLAG_REALONLY;
+
+ if(slapi_control_present(ctrlp, LDAP_CONTROL_VIRT_ATTRS_ONLY, NULL, NULL))
+ {
+ if(real_attrs_only != SLAPI_SEND_VATTR_FLAG_REALONLY)
+ real_attrs_only = SLAPI_SEND_VATTR_FLAG_VIRTUALONLY;
+ else
+ {
+ /* we cannot service a request for virtual only and real only */
+ ber_free( ber, 1 );
+ slapi_ch_free( (void **) &dontsendattr );
+ send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL,
+ "Both real and virtual attributes only controls", 0, NULL );
+ return( -1 );
+ }
+ }
+
+ /* look through each attribute in the entry */
+ if ( alluserattrs ) {
+ rc = send_all_attrs(e,attrs,op,pb,ber,attrsonly,conn->c_ldapversion,dontsendattr, real_attrs_only, some_named_attrs);
+ }
+
+ /* if the client explicitly specified a list of attributes look through each attribute requested */
+ if( (rc == 0) && (attrs!=NULL)) {
+ rc = send_specific_attrs(e,attrs,op,pb,ber,attrsonly,conn->c_ldapversion,dontsendattr,real_attrs_only);
+ }
+
+ /* Append effective rights to the stream of attribute list */
+ if ( operation->o_flags & OP_FLAG_GET_EFFECTIVE_RIGHTS )
+ {
+ char *gerstr;
+ char *entryrights;
+ char *attributerights;
+ char *p;
+
+ slapi_pblock_get (pb, SLAPI_PB_RESULT_TEXT, &gerstr);
+
+ /* Syntax check - see acleffectiverights.c */
+ if (gerstr && (p = strchr(gerstr, '\n')) != NULL &&
+ strncasecmp (gerstr, "entryLevelRights: ",
+ strlen("entryLevelRights: ")) == 0 &&
+ strncasecmp (p+1, "attributeLevelRights: ",
+ strlen("attributeLevelRights: ")) == 0 )
+ {
+ entryrights = gerstr + strlen ("entryLevelRights: ");
+ *p = '\0';
+ attributerights = p + 1 + strlen ("attributeLevelRights: ");
+ ber_printf( ber, "{s[o]}", "entryLevelRights", entryrights, strlen(entryrights) );
+ ber_printf( ber, "{s[o]}", "attributeLevelRights", attributerights, strlen(attributerights) );
+ }
+ }
+
+ slapi_ch_free( (void **) &dontsendattr ); /* I know it looks like we could free this when it wasn't allocated, the function ignores null pointers */
+
+ if (rc != 0) {
+ ber_free( ber, 1 );
+ goto exit;
+ }
+
+ rc = ber_printf( ber, "}}" );
+
+ if ( conn->c_ldapversion >= LDAP_VERSION3 ) {
+ if ( ectrls != NULL ) {
+ rc = write_controls( ber, ectrls );
+ }
+ /*
+ * The get-effective-rights control is called within
+ * the current function. Hence it can't be already in
+ * ectrls
+ */
+ if ( operation->o_flags & OP_FLAG_GET_EFFECTIVE_RIGHTS ) {
+ LDAPControl *gerctrl[2];
+ slapi_pblock_get (pb, SLAPI_RESCONTROLS, &ctrlp);
+ for ( i = 0; ctrlp != NULL && ctrlp[i] != NULL; i++ ) {
+ if (strcmp(ctrlp[i]->ldctl_oid, LDAP_CONTROL_GET_EFFECTIVE_RIGHTS ) == 0 ) {
+ gerctrl[0] = ctrlp[i];
+ gerctrl[1] = NULL;
+ rc = write_controls( ber, gerctrl );
+ break;
+ }
+ }
+ }
+ }
+
+ if ( rc != -1 ) {
+ rc = ber_printf( ber, "}" );
+ }
+
+ if ( rc == -1 ) {
+ LDAPDebug( LDAP_DEBUG_ANY, "ber_printf failed\n", 0, 0, 0 );
+ ber_free( ber, 1 );
+ send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL,
+ "ber_printf entry end", 0, NULL );
+ return( -1 );
+ }
+
+ if (send_result) {
+ send_ldap_result_ext( pb, LDAP_SUCCESS, NULL, NULL, nentries, urls, ber);
+ }
+
+ /* write only one pdu at a time - wait til it's our turn */
+ if ( (rc = flush_ber( pb, conn, op, ber, _LDAP_SEND_ENTRY )) == 0 ) {
+ logit = 1;
+ }
+
+log_and_return:
+ if ( logit && operation_is_flag_set(operation,
+ OP_FLAG_ACTION_LOG_ACCESS)){
+
+ log_entry( op, e );
+
+ if (send_result) {
+ unsigned long tag;
+
+ switch ( op->o_tag ) {
+ case LBER_DEFAULT:
+ tag = LBER_SEQUENCE;
+ break;
+
+ case LDAP_REQ_SEARCH:
+ tag = LDAP_RES_SEARCH_RESULT;
+ break;
+
+ case LDAP_REQ_DELETE:
+ tag = LDAP_RES_DELETE;
+ break;
+
+ case LDAP_REFERRAL:
+ if ( conn != NULL && conn->c_ldapversion > LDAP_VERSION2 ) {
+ tag = LDAP_TAG_REFERRAL;
+ break;
+ }
+ /* fallthru */
+
+ default:
+ tag = op->o_tag + 1;
+ break;
+ }
+
+ log_result( pb, op, LDAP_SUCCESS, tag, nentries );
+ }
+ }
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "<= send_ldap_search_entry\n", 0, 0, 0 );
+exit:
+ return( rc );
+}
+
+
+
+
+/*
+ * always frees the ber
+ */
+static int
+flush_ber(
+ Slapi_PBlock *pb,
+ Connection *conn,
+ Operation *op,
+ BerElement *ber,
+ int type
+)
+{
+ unsigned long bytes;
+ int rc = 0;
+
+ switch ( type ) {
+ case _LDAP_SEND_RESULT:
+ rc = plugin_call_plugins( pb, SLAPI_PLUGIN_PRE_RESULT_FN );
+ break;
+ case _LDAP_SEND_REFERRAL:
+ rc = plugin_call_plugins( pb, SLAPI_PLUGIN_PRE_REFERRAL_FN );
+ break;
+ case _LDAP_SEND_ENTRY:
+ rc = plugin_call_plugins( pb, SLAPI_PLUGIN_PRE_ENTRY_FN );
+ break;
+ }
+
+ if ( rc != 0 ) {
+ ber_free( ber, 1 );
+ return( rc );
+ }
+
+ if ((conn->c_flags & CONN_FLAG_CLOSING) || slapi_op_abandoned(pb)) {
+ LDAPDebug(LDAP_DEBUG_CONNS, "ber_flush skipped because the "
+ "connection was marked to be closed or abandoned\n", 0, 0, 0);
+ ber_free( ber, 1 );
+ /* One of the failure can be because the client has reset the connection ( closed )
+ * and the status needs to be updated to reflect it */
+ op->o_status = SLAPI_OP_STATUS_ABANDONED;
+ rc = -1;
+ } else {
+ ber_get_option( ber, LBER_OPT_BYTES_TO_WRITE, &bytes );
+
+ PR_Lock( conn->c_pdumutex );
+ rc = ber_flush( conn->c_sb, ber, 1 );
+ PR_Unlock( conn->c_pdumutex );
+
+ if ( rc != 0 ) {
+ int oserr = errno;
+ /* One of the failure can be because the client has reset the connection ( closed )
+ * and the status needs to be updated to reflect it */
+ op->o_status = SLAPI_OP_STATUS_ABANDONED;
+
+ LDAPDebug( LDAP_DEBUG_CONNS,
+ "ber_flush failed, error %d (%s)\n",
+ oserr, slapd_system_strerror( oserr ), 0 );
+ if (op->o_flags & OP_FLAG_PS) {
+ /* We need to tell disconnect_server() not to ding
+ * all the psearches if one if them disconnected
+ * But we do need to terminate all persistent searches that are using
+ * this connection
+ * op->o_flags |= OP_FLAG_PS_SEND_FAILED;
+ */
+ }
+ do_disconnect_server( conn, op->o_connid, op->o_opid );
+ ber_free( ber, 1 );
+ } else {
+ PRUint64 b;
+ LDAPDebug( LDAP_DEBUG_BER,
+ "flush_ber() wrote %lu bytes to socket %d\n",
+ bytes, conn->c_sd, 0 );
+ LL_I2L ( b, bytes ) ;
+ LL_ADD ( num_bytes_sent, num_bytes_sent, b);
+
+ if ( type == _LDAP_SEND_ENTRY ) {
+ LL_I2L ( b, 1 );
+ LL_ADD ( num_entries_sent, num_entries_sent, b );
+ }
+ if (! config_check_referral_mode())
+ (*(g_get_global_snmp_vars()->ops_tbl.dsBytesSent))+= bytes;
+ }
+ }
+
+ switch ( type ) {
+ case _LDAP_SEND_RESULT:
+ plugin_call_plugins( pb, SLAPI_PLUGIN_POST_RESULT_FN );
+ break;
+ case _LDAP_SEND_REFERRAL:
+ PR_AtomicIncrement(g_get_global_snmp_vars()->ops_tbl.dsReferralsReturned);
+ plugin_call_plugins( pb, SLAPI_PLUGIN_POST_REFERRAL_FN );
+ break;
+ case _LDAP_SEND_ENTRY:
+ PR_AtomicIncrement(g_get_global_snmp_vars()->ops_tbl.dsEntriesReturned);
+ plugin_call_plugins( pb, SLAPI_PLUGIN_POST_ENTRY_FN );
+ break;
+ }
+
+ return( rc );
+}
+
+/*
+ Puts the default result handlers into the pblock.
+ This routine is called before any server call to a
+ database backend.
+ Returns : 0 on success, -1 on failure.
+*/
+int set_db_default_result_handlers(Slapi_PBlock *pb)
+{
+ int rc = -1;
+ if (0 != pb)
+ {
+ rc = slapi_pblock_set( pb, SLAPI_PLUGIN_DB_ENTRY_FN,
+ (void *) send_ldap_search_entry );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_DB_RESULT_FN,
+ (void *) send_ldap_result );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_DB_REFERRAL_FN,
+ (void *) send_ldap_referral );
+ }
+ return rc;
+}
+
+
+struct slapi_note_map {
+ unsigned int snp_noteid;
+ char *snp_string;
+};
+
+static struct slapi_note_map notemap[] = {
+ { SLAPI_OP_NOTE_UNINDEXED, "U" },
+};
+
+#define SLAPI_NOTEMAP_COUNT ( sizeof(notemap) / sizeof(struct slapi_note_map))
+
+
+/*
+ * fill buf with a string representation of the bits present in notes.
+ *
+ * each bit is mapped to a character string (see table above).
+ * the result looks like "notes=U,Z" or similar.
+ * if no known notes are present, a zero-length string is generated.
+ * if buflen is too small, the output is truncated.
+ *
+ * Return value: buf itself.
+ */
+static char *
+notes2str( unsigned int notes, char *buf, size_t buflen )
+{
+ char *p;
+ int i;
+ size_t len;
+
+ *buf = '\0';
+ --buflen;
+ if ( buflen < 7 ) { /* must be room for "notes=X" at least */
+ return( buf );
+ }
+
+ p = buf;
+ for ( i = 0; i < SLAPI_NOTEMAP_COUNT; ++i ) {
+ if (( notemap[i].snp_noteid & notes ) != 0 ) {
+ if ( p > buf && buflen > 0 ) {
+ *p++ = ',';
+ *p = '\0';
+ --buflen;
+ } else {
+ strcpy( p, "notes=" );
+ p += 6;
+ buflen -= 6;
+ }
+ len = strlen( notemap[i].snp_string );
+ if ( buflen < len ) {
+ break; /* bail out (result is truncated) */
+ }
+ memcpy( p, notemap[i].snp_string, len );
+ buflen -= len;
+ p += len;
+ *p ='\0';
+ }
+ }
+
+ return( buf );
+}
+
+
+#define ETIME_BUFSIZ 16 /* room for 99999999.999999 */
+
+static void
+log_result( Slapi_PBlock *pb, Operation *op, int err, unsigned long tag,
+ int nentries )
+{
+ char *notes_str, notes_buf[ 256 ];
+ int internal_op;
+ CSN *operationcsn = NULL;
+ char csn_str[CSN_STRSIZE + 5];
+ char etime[ETIME_BUFSIZ];
+
+ internal_op = operation_is_flag_set( op, OP_FLAG_INTERNAL );
+
+ if ( (config_get_accesslog_level() & LDAP_DEBUG_TIMING) &&
+ (op->o_interval != (PRIntervalTime) 0) ) {
+ PRIntervalTime delta = PR_IntervalNow() - op->o_interval;
+ PR_snprintf(etime, ETIME_BUFSIZ, "%f",
+ (PRFloat64)delta/PR_TicksPerSecond());
+ } else {
+ PR_snprintf(etime, ETIME_BUFSIZ, "%d", current_time() - op->o_time);
+ }
+
+ if ( 0 == pb->pb_operation_notes ) {
+ notes_str = "";
+ } else {
+ notes_str = notes_buf;
+ *notes_buf = ' ';
+ notes2str( pb->pb_operation_notes, notes_buf + 1,
+ sizeof( notes_buf ) - 1 );
+ }
+
+ csn_str[0] = '\0';
+ if (config_get_csnlogging() == LDAP_ON)
+ {
+ operationcsn = operation_get_csn(op);
+ if (NULL != operationcsn)
+ {
+ char tmp_csn_str[CSN_STRSIZE];
+ sprintf(csn_str, " csn=%s", csn_as_string(operationcsn, PR_FALSE, tmp_csn_str));
+ }
+ }
+
+ if (op->o_tag == LDAP_REQ_BIND && err == LDAP_SASL_BIND_IN_PROGRESS) {
+ /*
+ * Not actually an error.
+ * Make that clear in the log.
+ */
+ if ( !internal_op )
+ {
+ slapi_log_access( LDAP_DEBUG_STATS,
+ "conn=%d op=%d RESULT err=%d"
+ " tag=%d nentries=%d etime=%s%s%s"
+ ", SASL bind in progress\n",
+ op->o_connid,
+ op->o_opid,
+ err, tag, nentries,
+ etime,
+ notes_str, csn_str );
+ }
+ else
+ {
+ slapi_log_access( LDAP_DEBUG_ARGS,
+ "conn=%s op=%d RESULT err=%d"
+ " tag=%d nentries=%d etime=%s%s%s"
+ ", SASL bind in progress\n",
+ LOG_INTERNAL_OP_CON_ID,
+ LOG_INTERNAL_OP_OP_ID,
+ err, tag, nentries,
+ etime,
+ notes_str, csn_str );
+ }
+ } else if (op->o_tag == LDAP_REQ_BIND && err == LDAP_SUCCESS) {
+ char *dn = NULL;
+
+ /*
+ * For methods other than simple, the dn in the bind request
+ * may be irrelevant. Log the actual authenticated dn.
+ */
+ slapi_pblock_get(pb, SLAPI_CONN_DN, &dn);
+ if ( !internal_op )
+ {
+ slapi_log_access( LDAP_DEBUG_STATS,
+ "conn=%d op=%d RESULT err=%d"
+ " tag=%d nentries=%d etime=%s%s%s"
+ " dn=\"%s\"\n",
+ op->o_connid,
+ op->o_opid,
+ err, tag, nentries,
+ etime,
+ notes_str, csn_str, dn ? dn : "");
+ }
+ else
+ {
+ slapi_log_access( LDAP_DEBUG_ARGS,
+ "conn=%s op=%d RESULT err=%d"
+ " tag=%d nentries=%d etime=%s%s%s"
+ " dn=\"%s\"\n",
+ LOG_INTERNAL_OP_CON_ID,
+ LOG_INTERNAL_OP_OP_ID,
+ err, tag, nentries,
+ etime,
+ notes_str, csn_str, dn ? dn : "");
+ }
+ slapi_ch_free((void**)&dn);
+ } else {
+ if ( !internal_op )
+ {
+ slapi_log_access( LDAP_DEBUG_STATS,
+ "conn=%d op=%d RESULT err=%d"
+ " tag=%d nentries=%d etime=%s%s%s\n",
+ op->o_connid,
+ op->o_opid,
+ err, tag, nentries,
+ etime,
+ notes_str, csn_str );
+ }
+ else
+ {
+ slapi_log_access( LDAP_DEBUG_ARGS,
+ "conn=%s op=%d RESULT err=%d"
+ " tag=%d nentries=%d etime=%s%s%s\n",
+ LOG_INTERNAL_OP_CON_ID,
+ LOG_INTERNAL_OP_OP_ID,
+ err, tag, nentries,
+ etime,
+ notes_str, csn_str );
+ }
+ }
+}
+
+
+static void
+log_entry( Operation *op, Slapi_Entry *e )
+{
+ int internal_op;
+ char ebuf[ BUFSIZ ];
+
+ internal_op = operation_is_flag_set( op, OP_FLAG_INTERNAL );
+
+ if ( !internal_op )
+ {
+ slapi_log_access( LDAP_DEBUG_STATS2, "conn=%d op=%d ENTRY dn=\"%s\"\n",
+ op->o_connid, op->o_opid,
+ escape_string( slapi_entry_get_dn_const(e), ebuf ));
+ }
+ else
+ {
+ if ( config_get_accesslog_level() & LDAP_DEBUG_STATS2 )
+ {
+ slapi_log_access( LDAP_DEBUG_ARGS, "conn=%s op=%d ENTRY dn=\"%s\"\n",
+ LOG_INTERNAL_OP_CON_ID, LOG_INTERNAL_OP_OP_ID,
+ escape_string( slapi_entry_get_dn_const(e), ebuf ));
+ }
+ }
+}
+
+
+static void
+log_referral( Operation *op )
+{
+ int internal_op;
+
+ internal_op = operation_is_flag_set( op, OP_FLAG_INTERNAL );
+
+ if ( !internal_op )
+ {
+ slapi_log_access( LDAP_DEBUG_STATS2, "conn=%d op=%d REFERRAL\n",
+ op->o_connid, op->o_opid );
+ }
+ else
+ {
+ if ( config_get_accesslog_level() & LDAP_DEBUG_STATS2 )
+ {
+ slapi_log_access( LDAP_DEBUG_ARGS, "conn=%s op=%d REFERRAL\n",
+ LOG_INTERNAL_OP_CON_ID, LOG_INTERNAL_OP_OP_ID );
+ }
+ }
+}