示例#1
0
文件: slapi_ops.c 项目: dago/openldap
static int
slapi_int_search_entry_callback( Slapi_Entry *entry, void *callback_data )
{
	int		nentries = 0, i = 0;
	Slapi_Entry	**head = NULL, **tp;
	Slapi_PBlock	*pb = (Slapi_PBlock *)callback_data;

	PBLOCK_ASSERT_INTOP( pb, LDAP_REQ_SEARCH );

	entry = slapi_entry_dup( entry );
	if ( entry == NULL ) {
		return LDAP_NO_MEMORY;
	}

	slapi_pblock_get( pb, SLAPI_NENTRIES, &nentries );
	slapi_pblock_get( pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &head );
	
	i = nentries + 1;
	if ( nentries == 0 ) {
		tp = (Slapi_Entry **)slapi_ch_malloc( 2 * sizeof(Slapi_Entry *) );
		if ( tp == NULL ) {
			slapi_entry_free( entry );
			return LDAP_NO_MEMORY;
		}

		tp[0] = entry;
	} else {
		tp = (Slapi_Entry **)slapi_ch_realloc( (char *)head,
				sizeof(Slapi_Entry *) * ( i + 1 ) );
		if ( tp == NULL ) {
			slapi_entry_free( entry );
			return LDAP_NO_MEMORY;
		}
		tp[i - 1] = entry;
	}
	tp[i] = NULL;
	          
	slapi_pblock_set( pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, (void *)tp );
	slapi_pblock_set( pb, SLAPI_NENTRIES, (void *)&i );

	return LDAP_SUCCESS;
}
示例#2
0
/*
 * Check if we are modifying the config, or changing the shared config entry
 */
