Example #1
0
/*
 * Check if User Private Groups are enabled in given IPA domain
 * Returns: 0 - UPG are enabled
 *          1 - UPG are disabled
 *         -1 - some sort of error
 */
static int
ipa_winsync_upg_enabled(const Slapi_DN *ds_subtree)
{
    int ret = -1;
    int rc;
    char * dn = NULL;
    Slapi_Entry *entry = NULL;
    Slapi_Backend *be;
    const Slapi_DN *ds_suffix = NULL;
    Slapi_DN *sdn = NULL;
    const char *attrs_list[] = {IPA_WINSYNC_UPG_DEF_ATTR, 0};
    char * value = NULL;

    /* find ancestor base DN */
    be = slapi_be_select(ds_subtree);
    ds_suffix = slapi_be_getsuffix(be, 0);
    if (ds_suffix == NULL) {
        LOG_FATAL("Invalid DS subtree [%s]\n", slapi_sdn_get_dn(ds_subtree));
        goto done;
    }

    dn = slapi_ch_smprintf(IPA_WINSYNC_UPG_DEF_DN, slapi_sdn_get_dn(ds_suffix));

    if (!dn) {
        LOG_OOM();
        goto done;
    }

    sdn = slapi_sdn_new_dn_byref(dn);
    rc = slapi_search_internal_get_entry(sdn, (char **) attrs_list, &entry,
                                         ipa_winsync_get_plugin_identity());

    if (rc) {
        LOG("failed to retrieve UPG definition (%s) with rc %d\n", dn, rc);
        goto done;
    }

    value = slapi_entry_attr_get_charptr(entry, IPA_WINSYNC_UPG_DEF_ATTR);

    if (!value) {
        LOG("failed to read %s from UPG definition (%s)\n",
             IPA_WINSYNC_UPG_DEF_ATTR, dn);
        goto done;
    }

    if (strstr(value, IPA_WINSYNC_UPG_DEF_DISABLED) == NULL) {
        ret = 0;
    } else {
        ret = 1;
    }

done:
    slapi_ch_free_string(&dn);
    slapi_sdn_free(&sdn);
    slapi_ch_free_string(&value);
    slapi_entry_free(entry);

    return ret;
}
Example #2
0
int ipapwd_gen_checks(Slapi_PBlock *pb, char **errMesg,
                      struct ipapwd_krbcfg **config, int check_flags)
{
    int ret, ssf;
    int rc = LDAP_SUCCESS;
    Slapi_Backend *be;
    const Slapi_DN *psdn;
    Slapi_DN *sdn;
    char *dn = NULL;

    LOG_TRACE("=>\n");

#ifdef LDAP_EXTOP_PASSMOD_CONN_SECURE
    if (check_flags & IPAPWD_CHECK_CONN_SECURE) {
       /* Allow password modify on all connections with a Security Strength
        * Factor (SSF) higher than 1 */
        if (slapi_pblock_get(pb, SLAPI_OPERATION_SSF, &ssf) != 0) {
            LOG("Could not get SSF from connection\n");
            *errMesg = "Operation requires a secure connection.\n";
            rc = LDAP_OPERATIONS_ERROR;
            goto done;
        }

        if (ssf <= 1) {
            *errMesg = "Operation requires a secure connection.\n";
            rc = LDAP_CONFIDENTIALITY_REQUIRED;
            goto done;
        }
    }
#endif

    if (check_flags & IPAPWD_CHECK_DN) {
        /* check we have a valid DN in the pblock or just abort */
        ret = slapi_pblock_get(pb, SLAPI_TARGET_DN, &dn);
        if (ret) {
            LOG("Tried to change password for an invalid DN [%s]\n",
                dn ? dn : "<NULL>");
            *errMesg = "Invalid DN";
            rc = LDAP_OPERATIONS_ERROR;
            goto done;
        }
        sdn = slapi_sdn_new_dn_byref(dn);
        if (!sdn) {
            LOG_FATAL("Unable to convert dn to sdn %s", dn ? dn : "<NULL>");
            *errMesg = "Internal Error";
            rc = LDAP_OPERATIONS_ERROR;
            goto done;
        }
        be = slapi_be_select(sdn);
        slapi_sdn_free(&sdn);

        psdn = slapi_be_getsuffix(be, 0);
        if (!psdn) {
            *errMesg = "Invalid DN";
            rc = LDAP_OPERATIONS_ERROR;
            goto done;
        }
    }

