diff options
Diffstat (limited to 'ldap/servers/slapd/opshared.c')
-rw-r--r-- | ldap/servers/slapd/opshared.c | 1163 |
1 files changed, 1163 insertions, 0 deletions
diff --git a/ldap/servers/slapd/opshared.c b/ldap/servers/slapd/opshared.c new file mode 100644 index 00000000..e265d420 --- /dev/null +++ b/ldap/servers/slapd/opshared.c @@ -0,0 +1,1163 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ +/* opshared.c - functions shared between regular and internal operations */ + +#include "slap.h" +#include "index_subsys.h" + +/* helper functions */ +static void compute_limits (Slapi_PBlock *pb); + +/* attributes that no clients are allowed to add or modify */ +static char *protected_attrs_all [] = { PSEUDO_ATTR_UNHASHEDUSERPASSWORD, + NULL + }; +static char *pwpolicy_lock_attrs_all [] = { "passwordRetryCount", + "retryCountResetTime", + "accountUnlockTime", + NULL}; +/* Forward declarations */ +static void compute_limits (Slapi_PBlock *pb); +static int send_results (Slapi_PBlock *pb, int send_result, int *nentries); +static int process_entry(Slapi_PBlock *pb, Slapi_Entry *e, int send_result); + +int op_shared_is_allowed_attr (const char *attr_name, int replicated_op) +{ + int i; + slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig(); + + /* check list of attributes that no client is allowed to specify */ + for (i = 0; protected_attrs_all[i]; i ++) + { + if (strcasecmp (attr_name, protected_attrs_all[i]) == 0) + { + /* this attribute is not allowed */ + return 0; + } + } + + /* ONREPL - should allow backends to plugin here to specify + attributes that are not allowed */ + + if (!replicated_op) + { + /* + * check to see if attribute is marked as one clients can't modify + */ + struct asyntaxinfo *asi; + int no_user_mod = 0; + + asi = attr_syntax_get_by_name( attr_name ); + if ( NULL != asi && + 0 != ( asi->asi_flags & SLAPI_ATTR_FLAG_NOUSERMOD )) + { + /* this attribute is not allowed */ + no_user_mod = 1; + } + attr_syntax_return( asi ); + + if ( no_user_mod ) { + return( 0 ); + } + } else if (!slapdFrontendConfig->pw_is_global_policy) { + /* check list of password policy attributes for locking accounts */ + for (i = 0; pwpolicy_lock_attrs_all[i]; i ++) + { + if (strcasecmp (attr_name, pwpolicy_lock_attrs_all[i]) == 0) + { + /* this attribute is not allowed */ + return 0; + } + } + } + + /* this attribute is ok */ + return 1; +} + + +static ps_service_fn_ptr ps_service_fn = NULL; + +void +do_ps_service(Slapi_Entry *e, Slapi_Entry *eprev, int chgtype, int chgnum) +{ + if (NULL == ps_service_fn) { + if (get_entry_point(ENTRY_POINT_PS_SERVICE, (caddr_t *)(&ps_service_fn)) < 0) { + return; + } + } + (ps_service_fn)(e, eprev, chgtype, chgnum); +} + +void modify_update_last_modified_attr(Slapi_PBlock *pb, Slapi_Mods *smods) +{ + char buf[20]; + struct berval bv; + struct berval *bvals[2]; + time_t curtime; + struct tm utm; + Operation *op; + + LDAPDebug(LDAP_DEBUG_TRACE, "modify_update_last_modified_attr\n", 0, 0, 0); + + slapi_pblock_get(pb, SLAPI_OPERATION, &op); + + bvals[0] = &bv; + bvals[1] = NULL; + + /* fill in modifiersname */ + if (slapi_sdn_isempty(&op->o_sdn)) { + bv.bv_val = ""; + bv.bv_len = strlen(bv.bv_val); + } else { + bv.bv_val = (char*)slapi_sdn_get_dn(&op->o_sdn); + bv.bv_len = strlen(bv.bv_val); + } + + slapi_mods_add_modbvps(smods, LDAP_MOD_REPLACE | LDAP_MOD_BVALUES, + "modifiersname", bvals); + + /* fill in modifytimestamp */ + curtime = current_time(); +#ifdef _WIN32 +{ + struct tm *pt; + pt = gmtime(&curtime); + memcpy(&utm, pt, sizeof(struct tm)); +} +#else + gmtime_r(&curtime, &utm); +#endif + strftime(buf, sizeof(buf), "%Y%m%d%H%M%SZ", &utm); + bv.bv_val = buf; + bv.bv_len = strlen(bv.bv_val); + slapi_mods_add_modbvps(smods, LDAP_MOD_REPLACE | LDAP_MOD_BVALUES, + "modifytimestamp", bvals); +} + +/* + * Returns: 0 - if the operation is successful + * < 0 - if operation fails. + * Note that an operation is considered "failed" if a result is sent + * directly to the client when send_result is 0. + */ +void +op_shared_search (Slapi_PBlock *pb, int send_result) +{ + char *base, *fstr; + int scope; + Slapi_Backend *be = NULL; + Slapi_Backend *be_single = NULL; + Slapi_Backend *be_list[BE_LIST_SIZE]; + Slapi_Entry *referral_list[BE_LIST_SIZE]; + char ebuf[ BUFSIZ ]; + char attrlistbuf[ 1024 ], *attrliststr, **attrs = NULL; + int rc = 0; + int internal_op; + Slapi_DN sdn; + Slapi_Operation *operation; + Slapi_Entry *referral = NULL; + + char errorbuf[BUFSIZ]; + int nentries,pnentries; + int flag_search_base_found = 0; + int flag_no_such_object = 0; + int flag_referral = 0; + int flag_psearch = 0; + int err_code = LDAP_SUCCESS; + LDAPControl **ctrlp; + struct berval *ctl_value = NULL; + int iscritical = 0; + char * be_name = NULL; + int index = 0; + + be_list[0] = NULL; + referral_list[0] = NULL; + + /* get search parameters */ + slapi_pblock_get(pb, SLAPI_SEARCH_TARGET, &base); + slapi_pblock_get(pb, SLAPI_SEARCH_SCOPE, &scope); + slapi_pblock_get(pb, SLAPI_SEARCH_STRFILTER, &fstr); + slapi_pblock_get(pb, SLAPI_SEARCH_ATTRS, &attrs); + slapi_pblock_get (pb, SLAPI_OPERATION, &operation); + internal_op= operation_is_flag_set(operation, OP_FLAG_INTERNAL); + flag_psearch = operation_is_flag_set(operation, OP_FLAG_PS); + + slapi_sdn_init_dn_byref(&sdn, base); + + if (operation_is_flag_set(operation,OP_FLAG_ACTION_LOG_ACCESS)) + { + char *fmtstr; + +#define SLAPD_SEARCH_FMTSTR_BASE "conn=%d op=%d SRCH base=\"%s\" scope=%d " +#define SLAPD_SEARCH_FMTSTR_BASE_INT "conn=%s op=%d SRCH base=\"%s\" scope=%d " +#define SLAPD_SEARCH_FMTSTR_REMAINDER " attrs=%s%s\n" + + if ( strlen(fstr) > 1024 ) + { + /* + * slapi_log_access() throws away log lines that are longer than + * 2048 characters, so we limit the filter string to 1024 (better + * to log something rather than nothing) + */ + if ( !internal_op ) + { + fmtstr = SLAPD_SEARCH_FMTSTR_BASE "filter=\"%.1024s...\"" SLAPD_SEARCH_FMTSTR_REMAINDER; + } + else + { + fmtstr = SLAPD_SEARCH_FMTSTR_BASE_INT "filter=\"%.1024s...\"" SLAPD_SEARCH_FMTSTR_REMAINDER; + } + } else { + if ( !internal_op ) + { + fmtstr = SLAPD_SEARCH_FMTSTR_BASE "filter=\"%s\"" SLAPD_SEARCH_FMTSTR_REMAINDER; + } + else + { + fmtstr = SLAPD_SEARCH_FMTSTR_BASE_INT "filter=\"%s\"" SLAPD_SEARCH_FMTSTR_REMAINDER; + } + } + + if ( NULL == attrs ) { + attrliststr = "ALL"; + } else { + strarray2str( attrs, attrlistbuf, sizeof( attrlistbuf ), + 1 /* include quotes */ ); + attrliststr = attrlistbuf; + } + + if ( !internal_op ) + { + slapi_log_access(LDAP_DEBUG_STATS, fmtstr, + pb->pb_conn->c_connid, + pb->pb_op->o_opid, + escape_string(slapi_sdn_get_dn (&sdn), ebuf), + scope, fstr, attrliststr, + flag_psearch ? " options=persistent" : ""); + } + else + { + slapi_log_access(LDAP_DEBUG_ARGS, fmtstr, + LOG_INTERNAL_OP_CON_ID, + LOG_INTERNAL_OP_OP_ID, + escape_string(slapi_sdn_get_dn (&sdn), ebuf), + scope, fstr, attrliststr, + flag_psearch ? " options=persistent" : ""); + } + } + + slapi_pblock_set(pb, SLAPI_SEARCH_TARGET, (void*)slapi_sdn_get_ndn (&sdn)); + + /* target spec is used to decide which plugins are applicable for the operation */ + operation_set_target_spec (pb->pb_op, &sdn); + + /* this is time to check if mapping tree specific control + * was used to specify that we want to parse only + * one backend + */ + slapi_pblock_get(pb, SLAPI_REQCONTROLS, &ctrlp); + if (NULL != ctrlp) + { + if (slapi_control_present(ctrlp, MTN_CONTROL_USE_ONE_BACKEND_EXT_OID, + &ctl_value, &iscritical)) + { + /* this control is the smart version of MTN_CONTROL_USE_ONE_BACKEND_OID, + * it works out for itself what back end is required (thereby relieving + * the client of working out which backend it needs) by looking at the + * base of the search if no value is supplied + */ + + if((ctl_value->bv_len != 0) && ctl_value->bv_val) + { + be_name = ctl_value->bv_val; + } + else + { + /* we don't need no steenkin values */ + Slapi_Backend *searchbe = slapi_be_select( &sdn ); + + if(searchbe && searchbe != defbackend_get_backend()) + { + be_name = slapi_be_get_name(searchbe); + } + } + } + else + { + if (slapi_control_present(ctrlp, MTN_CONTROL_USE_ONE_BACKEND_OID, + &ctl_value, &iscritical)) + { + if ((ctl_value->bv_len == 0) || (ctl_value->bv_val == NULL)) + { + rc = -1; + send_ldap_result(pb, LDAP_PROTOCOL_ERROR, NULL, NULL, 0, NULL); + goto free_and_return_nolock; + } + else + { + be_name = ctl_value->bv_val; + if (be_name == NULL) + { + rc = -1; + send_ldap_result(pb, LDAP_PROTOCOL_ERROR, NULL, NULL, 0, NULL); + goto free_and_return_nolock; + } + } + } + } + + if ( slapi_control_present (ctrlp, LDAP_CONTROL_GET_EFFECTIVE_RIGHTS, + &ctl_value, &iscritical) ) + { + operation->o_flags |= OP_FLAG_GET_EFFECTIVE_RIGHTS; + } + } + + if (be_name == NULL) + { + /* no specific backend was requested, use the mapping tree + */ + err_code = slapi_mapping_tree_select_all(pb, be_list, referral_list, errorbuf); + if (((err_code != LDAP_SUCCESS) && (err_code != LDAP_OPERATIONS_ERROR) && (err_code != LDAP_REFERRAL)) + || ((err_code == LDAP_OPERATIONS_ERROR) && ((be_list == NULL) || be_list[0] == NULL))) + + { + send_ldap_result(pb, err_code, NULL, errorbuf, 0, NULL); + rc = -1; + goto free_and_return; + } + if (be_list[0] != NULL) + { + index = 0; + while (be_list[index] && be_list[index+1]) + index++; + be = be_list[index]; + } + else + be = NULL; + } + else + { + /* specific backend be_name was requested, use slapi_be_select_by_instance_name + */ + be_single = be = slapi_be_select_by_instance_name(be_name); + if (be_single) + slapi_be_Rlock(be_single); + be_list[0] = NULL; + referral_list[0] = NULL; + referral = NULL; + } + + slapi_pblock_set(pb, SLAPI_BACKEND_COUNT, &index); + + if (be) + { + slapi_pblock_set(pb, SLAPI_BACKEND, be); + + /* adjust time and size limits */ + compute_limits (pb); + + /* call the pre-search plugins. if they succeed, call the backend + search function. then call the post-search plugins. */ + + /* ONREPL - should regular plugin be called for internal searches ? */ + if (plugin_call_plugins(pb, SLAPI_PLUGIN_PRE_SEARCH_FN) == 0) + { + slapi_pblock_set(pb, SLAPI_PLUGIN, be->be_database); + set_db_default_result_handlers(pb); + + /* Now would be a good time to call the search rewriters for computed attrs */ + rc = compute_rewrite_search_filter(pb); + switch (rc) + { + case 1: /* A rewriter says that we should refuse to perform this search. + The potential exists that we will refuse to perform a search + which we were going to refer, perhaps to a server which would + be willing to perform the search. That's bad. The rewriter + could be clever enough to spot this and do the right thing though. */ + send_ldap_result(pb, LDAP_UNWILLING_TO_PERFORM, NULL, "Search not supported", 0, NULL); + rc = -1; + goto free_and_return; + + case -2: /* memory was allocated */ + /* take note of any changes */ + slapi_pblock_get(pb, SLAPI_SEARCH_TARGET, &base); + slapi_pblock_get(pb, SLAPI_SEARCH_SCOPE, &scope); + + slapi_sdn_set_dn_byref(&sdn, base); + break; + + case -1: + case 0: /* OK */ + break; + + case 2: /* Operations error */ + send_ldap_result(pb, LDAP_OPERATIONS_ERROR, NULL, "search rewriter", 0, NULL); + rc = -1; + goto free_and_return; + } + + } else { + /* + * A pre-operation plugin handled this search. Grab the return code + * (it may have been set by a plugin) and return. + * + * In DS 5.x, the following two lines of code did not exist, which + * means a pre-search function might return a non-zero value (which + * indicates that a result was returned to the client) but the code + * below would execute the search anyway. This was a regression from + * the documented plugin API behavior (and from DS 3.x and 4.x). + */ + slapi_pblock_get(pb, SLAPI_PLUGIN_OPRETURN, &rc); + goto free_and_return; + } + } + + /* PAR: now filters have been rewritten, we can assign plugins to work on them */ + index_subsys_assign_filter_decoders(pb); + + nentries = 0; + rc = -1; /* zero backends would mean failure */ + while (be) + { + const Slapi_DN * be_suffix; + + if (be->be_search == NULL) + { + send_ldap_result(pb, LDAP_UNWILLING_TO_PERFORM, NULL, "Function not implemented", 0, NULL); + rc = -1; + goto free_and_return; + } + + pnentries = 0; + + /* the backends returns no such object when a + * search is attempted in a node above their nsslapd-suffix + * this is correct but a bit annoying when a backends + * is below another backend because in that case the + * such searches should sometimes succeed + * To allow this we therefore have to change the + * SLAPI_SEARCH_TARGET parameter in the pblock + * + * Also when we climb down the mapping tree we have to + * change ONE-LEVEL searches to BASE + */ + + /* that's mean we only support one suffix per backend */ + be_suffix = slapi_be_getsuffix(be, 0); + + /* be_suffix null means that we are searching the default backend + * -> don't change the search parameters in pblock + */ + if (be_suffix != NULL) + { + if ((be_name == NULL) && (scope == LDAP_SCOPE_ONELEVEL)) + { + /* one level searches + * - depending on the suffix of the backend we might have to + * do a one level search or a base search + * - we might also have to change the search target + */ + if (slapi_sdn_isparent(&sdn, be_suffix) + || (slapi_sdn_get_ndn_len(&sdn) == 0)) + { + int tmp_scope = LDAP_SCOPE_BASE; + slapi_pblock_set(pb, SLAPI_SEARCH_SCOPE, &tmp_scope); + slapi_pblock_set(pb, SLAPI_SEARCH_TARGET, + (void *)slapi_sdn_get_ndn(be_suffix)); + } + else if (slapi_sdn_issuffix(&sdn, be_suffix)) + { + int tmp_scope = LDAP_SCOPE_ONELEVEL; + slapi_pblock_set(pb, SLAPI_SEARCH_SCOPE, &tmp_scope); + slapi_pblock_set(pb, SLAPI_SEARCH_TARGET, + (void *)slapi_sdn_get_ndn (&sdn)); + } + else + goto next_be; + } + + /* subtree searches : + * if the search was started above the backend suffix + * - temporarily set the SLAPI_SEARCH_TARGET to the + * base of the node so that we don't get a NO SUCH OBJECT error + * - do not change the scope + */ + if (scope == LDAP_SCOPE_SUBTREE) + { + if (slapi_sdn_issuffix(be_suffix, &sdn)) + { + slapi_pblock_set(pb, SLAPI_SEARCH_TARGET, + (void *)slapi_sdn_get_ndn(be_suffix)); + } + else + slapi_pblock_set(pb, SLAPI_SEARCH_TARGET, (void *)slapi_sdn_get_ndn(&sdn)); + } + } + + slapi_pblock_set(pb, SLAPI_BACKEND, be); + slapi_pblock_set(pb, SLAPI_PLUGIN, be->be_database); + slapi_pblock_set(pb, SLAPI_SEARCH_RESULT_SET, NULL); + + /* ONREPL - we need to be able to tell the backend not to send results directly */ + rc = (*be->be_search)(pb); + switch (rc) + { + int err; + case 1: /* backend successfully sent result to the client */ + rc = SLAPI_FAIL_GENERAL; + /* fall through */ + + case -1: /* an error occurred */ + slapi_pblock_get(pb, SLAPI_RESULT_CODE, &err); + if (err == LDAP_NO_SUCH_OBJECT) + { + /* may be the object exist somewhere else + * wait the end of the loop to send back this error + */ + flag_no_such_object = 1; + break; + } + else + { + /* for error other than LDAP_NO_SUCH_OBJECT + * the error has already been sent + * stop the search here + */ + goto free_and_return; + } + + /* when rc == SLAPI_FAIL_DISKFULL this case is executed */ + + case SLAPI_FAIL_DISKFULL: + operation_out_of_disk_space(); + goto free_and_return; + + case 0: /* search was successful and we need to send the result */ + flag_search_base_found++; + rc = send_results (pb, 1, &pnentries); + + /* if rc != 0 an error occurred while sending back the entries + * to the LDAP client + * LDAP error should already have been sent to the client + * stop the search, free and return + */ + if (rc != 0) + goto free_and_return; + break; + } + + nentries += pnentries; + + next_be: + if (be_list[0] == NULL) + { + be = NULL; + } + else + { + index--; + if (index>=0) + be = be_list[index]; + else + be = NULL; + } + } + + /* if referrals were sent back by the mapping tree + * add them to the list of referral in the pblock instead + * of searching the backend + */ + index = 0; + while ((referral = referral_list[index++]) != NULL) + { + slapi_pblock_set(pb, SLAPI_BACKEND, NULL); + if (err_code == LDAP_REFERRAL) + { + send_referrals_from_entry(pb,referral); + goto free_and_return; + } + else + { + if (process_entry(pb, referral, 1)) + { + flag_referral++; + } + else + { + /* Manage DSA was set, referral must be sent as an entry */ + int attrsonly; + char **attrs = NULL; + + slapi_pblock_get(pb, SLAPI_SEARCH_ATTRS, &attrs); + slapi_pblock_get(pb, SLAPI_SEARCH_ATTRSONLY, &attrsonly); + slapi_pblock_set(pb, SLAPI_SEARCH_RESULT_ENTRY, referral); + switch (send_ldap_search_entry(pb, referral, NULL, attrs, attrsonly)) + { + case 0: + flag_search_base_found++; + nentries++; + break; + case 1: /* entry not sent */ + case -1: /* connection closed */ + break; + } + } + } + } + + if (flag_search_base_found || flag_referral) { + rc = 0; + } + + /* ONREPL - we currently call postop only if operation is successful; + We should always send result and pass error code to the plugin */ + if (rc == 0) { + plugin_call_plugins(pb, SLAPI_PLUGIN_POST_SEARCH_FN); + } + else + { + plugin_call_plugins(pb, SLAPI_PLUGIN_POST_SEARCH_FAIL_FN); + } + + if (send_result) { + if (rc == 0) + { + /* at least one backend returned something and there was no critical error + * from the LDAP client point of view the search was successful + */ + struct berval **urls = NULL; + + slapi_pblock_get(pb, SLAPI_SEARCH_REFERRALS, &urls); + send_ldap_result(pb, err_code, NULL, NULL, nentries, urls); + } + else if (flag_no_such_object) + { + /* there was at least 1 backend that was called to process + * the operation and all backends returned NO SUCH OBJECTS + */ + slapi_send_ldap_result_from_pb(pb); + } + else + { + /* No backend was found in the mapping tree to process + * the operation : return NO SUCH OBJECT + */ + send_ldap_result(pb, LDAP_NO_SUCH_OBJECT, NULL, NULL, 0, NULL); + } + } else { + /* persistent search: ignore error locating base entry */ + rc = 0; + } + + free_and_return: + if ((be_list[0] != NULL) || (referral_list[0] != NULL)) + slapi_mapping_tree_free_all(be_list, referral_list); + else if (be_single) + slapi_be_Unlock(be_single); + + free_and_return_nolock: + slapi_pblock_set(pb, SLAPI_SEARCH_TARGET, base); + slapi_pblock_set(pb, SLAPI_PLUGIN_OPRETURN, &rc); + index_subsys_filter_decoders_done(pb); + slapi_sdn_done(&sdn); +} + +/* Returns 1 if this processing on this entry is finished + * and doesn't need to be sent. + */ +static int +process_entry(Slapi_PBlock *pb, Slapi_Entry *e, int send_result) +{ + int managedsait; + Slapi_Attr *a=NULL; + int numValues=0, i; + + if (!send_result) + { + /* server requested that we don't send results to the client, + for instance, in case of a persistent search + */ + return 1; + } + + /* ONREPL - check if the entry should be referred (because of the copyingFrom) */ + + /* + * If this is a referral, and the managedsait control is not present, + * arrange for a referral to be sent. For v2 connections, + * the referrals are just squirreled away and sent with the + * final result. For v3, the referrals are sent in separate LDAP messages. + */ + slapi_pblock_get(pb, SLAPI_MANAGEDSAIT, &managedsait); + if (!managedsait && slapi_entry_attr_find(e, "ref", &a)== 0) + { + /* to fix 522189: when rootDSE, don't interpret attribute ref as a referral entry */ + + if ( slapi_is_rootdse(slapi_entry_get_dn_const(e)) ) + return 0; /* more to do for this entry, e.g., send it back to the client */ + + /* end fix */ + slapi_attr_get_numvalues(a, &numValues ); + if (numValues == 0) + { + char ebuf[ BUFSIZ ]; + LDAPDebug(LDAP_DEBUG_ANY, "null ref in (%s)\n", + escape_string(slapi_entry_get_dn_const(e), ebuf), 0, 0); + } + else + { + Slapi_Value *val=NULL; + struct berval **refscopy=NULL; + struct berval **urls, **tmpUrls=NULL; + tmpUrls=(struct berval **) slapi_ch_malloc((numValues + 1) * sizeof(struct berval*)); + for ( i = slapi_attr_first_value(a, &val); i != -1; + i = slapi_attr_next_value(a, i, &val)) { + tmpUrls[i]=(struct berval*)slapi_value_get_berval(val); + } + tmpUrls[numValues]=NULL; + refscopy = ref_adjust(pb, tmpUrls, slapi_entry_get_sdn_const(e), 1); + slapi_pblock_get(pb, SLAPI_SEARCH_REFERRALS, &urls); + send_ldap_referral(pb, e, refscopy, &urls); + slapi_pblock_set(pb, SLAPI_SEARCH_REFERRALS, urls); + if (NULL != refscopy) + { + ber_bvecfree(refscopy); + refscopy = NULL; + } + if( NULL != tmpUrls) { + slapi_ch_free( (void **)&tmpUrls ); + } + } + + return 1; /* done with this entry */ + } + + return 0; +} + + +/* Loops through search entries and sends them to the client. + * returns -1 on error, 0 if result packet was sent or 1 if + * result packet wasn't sent + */ +static int +iterate_with_lookahead(Slapi_PBlock *pb, Slapi_Backend *be, int send_result, int *pnentries) +{ + int rc; + int attrsonly; + int done = 0; + Slapi_Entry *e; + void *backend_info_ptr; + Slapi_Entry *next_e; + void *next_backend_info_ptr; + char **attrs = NULL; + int send_result_status = 0; + + slapi_pblock_get(pb, SLAPI_SEARCH_ATTRS, &attrs); + slapi_pblock_get(pb, SLAPI_SEARCH_ATTRSONLY, &attrsonly); + + /* setup for the loop */ + rc = be->be_next_search_entry_ext(pb, 1); + if (rc < 0) + { + /* + * Some exceptional condition occurred. Results + * have been sent, so we're finished. + */ + if (rc == SLAPI_FAIL_DISKFULL) + { + operation_out_of_disk_space(); + } + return -1; + } + + slapi_pblock_get(pb, SLAPI_SEARCH_RESULT_ENTRY, &next_e); + slapi_pblock_get(pb, SLAPI_SEARCH_RESULT_ENTRY_EXT, &next_backend_info_ptr); + if (NULL == next_e) + { + /* no entries */ + done = 1; + } + + backend_info_ptr = NULL; + + /* Done setting up the loop, now here it comes */ + + while (!done) + { + /* Allow the backend to free the entry we just finished using */ + /* It is ok to call this when backend_info_ptr is NULL */ + be->be_entry_release(pb, backend_info_ptr); + e = next_e; + backend_info_ptr = next_backend_info_ptr; + + rc = be->be_next_search_entry_ext(pb, 1); + if (rc < 0) + { + /* + * Some exceptional condition occurred. Results + * have been sent, so we're finished. + */ + if (rc == SLAPI_FAIL_DISKFULL) + { + operation_out_of_disk_space(); + } + return -1; + } + else + { + slapi_pblock_get(pb, SLAPI_SEARCH_RESULT_ENTRY, &next_e); + slapi_pblock_get(pb, SLAPI_SEARCH_RESULT_ENTRY_EXT, &next_backend_info_ptr); + if (next_e == NULL) + { + /* no more entries */ + done = 1; + } + } + + if (process_entry(pb, e, send_result)) + { + /* shouldn't send this entry */ + continue; + } + + /* + * It's a regular entry, or it's a referral and + * managedsait control is on. In either case, send the entry. + */ + if (done) + { + struct berval **urls = NULL; + /* Send the entry and the result at the same time */ + slapi_pblock_get(pb, SLAPI_SEARCH_REFERRALS, &urls); + rc = send_ldap_search_entry_ext(pb, e, NULL, attrs, attrsonly, 1, + (*pnentries)+1, urls); + if (rc == 1) + { + /* this means we didn't have access to the entry. Since the + * entry was not sent, we need to send the done packet. + */ + send_result_status = 1; + } + } + else + { + /* Send the entry */ + rc = send_ldap_search_entry(pb, e, NULL, attrs, + attrsonly); + } + switch (rc) + { + case 0: /* entry sent ok */ + (*pnentries)++; + slapi_pblock_set(pb, SLAPI_NENTRIES, pnentries); + break; + case 1: /* entry not sent */ + break; + case -1: /* connection closed */ + /* + * mark the operation as abandoned so the backend + * next entry function gets called again and has + * a chance to clean things up. + */ + pb->pb_op->o_status = SLAPI_OP_STATUS_ABANDONED; + break; + } + } + + be->be_entry_release(pb, backend_info_ptr); + if (*pnentries == 0 || send_result_status) + { + /* We didn't send the result done message so the caller + * must send it */ + return 1; + } + else + { + /* The result message has been sent */ + return 0; + } +} + +/* Loops through search entries and sends them to the client. + * returns -1 on error or 1 if result packet wasn't sent. + * This function never returns 0 because it doesn't send + * the result packet back with the last entry like + * iterate_with_lookahead trys to do. + */ +static int +iterate(Slapi_PBlock *pb, Slapi_Backend *be, int send_result, int *pnentries) +{ + int rc; + int attrsonly; + int done = 0; + Slapi_Entry *e; + char **attrs = NULL; + + slapi_pblock_get(pb, SLAPI_SEARCH_ATTRS, &attrs); + slapi_pblock_get(pb, SLAPI_SEARCH_ATTRSONLY, &attrsonly); + + *pnentries = 0; + + while (!done) + { + rc = be->be_next_search_entry(pb); + if (rc < 0) + { + /* + * Some exceptional condition occurred. Results have been sent, so we're finished. + */ + if (rc == SLAPI_FAIL_DISKFULL) + { + operation_out_of_disk_space(); + } + return -1; + } + else + { + slapi_pblock_get(pb, SLAPI_SEARCH_RESULT_ENTRY, &e); + if (e == NULL) + { + /* no more entries */ + done = 1; + continue; + } + } + + if (process_entry(pb, e, send_result)) + { + /* shouldn't send this entry */ + continue; + } + + /* + * It's a regular entry, or it's a referral and + * managedsait control is on. In either case, send + * the entry. + */ + switch (send_ldap_search_entry(pb, e, NULL, attrs, attrsonly)) + { + case 0: /* entry sent ok */ + (*pnentries)++; + slapi_pblock_set(pb, SLAPI_NENTRIES, pnentries); + break; + case 1: /* entry not sent */ + break; + case -1: /* connection closed */ + /* + * mark the operation as abandoned so the backend + * next entry function gets called again and has + * a chance to clean things up. + */ + pb->pb_op->o_status = SLAPI_OP_STATUS_ABANDONED; + break; + } + } + + return 1; +} + + +static int timelimit_reslimit_handle = -1; +static int sizelimit_reslimit_handle = -1; + +/* + * Register size and time limit with the binder-based resource limits + * subsystem. A SLAPI_RESLIMIT_STATUS_... code is returned. + */ +int +search_register_reslimits( void ) +{ + int rc1, rc2; + + rc1 = slapi_reslimit_register( SLAPI_RESLIMIT_TYPE_INT, + "nsSizeLimit" , &sizelimit_reslimit_handle ); + rc2 = slapi_reslimit_register( SLAPI_RESLIMIT_TYPE_INT, + "nsTimeLimit", &timelimit_reslimit_handle ); + + if ( rc1 != SLAPI_RESLIMIT_STATUS_SUCCESS ) { + return( rc1 ); + } else { + return( rc2 ); + } +} + + +/* + * Compute size and time limits based on the connection (bind identity). + * Binder-based resource limits get top priority, followed by those associated + * with the backend we are using. + * + * If the binder is the root DN and there is nothing in the root DN's entry + * to say otherwise, no limits are used. Otherwise, the lower of the limit + * that was sent in the LDAP request and that available based on the + * connection bind identity or configured backend limit is used. + */ +static void +compute_limits (Slapi_PBlock *pb) +{ + int timelimit, sizelimit; + int requested_timelimit, max_timelimit, requested_sizelimit, max_sizelimit; + int isroot; + int isCertAuth; + Slapi_ComponentId *component_id = NULL; + Slapi_Backend *be; + + slapi_pblock_get (pb, SLAPI_SEARCH_TIMELIMIT, &requested_timelimit); + slapi_pblock_get (pb, SLAPI_SEARCH_SIZELIMIT, &requested_sizelimit); + slapi_pblock_get (pb, SLAPI_REQUESTOR_ISROOT, &isroot); + slapi_pblock_get (pb, SLAPI_BACKEND, &be); + + + /* If the search belongs to the client authentication process, take the value at + * nsslapd-timelimit as the actual time limit. + */ + + slapi_pblock_get (pb, SLAPI_PLUGIN_IDENTITY, &component_id); + if (component_id) { + isCertAuth = (! strcasecmp(component_id->sci_component_name, COMPONENT_CERT_AUTH) ) ? 1 : 0; + if (isCertAuth) { + timelimit = config_get_timelimit(); + goto set_timelimit; + } + } + + /* + * Compute the time limit. + */ + if ( slapi_reslimit_get_integer_limit( pb->pb_conn, + timelimit_reslimit_handle, &max_timelimit ) + != SLAPI_RESLIMIT_STATUS_SUCCESS ) { + /* + * no limit associated with binder/connection or some other error + * occurred. use the default maximum. + */ + if ( isroot ) { + max_timelimit = -1; /* no limit */ + } else { + max_timelimit = be->be_timelimit; + } + } + + if ( requested_timelimit == 0 ) { + timelimit = ( max_timelimit == -1 ) ? -1 : max_timelimit; + } else if ( max_timelimit == -1 || requested_timelimit < max_timelimit ) { + timelimit = requested_timelimit; + } else { + timelimit = max_timelimit; + } + + set_timelimit: + slapi_pblock_set(pb, SLAPI_SEARCH_TIMELIMIT, &timelimit); + + + /* + * Compute the size limit. + */ + if ( slapi_reslimit_get_integer_limit( pb->pb_conn, + sizelimit_reslimit_handle, &max_sizelimit ) + != SLAPI_RESLIMIT_STATUS_SUCCESS ) { + /* + * no limit associated with binder/connection or some other error + * occurred. use the default maximum. + */ + if ( isroot ) { + max_sizelimit = -1; /* no limit */ + } else { + max_sizelimit = be->be_sizelimit; + } + } + + if ( requested_sizelimit == 0 ) { + sizelimit = ( max_sizelimit == -1 ) ? -1 : max_sizelimit; + } else if ( max_sizelimit == -1 || requested_sizelimit < max_sizelimit ) { + sizelimit = requested_sizelimit; + } else { + sizelimit = max_sizelimit; + } + slapi_pblock_set(pb, SLAPI_SEARCH_SIZELIMIT, &sizelimit); + + LDAPDebug( LDAP_DEBUG_TRACE, + "=> compute_limits: sizelimit=%d, timelimit=%d\n", + sizelimit, timelimit, 0 ); +} + + +/* Iterates through results and send them to the client. + * Returns 0 if successful and -1 otherwise + */ +static int send_results (Slapi_PBlock *pb, int send_result, int * nentries) +{ + Slapi_Backend *be; + int rc; + struct berval **urls = NULL; + + slapi_pblock_get (pb, SLAPI_BACKEND, &be); + + if (be->be_next_search_entry == NULL) + { + /* we need to send the result, but the function to iterate through + the result set is not implemented */ + /* ONREPL - log error */ + send_ldap_result(pb, LDAP_UNWILLING_TO_PERFORM, NULL, "Search not supported", 0, NULL); + return -1; + } + + /* Iterate through the returned result set */ + if (be->be_next_search_entry_ext != NULL) + { + /* The iterate look ahead is causing a whole mess with the ACL. + ** the entries are now visiting the ACL land in a random way + ** and not the ordered way it was before. Until we figure out + ** let's not change the behavior. + ** + ** Don't use iterate_with_lookahead because it sends the result + * in the same times as the entry and this can cause failure + * of the mapping tree scanning algorithme + * if (getFrontendConfig()->result_tweak) + * { + * rc = iterate_with_lookahead(pb, be, send_result, nentries); + * } + * else + */ + rc = iterate(pb, be, send_result, nentries); + } + else + { + rc = iterate(pb, be, send_result, nentries); + } + + switch(rc) + { + case -1: /* an error occured */ + + case 0 : /* everything is ok - result is sent */ + /* If this happens we are dead but hopefully iterate + * never sends the result itself + */ + break; + + case 1: /* everything is ok - don't send the result */ + rc = 0; + } + + return rc; +} + +void op_shared_log_error_access (Slapi_PBlock *pb, const char *type, const char *dn, const char *msg) +{ + char ebuf[BUFSIZ]; + slapi_log_access( LDAP_DEBUG_STATS, "conn=%d op=%d %s dn=\"%s\", %s\n", + ( pb->pb_conn ? pb->pb_conn->c_connid : 0), + ( pb->pb_op ? pb->pb_op->o_opid : 0), + type, + escape_string( dn, ebuf ), + msg ? msg : "" ); + +} + |