int
memberof_shared_config_validate(Slapi_PBlock *pb)
{
	Slapi_Entry *e = 0;
	Slapi_Entry *resulting_e = 0;
	Slapi_Entry *config_entry = NULL;
	Slapi_DN *sdn = NULL;
	Slapi_DN *config_sdn = NULL;
	Slapi_Mods *smods = 0;
	Slapi_Mod *smod = NULL, *nextmod = NULL;
	LDAPMod **mods = NULL;
	char returntext[SLAPI_DSE_RETURNTEXT_SIZE];
	char *configarea_dn = NULL;
	int ret = SLAPI_PLUGIN_SUCCESS;

	slapi_pblock_get(pb, SLAPI_TARGET_SDN, &sdn);

	if (slapi_sdn_compare(sdn, memberof_get_plugin_area()) == 0 ||
	    slapi_sdn_compare(sdn, memberof_get_config_area()) == 0)
	{
		slapi_pblock_get(pb, SLAPI_ENTRY_PRE_OP, &e);
		if(e){
			/*
			 * Create a copy of the entry and apply the
			 * mods to create the resulting entry.
			 */
			slapi_pblock_get(pb, SLAPI_MODIFY_MODS, &mods);
			smods = slapi_mods_new();
			slapi_mods_init_byref(smods, mods);
			resulting_e = slapi_entry_dup(e);
			if (mods && (slapi_entry_apply_mods(resulting_e, mods) != LDAP_SUCCESS)) {
				/* we don't care about this, the update is invalid and will be caught later */
				goto bail;
			}

			if (slapi_sdn_compare(sdn, memberof_get_plugin_area())){
				/*
				 * This entry is a plugin config area entry, validate it.
				 */
				if( SLAPI_DSE_CALLBACK_ERROR == memberof_validate_config (pb, NULL, resulting_e, &ret, returntext,0)) {
					ret = LDAP_UNWILLING_TO_PERFORM;
				}
			} else {
				/*
				 * This is the memberOf plugin entry, check if we are adding/replacing the
				 * plugin config area.
				 */
				nextmod = slapi_mod_new();
				for (smod = slapi_mods_get_first_smod(smods, nextmod);
					 smod != NULL;
					 smod = slapi_mods_get_next_smod(smods, nextmod) )
				{
					if ( PL_strcasecmp(SLAPI_PLUGIN_SHARED_CONFIG_AREA, slapi_mod_get_type(smod)) == 0 )
					{
						/*
						 * Okay, we are modifying the plugin config area, we only care about
						 * adds and replaces.
						 */
						if(SLAPI_IS_MOD_REPLACE(slapi_mod_get_operation(smod)) ||
						   SLAPI_IS_MOD_ADD(slapi_mod_get_operation(smod)))
						{
						    struct berval *bv = NULL;
						    int rc = 0;

							bv = slapi_mod_get_first_value(smod);
							configarea_dn = slapi_ch_strdup(bv->bv_val);
							if(configarea_dn){
								/* Check the DN syntax */
								rc = slapi_dn_syntax_check(pb, configarea_dn, 1);
								if (rc) { /* syntax check failed */
									PR_snprintf(returntext, SLAPI_DSE_RETURNTEXT_SIZE,
										"%s does not contain a valid DN (%s)",
										SLAPI_PLUGIN_SHARED_CONFIG_AREA, configarea_dn);
									ret = LDAP_UNWILLING_TO_PERFORM;
									goto bail;
								}

								/* Check if the plugin config area entry exists */
								if((config_sdn = slapi_sdn_new_dn_byval(configarea_dn))){
									rc = slapi_search_internal_get_entry(config_sdn, NULL, &config_entry,
										memberof_get_plugin_id());
									if(config_entry){
										int err = 0;
										/*
										 * Validate the settings from the new config area.
										 */
										if ( memberof_validate_config(pb, NULL, config_entry, &err, returntext,0)
											 == SLAPI_DSE_CALLBACK_ERROR )
										{
											ret = LDAP_UNWILLING_TO_PERFORM;
											goto bail;

										}
									} else {
										/* The config area does not exist */
										PR_snprintf(returntext, SLAPI_DSE_RETURNTEXT_SIZE,
											"Unable to locate shared config entry (%s) error %d",
											slapi_sdn_get_dn(memberof_get_config_area()), rc);
										ret = LDAP_UNWILLING_TO_PERFORM;
										goto bail;
									}
								}
							}
							slapi_ch_free_string(&configarea_dn);
							slapi_sdn_free(&config_sdn);
						}
					}
				}
			}
		} else {
			PR_snprintf(returntext, SLAPI_DSE_RETURNTEXT_SIZE,"Unable to locate shared config entry (%s)",
				slapi_sdn_get_dn(memberof_get_config_area()));
			ret = LDAP_UNWILLING_TO_PERFORM;
		}
   	}

bail:

	if (ret){
		slapi_pblock_set(pb, SLAPI_RESULT_CODE, &ret);
		slapi_pblock_set(pb, SLAPI_PB_RESULT_TEXT, returntext);
		slapi_log_err(SLAPI_LOG_ERR, MEMBEROF_PLUGIN_SUBSYSTEM, "memberof_shared_config_validate - %s/n",
			returntext);
	}
	slapi_sdn_free(&config_sdn);
	if(nextmod)
		slapi_mod_free(&nextmod);
	slapi_mods_free(&smods);
	slapi_entry_free(resulting_e);
	slapi_entry_free(config_entry);
	slapi_ch_free_string(&configarea_dn);

	return ret;
}
示例#3
0
文件: add.c 项目: Firstyear/ds
/* Code shared between regular and internal add operation */
static void op_shared_add (Slapi_PBlock *pb)
{
	Slapi_Operation *operation;
	Slapi_Entry	*e, *pse;
	Slapi_Backend *be = NULL;
	int	err;
	int internal_op, repl_op, legacy_op, lastmod;
	char *pwdtype = NULL;
	Slapi_Attr *attr = NULL;
	Slapi_Entry *referral;
	char errorbuf[SLAPI_DSE_RETURNTEXT_SIZE];
	struct slapdplugin  *p = NULL;
	char *proxydn = NULL;
	char *proxystr = NULL;
	int proxy_err = LDAP_SUCCESS;
	char *errtext = NULL;
	Slapi_DN *sdn = NULL;
	passwdPolicy *pwpolicy;

	slapi_pblock_get (pb, SLAPI_OPERATION, &operation);
	slapi_pblock_get (pb, SLAPI_ADD_ENTRY, &e);
	slapi_pblock_get (pb, SLAPI_IS_REPLICATED_OPERATION, &repl_op);	
	slapi_pblock_get (pb, SLAPI_IS_LEGACY_REPLICATED_OPERATION, &legacy_op);
	internal_op= operation_is_flag_set(operation, OP_FLAG_INTERNAL);
	pwpolicy = new_passwdPolicy(pb, slapi_entry_get_dn(e));

	/* target spec is used to decide which plugins are applicable for the operation */
	operation_set_target_spec (operation, slapi_entry_get_sdn (e));

	if ((err = slapi_entry_add_rdn_values(e)) != LDAP_SUCCESS) 
	{
	  send_ldap_result(pb, err, NULL, "failed to add RDN values", 0, NULL);
	  goto done;
	}

	/* get the proxy auth dn if the proxy auth control is present */
	proxy_err = proxyauth_get_dn(pb, &proxydn, &errtext);

	if (operation_is_flag_set(operation,OP_FLAG_ACTION_LOG_ACCESS))
	{
		if (proxydn)
		{
			proxystr = slapi_ch_smprintf(" authzid=\"%s\"", proxydn);
		}

		if ( !internal_op )
		{
			slapi_log_access(LDAP_DEBUG_STATS, "conn=%" NSPRIu64 " op=%d ADD dn=\"%s\"%s\n",
							 pb->pb_conn->c_connid,
							 operation->o_opid,
							 slapi_entry_get_dn_const(e),
							 proxystr ? proxystr : "");
		}
		else
		{
			slapi_log_access(LDAP_DEBUG_ARGS, "conn=%s op=%d ADD dn=\"%s\"\n",
							 LOG_INTERNAL_OP_CON_ID,
							 LOG_INTERNAL_OP_OP_ID,
							 slapi_entry_get_dn_const(e));
		}
	}

	/* If we encountered an error parsing the proxy control, return an error
	 * to the client.  We do this here to ensure that we log the operation first. */
	if (proxy_err != LDAP_SUCCESS)
	{
		send_ldap_result(pb, proxy_err, NULL, errtext, 0, NULL);
		goto done;
	}

	/*
	 * We could be serving multiple database backends.  Select the
	 * appropriate one.
	 */
	if ((err = slapi_mapping_tree_select(pb, &be, &referral, errorbuf, sizeof(errorbuf))) != LDAP_SUCCESS) {
		send_ldap_result(pb, err, NULL, errorbuf, 0, NULL);
		be = NULL;
		goto done;
	}

	if (referral)
	{
		int managedsait;

		slapi_pblock_get(pb, SLAPI_MANAGEDSAIT, &managedsait);
		if (managedsait)
		{
			send_ldap_result(pb, LDAP_UNWILLING_TO_PERFORM, NULL,
							 "cannot update referral", 0, NULL);
			slapi_entry_free(referral);
			goto done;
		}
	
		slapi_pblock_set(pb, SLAPI_TARGET_SDN, (void*)operation_get_target_spec (operation));
		send_referrals_from_entry(pb,referral);
		slapi_entry_free(referral);
		goto done;
	}

	if (!slapi_be_is_flag_set(be,SLAPI_BE_FLAG_REMOTE_DATA)) {
		Slapi_Value **unhashed_password_vals = NULL;
		Slapi_Value **present_values = NULL;

		/* Setting unhashed password to the entry extension. */
		if (repl_op) {
			/* replicated add ==> get unhashed pw from entry, if any.
			 * set it to the extension */
			slapi_entry_attr_find(e, PSEUDO_ATTR_UNHASHEDUSERPASSWORD, &attr);
			if (attr) {
				present_values = attr_get_present_values(attr);
				valuearray_add_valuearray(&unhashed_password_vals,
				                          present_values, 0);
#if !defined(USE_OLD_UNHASHED)
			 	/* and remove it from the entry. */
				slapi_entry_attr_delete(e, PSEUDO_ATTR_UNHASHEDUSERPASSWORD);
#endif
			}
		} else {
			/* ordinary add ==>
			 * get unhashed pw from userpassword before encrypting it */
			/* look for user password attribute */
			slapi_entry_attr_find(e, SLAPI_USERPWD_ATTR, &attr);
			if (attr) {
				Slapi_Value **vals = NULL;

				/* Set the backend in the pblock. 
				 * The slapi_access_allowed function
				 * needs this set to work properly. */
				slapi_pblock_set(pb, SLAPI_BACKEND,
				                 slapi_be_select(slapi_entry_get_sdn_const(e)));

				/* Check ACI before checking password syntax */
				if ((err = slapi_access_allowed(pb, e, SLAPI_USERPWD_ATTR, NULL,
				                              SLAPI_ACL_ADD)) != LDAP_SUCCESS) {
					send_ldap_result(pb, err, NULL,
					                 "Insufficient 'add' privilege to the "
					                 "'userPassword' attribute", 0, NULL);
					goto done;
				}

				/*
				 * Check password syntax, unless this is a pwd admin/rootDN
				 */
				present_values = attr_get_present_values(attr);
				if (!pw_is_pwp_admin(pb, pwpolicy) &&
				    check_pw_syntax(pb, slapi_entry_get_sdn_const(e),
				                    present_values, NULL, e, 0) != 0) {
					/* error result is sent from check_pw_syntax */
					goto done;
				}
				/* pw syntax is valid */
				valuearray_add_valuearray(&unhashed_password_vals,
				                          present_values, 0);
				valuearray_add_valuearray(&vals, present_values, 0);
				pw_encodevals_ext(pb, slapi_entry_get_sdn (e), vals);
				add_password_attrs(pb, operation, e);
				slapi_entry_attr_replace_sv(e, SLAPI_USERPWD_ATTR, vals);
				valuearray_free(&vals);
#if defined(USE_OLD_UNHASHED)
				/* Add the unhashed password pseudo-attribute to the entry */
				pwdtype = 
				  slapi_attr_syntax_normalize(PSEUDO_ATTR_UNHASHEDUSERPASSWORD);
				slapi_entry_add_values_sv(e, pwdtype, unhashed_password_vals);
#endif
			}
		}
		if (unhashed_password_vals &&
		    (SLAPD_UNHASHED_PW_OFF != config_get_unhashed_pw_switch())) {
			/* unhashed_password_vals is consumed if successful. */
			err = slapi_pw_set_entry_ext(e, unhashed_password_vals,
			                             SLAPI_EXT_SET_ADD);
			if (err) {
				valuearray_free(&unhashed_password_vals);
			}
		}

#if defined(THISISTEST)
		{
			/* test code to retrieve an unhashed pw from the entry extention &
			 * PSEUDO_ATTR_UNHASHEDUSERPASSWORD attribute */
			char *test_str = slapi_get_first_clear_text_pw(e);
			if (test_str) {
				slapi_log_err(SLAPI_LOG_ERR,
				              "Value from extension: %s\n", test_str);
				slapi_ch_free_string(&test_str);
			}
#if defined(USE_OLD_UNHASHED)
			test_str = slapi_entry_attr_get_charptr(e,
			                                  PSEUDO_ATTR_UNHASHEDUSERPASSWORD);
			if (test_str) {
				slapi_log_err(SLAPI_LOG_ERR,
				              "Value from attr: %s\n", test_str);
				slapi_ch_free_string(&test_str);
			}
#endif /* USE_OLD_UNHASHED */
		}
#endif /* THISISTEST */

        /* look for multiple backend local credentials or replication local credentials */
        for ( p = get_plugin_list(PLUGIN_LIST_REVER_PWD_STORAGE_SCHEME); p != NULL && !repl_op;
            p = p->plg_next )
        {
            char *L_attr = NULL;
            int i=0;

            /* Get the appropriate decoding function */
            for ( L_attr = p->plg_argv[i]; i<p->plg_argc; L_attr = p->plg_argv[++i])
            {
                /* look for multiple backend local credentials or replication local credentials */
                char *L_normalized = slapi_attr_syntax_normalize(L_attr);
                slapi_entry_attr_find(e, L_normalized, &attr);
                if (attr)
                {
                    Slapi_Value **present_values = NULL;
                    Slapi_Value **vals = NULL;

                    present_values= attr_get_present_values(attr);

                    valuearray_add_valuearray(&vals, present_values, 0);
                    pw_rever_encode(vals, L_normalized);
                    slapi_entry_attr_replace_sv(e, L_normalized, vals);
                    valuearray_free(&vals);
                }
                if (L_normalized)
                    slapi_ch_free ((void**)&L_normalized);
            }
        }
    }

	slapi_pblock_set(pb, SLAPI_BACKEND, be);

	if (!repl_op)
	{
		/* can get lastmod only after backend is selected */
		slapi_pblock_get(pb, SLAPI_BE_LASTMOD, &lastmod);

		if (lastmod && add_created_attrs(pb, e) != 0)
		{
			send_ldap_result(pb, LDAP_UNWILLING_TO_PERFORM, NULL,
				"cannot insert computed attributes", 0, NULL);
			goto done;
		}
		/* expand objectClass values to reflect the inheritance hierarchy */
		slapi_schema_expand_objectclasses( e );
	}

    /* uniqueid needs to be generated for entries added during legacy replication */
    if (legacy_op){
    	if (add_uniqueid(e) != UID_SUCCESS)
    	{
    		send_ldap_result(pb, LDAP_UNWILLING_TO_PERFORM, NULL,
    				"cannot insert computed attributes", 0, NULL);
    		goto done;
    	}
    }

	/*
	 * call the pre-add plugins. if they succeed, call
	 * the backend add function. then call the post-add
	 * plugins.
	 */
	
	sdn = slapi_sdn_dup(slapi_entry_get_sdn_const(e));
	slapi_pblock_set(pb, SLAPI_ADD_TARGET_SDN, (void *)sdn);
	if (plugin_call_plugins(pb, internal_op ? SLAPI_PLUGIN_INTERNAL_PRE_ADD_FN :
	                        SLAPI_PLUGIN_PRE_ADD_FN) == SLAPI_PLUGIN_SUCCESS)
	{
		int	rc;
		Slapi_Entry	*ec;
		Slapi_DN *add_target_sdn = NULL;
		Slapi_Entry *save_e = NULL;

		slapi_pblock_set(pb, SLAPI_PLUGIN, be->be_database);
		set_db_default_result_handlers(pb);
		/* because be_add frees the entry */
		ec = slapi_entry_dup(e);
		add_target_sdn = slapi_sdn_dup(slapi_entry_get_sdn_const(ec));
		slapi_pblock_get(pb, SLAPI_ADD_TARGET_SDN, &sdn);
		slapi_sdn_free(&sdn);
		slapi_pblock_set(pb, SLAPI_ADD_TARGET_SDN, add_target_sdn);
		
		if (be->be_add != NULL)
		{
			rc = (*be->be_add)(pb);
			/* backend may change this if errors and not consumed */
			slapi_pblock_get(pb, SLAPI_ADD_ENTRY, &save_e);
			slapi_pblock_set(pb, SLAPI_ADD_ENTRY, ec);
			if (rc == 0)
			{
				/* acl is not enabled for internal operations */
				/* don't update aci store for remote acis     */
				if ((!internal_op) && 
					(!slapi_be_is_flag_set(be,SLAPI_BE_FLAG_REMOTE_DATA)))
				{
					plugin_call_acl_mods_update (pb, SLAPI_OPERATION_ADD);
				}

				if (operation_is_flag_set(operation,OP_FLAG_ACTION_LOG_AUDIT))
				{ 
					write_audit_log_entry(pb); /* Record the operation in the audit log */
				}

				slapi_pblock_get(pb, SLAPI_ENTRY_POST_OP, &pse);
				do_ps_service(pse, NULL, LDAP_CHANGETYPE_ADD, 0);
				/* 
				 * If be_add succeeded, then e is consumed except the resurrect case.
				 * If it is resurrect, the corresponding tombstone entry is resurrected
				 * and put into the cache.
				 * Otherwise, we set e to NULL to prevent freeing it ourselves.
				 */
				if (operation_is_flag_set(operation,OP_FLAG_RESURECT_ENTRY) && save_e) {
					e = save_e;
				} else {
					e = NULL;
				}
			}
			else
			{
				/* PR_ASSERT(!save_e); save_e is supposed to be freed in the backend.  */
				e = save_e;
				if (rc == SLAPI_FAIL_DISKFULL)
				{
					operation_out_of_disk_space();
					goto done;
				}
				/* If the disk is full we don't want to make it worse ... */
				if (operation_is_flag_set(operation,OP_FLAG_ACTION_LOG_AUDIT))
				{ 
					write_auditfail_log_entry(pb); /* Record the operation in the audit log */
				}
			}
		}
		else
		{
			send_ldap_result(pb, LDAP_UNWILLING_TO_PERFORM, NULL,
							 "Function not implemented", 0, NULL);
		}
		slapi_pblock_set(pb, SLAPI_PLUGIN_OPRETURN, &rc);
		plugin_call_plugins(pb, internal_op ? SLAPI_PLUGIN_INTERNAL_POST_ADD_FN : 
							SLAPI_PLUGIN_POST_ADD_FN);
		slapi_entry_free(ec);
	}
	slapi_pblock_get(pb, SLAPI_ADD_TARGET_SDN, &sdn);
	slapi_sdn_free(&sdn);

done:
	if (be)
		slapi_be_Unlock(be);
	slapi_pblock_get(pb, SLAPI_ENTRY_POST_OP, &pse);
	slapi_entry_free(pse);
	slapi_ch_free((void **)&operation->o_params.p.p_add.parentuniqueid);
	slapi_entry_free(e);
	slapi_pblock_set(pb, SLAPI_ADD_ENTRY, NULL);
	slapi_ch_free((void**)&pwdtype);
	slapi_ch_free_string(&proxydn);
	slapi_ch_free_string(&proxystr);
}
示例#4
0
/*
 * Perform the agreement/domain specific configuration.
 * IPA stores its configuration in the tree.  We use the
 * ds_subtree to search for the domain/realm specific
 * configuration entries.
 */