    /* get the kerberos context and master key */
    *config = ipapwd_getConfig();
    if (NULL == *config) {
        LOG_FATAL("Error Retrieving Master Key");
        *errMesg = "Fatal Internal Error";
        rc = LDAP_OPERATIONS_ERROR;
    }

done:
    return rc;
}
Example #3
0
int
update_integrity(char **argv, Slapi_DN *origSDN,
                 char *newrDN, Slapi_DN *newsuperior, 
                 int logChanges)
{
    Slapi_PBlock *search_result_pb = NULL;
    Slapi_PBlock *mod_pb = slapi_pblock_new();
    Slapi_Entry  **search_entries = NULL;
    Slapi_DN *sdn = NULL;
    Slapi_Attr *attr = NULL;
    void *node = NULL;
    const char *origDN = slapi_sdn_get_dn(origSDN);
    const char *search_base = NULL;
    char *attrName = NULL;
    char *filter = NULL;
    char *attrs[2];
    int search_result;
    int nval = 0;
    int i, j;
    int rc;

    if ( argv == NULL ){
        slapi_log_error( SLAPI_LOG_FATAL, REFERINT_PLUGIN_SUBSYSTEM,
            "referint_postop required config file arguments missing\n" );
        rc = -1;
        goto free_and_return;
    } 
    /*
     *  For now, just putting attributes to keep integrity on in conf file,
     *  until resolve the other timing mode issue
     */
    search_result_pb = slapi_pblock_new();

    /* Search each namingContext in turn */
    for ( sdn = slapi_get_first_suffix( &node, 0 ); sdn != NULL;
          sdn = slapi_get_next_suffix( &node, 0 ))
    {
        Slapi_Backend *be = slapi_be_select(sdn);
        search_base = slapi_sdn_get_dn( sdn );

        for(i = 3; argv[i] != NULL; i++){
            filter = slapi_filter_sprintf("(%s=*%s%s)", argv[i], ESC_NEXT_VAL, origDN);
            if ( filter ) {
                /* Need only the current attribute and its subtypes */
                attrs[0] = argv[i];
                attrs[1] = NULL;

                /* Use new search API */
                slapi_pblock_init(search_result_pb);
                slapi_pblock_set(search_result_pb, SLAPI_BACKEND, be);
                slapi_search_internal_set_pb(search_result_pb, search_base, 
                    LDAP_SCOPE_SUBTREE, filter, attrs, 0 /* attrs only */,
                    NULL, NULL, referint_plugin_identity, 0);
                slapi_search_internal_pb(search_result_pb);
  
                slapi_pblock_get( search_result_pb, SLAPI_PLUGIN_INTOP_RESULT, &search_result);

                /* if search successfull then do integrity update */
                if(search_result == LDAP_SUCCESS)
                {
                    slapi_pblock_get(search_result_pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES,
                                     &search_entries);

                    for(j = 0; search_entries[j] != NULL; j++){
                        attr = NULL;
                        attrName = NULL;
                        /*
                         *  Loop over all the attributes of the entry and search
                         *  for the integrity attribute and its subtypes
                         */
                        for (slapi_entry_first_attr(search_entries[j], &attr); attr; 
                             slapi_entry_next_attr(search_entries[j], attr, &attr))
                        {
                            /*
                             *  Take into account only the subtypes of the attribute
                             *  in argv[i] having the necessary value  - origDN
                             */
                            slapi_attr_get_type(attr, &attrName);
                            if (slapi_attr_type_cmp(argv[i], attrName,
                                                    SLAPI_TYPE_CMP_SUBTYPE) == 0)
                            {
                                nval = 0;
                                slapi_attr_get_numvalues(attr, &nval);
                                /* 
                                 * We want to reduce the "modify" call as much as
                                 * possible. But if an entry contains 1000s of 
                                 * attributes which need to be updated by the 
                                 * referint plugin (e.g., a group containing 1000s 
                                 * of members), we want to avoid to allocate too 
                                 * many mods * in one "modify" call.
                                 * This is a compromise: If an attribute type has
                                 * more than 128 values, we update the attribute 
                                 * value one by one. Otherwise, update all values
                                 * in one "modify" call.
                                 */
                                if (nval > 128) {
                                    rc = _update_one_per_mod(
                                         slapi_entry_get_sdn(search_entries[j]),
                                         attr, attrName, origSDN, newrDN, 
                                         slapi_sdn_get_dn(newsuperior),
                                         mod_pb);
                                } else {
                                    rc = _update_all_per_mod(
                                         slapi_entry_get_sdn(search_entries[j]),
                                         attr, attrName, origSDN, newrDN, 
                                         slapi_sdn_get_dn(newsuperior),
                                         mod_pb);
                                }
                                /* Should we stop if one modify returns an error? */
                            }
                        }
                    }
                } else {
                    if (isFatalSearchError(search_result)){
                        slapi_log_error( SLAPI_LOG_FATAL, REFERINT_PLUGIN_SUBSYSTEM,
                            "update_integrity search (base=%s filter=%s) returned "
                            "error %d\n", search_base, filter, search_result);
                        rc = -1;
                        goto free_and_return;
                    }
                }
                slapi_ch_free_string(&filter);
            }
            slapi_free_search_results_internal(search_result_pb);
        }
    }
    /* if got here, then everything good rc = 0 */
    rc = 0;

free_and_return:
    /* free filter and search_results_pb */
    slapi_ch_free_string(&filter);

    slapi_pblock_destroy(mod_pb);
    if (search_result_pb) {
        slapi_free_search_results_internal(search_result_pb);
        slapi_pblock_destroy(search_result_pb);
    }
 
    return(rc);
}
Example #4
0
File: add.c Project: 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);
}
Example #5
0
static int
linked_attrs_add_backlinks_callback(Slapi_Entry *e, void *callback_data)
{
    int rc = 0;
    char *linkdn = slapi_entry_get_dn(e);
    struct configEntry *config = (struct configEntry *)callback_data;
    Slapi_PBlock *pb = slapi_pblock_new();
    int i = 0;
    char **targets = NULL;
    char *val[2];
    LDAPMod mod;
    LDAPMod *mods[2];

    /* Setup the modify operation.  Only the target will
     * change, so we only need to do this once. */
    val[0] = linkdn;
    val[1] = 0;

    mod.mod_op = LDAP_MOD_ADD;
    mod.mod_type = config->managedtype;
    mod.mod_values = val;

    mods[0] = &mod;
    mods[1] = 0;

    targets = slapi_entry_attr_get_charray(e, config->linktype);
    for (i = 0; targets && targets[i]; ++i) {
        char *targetdn = (char *)targets[i];
        int perform_update = 0;
        Slapi_DN *targetsdn = NULL;

        if (slapi_is_shutting_down()) {
            rc = -1;
            goto done;
        }

        targetsdn = slapi_sdn_new_normdn_byref(targetdn);
        if (config->scope) {
            /* Check if the target is within the scope. */
            perform_update = slapi_dn_issuffix(targetdn, config->scope);
        } else {
            /* Find out the root suffix that the linkdn is in
             * and see if the target is in the same backend. */
            Slapi_Backend *be = NULL;
            Slapi_DN *linksdn = slapi_sdn_new_normdn_byref(linkdn);

            if ((be = slapi_be_select(linksdn))) {
                perform_update = slapi_sdn_issuffix(targetsdn, slapi_be_getsuffix(be, 0));
            }

            slapi_sdn_free(&linksdn);
        }

        if (perform_update) {
            slapi_log_error(SLAPI_LOG_PLUGIN, LINK_PLUGIN_SUBSYSTEM,
                            "Adding backpointer (%s) in entry (%s)\n",
                            linkdn, targetdn);

            /* Perform the modify operation. */
            slapi_modify_internal_set_pb_ext(pb, targetsdn, mods, 0, 0,
                                             linked_attrs_get_plugin_id(), 0);
            slapi_modify_internal_pb(pb);

            /* Initialize the pblock so we can reuse it. */
            slapi_pblock_init(pb);
        }
        slapi_sdn_free(&targetsdn);
    }

done:
    slapi_ch_array_free(targets);
    slapi_pblock_destroy(pb);

    return rc;
}