/* * handle the LDAP_RES_INTERMEDIATE response */ static int ldap_sync_search_intermediate( ldap_sync_t *ls, LDAPMessage *res, int *refreshDone ) { int rc; char *retoid = NULL; struct berval *retdata = NULL; BerElement *ber = NULL; ber_len_t len; ber_tag_t syncinfo_tag; struct berval cookie; int refreshDeletes = 0; BerVarray syncUUIDs = NULL; ldap_sync_refresh_t phase; #ifdef LDAP_SYNC_TRACE fprintf( stderr, "\tgot LDAP_RES_INTERMEDIATE\n" ); #endif /* LDAP_SYNC_TRACE */ assert( ls != NULL ); assert( res != NULL ); assert( refreshDone != NULL ); *refreshDone = 0; rc = ldap_parse_intermediate( ls->ls_ld, res, &retoid, &retdata, NULL, 0 ); #ifdef LDAP_SYNC_TRACE fprintf( stderr, "\t%sldap_parse_intermediate(%s) == %d\n", rc != LDAP_SUCCESS ? "!!! " : "", retoid == NULL ? "\"\"" : retoid, rc ); #endif /* LDAP_SYNC_TRACE */ /* parsing must be successful, and yield the OID * of the sync info intermediate response */ if ( rc != LDAP_SUCCESS ) { goto done; } rc = LDAP_OTHER; if ( retoid == NULL || strcmp( retoid, LDAP_SYNC_INFO ) != 0 ) { goto done; } /* init ber using the value in the response */ ber = ber_init( retdata ); if ( ber == NULL ) { goto done; } syncinfo_tag = ber_peek_tag( ber, &len ); switch ( syncinfo_tag ) { case LDAP_TAG_SYNC_NEW_COOKIE: if ( ber_scanf( ber, "m", &cookie ) == LBER_ERROR ) { goto done; } if ( cookie.bv_val != NULL ) { ber_bvreplace( &ls->ls_cookie, &cookie ); } #ifdef LDAP_SYNC_TRACE fprintf( stderr, "\t\tgot cookie=%s\n", cookie.bv_val ? cookie.bv_val : "(null)" ); #endif /* LDAP_SYNC_TRACE */ break; case LDAP_TAG_SYNC_REFRESH_DELETE: case LDAP_TAG_SYNC_REFRESH_PRESENT: if ( syncinfo_tag == LDAP_TAG_SYNC_REFRESH_DELETE ) { #ifdef LDAP_SYNC_TRACE fprintf( stderr, "\t\tgot refreshDelete\n" ); #endif /* LDAP_SYNC_TRACE */ switch ( ls->ls_refreshPhase ) { case LDAP_SYNC_CAPI_NONE: case LDAP_SYNC_CAPI_PRESENTS: ls->ls_refreshPhase = LDAP_SYNC_CAPI_DELETES; break; default: /* TODO: impossible; handle */ goto done; } } else { #ifdef LDAP_SYNC_TRACE fprintf( stderr, "\t\tgot refreshPresent\n" ); #endif /* LDAP_SYNC_TRACE */ switch ( ls->ls_refreshPhase ) { case LDAP_SYNC_CAPI_NONE: ls->ls_refreshPhase = LDAP_SYNC_CAPI_PRESENTS; break; default: /* TODO: impossible; handle */ goto done; } } if ( ber_scanf( ber, "{" /*"}"*/ ) == LBER_ERROR ) { goto done; } if ( ber_peek_tag( ber, &len ) == LDAP_TAG_SYNC_COOKIE ) { if ( ber_scanf( ber, "m", &cookie ) == LBER_ERROR ) { goto done; } if ( cookie.bv_val != NULL ) { ber_bvreplace( &ls->ls_cookie, &cookie ); } #ifdef LDAP_SYNC_TRACE fprintf( stderr, "\t\tgot cookie=%s\n", cookie.bv_val ? cookie.bv_val : "(null)" ); #endif /* LDAP_SYNC_TRACE */ } *refreshDone = 1; if ( ber_peek_tag( ber, &len ) == LDAP_TAG_REFRESHDONE ) { if ( ber_scanf( ber, "b", refreshDone ) == LBER_ERROR ) { goto done; } } #ifdef LDAP_SYNC_TRACE fprintf( stderr, "\t\tgot refreshDone=%s\n", *refreshDone ? "TRUE" : "FALSE" ); #endif /* LDAP_SYNC_TRACE */ if ( ber_scanf( ber, /*"{"*/ "}" ) == LBER_ERROR ) { goto done; } if ( *refreshDone ) { ls->ls_refreshPhase = LDAP_SYNC_CAPI_DONE; } if ( ls->ls_intermediate ) { ls->ls_intermediate( ls, res, NULL, ls->ls_refreshPhase ); } break; case LDAP_TAG_SYNC_ID_SET: #ifdef LDAP_SYNC_TRACE fprintf( stderr, "\t\tgot syncIdSet\n" ); #endif /* LDAP_SYNC_TRACE */ if ( ber_scanf( ber, "{" /*"}"*/ ) == LBER_ERROR ) { goto done; } if ( ber_peek_tag( ber, &len ) == LDAP_TAG_SYNC_COOKIE ) { if ( ber_scanf( ber, "m", &cookie ) == LBER_ERROR ) { goto done; } if ( cookie.bv_val != NULL ) { ber_bvreplace( &ls->ls_cookie, &cookie ); } #ifdef LDAP_SYNC_TRACE fprintf( stderr, "\t\tgot cookie=%s\n", cookie.bv_val ? cookie.bv_val : "(null)" ); #endif /* LDAP_SYNC_TRACE */ } if ( ber_peek_tag( ber, &len ) == LDAP_TAG_REFRESHDELETES ) { if ( ber_scanf( ber, "b", &refreshDeletes ) == LBER_ERROR ) { goto done; } } if ( ber_scanf( ber, /*"{"*/ "[W]}", &syncUUIDs ) == LBER_ERROR || syncUUIDs == NULL ) { goto done; } if ( refreshDeletes ) { phase = LDAP_SYNC_CAPI_DELETES_IDSET; } else { phase = LDAP_SYNC_CAPI_PRESENTS_IDSET; } /* FIXME: should touch ls->ls_refreshPhase? */ if ( ls->ls_intermediate ) { ls->ls_intermediate( ls, res, syncUUIDs, phase ); } ber_bvarray_free( syncUUIDs ); break; default: #ifdef LDAP_SYNC_TRACE fprintf( stderr, "\t\tunknown tag!\n" ); #endif /* LDAP_SYNC_TRACE */ goto done; } rc = LDAP_SUCCESS; done:; if ( ber != NULL ) { ber_free( ber, 1 ); } if ( retoid != NULL ) { ldap_memfree( retoid ); } if ( retdata != NULL ) { ber_bvfree( retdata ); } return rc; }
/** Perform basic parsing of multiple types of messages, checking for error conditions * * @note Error messages should be retrieved with fr_strerror() and fr_strerror_pop() * * @param[out] ctrls Server ctrls returned to the client. May be NULL if not required. * Must be freed with ldap_free_ctrls. * @param[in] conn the message was received on. * @param[in] msg we're parsing. * @param[in] dn if processing the result from a search request. * @return One of the LDAP_PROC_* (#fr_ldap_rcode_t) values. */ fr_ldap_rcode_t fr_ldap_error_check(LDAPControl ***ctrls, fr_ldap_connection_t const *conn, LDAPMessage *msg, char const *dn) { fr_ldap_rcode_t status = LDAP_PROC_SUCCESS; int msg_type; int lib_errno = LDAP_SUCCESS; /* errno returned by the library */ int srv_errno = LDAP_SUCCESS; /* errno in the result message */ char *part_dn = NULL; /* Partial DN match */ char *srv_err = NULL; /* Server's extended error message */ ssize_t len; if (ctrls) *ctrls = NULL; if (!msg) { ldap_get_option(conn->handle, LDAP_OPT_ERROR_NUMBER, &lib_errno); if (lib_errno != LDAP_SUCCESS) goto process_error; fr_strerror_printf("No result available"); return LDAP_PROC_NO_RESULT; } msg_type = ldap_msgtype(msg); switch (msg_type) { /* * Parse the result and check for errors sent by the server */ case LDAP_RES_SEARCH_RESULT: /* The result of a search */ case LDAP_RES_BIND: /* The result of a bind operation */ case LDAP_RES_EXTENDED: lib_errno = ldap_parse_result(conn->handle, msg, &srv_errno, &part_dn, &srv_err, NULL, ctrls, 0); break; /* * These are messages containing objects so unless they're * malformed they can't contain errors. */ case LDAP_RES_SEARCH_ENTRY: if (ctrls) lib_errno = ldap_get_entry_controls(conn->handle, msg, ctrls); break; /* * An intermediate message updating us on the result of an operation */ case LDAP_RES_INTERMEDIATE: lib_errno = ldap_parse_intermediate(conn->handle, msg, NULL, NULL, ctrls, 0); break; /* * Can't extract any more useful information. */ default: return LDAP_PROC_SUCCESS; } /* * Stupid messy API */ if (lib_errno != LDAP_SUCCESS) { rad_assert(!ctrls || !*ctrls); ldap_get_option(conn->handle, LDAP_OPT_ERROR_NUMBER, &lib_errno); } process_error: if ((lib_errno == LDAP_SUCCESS) && (srv_errno != LDAP_SUCCESS)) { lib_errno = srv_errno; } else if ((lib_errno != LDAP_SUCCESS) && (srv_errno == LDAP_SUCCESS)) { srv_errno = lib_errno; } switch (lib_errno) { case LDAP_SUCCESS: fr_strerror_printf("Success"); break; case LDAP_SASL_BIND_IN_PROGRESS: fr_strerror_printf("Continuing"); status = LDAP_PROC_CONTINUE; break; case LDAP_NO_SUCH_OBJECT: fr_strerror_printf("The specified DN wasn't found"); status = LDAP_PROC_BAD_DN; /* * Build our own internal diagnostic string */ if (dn && part_dn) { char *spaces; char *text; len = fr_ldap_common_dn(dn, part_dn); if (len < 0) break; fr_canonicalize_error(NULL, &spaces, &text, -len, dn); fr_strerror_printf_push("%s", text); fr_strerror_printf_push("%s^ %s", spaces, "match stopped here"); talloc_free(spaces); talloc_free(text); } goto error_string; case LDAP_INSUFFICIENT_ACCESS: fr_strerror_printf("Insufficient access. Check the identity and password configuration directives"); status = LDAP_PROC_NOT_PERMITTED; break; case LDAP_UNWILLING_TO_PERFORM: fr_strerror_printf("Server was unwilling to perform"); status = LDAP_PROC_NOT_PERMITTED; break; case LDAP_FILTER_ERROR: fr_strerror_printf("Bad search filter"); status = LDAP_PROC_ERROR; break; case LDAP_TIMEOUT: fr_strerror_printf("Timed out while waiting for server to respond"); status = LDAP_PROC_TIMEOUT; break; case LDAP_TIMELIMIT_EXCEEDED: fr_strerror_printf("Time limit exceeded"); status = LDAP_PROC_TIMEOUT; break; case LDAP_BUSY: case LDAP_UNAVAILABLE: case LDAP_SERVER_DOWN: status = LDAP_PROC_BAD_CONN; goto error_string; case LDAP_INVALID_CREDENTIALS: case LDAP_CONSTRAINT_VIOLATION: status = LDAP_PROC_REJECT; goto error_string; case LDAP_OPERATIONS_ERROR: fr_strerror_printf("Please set 'chase_referrals=yes' and 'rebind=yes'. " "See the ldap module configuration for details"); /* FALL-THROUGH */ default: status = LDAP_PROC_ERROR; error_string: if (lib_errno == srv_errno) { fr_strerror_printf("lib error: %s (%u)", ldap_err2string(lib_errno), lib_errno); } else { fr_strerror_printf("lib error: %s (%u), srv error: %s (%u)", ldap_err2string(lib_errno), lib_errno, ldap_err2string(srv_errno), srv_errno); } if (srv_err) fr_strerror_printf_push("Server said: %s", srv_err); break; } /* * Cleanup memory */ if (srv_err) ldap_memfree(srv_err); if (part_dn) ldap_memfree(part_dn); return status; }
int asyncmeta_handle_search_msg(LDAPMessage *res, a_metaconn_t *mc, bm_context_t *bc, int candidate) { a_metainfo_t *mi; a_metatarget_t *mt; a_metasingleconn_t *msc; Operation *op = bc->op; SlapReply *rs; int i, rc = LDAP_SUCCESS, sres; SlapReply *candidates; char **references = NULL; LDAPControl **ctrls = NULL; a_dncookie dc; LDAPMessage *msg; ber_int_t id; rs = &bc->rs; mi = mc->mc_info; mt = mi->mi_targets[ candidate ]; msc = &mc->mc_conns[ candidate ]; dc.op = op; dc.target = mt; dc.to_from = MASSAGE_REP; id = ldap_msgid(res); candidates = bc->candidates; i = candidate; while (res && !META_BACK_CONN_INVALID(msc)) { for (msg = ldap_first_message(msc->msc_ldr, res); msg; msg = ldap_next_message(msc->msc_ldr, msg)) { switch(ldap_msgtype(msg)) { case LDAP_RES_SEARCH_ENTRY: Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_handle_search_msg: msc %p entry\n", op->o_log_prefix, msc ); if ( candidates[ i ].sr_type == REP_INTERMEDIATE ) { /* don't retry any more... */ candidates[ i ].sr_type = REP_RESULT; } /* count entries returned by target */ candidates[ i ].sr_nentries++; if (bc->c_peer_name.bv_val == op->o_conn->c_peer_name.bv_val && !op->o_abandon) { rs->sr_err = asyncmeta_send_entry( &bc->copy_op, rs, mc, i, msg ); } else { goto err_cleanup; } switch ( rs->sr_err ) { case LDAP_SIZELIMIT_EXCEEDED: asyncmeta_send_ldap_result(bc, op, rs); rs->sr_err = LDAP_SUCCESS; goto err_cleanup; case LDAP_UNAVAILABLE: rs->sr_err = LDAP_OTHER; break; default: break; } bc->is_ok++; break; case LDAP_RES_SEARCH_REFERENCE: if ( META_BACK_TGT_NOREFS( mt ) ) { rs->sr_err = LDAP_OTHER; asyncmeta_send_ldap_result(bc, op, rs); goto err_cleanup; } if ( candidates[ i ].sr_type == REP_INTERMEDIATE ) { /* don't retry any more... */ candidates[ i ].sr_type = REP_RESULT; } bc->is_ok++; rc = ldap_parse_reference( msc->msc_ldr, msg, &references, &rs->sr_ctrls, 0 ); if ( rc != LDAP_SUCCESS || references == NULL ) { rs->sr_err = LDAP_OTHER; asyncmeta_send_ldap_result(bc, op, rs); goto err_cleanup; } /* FIXME: merge all and return at the end */ { int cnt; for ( cnt = 0; references[ cnt ]; cnt++ ) ; rs->sr_ref = ber_memalloc_x( sizeof( struct berval ) * ( cnt + 1 ), op->o_tmpmemctx ); for ( cnt = 0; references[ cnt ]; cnt++ ) { ber_str2bv_x( references[ cnt ], 0, 1, &rs->sr_ref[ cnt ], op->o_tmpmemctx ); } BER_BVZERO( &rs->sr_ref[ cnt ] ); } { dc.memctx = op->o_tmpmemctx; ( void )asyncmeta_referral_result_rewrite( &dc, rs->sr_ref ); } if ( rs->sr_ref != NULL ) { if (!BER_BVISNULL( &rs->sr_ref[ 0 ] ) ) { /* ignore return value by now */ ( void )send_search_reference( op, rs ); } ber_bvarray_free_x( rs->sr_ref, op->o_tmpmemctx ); rs->sr_ref = NULL; } /* cleanup */ if ( references ) { ber_memvfree( (void **)references ); } if ( rs->sr_ctrls ) { ldap_controls_free( rs->sr_ctrls ); rs->sr_ctrls = NULL; } break; case LDAP_RES_INTERMEDIATE: if ( candidates[ i ].sr_type == REP_INTERMEDIATE ) { /* don't retry any more... */ candidates[ i ].sr_type = REP_RESULT; } bc->is_ok++; /* FIXME: response controls * are passed without checks */ rs->sr_err = ldap_parse_intermediate( msc->msc_ldr, msg, (char **)&rs->sr_rspoid, &rs->sr_rspdata, &rs->sr_ctrls, 0 ); if ( rs->sr_err != LDAP_SUCCESS ) { candidates[ i ].sr_type = REP_RESULT; rs->sr_err = LDAP_OTHER; asyncmeta_send_ldap_result(bc, op, rs); goto err_cleanup; } slap_send_ldap_intermediate( op, rs ); if ( rs->sr_rspoid != NULL ) { ber_memfree( (char *)rs->sr_rspoid ); rs->sr_rspoid = NULL; } if ( rs->sr_rspdata != NULL ) { ber_bvfree( rs->sr_rspdata ); rs->sr_rspdata = NULL; } if ( rs->sr_ctrls != NULL ) { ldap_controls_free( rs->sr_ctrls ); rs->sr_ctrls = NULL; } break; case LDAP_RES_SEARCH_RESULT: if ( mi->mi_idle_timeout != 0 ) { asyncmeta_set_msc_time(msc); } Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_handle_search_msg: msc %p result\n", op->o_log_prefix, msc ); candidates[ i ].sr_type = REP_RESULT; candidates[ i ].sr_msgid = META_MSGID_IGNORE; /* NOTE: ignores response controls * (and intermediate response controls * as well, except for those with search * references); this may not be correct, * but if they're not ignored then * back-meta would need to merge them * consistently (think of pagedResults...) */ /* FIXME: response controls? */ rs->sr_err = ldap_parse_result( msc->msc_ldr, msg, &candidates[ i ].sr_err, (char **)&candidates[ i ].sr_matched, (char **)&candidates[ i ].sr_text, &references, &ctrls /* &candidates[ i ].sr_ctrls (unused) */ , 0 ); if ( rs->sr_err != LDAP_SUCCESS ) { candidates[ i ].sr_err = rs->sr_err; sres = slap_map_api2result( &candidates[ i ] ); candidates[ i ].sr_type = REP_RESULT; goto finish; } rs->sr_err = candidates[ i ].sr_err; /* massage matchedDN if need be */ if ( candidates[ i ].sr_matched != NULL ) { struct berval match, mmatch; ber_str2bv( candidates[ i ].sr_matched, 0, 0, &match ); candidates[ i ].sr_matched = NULL; dc.memctx = NULL; asyncmeta_dn_massage( &dc, &match, &mmatch ); if ( mmatch.bv_val == match.bv_val ) { candidates[ i ].sr_matched = ch_strdup( mmatch.bv_val ); } else { candidates[ i ].sr_matched = mmatch.bv_val; } bc->candidate_match++; ldap_memfree( match.bv_val ); } /* add references to array */ /* RFC 4511: referrals can only appear * if result code is LDAP_REFERRAL */ if ( references != NULL && references[ 0 ] != NULL && references[ 0 ][ 0 ] != '\0' ) { if ( rs->sr_err != LDAP_REFERRAL ) { Debug( LDAP_DEBUG_ANY, "%s asncmeta_search_result[%d]: " "got referrals with err=%d\n", op->o_log_prefix, i, rs->sr_err ); } else { BerVarray sr_ref; int cnt; for ( cnt = 0; references[ cnt ]; cnt++ ) ; sr_ref = ber_memalloc_x( sizeof( struct berval ) * ( cnt + 1 ), op->o_tmpmemctx ); for ( cnt = 0; references[ cnt ]; cnt++ ) { ber_str2bv_x( references[ cnt ], 0, 1, &sr_ref[ cnt ], op->o_tmpmemctx ); } BER_BVZERO( &sr_ref[ cnt ] ); dc.memctx = op->o_tmpmemctx; ( void )asyncmeta_referral_result_rewrite( &dc, sr_ref ); if ( rs->sr_v2ref == NULL ) { rs->sr_v2ref = sr_ref; } else { for ( cnt = 0; !BER_BVISNULL( &sr_ref[ cnt ] ); cnt++ ) { ber_bvarray_add_x( &rs->sr_v2ref, &sr_ref[ cnt ], op->o_tmpmemctx ); } ber_memfree_x( sr_ref, op->o_tmpmemctx ); } } } else if ( rs->sr_err == LDAP_REFERRAL ) { Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_search_result[%d]: " "got err=%d with null " "or empty referrals\n", op->o_log_prefix, i, rs->sr_err ); rs->sr_err = LDAP_NO_SUCH_OBJECT; } /* cleanup */ ber_memvfree( (void **)references ); sres = slap_map_api2result( rs ); if ( candidates[ i ].sr_err == LDAP_SUCCESS ) { Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_search_result[%d] " "match=\"%s\" err=%ld", op->o_log_prefix, i, candidates[ i ].sr_matched ? candidates[ i ].sr_matched : "", (long) candidates[ i ].sr_err ); } else { Debug( LDAP_DEBUG_ANY, "%s asyncmeta_search_result[%d] " "match=\"%s\" err=%ld (%s)", op->o_log_prefix, i, candidates[ i ].sr_matched ? candidates[ i ].sr_matched : "", (long) candidates[ i ].sr_err, ldap_err2string( candidates[ i ].sr_err ) ); } switch ( sres ) { case LDAP_NO_SUCH_OBJECT: /* is_ok is touched any time a valid * (even intermediate) result is * returned; as a consequence, if * a candidate returns noSuchObject * it is ignored and the candidate * is simply demoted. */ if ( bc->is_ok ) { sres = LDAP_SUCCESS; } break; case LDAP_SUCCESS: if ( ctrls != NULL && ctrls[0] != NULL ) { #ifdef SLAPD_META_CLIENT_PR LDAPControl *pr_c; pr_c = ldap_control_find( LDAP_CONTROL_PAGEDRESULTS, ctrls, NULL ); if ( pr_c != NULL ) { BerElementBuffer berbuf; BerElement *ber = (BerElement *)&berbuf; ber_tag_t tag; ber_int_t prsize; struct berval prcookie; /* unsolicited, do not accept */ if ( mt->mt_ps == 0 ) { rs->sr_err = LDAP_OTHER; goto err_pr; } ber_init2( ber, &pr_c->ldctl_value, LBER_USE_DER ); tag = ber_scanf( ber, "{im}", &prsize, &prcookie ); if ( tag == LBER_ERROR ) { rs->sr_err = LDAP_OTHER; goto err_pr; } /* more pages? new search request */ if ( !BER_BVISNULL( &prcookie ) && !BER_BVISEMPTY( &prcookie ) ) { if ( mt->mt_ps > 0 ) { /* ignore size if specified */ prsize = 0; } else if ( prsize == 0 ) { /* guess the page size from the entries returned so far */ prsize = candidates[ i ].sr_nentries; } candidates[ i ].sr_nentries = 0; candidates[ i ].sr_msgid = META_MSGID_IGNORE; candidates[ i ].sr_type = REP_INTERMEDIATE; assert( candidates[ i ].sr_matched == NULL ); assert( candidates[ i ].sr_text == NULL ); assert( candidates[ i ].sr_ref == NULL ); switch ( asyncmeta_back_search_start( &bc->copy_op, rs, mc, bc, i, &prcookie, prsize, 1 ) ) { case META_SEARCH_CANDIDATE: assert( candidates[ i ].sr_msgid >= 0 ); ldap_controls_free( ctrls ); // goto free_message; case META_SEARCH_ERR: case META_SEARCH_NEED_BIND: err_pr:; candidates[ i ].sr_err = rs->sr_err; candidates[ i ].sr_type = REP_RESULT; if ( META_BACK_ONERR_STOP( mi ) ) { asyncmeta_send_ldap_result(bc, op, rs); ldap_controls_free( ctrls ); goto err_cleanup; } /* fallthru */ case META_SEARCH_NOT_CANDIDATE: /* means that asyncmeta_back_search_start() * failed but onerr == continue */ candidates[ i ].sr_msgid = META_MSGID_IGNORE; candidates[ i ].sr_type = REP_RESULT; break; default: /* impossible */ assert( 0 ); break; } break; } } #endif /* SLAPD_META_CLIENT_PR */ ldap_controls_free( ctrls ); } /* fallthru */ case LDAP_REFERRAL: bc->is_ok++; break; case LDAP_SIZELIMIT_EXCEEDED: /* if a target returned sizelimitExceeded * and the entry count is equal to the * proxy's limit, the target would have * returned more, and the error must be * propagated to the client; otherwise, * the target enforced a limit lower * than what requested by the proxy; * ignore it */ candidates[ i ].sr_err = rs->sr_err; if ( rs->sr_nentries == op->ors_slimit || META_BACK_ONERR_STOP( mi ) ) { const char *save_text; got_err: save_text = rs->sr_text; rs->sr_text = candidates[ i ].sr_text; asyncmeta_send_ldap_result(bc, op, rs); if (candidates[ i ].sr_text != NULL) { ch_free( (char *)candidates[ i ].sr_text ); candidates[ i ].sr_text = NULL; } rs->sr_text = save_text; ldap_controls_free( ctrls ); goto err_cleanup; } break; default: candidates[ i ].sr_err = rs->sr_err; if ( META_BACK_ONERR_STOP( mi ) ) { goto got_err; } break; } /* if this is the last result we will ever receive, send it back */ rc = rs->sr_err; if (asyncmeta_is_last_result(mc, bc, i) == 0) { Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_handle_search_msg: msc %p last result\n", op->o_log_prefix, msc ); asyncmeta_search_last_result(mc, bc, i, sres); err_cleanup: rc = rs->sr_err; ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex ); asyncmeta_drop_bc( mc, bc); asyncmeta_clear_bm_context(bc); ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex ); ldap_msgfree(res); return rc; } finish: break; default: continue; } } ldap_msgfree(res); res = NULL; if (candidates[ i ].sr_type != REP_RESULT) { struct timeval tv = {0}; rc = ldap_result( msc->msc_ldr, id, LDAP_MSG_RECEIVED, &tv, &res ); if (res != NULL) { msc->msc_result_time = slap_get_time(); } } } ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex ); bc->bc_active--; ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex ); return rc; }
int ldap_back_search( Operation *op, SlapReply *rs ) { ldapinfo_t *li = (ldapinfo_t *) op->o_bd->be_private; ldapconn_t *lc = NULL; struct timeval tv; time_t stoptime = (time_t)(-1); LDAPMessage *res, *e; int rc = 0, msgid; struct berval match = BER_BVNULL, filter = BER_BVNULL; int i, x; char **attrs = NULL; int freetext = 0, filter_undef = 0; int do_retry = 1, dont_retry = 0; LDAPControl **ctrls = NULL; char **references = NULL; rs_assert_ready( rs ); rs->sr_flags &= ~REP_ENTRY_MASK; /* paranoia, we can set rs = non-entry */ if ( !ldap_back_dobind( &lc, op, rs, LDAP_BACK_SENDERR ) ) { return rs->sr_err; } /* * FIXME: in case of values return filter, we might want * to map attrs and maybe rewrite value */ if ( op->ors_tlimit != SLAP_NO_LIMIT ) { tv.tv_sec = op->ors_tlimit; tv.tv_usec = 0; stoptime = op->o_time + op->ors_tlimit; } else { LDAP_BACK_TV_SET( &tv ); } i = 0; if ( op->ors_attrs ) { for ( ; !BER_BVISNULL( &op->ors_attrs[i].an_name ); i++ ) /* just count attrs */ ; } x = 0; if ( op->o_bd->be_extra_anlist ) { for ( ; !BER_BVISNULL( &op->o_bd->be_extra_anlist[x].an_name ); x++ ) /* just count attrs */ ; } if ( i > 0 || x > 0 ) { int j = 0; attrs = op->o_tmpalloc( ( i + x + 1 )*sizeof( char * ), op->o_tmpmemctx ); if ( attrs == NULL ) { rs->sr_err = LDAP_NO_MEMORY; rc = -1; goto finish; } if ( i > 0 ) { for ( i = 0; !BER_BVISNULL( &op->ors_attrs[i].an_name ); i++, j++ ) { attrs[ j ] = op->ors_attrs[i].an_name.bv_val; } } if ( x > 0 ) { for ( x = 0; !BER_BVISNULL( &op->o_bd->be_extra_anlist[x].an_name ); x++, j++ ) { if ( op->o_bd->be_extra_anlist[x].an_desc && ad_inlist( op->o_bd->be_extra_anlist[x].an_desc, op->ors_attrs ) ) { continue; } attrs[ j ] = op->o_bd->be_extra_anlist[x].an_name.bv_val; } } attrs[ j ] = NULL; } ctrls = op->o_ctrls; rc = ldap_back_controls_add( op, rs, lc, &ctrls ); if ( rc != LDAP_SUCCESS ) { goto finish; } /* deal with <draft-zeilenga-ldap-t-f> filters */ filter = op->ors_filterstr; retry: /* this goes after retry because ldap_back_munge_filter() * optionally replaces RFC 4526 T-F filters (&) (|) * if already computed, they will be re-installed * by filter2bv_undef_x() later */ if ( !LDAP_BACK_T_F( li ) ) { ldap_back_munge_filter( op, &filter ); } rs->sr_err = ldap_pvt_search( lc->lc_ld, op->o_req_dn.bv_val, op->ors_scope, filter.bv_val, attrs, op->ors_attrsonly, ctrls, NULL, tv.tv_sec ? &tv : NULL, op->ors_slimit, op->ors_deref, &msgid ); ldap_pvt_thread_mutex_lock( &li->li_counter_mutex ); ldap_pvt_mp_add( li->li_ops_completed[ SLAP_OP_SEARCH ], 1 ); ldap_pvt_thread_mutex_unlock( &li->li_counter_mutex ); if ( rs->sr_err != LDAP_SUCCESS ) { switch ( rs->sr_err ) { case LDAP_SERVER_DOWN: if ( do_retry ) { do_retry = 0; if ( ldap_back_retry( &lc, op, rs, LDAP_BACK_DONTSEND ) ) { goto retry; } } if ( lc == NULL ) { /* reset by ldap_back_retry ... */ rs->sr_err = slap_map_api2result( rs ); } else { rc = ldap_back_op_result( lc, op, rs, msgid, 0, LDAP_BACK_DONTSEND ); } goto finish; case LDAP_FILTER_ERROR: /* first try? */ if ( !filter_undef && strstr( filter.bv_val, "(?" ) && !LDAP_BACK_NOUNDEFFILTER( li ) ) { BER_BVZERO( &filter ); filter2bv_undef_x( op, op->ors_filter, 1, &filter ); filter_undef = 1; goto retry; } /* invalid filters return success with no data */ rs->sr_err = LDAP_SUCCESS; rs->sr_text = NULL; goto finish; default: rs->sr_err = slap_map_api2result( rs ); rs->sr_text = NULL; goto finish; } } /* if needed, initialize timeout */ if ( li->li_timeout[ SLAP_OP_SEARCH ] ) { if ( tv.tv_sec == 0 || tv.tv_sec > li->li_timeout[ SLAP_OP_SEARCH ] ) { tv.tv_sec = li->li_timeout[ SLAP_OP_SEARCH ]; tv.tv_usec = 0; } } /* We pull apart the ber result, stuff it into a slapd entry, and * let send_search_entry stuff it back into ber format. Slow & ugly, * but this is necessary for version matching, and for ACL processing. */ for ( rc = -2; rc != -1; rc = ldap_result( lc->lc_ld, msgid, LDAP_MSG_ONE, &tv, &res ) ) { /* check for abandon */ if ( op->o_abandon || LDAP_BACK_CONN_ABANDON( lc ) ) { if ( rc > 0 ) { ldap_msgfree( res ); } (void)ldap_back_cancel( lc, op, rs, msgid, LDAP_BACK_DONTSEND ); rc = SLAPD_ABANDON; goto finish; } if ( rc == 0 || rc == -2 ) { ldap_pvt_thread_yield(); /* check timeout */ if ( li->li_timeout[ SLAP_OP_SEARCH ] ) { if ( rc == 0 ) { (void)ldap_back_cancel( lc, op, rs, msgid, LDAP_BACK_DONTSEND ); rs->sr_text = "Operation timed out"; rc = rs->sr_err = op->o_protocol >= LDAP_VERSION3 ? LDAP_ADMINLIMIT_EXCEEDED : LDAP_OTHER; goto finish; } } else { LDAP_BACK_TV_SET( &tv ); } /* check time limit */ if ( op->ors_tlimit != SLAP_NO_LIMIT && slap_get_time() > stoptime ) { (void)ldap_back_cancel( lc, op, rs, msgid, LDAP_BACK_DONTSEND ); rc = rs->sr_err = LDAP_TIMELIMIT_EXCEEDED; goto finish; } continue; } else { /* only touch when activity actually took place... */ if ( li->li_idle_timeout && lc ) { lc->lc_time = op->o_time; } /* don't retry any more */ dont_retry = 1; } if ( rc == LDAP_RES_SEARCH_ENTRY ) { Entry ent = { 0 }; struct berval bdn = BER_BVNULL; do_retry = 0; e = ldap_first_entry( lc->lc_ld, res ); rc = ldap_build_entry( op, e, &ent, &bdn ); if ( rc == LDAP_SUCCESS ) { ldap_get_entry_controls( lc->lc_ld, res, &rs->sr_ctrls ); rs->sr_entry = &ent; rs->sr_attrs = op->ors_attrs; rs->sr_operational_attrs = NULL; rs->sr_flags = 0; rs->sr_err = LDAP_SUCCESS; rc = rs->sr_err = send_search_entry( op, rs ); if ( rs->sr_ctrls ) { ldap_controls_free( rs->sr_ctrls ); rs->sr_ctrls = NULL; } rs->sr_entry = NULL; rs->sr_flags = 0; if ( !BER_BVISNULL( &ent.e_name ) ) { assert( ent.e_name.bv_val != bdn.bv_val ); op->o_tmpfree( ent.e_name.bv_val, op->o_tmpmemctx ); BER_BVZERO( &ent.e_name ); } if ( !BER_BVISNULL( &ent.e_nname ) ) { op->o_tmpfree( ent.e_nname.bv_val, op->o_tmpmemctx ); BER_BVZERO( &ent.e_nname ); } entry_clean( &ent ); } ldap_msgfree( res ); switch ( rc ) { case LDAP_SUCCESS: case LDAP_INSUFFICIENT_ACCESS: break; default: if ( rc == LDAP_UNAVAILABLE ) { rc = rs->sr_err = LDAP_OTHER; } else { (void)ldap_back_cancel( lc, op, rs, msgid, LDAP_BACK_DONTSEND ); } goto finish; } } else if ( rc == LDAP_RES_SEARCH_REFERENCE ) { if ( LDAP_BACK_NOREFS( li ) ) { ldap_msgfree( res ); continue; } do_retry = 0; rc = ldap_parse_reference( lc->lc_ld, res, &references, &rs->sr_ctrls, 1 ); if ( rc != LDAP_SUCCESS ) { continue; } /* FIXME: there MUST be at least one */ if ( references && references[ 0 ] && references[ 0 ][ 0 ] ) { int cnt; for ( cnt = 0; references[ cnt ]; cnt++ ) /* NO OP */ ; /* FIXME: there MUST be at least one */ rs->sr_ref = op->o_tmpalloc( ( cnt + 1 ) * sizeof( struct berval ), op->o_tmpmemctx ); for ( cnt = 0; references[ cnt ]; cnt++ ) { ber_str2bv( references[ cnt ], 0, 0, &rs->sr_ref[ cnt ] ); } BER_BVZERO( &rs->sr_ref[ cnt ] ); /* ignore return value by now */ RS_ASSERT( !(rs->sr_flags & REP_ENTRY_MASK) ); rs->sr_entry = NULL; ( void )send_search_reference( op, rs ); } else { Debug( LDAP_DEBUG_ANY, "%s ldap_back_search: " "got SEARCH_REFERENCE " "with no referrals\n", op->o_log_prefix, 0, 0 ); } /* cleanup */ if ( references ) { ber_memvfree( (void **)references ); op->o_tmpfree( rs->sr_ref, op->o_tmpmemctx ); rs->sr_ref = NULL; references = NULL; } if ( rs->sr_ctrls ) { ldap_controls_free( rs->sr_ctrls ); rs->sr_ctrls = NULL; } } else if ( rc == LDAP_RES_INTERMEDIATE ) { /* FIXME: response controls * are passed without checks */ rc = ldap_parse_intermediate( lc->lc_ld, res, (char **)&rs->sr_rspoid, &rs->sr_rspdata, &rs->sr_ctrls, 0 ); if ( rc != LDAP_SUCCESS ) { continue; } slap_send_ldap_intermediate( op, rs ); if ( rs->sr_rspoid != NULL ) { ber_memfree( (char *)rs->sr_rspoid ); rs->sr_rspoid = NULL; } if ( rs->sr_rspdata != NULL ) { ber_bvfree( rs->sr_rspdata ); rs->sr_rspdata = NULL; } if ( rs->sr_ctrls != NULL ) { ldap_controls_free( rs->sr_ctrls ); rs->sr_ctrls = NULL; } } else { char *err = NULL; rc = ldap_parse_result( lc->lc_ld, res, &rs->sr_err, &match.bv_val, &err, &references, &rs->sr_ctrls, 1 ); if ( rc == LDAP_SUCCESS ) { if ( err ) { rs->sr_text = err; freetext = 1; } } else { rs->sr_err = rc; } rs->sr_err = slap_map_api2result( rs ); /* RFC 4511: referrals can only appear * if result code is LDAP_REFERRAL */ if ( references && references[ 0 ] && references[ 0 ][ 0 ] ) { if ( rs->sr_err != LDAP_REFERRAL ) { Debug( LDAP_DEBUG_ANY, "%s ldap_back_search: " "got referrals with err=%d\n", op->o_log_prefix, rs->sr_err, 0 ); } else { int cnt; for ( cnt = 0; references[ cnt ]; cnt++ ) /* NO OP */ ; rs->sr_ref = op->o_tmpalloc( ( cnt + 1 ) * sizeof( struct berval ), op->o_tmpmemctx ); for ( cnt = 0; references[ cnt ]; cnt++ ) { /* duplicating ...*/ ber_str2bv( references[ cnt ], 0, 0, &rs->sr_ref[ cnt ] ); } BER_BVZERO( &rs->sr_ref[ cnt ] ); } } else if ( rs->sr_err == LDAP_REFERRAL ) { Debug( LDAP_DEBUG_ANY, "%s ldap_back_search: " "got err=%d with null " "or empty referrals\n", op->o_log_prefix, rs->sr_err, 0 ); rs->sr_err = LDAP_NO_SUCH_OBJECT; } if ( match.bv_val != NULL ) { match.bv_len = strlen( match.bv_val ); } rc = 0; break; } /* if needed, restore timeout */ if ( li->li_timeout[ SLAP_OP_SEARCH ] ) { if ( tv.tv_sec == 0 || tv.tv_sec > li->li_timeout[ SLAP_OP_SEARCH ] ) { tv.tv_sec = li->li_timeout[ SLAP_OP_SEARCH ]; tv.tv_usec = 0; } } } if ( rc == -1 ) { if ( dont_retry == 0 ) { if ( do_retry ) { do_retry = 0; if ( ldap_back_retry( &lc, op, rs, LDAP_BACK_DONTSEND ) ) { goto retry; } } rs->sr_err = LDAP_SERVER_DOWN; rs->sr_err = slap_map_api2result( rs ); goto finish; } else if ( LDAP_BACK_ONERR_STOP( li ) ) { /* if onerr == STOP */ rs->sr_err = LDAP_SERVER_DOWN; rs->sr_err = slap_map_api2result( rs ); goto finish; } } /* * Rewrite the matched portion of the search base, if required */ if ( !BER_BVISNULL( &match ) && !BER_BVISEMPTY( &match ) ) { struct berval pmatch; if ( dnPretty( NULL, &match, &pmatch, op->o_tmpmemctx ) != LDAP_SUCCESS ) { pmatch.bv_val = match.bv_val; match.bv_val = NULL; } rs->sr_matched = pmatch.bv_val; rs->sr_flags |= REP_MATCHED_MUSTBEFREED; } finish:; if ( !BER_BVISNULL( &match ) ) { ber_memfree( match.bv_val ); } if ( rs->sr_v2ref ) { rs->sr_err = LDAP_REFERRAL; } if ( LDAP_BACK_QUARANTINE( li ) ) { ldap_back_quarantine( op, rs ); } if ( filter.bv_val != op->ors_filterstr.bv_val ) { op->o_tmpfree( filter.bv_val, op->o_tmpmemctx ); } #if 0 /* let send_ldap_result play cleanup handlers (ITS#4645) */ if ( rc != SLAPD_ABANDON ) #endif { send_ldap_result( op, rs ); } (void)ldap_back_controls_free( op, rs, &ctrls ); if ( rs->sr_ctrls ) { ldap_controls_free( rs->sr_ctrls ); rs->sr_ctrls = NULL; } if ( rs->sr_text ) { if ( freetext ) { ber_memfree( (char *)rs->sr_text ); } rs->sr_text = NULL; } if ( rs->sr_ref ) { op->o_tmpfree( rs->sr_ref, op->o_tmpmemctx ); rs->sr_ref = NULL; } if ( references ) { ber_memvfree( (void **)references ); } if ( attrs ) { op->o_tmpfree( attrs, op->o_tmpmemctx ); } if ( lc != NULL ) { ldap_back_release_conn( li, lc ); } return rs->sr_err; }