void
ipa_winsync_config_refresh_domain(
    void *cbdata, const Slapi_DN *ds_subtree,
    const Slapi_DN *ad_subtree
)
{
    IPA_WinSync_Domain_Config *iwdc =
        (IPA_WinSync_Domain_Config *)cbdata;
    Slapi_DN *config_dn = slapi_sdn_dup(ds_subtree);
    char *realm_filter = NULL;
    char *realm_attr = NULL;
    char *new_entry_filter = NULL;
    char *new_user_oc_attr = NULL; /* don't care about groups for now */
    char *homedir_prefix_attr = NULL;
    char *login_shell_attr = NULL;
    char *default_group_attr = NULL;
    char *default_group_filter = NULL;
    char *default_group_name = NULL;
    char *real_group_filter = NULL;
    char *default_gid = NULL;
    Slapi_ValueSet *new_user_objclasses = NULL; /* don't care about groups for now */
    int loopdone = 0;
    int search_scope = LDAP_SCOPE_SUBTREE;
    int ret = LDAP_SUCCESS;
    int acct_disable;
    char *inactivated_filter = NULL;
    char *activated_filter = NULL;
    char *inactivated_group_dn = NULL;
    char *activated_group_dn = NULL;
    int upg = -1;

    slapi_lock_mutex(theConfig.lock);
    realm_filter = slapi_ch_strdup(theConfig.realm_filter);
    realm_attr = slapi_ch_strdup(theConfig.realm_attr);
    new_entry_filter = slapi_ch_strdup(theConfig.new_entry_filter);
    new_user_oc_attr = slapi_ch_strdup(theConfig.new_user_oc_attr);
    homedir_prefix_attr = slapi_ch_strdup(theConfig.homedir_prefix_attr);
    if (theConfig.login_shell_attr) {
        login_shell_attr = slapi_ch_strdup(theConfig.login_shell_attr);
    }
    default_group_attr = slapi_ch_strdup(theConfig.default_group_attr);
    default_group_filter = slapi_ch_strdup(theConfig.default_group_filter);
    acct_disable = theConfig.acct_disable;
    if (acct_disable != ACCT_DISABLE_NONE) {
        if (theConfig.inactivated_filter) {
            inactivated_filter = slapi_ch_strdup(theConfig.inactivated_filter);
        }
        if (theConfig.activated_filter) {
            activated_filter = slapi_ch_strdup(theConfig.activated_filter);
        }
    }
    slapi_unlock_mutex(theConfig.lock);

    /* starting at ds_subtree, search for the entry
       containing the Kerberos realm to use */
    slapi_ch_free_string(&iwdc->realm_name);
    while(!loopdone && !slapi_sdn_isempty(config_dn)) {
        ret = internal_find_entry_get_attr_val(config_dn, search_scope,
                                               realm_filter, realm_attr,
                                               NULL, &iwdc->realm_name);

        if ((0 == ret) && iwdc->realm_name) {
            loopdone = 1;
        } else if ((LDAP_NO_SUCH_OBJECT == ret) && !iwdc->realm_name) {
            /* try again */
            Slapi_DN *parent_dn = slapi_sdn_new();
            slapi_sdn_get_parent(config_dn, parent_dn);
            slapi_sdn_free(&config_dn);
            config_dn = parent_dn;
        } else { /* error */
            goto out;
        }
    }

    if (!iwdc->realm_name) {
        /* error - could not find the IPA config entry with the realm name */
        LOG_FATAL("Error: could not find the entry containing the realm name "
                  "[%d] ds subtree [%s] filter [%s] attr [%s]\n",
                  ret, slapi_sdn_get_dn(ds_subtree), realm_filter, realm_attr);
        goto out;
    }

    /* look for the entry containing the default objectclasses
       to add to new entries */
    ret = internal_find_entry_get_attr_val(config_dn, search_scope,
                                           new_entry_filter, new_user_oc_attr,
                                           &new_user_objclasses, NULL);
    if (!new_user_objclasses) {
        /* error - could not find the entry containing list of objectclasses */
        LOG_FATAL("Error: could not find the entry containing the new user objectclass list "
                  "[%d] ds subtree [%s] filter [%s] attr [%s]\n",
                  ret, slapi_sdn_get_dn(ds_subtree), new_entry_filter, new_user_oc_attr);
        goto out;
    }

    /* get the home directory prefix value */
    /* note - this is in the same entry as the new entry template, so
       use the same filter */
    slapi_ch_free_string(&iwdc->homedir_prefix);
    ret = internal_find_entry_get_attr_val(config_dn, search_scope,
                                           new_entry_filter, homedir_prefix_attr,
                                           NULL, &iwdc->homedir_prefix);
    if (!iwdc->homedir_prefix) {
        /* error - could not find the home dir prefix */
        LOG_FATAL("Error: could not find the entry containing the home directory prefix "
                  "[%d] ds subtree [%s] filter [%s] attr [%s]\n",
                  ret, slapi_sdn_get_dn(ds_subtree), new_entry_filter, homedir_prefix_attr);
        goto out;
    }

    /* get the login shell value */
    /* note - this is in the same entry as the new entry template, so
       use the same filter */
    slapi_ch_free_string(&iwdc->login_shell);
    if (login_shell_attr) {
        ret = internal_find_entry_get_attr_val(config_dn, search_scope,
                                               new_entry_filter,
                                               login_shell_attr,
                                               NULL, &iwdc->login_shell);
        if (!iwdc->login_shell) {
            LOG("Warning: could not find the entry containing the login shell "
                "attribute [%d] ds subtree [%s] filter [%s] attr [%s]\n",
                ret, slapi_sdn_get_dn(ds_subtree), new_entry_filter,
                login_shell_attr);
        }
    }
    if (!iwdc->login_shell) {
        /* could not find the login shell or was not configured */
        LOG("Warning: no login shell configured!");
    }

    /* find the default group - the entry above contains the group name, but
       we need the gidNumber for posixAccount - so first find the entry
       and attr value which has the group name, then lookup the group
       number from the group name */
    ret = internal_find_entry_get_attr_val(config_dn, search_scope,
                                           new_entry_filter, default_group_attr,
                                           NULL, &default_group_name);
    if (!default_group_name) {
        /* error - could not find the default group name */
        LOG_FATAL("Error: could not find the entry containing the default group name "
                  "[%d] ds subtree [%s] filter [%s] attr [%s]\n",
                  ret, slapi_sdn_get_dn(ds_subtree), new_entry_filter, default_group_attr);
        goto out;
    }

    /* check if User Private Groups are enabled */
    upg = ipa_winsync_upg_enabled(ds_subtree);

    /* next, find the group whose name is default_group_name - construct the filter
       based on the filter attribute value - assumes the group name is stored
       in the cn attribute value, and the gidNumber in the gidNumber attribute value */
    real_group_filter = slapi_ch_smprintf("(&(cn=%s)%s)", default_group_name,
                                          default_group_filter);
    ret = internal_find_entry_get_attr_val(config_dn, search_scope,
                                           real_group_filter, "gidNumber",
                                           NULL, &default_gid);
    if (!default_gid) {
        /* error - could not find the default gidNumber
           This is not a fatal error if User Private Groups (UPG) are enabled.
         */
        if (upg) {
            LOG_FATAL("Error: could not find the entry containing the default gidNumber "
                      "UPG [%d] ds subtree [%s] filter [%s] attr [%s]\n",
                      ret, slapi_sdn_get_dn(ds_subtree), new_entry_filter, "gidNumber");
            goto out;
        } else {
            ret = LDAP_SUCCESS;
        }
    }

    /* If we are syncing account disable, we need to find the groups used
       to denote active and inactive users e.g.
       dn: cn=inactivated,cn=account inactivation,cn=accounts,$SUFFIX

       dn: cn=Activated,cn=Account Inactivation,cn=accounts,$SUFFIX

    */
    if (acct_disable != ACCT_DISABLE_NONE) {
        if (inactivated_filter) {
            ret = internal_find_entry_get_attr_val(config_dn, search_scope,
                                                   inactivated_filter, "dn",
                                                   NULL, &inactivated_group_dn);
            if (!inactivated_group_dn) {
                /* error - could not find the inactivated group dn */
                LOG("Could not find the DN of the inactivated users group "
                    "[%d] ds subtree [%s] filter [%s]. Ignoring\n",
                    ret, slapi_sdn_get_dn(ds_subtree), inactivated_filter);
                goto out;
            }
        }
        if (activated_filter) {
            ret = internal_find_entry_get_attr_val(config_dn, search_scope,
                                                   activated_filter, "dn",
                                                   NULL, &activated_group_dn);
            if (!activated_group_dn) {
                /* error - could not find the activated group dn */
                LOG("Could not find the DN of the activated users group "
                    "[%d] ds subtree [%s] filter [%s]. Ignoring\n",
                    ret, slapi_sdn_get_dn(ds_subtree), activated_filter);
                goto out;
            }
        }
    }

    /* ok, we have our values */
    /* first, clear out the old domain config */
    slapi_entry_free(iwdc->domain_e);
    iwdc->domain_e = NULL;

    /* next, copy the global attr config */
    slapi_lock_mutex(theConfig.lock);
    iwdc->domain_e = slapi_entry_dup(theConfig.config_e);
    slapi_unlock_mutex(theConfig.lock);

    /* set the objectclasses in the domain_e */
    slapi_entry_attr_delete(iwdc->domain_e, "objectclass");
    /* this copies new_user_objclasses */
    slapi_entry_add_valueset(iwdc->domain_e, "objectclass", new_user_objclasses);

    /* When UPG is disabled, set the default gid number */
    if (upg && default_gid) {
        slapi_entry_attr_set_charptr(iwdc->domain_e,  "gidNumber", default_gid);
    }

    slapi_ch_free_string(&iwdc->inactivated_group_dn);
    iwdc->inactivated_group_dn = inactivated_group_dn;
    inactivated_group_dn = NULL;
    slapi_ch_free_string(&iwdc->activated_group_dn);
    iwdc->activated_group_dn = activated_group_dn;
    activated_group_dn = NULL;

out:
    slapi_valueset_free(new_user_objclasses);
    slapi_sdn_free(&config_dn);
    slapi_ch_free_string(&realm_filter);
    slapi_ch_free_string(&realm_attr);
    slapi_ch_free_string(&new_entry_filter);
    slapi_ch_free_string(&new_user_oc_attr);
    slapi_ch_free_string(&homedir_prefix_attr);
    slapi_ch_free_string(&login_shell_attr);
    slapi_ch_free_string(&default_group_attr);
    slapi_ch_free_string(&default_group_filter);
    slapi_ch_free_string(&default_group_name);
    slapi_ch_free_string(&real_group_filter);
    slapi_ch_free_string(&default_gid);
    slapi_ch_free_string(&inactivated_filter);
    slapi_ch_free_string(&inactivated_group_dn);
    slapi_ch_free_string(&activated_filter);
    slapi_ch_free_string(&activated_group_dn);

    if (LDAP_SUCCESS != ret) {
        slapi_ch_free_string(&iwdc->realm_name);
        slapi_ch_free_string(&iwdc->homedir_prefix);
        slapi_ch_free_string(&iwdc->login_shell);
        slapi_entry_free(iwdc->domain_e);
        iwdc->domain_e = NULL;
    }

    return;
}
示例#5
0
文件: urp.c 项目: ohamada/389ds
/*
 * An URP Naming Collision helper function. Retreives a list of entries
 * that have the given dn excluding the unique id of the entry. Any 
 * entries returned will be entries that have been added with the same
 * dn, but caused a naming conflict when replicated. The URP to fix
 * this constraint violation is to append the unique id of the entry
 * to its RDN.
 */
static Slapi_Entry *
urp_get_min_naming_conflict_entry ( Slapi_PBlock *pb, char *sessionid, CSN *opcsn )
{
	Slapi_PBlock *newpb = NULL;
	LDAPControl **server_ctrls = NULL;
	Slapi_Entry **entries = NULL;
	Slapi_Entry *min_naming_conflict_entry = NULL;
	const CSN *min_csn = NULL;
	char *filter = NULL;
	char *parent_dn = NULL;
	char *basedn;
	int i = 0;
	int min_i = -1;
	int op_result = LDAP_SUCCESS;

	slapi_pblock_get (pb, SLAPI_URP_NAMING_COLLISION_DN, &basedn);
	if (NULL == basedn || strncmp (basedn, SLAPI_ATTR_UNIQUEID, strlen(SLAPI_ATTR_UNIQUEID)) == 0)
		return NULL;

	slapi_log_error ( SLAPI_LOG_REPL, sessionid,
		"Enter urp_get_min_naming_conflict_entry for %s\n", basedn);

	filter = slapi_filter_sprintf("(%s=%s %s%s)", ATTR_NSDS5_REPLCONFLICT, REASON_ANNOTATE_DN,
			ESC_NEXT_VAL, basedn);

	/* server_ctrls will be freed when newpb is destroyed */
	server_ctrls = (LDAPControl **)slapi_ch_calloc (2, sizeof (LDAPControl *));
	server_ctrls[0] = create_managedsait_control();
	server_ctrls[1] = NULL;
	
	newpb = slapi_pblock_new();
	parent_dn = slapi_dn_parent (basedn);
	slapi_search_internal_set_pb(newpb,
								 parent_dn, /* Base DN */
								 LDAP_SCOPE_ONELEVEL,
								 filter,
								 NULL, /* Attrs */
								 0, /* AttrOnly */
								 server_ctrls, /* Controls */
								 NULL, /* UniqueID */
								 repl_get_plugin_identity(PLUGIN_MULTIMASTER_REPLICATION),
								 0);
	slapi_search_internal_pb(newpb);
	slapi_pblock_get(newpb, SLAPI_PLUGIN_INTOP_RESULT, &op_result);
	slapi_pblock_get(newpb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries);
	if ( (op_result != LDAP_SUCCESS) || (entries == NULL) )
	{
		/* Log a message */
		goto done;
	}
	/* For all entries, get the one with the smallest dn csn */
	for (i = 0; NULL != entries[i]; i++)
	{
		const CSN *dncsn;
		dncsn = entry_get_dncsn(entries[i]);
		if ((dncsn != opcsn) && 
			((min_csn == NULL) || (csn_compare(dncsn, min_csn) < 0)) &&
			!is_tombstone_entry (entries[i]))
		{
			min_csn = dncsn;
			min_i = i;
		}
		/*
		 * If there are too many conflicts, the current urp code has no
		 * guarantee for all servers to converge anyway, because the
		 * urp and the backend can't be done in one transaction due
		 * to either performance or the deadlock problem.
		 * Don't sacrifice the performance too much for impossible.
		 */
		if (min_csn && i > 5)
		{
			break;
		}
	}
	
	if (min_csn != NULL) {
		/* Found one entry */
		min_naming_conflict_entry = slapi_entry_dup(entries[min_i]);
	}

done:
	slapi_ch_free_string(&parent_dn);
	if (filter) {
		PR_smprintf_free(filter);
	}
	slapi_free_search_results_internal(newpb);
	slapi_pblock_destroy(newpb);
	newpb = NULL;

	slapi_log_error ( SLAPI_LOG_REPL, sessionid,
		"Leave urp_get_min_naming_conflict_entry (found %d entries)\n", i);

	return min_naming_conflict_entry;
}
示例#6
0
int
ldbm_back_modify( Slapi_PBlock *pb )
{
	backend *be;
	ldbm_instance *inst = NULL;
	struct ldbminfo		*li;
	struct backentry	*e = NULL, *ec = NULL;
	struct backentry	*original_entry = NULL, *tmpentry = NULL;
	Slapi_Entry		*postentry = NULL;
	LDAPMod			**mods = NULL;
	LDAPMod			**mods_original = NULL;
	Slapi_Mods smods = {0};
	back_txn txn;
	back_txnid		parent_txn;
	modify_context		ruv_c = {0};
	int			ruv_c_init = 0;
	int			retval = -1;
	char			*msg;
	char			*errbuf = NULL;
	int retry_count = 0;
	int disk_full = 0;
	int ldap_result_code= LDAP_SUCCESS;
	char *ldap_result_message= NULL;
	int rc = 0;
	Slapi_Operation *operation;
	entry_address *addr;
	int is_fixup_operation= 0;
	int is_ruv = 0;                 /* True if the current entry is RUV */
	CSN *opcsn = NULL;
	int repl_op;
	int opreturn = 0;
	int mod_count = 0;
	int not_an_error = 0;
	int fixup_tombstone = 0;
	int ec_locked = 0;
	int result_sent = 0;

	slapi_pblock_get( pb, SLAPI_BACKEND, &be);
	slapi_pblock_get( pb, SLAPI_PLUGIN_PRIVATE, &li );
	slapi_pblock_get( pb, SLAPI_TARGET_ADDRESS, &addr );
	slapi_pblock_get( pb, SLAPI_MODIFY_MODS, &mods );
	slapi_pblock_get( pb, SLAPI_TXN, (void**)&parent_txn );
	slapi_pblock_get( pb, SLAPI_IS_REPLICATED_OPERATION, &repl_op);
	slapi_pblock_get( pb, SLAPI_OPERATION, &operation );

	fixup_tombstone = operation_is_flag_set(operation, OP_FLAG_TOMBSTONE_FIXUP);

	dblayer_txn_init(li,&txn); /* must do this before first goto error_return */
	/* the calls to perform searches require the parent txn if any
	   so set txn to the parent_txn until we begin the child transaction */
	if (parent_txn) {
		txn.back_txn_txn = parent_txn;
	} else {
		parent_txn = txn.back_txn_txn;
		slapi_pblock_set( pb, SLAPI_TXN, parent_txn );
	}

	if (NULL == operation)
	{
		ldap_result_code = LDAP_OPERATIONS_ERROR;
		goto error_return;
	}

	is_fixup_operation = operation_is_flag_set(operation, OP_FLAG_REPL_FIXUP);
	is_ruv = operation_is_flag_set(operation, OP_FLAG_REPL_RUV);
	inst = (ldbm_instance *) be->be_instance_info;

	if (NULL == addr)
	{
		goto error_return;
	}
	if (inst && inst->inst_ref_count) {
		slapi_counter_increment(inst->inst_ref_count);
	} else {
		slapi_log_err(SLAPI_LOG_ERR, "ldbm_back_modify",
		              "Instance \"%s\" does not exist.\n",
		              inst ? inst->inst_name : "null instance");
		goto error_return;
	}

	/* no need to check the dn syntax as this is a replicated op */
	if(!repl_op){
		ldap_result_code = slapi_dn_syntax_check(pb, slapi_sdn_get_dn(addr->sdn), 1);
		if (ldap_result_code)
		{
			ldap_result_code = LDAP_INVALID_DN_SYNTAX;
			slapi_pblock_get(pb, SLAPI_PB_RESULT_TEXT, &ldap_result_message);
			goto error_return;
		}
	}

	/* The dblock serializes writes to the database,
	 * which reduces deadlocking in the db code,
	 * which means that we run faster.
	 *
	 * But, this lock is re-enterant for the fixup
	 * operations that the URP code in the Replication
	 * plugin generates.
	 *
	 * SERIALLOCK is moved to dblayer_txn_begin along with exposing be
	 * transaction to plugins (see slapi_back_transaction_* APIs).
	 *
	if(SERIALLOCK(li) && !operation_is_flag_set(operation,OP_FLAG_REPL_FIXUP)) {
		dblayer_lock_backend(be);
		dblock_acquired= 1;
	}
	 */
	if ( MANAGE_ENTRY_BEFORE_DBLOCK(li)) {
		/* find and lock the entry we are about to modify */
		if (fixup_tombstone) {
			e = find_entry2modify_only_ext( pb, be, addr, TOMBSTONE_INCLUDED, &txn, &result_sent );
		} else {
			e = find_entry2modify( pb, be, addr, &txn, &result_sent );
		}
		if (e == NULL) {
			ldap_result_code = -1;
			goto error_return; /* error result sent by find_entry2modify() */
		}
	}

	txn.back_txn_txn = NULL; /* ready to create the child transaction */
	for (retry_count = 0; retry_count < RETRY_TIMES; retry_count++) {
		int cache_rc = 0;
		int new_mod_count = 0;
		if (txn.back_txn_txn && (txn.back_txn_txn != parent_txn)) {
			/* don't release SERIAL LOCK */
			dblayer_txn_abort_ext(li, &txn, PR_FALSE); 
			slapi_pblock_set(pb, SLAPI_TXN, parent_txn);
			/*
			 * Since be_txn_preop functions could have modified the entry/mods,
			 * We need to grab the current mods, free them, and restore the
			 * originals.  Same thing for the entry.
			 */
			
			slapi_pblock_get(pb, SLAPI_MODIFY_MODS, &mods);
			ldap_mods_free(mods, 1);
			slapi_pblock_set(pb, SLAPI_MODIFY_MODS, copy_mods(mods_original));

			/* reset ec set cache in id2entry_add_ext */
			if (ec) {
				/* must duplicate ec before returning it to cache,
				 * which could free the entry. */
				if ((tmpentry = backentry_dup(original_entry?original_entry:ec)) == NULL) {
					ldap_result_code= LDAP_OPERATIONS_ERROR;
					goto error_return;
				}
				if (cache_is_in_cache(&inst->inst_cache, ec)) {
					CACHE_REMOVE(&inst->inst_cache, ec);
				}
				CACHE_RETURN(&inst->inst_cache, &ec);
				slapi_pblock_set( pb, SLAPI_MODIFY_EXISTING_ENTRY, original_entry->ep_entry );
				ec = original_entry;
				original_entry = tmpentry;
				tmpentry = NULL;
			}

			if (ruv_c_init) {
				/* reset the ruv txn stuff */
				modify_term(&ruv_c, be);
				ruv_c_init = 0;
			}

			slapi_log_err(SLAPI_LOG_BACKLDBM, "ldbm_back_modify",
			               "Modify Retrying Transaction\n");
#ifndef LDBM_NO_BACKOFF_DELAY
			{
			PRIntervalTime interval;
			interval = PR_MillisecondsToInterval(slapi_rand() % 100);
			DS_Sleep(interval);
			}
#endif
		}

		/* Nothing above here modifies persistent store, everything after here is subject to the transaction */
		/* dblayer_txn_begin holds SERIAL lock, 
		 * which should be outside of locking the entry (find_entry2modify) */
		if (0 == retry_count) {
			/* First time, hold SERIAL LOCK */
			retval = dblayer_txn_begin(be, parent_txn, &txn);
		} else {
			/* Otherwise, no SERIAL LOCK */
			retval = dblayer_txn_begin_ext(li, parent_txn, &txn, PR_FALSE);
		}
		if (0 != retval) {
			if (LDBM_OS_ERR_IS_DISKFULL(retval)) disk_full = 1;
			ldap_result_code= LDAP_OPERATIONS_ERROR;
			goto error_return;
		}
		/* stash the transaction for plugins */
		slapi_pblock_set(pb, SLAPI_TXN, txn.back_txn_txn);

		if (0 == retry_count) { /* just once */
			if ( !MANAGE_ENTRY_BEFORE_DBLOCK(li)) {
				/* find and lock the entry we are about to modify */
				if (fixup_tombstone) {
					e = find_entry2modify_only_ext( pb, be, addr, TOMBSTONE_INCLUDED, &txn, &result_sent );
				} else {
					e = find_entry2modify( pb, be, addr, &txn, &result_sent );
				}
				if (e == NULL) {
					ldap_result_code = -1;
					goto error_return; /* error result sent by find_entry2modify() */
				}
			}
		
			if ( !is_fixup_operation && !fixup_tombstone)
			{
				if (!repl_op && slapi_entry_flag_is_set(e->ep_entry, SLAPI_ENTRY_FLAG_TOMBSTONE))
				{
					ldap_result_code = LDAP_UNWILLING_TO_PERFORM;
                			ldap_result_message = "Operation not allowed on tombstone entry.";
					slapi_log_err(SLAPI_LOG_ERR, "ldbm_back_modify",
						"Attempt to modify a tombstone entry %s\n",
						slapi_sdn_get_dn(slapi_entry_get_sdn_const( e->ep_entry )));
					goto error_return;
				}
				opcsn = operation_get_csn (operation);
				if (NULL == opcsn && operation->o_csngen_handler)
				{
					/*
					 * Current op is a user request. Opcsn will be assigned
					 * if the dn is in an updatable replica.
					 */
					opcsn = entry_assign_operation_csn ( pb, e->ep_entry, NULL );
				}
				if (opcsn)
				{
					entry_set_maxcsn (e->ep_entry, opcsn);
				}
			}
		
			/* Save away a copy of the entry, before modifications */
			slapi_pblock_set( pb, SLAPI_ENTRY_PRE_OP, slapi_entry_dup( e->ep_entry ));
			
			if ( (ldap_result_code = plugin_call_acl_mods_access( pb, e->ep_entry, mods, &errbuf)) != LDAP_SUCCESS ) {
				ldap_result_message= errbuf;
				goto error_return;
			}
		
			/* create a copy of the entry and apply the changes to it */
			if ( (ec = backentry_dup( e )) == NULL ) {
				ldap_result_code= LDAP_OPERATIONS_ERROR;
				goto error_return;
			}
		
			if(!repl_op){
			    remove_illegal_mods(mods);
			}
		
			/* ec is the entry that our bepreop should get to mess with */
			slapi_pblock_set( pb, SLAPI_MODIFY_EXISTING_ENTRY, ec->ep_entry );
			slapi_pblock_set(pb, SLAPI_RESULT_CODE, &ldap_result_code);
		
			opreturn = plugin_call_plugins(pb, SLAPI_PLUGIN_BE_PRE_MODIFY_FN);
			if (opreturn ||
				(slapi_pblock_get(pb, SLAPI_RESULT_CODE, &ldap_result_code) && ldap_result_code) ||
				(slapi_pblock_get(pb, SLAPI_PLUGIN_OPRETURN, &opreturn) && opreturn)) {
				slapi_pblock_get(pb, SLAPI_RESULT_CODE, &ldap_result_code);
				slapi_pblock_get(pb, SLAPI_PLUGIN_OPRETURN, &opreturn);
				if (!ldap_result_code) {
					slapi_log_err(SLAPI_LOG_ERR, "ldbm_back_modify",
						"SLAPI_PLUGIN_BE_PRE_MODIFY_FN "
						"returned error but did not set SLAPI_RESULT_CODE\n");
					ldap_result_code = LDAP_OPERATIONS_ERROR;
				}
				if (SLAPI_PLUGIN_NOOP == opreturn) {
					not_an_error = 1;
					rc = opreturn = LDAP_SUCCESS;
				} else if (!opreturn) {
					opreturn = SLAPI_PLUGIN_FAILURE;
					slapi_pblock_set(pb, SLAPI_PLUGIN_OPRETURN, &opreturn);
				}
				slapi_pblock_get(pb, SLAPI_PB_RESULT_TEXT, &ldap_result_message);
				goto error_return;
			}
			/* The Plugin may have messed about with some of the PBlock parameters... ie. mods */
			slapi_pblock_get( pb, SLAPI_MODIFY_MODS, &mods );
		
			/* apply the mods, check for syntax, schema problems, etc. */
			if (modify_apply_check_expand(pb, operation, mods, e, ec, &postentry,
										  &ldap_result_code, &ldap_result_message)) {
				goto error_return;
			}
			/* the schema check could have added a repl conflict mod
			 * get the mods again */
			slapi_pblock_get( pb, SLAPI_MODIFY_MODS, &mods );
			slapi_mods_init_byref(&smods,mods);
			mod_count = slapi_mods_get_num_mods(&smods);
			/*
			 * Grab a copy of the mods and the entry in case the be_txn_preop changes
			 * the them.  If we have a failure, then we need to reset the mods to their
			 * their original state;
			 */
			mods_original = copy_mods(mods);
			if ( (original_entry = backentry_dup( ec )) == NULL ) {
				ldap_result_code= LDAP_OPERATIONS_ERROR;
				goto error_return;
			}
		} /* if (0 == retry_count) just once */

		/* call the transaction pre modify plugins just after creating the transaction */
		retval = plugin_call_plugins(pb, SLAPI_PLUGIN_BE_TXN_PRE_MODIFY_FN);
		if (retval) {
			slapi_log_err(SLAPI_LOG_TRACE, "ldbm_back_modify", "SLAPI_PLUGIN_BE_TXN_PRE_MODIFY_FN plugin "
						   "returned error code %d\n", retval );
			slapi_pblock_get(pb, SLAPI_RESULT_CODE, &ldap_result_code);
			slapi_pblock_get(pb, SLAPI_PLUGIN_OPRETURN, &opreturn);
			if (SLAPI_PLUGIN_NOOP == retval) {
				not_an_error = 1;
				rc = retval = LDAP_SUCCESS;
			}
			if (!opreturn) {
				slapi_pblock_set(pb, SLAPI_PLUGIN_OPRETURN, ldap_result_code ? &ldap_result_code : &retval);
			}
			slapi_pblock_get(pb, SLAPI_PB_RESULT_TEXT, &ldap_result_message);
			goto error_return;
		}

		/* the mods might have been changed, so get the latest */
		slapi_pblock_get( pb, SLAPI_MODIFY_MODS, &mods );

		/* make sure the betxnpreop did not alter any of the mods that
		   had already previously been applied */
		slapi_mods_done(&smods);
		slapi_mods_init_byref(&smods,mods);
		new_mod_count = slapi_mods_get_num_mods(&smods);
		if (new_mod_count < mod_count) {
			slapi_log_err(SLAPI_LOG_ERR, "ldbm_back_modify",
				"Error: BE_TXN_PRE_MODIFY plugin has removed "
				"mods from the original list - mod count was [%d] now [%d] "
				"mods will not be applied - mods list changes must be done "
				"in the BE_PRE_MODIFY plugin, not the BE_TXN_PRE_MODIFY\n",
				mod_count, new_mod_count );
		} else if (new_mod_count > mod_count) { /* apply the new betxnpremod mods */
			/* apply the mods, check for syntax, schema problems, etc. */
			if (modify_apply_check_expand(pb, operation, &mods[mod_count], e, ec, &postentry,
										  &ldap_result_code, &ldap_result_message)) {
				goto error_return;
			}
		} /* else if new_mod_count == mod_count then betxnpremod plugin did nothing */
			
		/*
		 * Update the ID to Entry index. 
		 * Note that id2entry_add replaces the entry, so the Entry ID 
		 * stays the same.
		 */
		retval = id2entry_add_ext( be, ec, &txn, 1, &cache_rc ); 
		if (DB_LOCK_DEADLOCK == retval)
		{
			/* Abort and re-try */
			continue;
		}
		if (0 != retval) {
			slapi_log_err(SLAPI_LOG_ERR, "ldbm_back_modify",
				"id2entry_add failed, err=%d %s\n",
				retval, (msg = dblayer_strerror( retval )) ? msg : "");
			if (LDBM_OS_ERR_IS_DISKFULL(retval)) disk_full = 1;
			MOD_SET_ERROR(ldap_result_code, LDAP_OPERATIONS_ERROR, retry_count);
			goto error_return;
		}
		retval = index_add_mods( be, mods, e, ec, &txn );
		if (DB_LOCK_DEADLOCK == retval)
		{
			/* Abort and re-try */
			continue;
		}
		if (0 != retval) {
			slapi_log_err(SLAPI_LOG_ERR, "ldbm_back_modify",
				"index_add_mods failed, err=%d %s\n",
				retval, (msg = dblayer_strerror( retval )) ? msg : "");
			if (LDBM_OS_ERR_IS_DISKFULL(retval)) disk_full = 1;
			MOD_SET_ERROR(ldap_result_code, LDAP_OPERATIONS_ERROR, retry_count);
			goto error_return;
		}
		/*
		 * Remove the old entry from the Virtual List View indexes.
		 * Add the new entry to the Virtual List View indexes.
		 * If the entry is ruv, no need to update vlv.
		 */
		if (!is_ruv) {
			retval= vlv_update_all_indexes(&txn, be, pb, e, ec);
			if (DB_LOCK_DEADLOCK == retval)
			{
				/* Abort and re-try */
				continue;
			}
			if (0 != retval) {
				slapi_log_err(SLAPI_LOG_ERR, "ldbm_back_modify",
					"vlv_update_index failed, err=%d %s\n",
					retval, (msg = dblayer_strerror( retval )) ? msg : "");
				if (LDBM_OS_ERR_IS_DISKFULL(retval)) disk_full = 1;
				MOD_SET_ERROR(ldap_result_code, 
							  LDAP_OPERATIONS_ERROR, retry_count);
				goto error_return;
			}

		}

		if (!is_ruv && !is_fixup_operation && !NO_RUV_UPDATE(li)) {
			ruv_c_init = ldbm_txn_ruv_modify_context( pb, &ruv_c );
			if (-1 == ruv_c_init) {
				slapi_log_err(SLAPI_LOG_ERR, "ldbm_back_modify",
					"ldbm_txn_ruv_modify_context failed to construct RUV modify context\n");
				ldap_result_code= LDAP_OPERATIONS_ERROR;
				retval = 0;
				goto error_return;
			}
		}

		if (ruv_c_init) {
			retval = modify_update_all( be, pb, &ruv_c, &txn );
			if (DB_LOCK_DEADLOCK == retval) {
				/* Abort and re-try */
				continue;
			}
			if (0 != retval) {
				slapi_log_err(SLAPI_LOG_ERR, "ldbm_back_modify",
					"modify_update_all failed, err=%d %s\n", retval,
					(msg = dblayer_strerror( retval )) ? msg : "");
				if (LDBM_OS_ERR_IS_DISKFULL(retval))
					disk_full = 1;
				ldap_result_code= LDAP_OPERATIONS_ERROR;
				goto error_return;
			}
		}

		if (0 == retval) {
			break;
		}
	}
	if (retry_count == RETRY_TIMES) {
		slapi_log_err(SLAPI_LOG_ERR, "ldbm_back_modify",
			"Retry count exceeded in modify\n");
	   	ldap_result_code= LDAP_BUSY;
		goto error_return;
	}

	if (ruv_c_init) {
		if (modify_switch_entries(&ruv_c, be) != 0 ) {
			ldap_result_code= LDAP_OPERATIONS_ERROR;
			slapi_log_err(SLAPI_LOG_ERR, "ldbm_back_modify",
				"modify_switch_entries failed\n");
			goto error_return;
		}
	}
	
	if (cache_replace( &inst->inst_cache, e, ec ) != 0 ) {
		MOD_SET_ERROR(ldap_result_code, LDAP_OPERATIONS_ERROR, retry_count);
		goto error_return;
	}
	/* e uncached */
	/* we must return both e (which has been deleted) and new entry ec to cache */
	/* cache_replace removes e from the cache hash tables */
	cache_unlock_entry( &inst->inst_cache, e );
	CACHE_RETURN( &inst->inst_cache, &e );
	/* lock new entry in cache to prevent usage until we are complete */
	cache_lock_entry( &inst->inst_cache, ec );
	ec_locked = 1;
	postentry = slapi_entry_dup( ec->ep_entry );
	slapi_pblock_set( pb, SLAPI_ENTRY_POST_OP, postentry );

	/* invalidate virtual cache */
	ec->ep_entry->e_virtual_watermark = 0;

	/* 
	 * LP Fix of crash when the commit will fail:
	 * If the commit fail, the common error path will
	 * try to unlock the entry again and crash (PR_ASSERT
	 * in debug mode.
	 * By just setting e to NULL, we avoid this. It's OK since
	 * we don't use e after that in the normal case.
	 */
	e = NULL;
	
	/* call the transaction post modify plugins just before the commit */
	if ((retval = plugin_call_plugins(pb, SLAPI_PLUGIN_BE_TXN_POST_MODIFY_FN))) {
		slapi_log_err(SLAPI_LOG_TRACE, "ldbm_back_modify",
			"SLAPI_PLUGIN_BE_TXN_POST_MODIFY_FN plugin "
			"returned error code %d\n", retval );
		if (!ldap_result_code) {
			slapi_pblock_get(pb, SLAPI_RESULT_CODE, &ldap_result_code);
		}
		if (!opreturn) {
			slapi_pblock_get(pb, SLAPI_PLUGIN_OPRETURN, &opreturn);
		}
		if (!opreturn) {
			slapi_pblock_set(pb, SLAPI_PLUGIN_OPRETURN, ldap_result_code ? &ldap_result_code : &retval);
		}
		slapi_pblock_get(pb, SLAPI_PB_RESULT_TEXT, &ldap_result_message);
		goto error_return;
	}

	/* Release SERIAL LOCK */
	retval = dblayer_txn_commit(be, &txn);
	/* after commit - txn is no longer valid - replace SLAPI_TXN with parent */
	slapi_pblock_set(pb, SLAPI_TXN, parent_txn);
	if (0 != retval) {
		if (LDBM_OS_ERR_IS_DISKFULL(retval)) disk_full = 1;
		ldap_result_code= LDAP_OPERATIONS_ERROR;
		goto error_return;
	}

	rc= 0;
	goto common_return;

error_return:
	if ( postentry != NULL ) 
	{
		slapi_entry_free( postentry );
		postentry = NULL;
		slapi_pblock_set( pb, SLAPI_ENTRY_POST_OP, NULL );
	}
	if (retval == DB_RUNRECOVERY) {
	  dblayer_remember_disk_filled(li);
	  ldbm_nasty("ldbm_back_modify","Modify",81,retval);
	  disk_full = 1;
	}

	if (disk_full) {
	    rc= return_on_disk_full(li);
	} else {
		if (txn.back_txn_txn && (txn.back_txn_txn != parent_txn)) {
			/* make sure SLAPI_RESULT_CODE and SLAPI_PLUGIN_OPRETURN are set */
			int val = 0;
			slapi_pblock_get(pb, SLAPI_RESULT_CODE, &val);
			if (!val) {
				if (!ldap_result_code) {
					ldap_result_code = LDAP_OPERATIONS_ERROR;
				}
				slapi_pblock_set(pb, SLAPI_RESULT_CODE, &ldap_result_code);
			}
			slapi_pblock_get( pb, SLAPI_PLUGIN_OPRETURN, &val );
			if (!val) {
				opreturn = -1;
				slapi_pblock_set( pb, SLAPI_PLUGIN_OPRETURN, &opreturn );
			}
			/* call the transaction post modify plugins just before the abort */
			/* plugins called before abort should check for the OPRETURN or RESULT_CODE
			   and skip processing if they don't want do anything - some plugins that
			   keep track of a counter (usn, dna) may want to "rollback" the counter
			   in this case */
			if ((retval = plugin_call_plugins(pb, SLAPI_PLUGIN_BE_TXN_POST_MODIFY_FN))) {
				slapi_log_err(SLAPI_LOG_TRACE, "ldbm_back_modify",
					"SLAPI_PLUGIN_BE_TXN_POST_MODIFY_FN plugin returned error code %d\n", retval );
				slapi_pblock_get(pb, SLAPI_RESULT_CODE, &ldap_result_code);
				slapi_pblock_get(pb, SLAPI_PB_RESULT_TEXT, &ldap_result_message);
				slapi_pblock_get(pb, SLAPI_PLUGIN_OPRETURN, &opreturn);
				if (!opreturn) {
					slapi_pblock_set(pb, SLAPI_PLUGIN_OPRETURN, ldap_result_code ? &ldap_result_code : &retval);
				}
			}

			/* It is safer not to abort when the transaction is not started. */
			/* Release SERIAL LOCK */
			dblayer_txn_abort(be, &txn); /* abort crashes in case disk full */
			/* txn is no longer valid - reset the txn pointer to the parent */
			slapi_pblock_set(pb, SLAPI_TXN, parent_txn);
		}
		if (!not_an_error) {
			rc = SLAPI_FAIL_GENERAL;
		}
	}

	/* if ec is in cache, remove it, then add back e if we still have it */
	if (inst && cache_is_in_cache(&inst->inst_cache, ec)) {
		CACHE_REMOVE( &inst->inst_cache, ec );
		/* if ec was in cache, e was not - add back e */
		if (e) {
			if (CACHE_ADD( &inst->inst_cache, e, NULL ) < 0) {
				slapi_log_err(SLAPI_LOG_CACHE, "ldbm_back_modify", "CACHE_ADD %s failed\n",
				              slapi_entry_get_dn(e->ep_entry));
			}
		}
	}

common_return:
	slapi_mods_done(&smods);
	
	if (inst) {
		if (ec_locked || cache_is_in_cache(&inst->inst_cache, ec)) {
			cache_unlock_entry(&inst->inst_cache, ec);
		} else if (e) {
			/* if ec was not in cache, cache_replace was not done.
			 * i.e., e was not unlocked. */
			cache_unlock_entry(&inst->inst_cache, e);
			CACHE_RETURN(&inst->inst_cache, &e);
		}
		CACHE_RETURN(&inst->inst_cache, &ec);
		if (inst->inst_ref_count) {
			slapi_counter_decrement(inst->inst_ref_count);
		}
	}

	/* result code could be used in the bepost plugin functions. */
	slapi_pblock_set(pb, SLAPI_RESULT_CODE, &ldap_result_code);

	/* The bepostop is called even if the operation fails. */
	if (!disk_full)
		plugin_call_plugins (pb, SLAPI_PLUGIN_BE_POST_MODIFY_FN);

	if (ruv_c_init) {
		modify_term(&ruv_c, be);
	}

	if (ldap_result_code == -1) {
		/* Reset to LDAP_NO_SUCH_OBJECT*/
		ldap_result_code = LDAP_NO_SUCH_OBJECT;
		slapi_pblock_set(pb, SLAPI_RESULT_CODE, &ldap_result_code);
	} else {
		if (not_an_error) {
			/* This is mainly used by urp.  Solved conflict is not an error.
			 * And we don't want the supplier to halt sending the updates. */
			ldap_result_code = LDAP_SUCCESS;
		}
		if (!result_sent) {
			/* result is already sent in find_entry. */
			slapi_send_ldap_result( pb, ldap_result_code, NULL, ldap_result_message, 0, NULL );
		}
	}

	/* free our backups */
	ldap_mods_free(mods_original, 1);
	backentry_free(&original_entry);
	backentry_free(&tmpentry);
	slapi_ch_free_string(&errbuf);

	return rc;
}
示例#7
0
/**
   Apply the mods to the ec entry.  Check for syntax, schema problems.
   Check for abandon.

   Return code:
   -1 - error - result code and message are set appropriately
   0 - successfully applied and checked
   1 - not an error - no mods to apply or op abandoned
 */
static int
modify_apply_check_expand(
	Slapi_PBlock *pb,
	Slapi_Operation *operation,
	LDAPMod **mods, /* list of mods to apply */
	struct backentry *e, /* original "before" entry */
	struct backentry *ec, /* "after" entry with mods applied */
	Slapi_Entry **postentry,
	int *ldap_result_code,
	char **ldap_result_message
)
{
	int rc = 0;
	int i;
	int repl_op;
	int change_entry = 0;
	Slapi_Mods smods = {0};
	CSN *csn = operation_get_csn(operation);

	slapi_pblock_get (pb, SLAPI_IS_REPLICATED_OPERATION, &repl_op);
	slapi_mods_init_byref( &smods, mods );

	if ( (change_entry = mods_have_effect (ec->ep_entry, &smods)) ) {
		*ldap_result_code = entry_apply_mods_wsi(ec->ep_entry, &smods, csn,
												 operation_is_flag_set(operation, OP_FLAG_REPLICATED));
		/*
		 * XXXmcs: it would be nice to get back an error message from
		 * the above call so we could pass it along to the client, e.g.,
		 * "duplicate value for attribute givenName."
		 */
	} else {
		Slapi_Entry *epostop = NULL;
		/* If the entry was not actually changed, we still need to
		 * set the SLAPI_ENTRY_POST_OP field in the pblock (post-op
		 * plugins expect that field to be present for all modify
		 * operations that return LDAP_SUCCESS).
		 */
		slapi_pblock_get ( pb, SLAPI_ENTRY_POST_OP, &epostop );
		slapi_entry_free ( epostop ); /* free existing one, if any */
		slapi_pblock_set ( pb, SLAPI_ENTRY_POST_OP, slapi_entry_dup( e->ep_entry ) );
		*postentry = NULL; /* to avoid free in main error cleanup code */
	}
	if ( !change_entry || *ldap_result_code != 0 ) {
		/* change_entry == 0 is not an error just a no-op */
		rc = change_entry ? -1 : 1;
		goto done;
	}

	/*
	 * If the objectClass attribute type was modified in any way, expand
	 * the objectClass values to reflect the inheritance hierarchy.
	 */
	for ( i = 0; mods && mods[i]; ++i ) {
		if ( 0 == strcasecmp( SLAPI_ATTR_OBJECTCLASS, mods[i]->mod_type )) {
			slapi_schema_expand_objectclasses( ec->ep_entry );
			break;
		}
	}

	/*
	 * We are about to pass the last abandon test, so from now on we are
	 * committed to finish this operation. Set status to "will complete"
	 * before we make our last abandon check to avoid race conditions in
	 * the code that processes abandon operations.
	 */
	operation->o_status = SLAPI_OP_STATUS_WILL_COMPLETE;
	if ( slapi_op_abandoned( pb ) ) {
		rc = 1;
		goto done;
	}

	/* multimaster replication can result in a schema violation,
	 * although the individual operations on each master were valid
	 * It is too late to resolve this. But we can check schema and
	 * add a replication conflict attribute.
	 */
	/* check that the entry still obeys the schema */
	if ((operation_is_flag_set(operation,OP_FLAG_ACTION_SCHEMA_CHECK)) &&
			slapi_entry_schema_check_ext( pb, ec->ep_entry, 1 ) != 0 ) {
		if(repl_op){
			Slapi_Attr *attr;
			Slapi_Mods smods;
			LDAPMod **lmods;
			if (slapi_entry_attr_find (ec->ep_entry, ATTR_NSDS5_REPLCONFLICT, &attr) == 0)
			{
				/* add value */ 
				Slapi_Value *val = slapi_value_new_string("Schema violation");
				slapi_attr_add_value(attr,val);
				slapi_value_free(&val);
			} else {
				/* Add new attribute */
				slapi_entry_add_string (ec->ep_entry, ATTR_NSDS5_REPLCONFLICT, "Schema violation");
			}
			/* the replconflict attribute is indexed and the index is built from the mods,
			 * so we need to extend the mods */
			slapi_pblock_get(pb, SLAPI_MODIFY_MODS, &lmods);
			slapi_mods_init_passin(&smods, lmods);
			slapi_mods_add_string (&smods, LDAP_MOD_ADD, ATTR_NSDS5_REPLCONFLICT, "Schema violation");
			lmods = slapi_mods_get_ldapmods_passout(&smods);
			slapi_pblock_set(pb, SLAPI_MODIFY_MODS, lmods);
			slapi_mods_done(&smods);
			
		} else {
			*ldap_result_code = LDAP_OBJECT_CLASS_VIOLATION;
			slapi_pblock_get(pb, SLAPI_PB_RESULT_TEXT, ldap_result_message);
			rc = -1;
			goto done;
		}
	}

	if(!repl_op){
		/* check attribute syntax for the new values */
		if (slapi_mods_syntax_check(pb, mods, 0) != 0) {
			*ldap_result_code = LDAP_INVALID_SYNTAX;
			slapi_pblock_get(pb, SLAPI_PB_RESULT_TEXT, ldap_result_message);
			rc = -1;
			goto done;
		}

		/*
		 * make sure the entry contains all values in the RDN.
		 * if not, the modification must have removed them.
		 */
		if ( ! slapi_entry_rdn_values_present( ec->ep_entry ) ) {
			*ldap_result_code= LDAP_NOT_ALLOWED_ON_RDN;
			rc = -1;
			goto done;
		}
	}

done:
	slapi_mods_done( &smods );

	return rc;
}
示例#8
0
文件: psearch.c 项目: leto/389-ds
/*
 * Check if there are any persistent searches.  If so,
 * the check to see if the chgtype is one of those the
 * client is interested in.  If so, then check to see if
 * the entry matches any of the filters the searches.
 * If so, then enqueue the entry on that persistent search's
 * ps_entryqueue and signal it to wake up and send the entry.
 *
 * Note that if eprev is NULL we assume that the entry's DN
 * was not changed by the op. that called this function.  If
 * chgnum is 0 it is unknown so we won't ever send it to a
 * client in the EntryChangeNotification control.
 */
void
ps_service_persistent_searches( Slapi_Entry *e, Slapi_Entry *eprev, ber_int_t chgtype,
	ber_int_t chgnum )
{
	LDAPControl *ctrl = NULL;
	PSearch	*ps = NULL;
	PSEQNode *pe = NULL;
	int  matched = 0;
	const char *edn;

	if ( !PS_IS_INITIALIZED()) {
		return;
	}

	if ( NULL == e ) {
		/* For now, some backends such as the chaining backend do not provide a post-op entry */
		return;
	}

	PSL_LOCK_READ();
	edn = slapi_entry_get_dn_const(e);

	for ( ps = psearch_list ? psearch_list->pl_head : NULL; NULL != ps; ps = ps->ps_next ) {
		char *origbase = NULL;
		Slapi_DN *base = NULL;
		Slapi_Filter	*f;
		int		scope;

		/* Skip the node that doesn't meet the changetype,
		 * or is unable to use the change in ps_send_results()
		 */
		if (( ps->ps_changetypes & chgtype ) == 0 ||
				ps->ps_pblock->pb_op == NULL ||
				slapi_op_abandoned( ps->ps_pblock ) ) {
			continue;
		}

		slapi_log_error(SLAPI_LOG_CONNS, "Persistent Search",
						"conn=%" NSPRIu64 " op=%d entry %s with chgtype %d "
						"matches the ps changetype %d\n",
						ps->ps_pblock->pb_conn->c_connid,
						ps->ps_pblock->pb_op->o_opid,
						edn, chgtype, ps->ps_changetypes);

		slapi_pblock_get( ps->ps_pblock, SLAPI_SEARCH_FILTER, &f );
		slapi_pblock_get( ps->ps_pblock, SLAPI_ORIGINAL_TARGET_DN, &origbase );
		slapi_pblock_get( ps->ps_pblock, SLAPI_SEARCH_TARGET_SDN, &base );
		slapi_pblock_get( ps->ps_pblock, SLAPI_SEARCH_SCOPE, &scope );
		if (NULL == base) {
			base = slapi_sdn_new_dn_byref(origbase);
			slapi_pblock_set(ps->ps_pblock, SLAPI_SEARCH_TARGET_SDN, base);
		}

		/*
		 * See if the entry meets the scope and filter criteria.
		 * We cannot do the acl check here as this thread
		 * would then potentially clash with the ps_send_results()
		 * thread on the aclpb in ps->ps_pblock.
		 * By avoiding the acl check in this thread, and leaving all the acl
		 * checking to the ps_send_results() thread we avoid
		 * the ps_pblock contention problem.
		 * The lesson here is "Do not give multiple threads arbitary access
		 * to the same pblock" this kind of muti-threaded access
		 * to the same pblock must be done carefully--there is currently no
		 * generic satisfactory way to do this.
		*/
		if ( slapi_sdn_scope_test( slapi_entry_get_sdn_const(e), base, scope ) &&
			 slapi_vattr_filter_test( ps->ps_pblock, e, f, 0 /* verify_access */ ) == 0 ) {
			PSEQNode *pOldtail;

			/* The scope and the filter match - enqueue it */

			matched++;
			pe = (PSEQNode *)slapi_ch_calloc( 1, sizeof( PSEQNode ));
			pe->pe_entry = slapi_entry_dup( e );
			if ( ps->ps_send_entchg_controls ) {
				/* create_entrychange_control() is more
				 * expensive than slapi_dup_control()
				 */
				if ( ctrl == NULL ) {
					int rc;
					rc = create_entrychange_control( chgtype, chgnum,
							eprev ? slapi_entry_get_dn_const(eprev) : NULL,
							&ctrl );
					if ( rc != LDAP_SUCCESS ) {
		   				LDAPDebug( LDAP_DEBUG_ANY, "ps_service_persistent_searches:"
						" unable to create EntryChangeNotification control for"
						" entry \"%s\" -- control won't be sent.\n",
						slapi_entry_get_dn_const(e), 0, 0 );
					}
				}
				if ( ctrl ) {
					pe->pe_ctrls[0] = slapi_dup_control( ctrl );
				}
			}

			/* Put it on the end of the list for this pers search */
			PR_Lock( ps->ps_lock );
			pOldtail = ps->ps_eq_tail;
			ps->ps_eq_tail = pe;
			if ( NULL == ps->ps_eq_head ) {
				ps->ps_eq_head = ps->ps_eq_tail;
			}
			else {
				pOldtail->pe_next = ps->ps_eq_tail;
			}
			PR_Unlock( ps->ps_lock );
		}
	}

   	PSL_UNLOCK_READ();

	/* Were there any matches? */
	if ( matched ) {
		ldap_control_free( ctrl );
		/* Turn 'em loose */
		ps_wakeup_all();
		LDAPDebug( LDAP_DEBUG_TRACE, "ps_service_persistent_searches: enqueued entry "
			"\"%s\" on %d persistent search lists\n", slapi_entry_get_dn_const(e), matched, 0 );
	} else {
		LDAPDebug( LDAP_DEBUG_TRACE, "ps_service_persistent_searches: entry "
			"\"%s\" not enqueued on any persistent search lists\n", slapi_entry_get_dn_const(e), 0, 0 );
	}

}