Home | History | Annotate | Line # | Download | only in back-sql
modrdn.c revision 1.4
      1 /*	$NetBSD: modrdn.c,v 1.4 2025/09/05 21:16:31 christos Exp $	*/
      2 
      3 /* $OpenLDAP$ */
      4 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
      5  *
      6  * Copyright 1999-2024 The OpenLDAP Foundation.
      7  * Portions Copyright 1999 Dmitry Kovalev.
      8  * Portions Copyright 2002 Pierangelo Masarati.
      9  * All rights reserved.
     10  *
     11  * Redistribution and use in source and binary forms, with or without
     12  * modification, are permitted only as authorized by the OpenLDAP
     13  * Public License.
     14  *
     15  * A copy of this license is available in the file LICENSE in the
     16  * top-level directory of the distribution or, alternatively, at
     17  * <http://www.OpenLDAP.org/license.html>.
     18  */
     19 /* ACKNOWLEDGEMENTS:
     20  * This work was initially developed by Dmitry Kovalev for inclusion
     21  * by OpenLDAP Software.  Additional significant contributors include
     22  * Pierangelo Masarati.
     23  */
     24 
     25 #include <sys/cdefs.h>
     26 __RCSID("$NetBSD: modrdn.c,v 1.4 2025/09/05 21:16:31 christos Exp $");
     27 
     28 #include "portable.h"
     29 
     30 #include <stdio.h>
     31 #include <sys/types.h>
     32 #include "ac/string.h"
     33 
     34 #include "slap.h"
     35 #include "proto-sql.h"
     36 
     37 int
     38 backsql_modrdn( Operation *op, SlapReply *rs )
     39 {
     40 	backsql_info		*bi = (backsql_info*)op->o_bd->be_private;
     41 	SQLHDBC			dbh = SQL_NULL_HDBC;
     42 	SQLHSTMT		sth = SQL_NULL_HSTMT;
     43 	RETCODE			rc;
     44 	backsql_entryID		e_id = BACKSQL_ENTRYID_INIT,
     45 				n_id = BACKSQL_ENTRYID_INIT;
     46 	backsql_srch_info	bsi = { 0 };
     47 	backsql_oc_map_rec	*oc = NULL;
     48 	struct berval		pdn = BER_BVNULL, pndn = BER_BVNULL,
     49 				*new_pdn = NULL, *new_npdn = NULL,
     50 				realnew_dn = BER_BVNULL;
     51 	Entry			r = { 0 },
     52 				p = { 0 },
     53 				n = { 0 },
     54 				*e = NULL;
     55 	int			manageDSAit = get_manageDSAit( op );
     56 	struct berval		*newSuperior = op->oq_modrdn.rs_newSup;
     57 
     58 	Debug( LDAP_DEBUG_TRACE, "==>backsql_modrdn() renaming entry \"%s\", "
     59 			"newrdn=\"%s\", newSuperior=\"%s\"\n",
     60 			op->o_req_dn.bv_val, op->oq_modrdn.rs_newrdn.bv_val,
     61 			newSuperior ? newSuperior->bv_val : "(NULL)" );
     62 
     63 	rs->sr_err = backsql_get_db_conn( op, &dbh );
     64 	if ( rs->sr_err != LDAP_SUCCESS ) {
     65 		Debug( LDAP_DEBUG_TRACE, "   backsql_modrdn(): "
     66 			"could not get connection handle - exiting\n" );
     67 		rs->sr_text = ( rs->sr_err == LDAP_OTHER )
     68 			?  "SQL-backend error" : NULL;
     69 		e = NULL;
     70 		goto done;
     71 	}
     72 
     73 	bsi.bsi_e = &r;
     74 	rs->sr_err = backsql_init_search( &bsi, &op->o_req_ndn,
     75 			LDAP_SCOPE_BASE,
     76 			(time_t)(-1), NULL, dbh, op, rs,
     77 			slap_anlist_all_attributes,
     78 			( BACKSQL_ISF_MATCHED | BACKSQL_ISF_GET_ENTRY | BACKSQL_ISF_GET_OC ) );
     79 	switch ( rs->sr_err ) {
     80 	case LDAP_SUCCESS:
     81 		break;
     82 
     83 	case LDAP_REFERRAL:
     84 		if ( manageDSAit && !BER_BVISNULL( &bsi.bsi_e->e_nname ) &&
     85 				dn_match( &op->o_req_ndn, &bsi.bsi_e->e_nname ) )
     86 		{
     87 			rs->sr_err = LDAP_SUCCESS;
     88 			rs->sr_text = NULL;
     89 			rs->sr_matched = NULL;
     90 			if ( rs->sr_ref ) {
     91 				ber_bvarray_free( rs->sr_ref );
     92 				rs->sr_ref = NULL;
     93 			}
     94 			break;
     95 		}
     96 		e = &r;
     97 		/* fallthru */
     98 
     99 	default:
    100 		Debug( LDAP_DEBUG_TRACE, "backsql_modrdn(): "
    101 			"could not retrieve modrdnDN ID - no such entry\n" );
    102 		if ( !BER_BVISNULL( &r.e_nname ) ) {
    103 			/* FIXME: should always be true! */
    104 			e = &r;
    105 
    106 		} else {
    107 			e = NULL;
    108 		}
    109 		goto done;
    110 	}
    111 
    112 	Debug( LDAP_DEBUG_TRACE,
    113 		"   backsql_modrdn(): entry id=" BACKSQL_IDFMT "\n",
    114 		BACKSQL_IDARG(e_id.eid_id) );
    115 
    116 	if ( get_assert( op ) &&
    117 			( test_filter( op, &r, get_assertion( op ) )
    118 			  != LDAP_COMPARE_TRUE ) )
    119 	{
    120 		rs->sr_err = LDAP_ASSERTION_FAILED;
    121 		e = &r;
    122 		goto done;
    123 	}
    124 
    125 	if ( backsql_has_children( op, dbh, &op->o_req_ndn ) == LDAP_COMPARE_TRUE ) {
    126 		Debug( LDAP_DEBUG_TRACE, "   backsql_modrdn(): "
    127 			"entry \"%s\" has children\n",
    128 			op->o_req_dn.bv_val );
    129 		rs->sr_err = LDAP_NOT_ALLOWED_ON_NONLEAF;
    130 		rs->sr_text = "subtree rename not supported";
    131 		e = &r;
    132 		goto done;
    133 	}
    134 
    135 	/*
    136 	 * Check for entry access to target
    137 	 */
    138 	if ( !access_allowed( op, &r, slap_schema.si_ad_entry,
    139 				NULL, ACL_WRITE, NULL ) ) {
    140 		Debug( LDAP_DEBUG_TRACE, "   no access to entry\n" );
    141 		rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
    142 		goto done;
    143 	}
    144 
    145 	dnParent( &op->o_req_dn, &pdn );
    146 	dnParent( &op->o_req_ndn, &pndn );
    147 
    148 	/*
    149 	 * namingContext "" is not supported
    150 	 */
    151 	if ( BER_BVISEMPTY( &pdn ) ) {
    152 		Debug( LDAP_DEBUG_TRACE, "   backsql_modrdn(): "
    153 			"parent is \"\" - aborting\n" );
    154 		rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
    155 		rs->sr_text = "not allowed within namingContext";
    156 		e = NULL;
    157 		goto done;
    158 	}
    159 
    160 	/*
    161 	 * Check for children access to parent
    162 	 */
    163 	bsi.bsi_e = &p;
    164 	e_id = bsi.bsi_base_id;
    165 	memset( &bsi.bsi_base_id, 0, sizeof( bsi.bsi_base_id ) );
    166 	rs->sr_err = backsql_init_search( &bsi, &pndn,
    167 			LDAP_SCOPE_BASE,
    168 			(time_t)(-1), NULL, dbh, op, rs,
    169 			slap_anlist_all_attributes,
    170 			BACKSQL_ISF_GET_ENTRY );
    171 
    172 	Debug( LDAP_DEBUG_TRACE,
    173 		"   backsql_modrdn(): old parent entry id is " BACKSQL_IDFMT "\n",
    174 		BACKSQL_IDARG(bsi.bsi_base_id.eid_id) );
    175 
    176 	if ( rs->sr_err != LDAP_SUCCESS ) {
    177 		Debug( LDAP_DEBUG_TRACE, "backsql_modrdn(): "
    178 			"could not retrieve renameDN ID - no such entry\n" );
    179 		e = &p;
    180 		goto done;
    181 	}
    182 
    183 	if ( !access_allowed( op, &p, slap_schema.si_ad_children, NULL,
    184 			newSuperior ? ACL_WDEL : ACL_WRITE, NULL ) )
    185 	{
    186 		Debug( LDAP_DEBUG_TRACE, "   no access to parent\n" );
    187 		rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
    188 		goto done;
    189 	}
    190 
    191 	if ( newSuperior ) {
    192 		(void)backsql_free_entryID( &bsi.bsi_base_id, 0, op->o_tmpmemctx );
    193 
    194 		/*
    195 		 * namingContext "" is not supported
    196 		 */
    197 		if ( BER_BVISEMPTY( newSuperior ) ) {
    198 			Debug( LDAP_DEBUG_TRACE, "   backsql_modrdn(): "
    199 				"newSuperior is \"\" - aborting\n" );
    200 			rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
    201 			rs->sr_text = "not allowed within namingContext";
    202 			e = NULL;
    203 			goto done;
    204 		}
    205 
    206 		new_pdn = newSuperior;
    207 		new_npdn = op->oq_modrdn.rs_nnewSup;
    208 
    209 		/*
    210 		 * Check for children access to new parent
    211 		 */
    212 		bsi.bsi_e = &n;
    213 		rs->sr_err = backsql_init_search( &bsi, new_npdn,
    214 				LDAP_SCOPE_BASE,
    215 				(time_t)(-1), NULL, dbh, op, rs,
    216 				slap_anlist_all_attributes,
    217 				( BACKSQL_ISF_MATCHED | BACKSQL_ISF_GET_ENTRY ) );
    218 		if ( rs->sr_err != LDAP_SUCCESS ) {
    219 			Debug( LDAP_DEBUG_TRACE, "backsql_modrdn(): "
    220 				"could not retrieve renameDN ID - no such entry\n" );
    221 			e = &n;
    222 			goto done;
    223 		}
    224 
    225 		n_id = bsi.bsi_base_id;
    226 
    227 		Debug( LDAP_DEBUG_TRACE,
    228 			"   backsql_modrdn(): new parent entry id=" BACKSQL_IDFMT "\n",
    229 			BACKSQL_IDARG(n_id.eid_id) );
    230 
    231 		if ( !access_allowed( op, &n, slap_schema.si_ad_children,
    232 					NULL, ACL_WADD, NULL ) ) {
    233 			Debug( LDAP_DEBUG_TRACE, "   backsql_modrdn(): "
    234 					"no access to new parent \"%s\"\n",
    235 					new_pdn->bv_val );
    236 			rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
    237 			e = &n;
    238 			goto done;
    239 		}
    240 
    241 	} else {
    242 		n_id = bsi.bsi_base_id;
    243 		new_pdn = &pdn;
    244 		new_npdn = &pndn;
    245 	}
    246 
    247 	memset( &bsi.bsi_base_id, 0, sizeof( bsi.bsi_base_id ) );
    248 
    249 	if ( newSuperior && dn_match( &pndn, new_npdn ) ) {
    250 		Debug( LDAP_DEBUG_TRACE, "   backsql_modrdn(): "
    251 			"newSuperior is equal to old parent - ignored\n" );
    252 		newSuperior = NULL;
    253 	}
    254 
    255 	if ( newSuperior && dn_match( &op->o_req_ndn, new_npdn ) ) {
    256 		Debug( LDAP_DEBUG_TRACE, "   backsql_modrdn(): "
    257 			"newSuperior is equal to entry being moved "
    258 			"- aborting\n" );
    259 		rs->sr_err = LDAP_OTHER;
    260 		rs->sr_text = "newSuperior is equal to old DN";
    261 		e = &r;
    262 		goto done;
    263 	}
    264 
    265 	Debug( LDAP_DEBUG_TRACE, "   backsql_modrdn(): new entry dn is \"%s\"\n",
    266 			op->orr_newDN.bv_val );
    267 
    268 	realnew_dn = op->orr_newDN;
    269 	if ( backsql_api_dn2odbc( op, rs, &realnew_dn ) ) {
    270 		Debug( LDAP_DEBUG_TRACE, "   backsql_modrdn(\"%s\"): "
    271 			"backsql_api_dn2odbc(\"%s\") failed\n",
    272 			op->o_req_dn.bv_val, realnew_dn.bv_val );
    273 		SQLFreeStmt( sth, SQL_DROP );
    274 
    275 		rs->sr_text = "SQL-backend error";
    276 		rs->sr_err = LDAP_OTHER;
    277 		e = NULL;
    278 		goto done;
    279 	}
    280 
    281 	Debug( LDAP_DEBUG_TRACE, "   backsql_modrdn(): "
    282 		"executing renentry_stmt\n" );
    283 
    284 	rc = backsql_Prepare( dbh, &sth, bi->sql_renentry_stmt, 0 );
    285 	if ( rc != SQL_SUCCESS ) {
    286 		Debug( LDAP_DEBUG_TRACE,
    287 			"   backsql_modrdn(): "
    288 			"error preparing renentry_stmt\n" );
    289 		backsql_PrintErrors( bi->sql_db_env, dbh,
    290 				sth, rc );
    291 
    292 		rs->sr_text = "SQL-backend error";
    293 		rs->sr_err = LDAP_OTHER;
    294 		e = NULL;
    295 		goto done;
    296 	}
    297 
    298 	rc = backsql_BindParamBerVal( sth, 1, SQL_PARAM_INPUT, &realnew_dn );
    299 	if ( rc != SQL_SUCCESS ) {
    300 		Debug( LDAP_DEBUG_TRACE,
    301 			"   backsql_modrdn(): "
    302 			"error binding DN parameter for objectClass %s\n",
    303 			oc->bom_oc->soc_cname.bv_val );
    304 		backsql_PrintErrors( bi->sql_db_env, dbh,
    305 			sth, rc );
    306 		SQLFreeStmt( sth, SQL_DROP );
    307 
    308 		rs->sr_text = "SQL-backend error";
    309 		rs->sr_err = LDAP_OTHER;
    310 		e = NULL;
    311 		goto done;
    312 	}
    313 
    314 	rc = backsql_BindParamID( sth, 2, SQL_PARAM_INPUT, &n_id.eid_id );
    315 	if ( rc != SQL_SUCCESS ) {
    316 		Debug( LDAP_DEBUG_TRACE,
    317 			"   backsql_modrdn(): "
    318 			"error binding parent ID parameter for objectClass %s\n",
    319 			oc->bom_oc->soc_cname.bv_val );
    320 		backsql_PrintErrors( bi->sql_db_env, dbh,
    321 			sth, rc );
    322 		SQLFreeStmt( sth, SQL_DROP );
    323 
    324 		rs->sr_text = "SQL-backend error";
    325 		rs->sr_err = LDAP_OTHER;
    326 		e = NULL;
    327 		goto done;
    328 	}
    329 
    330 	rc = backsql_BindParamID( sth, 3, SQL_PARAM_INPUT, &e_id.eid_keyval );
    331 	if ( rc != SQL_SUCCESS ) {
    332 		Debug( LDAP_DEBUG_TRACE,
    333 			"   backsql_modrdn(): "
    334 			"error binding entry ID parameter for objectClass %s\n",
    335 			oc->bom_oc->soc_cname.bv_val );
    336 		backsql_PrintErrors( bi->sql_db_env, dbh,
    337 			sth, rc );
    338 		SQLFreeStmt( sth, SQL_DROP );
    339 
    340 		rs->sr_text = "SQL-backend error";
    341 		rs->sr_err = LDAP_OTHER;
    342 		e = NULL;
    343 		goto done;
    344 	}
    345 
    346 	rc = backsql_BindParamID( sth, 4, SQL_PARAM_INPUT, &e_id.eid_id );
    347 	if ( rc != SQL_SUCCESS ) {
    348 		Debug( LDAP_DEBUG_TRACE,
    349 			"   backsql_modrdn(): "
    350 			"error binding ID parameter for objectClass %s\n",
    351 			oc->bom_oc->soc_cname.bv_val );
    352 		backsql_PrintErrors( bi->sql_db_env, dbh,
    353 			sth, rc );
    354 		SQLFreeStmt( sth, SQL_DROP );
    355 
    356 		rs->sr_text = "SQL-backend error";
    357 		rs->sr_err = LDAP_OTHER;
    358 		e = NULL;
    359 		goto done;
    360 	}
    361 
    362 	rc = SQLExecute( sth );
    363 	if ( rc != SQL_SUCCESS ) {
    364 		Debug( LDAP_DEBUG_TRACE, "   backsql_modrdn(): "
    365 			"could not rename ldap_entries record\n" );
    366 		backsql_PrintErrors( bi->sql_db_env, dbh, sth, rc );
    367 		SQLFreeStmt( sth, SQL_DROP );
    368 		rs->sr_err = LDAP_OTHER;
    369 		rs->sr_text = "SQL-backend error";
    370 		e = NULL;
    371 		goto done;
    372 	}
    373 	SQLFreeStmt( sth, SQL_DROP );
    374 
    375 	slap_mods_opattrs( op, &op->orr_modlist, 1 );
    376 
    377 	assert( e_id.eid_oc != NULL );
    378 	oc = e_id.eid_oc;
    379 
    380 	if ( op->orr_modlist != NULL ) {
    381 		rs->sr_err = backsql_modify_internal( op, rs, dbh, oc, &e_id, op->orr_modlist );
    382 		slap_graduate_commit_csn( op );
    383 		if ( rs->sr_err != LDAP_SUCCESS ) {
    384 			e = &r;
    385 			goto done;
    386 		}
    387 	}
    388 
    389 	if ( BACKSQL_CHECK_SCHEMA( bi ) ) {
    390 		char		textbuf[ SLAP_TEXT_BUFLEN ] = { '\0' };
    391 
    392 		backsql_entry_clean( op, &r );
    393 		(void)backsql_free_entryID( &e_id, 0, op->o_tmpmemctx );
    394 
    395 		bsi.bsi_e = &r;
    396 		rs->sr_err = backsql_init_search( &bsi, &op->orr_nnewDN,
    397 				LDAP_SCOPE_BASE,
    398 				(time_t)(-1), NULL, dbh, op, rs,
    399 				slap_anlist_all_attributes,
    400 				( BACKSQL_ISF_MATCHED | BACKSQL_ISF_GET_ENTRY ) );
    401 		switch ( rs->sr_err ) {
    402 		case LDAP_SUCCESS:
    403 			break;
    404 
    405 		case LDAP_REFERRAL:
    406 			if ( manageDSAit && !BER_BVISNULL( &bsi.bsi_e->e_nname ) &&
    407 					dn_match( &op->orr_nnewDN, &bsi.bsi_e->e_nname ) )
    408 			{
    409 				rs->sr_err = LDAP_SUCCESS;
    410 				rs->sr_text = NULL;
    411 				rs->sr_matched = NULL;
    412 				if ( rs->sr_ref ) {
    413 					ber_bvarray_free( rs->sr_ref );
    414 					rs->sr_ref = NULL;
    415 				}
    416 				break;
    417 			}
    418 			e = &r;
    419 			/* fallthru */
    420 
    421 		default:
    422 			Debug( LDAP_DEBUG_TRACE, "backsql_modrdn(): "
    423 				"could not retrieve modrdnDN ID - no such entry\n" );
    424 			if ( !BER_BVISNULL( &r.e_nname ) ) {
    425 				/* FIXME: should always be true! */
    426 				e = &r;
    427 
    428 			} else {
    429 				e = NULL;
    430 			}
    431 			goto done;
    432 		}
    433 
    434 		e_id = bsi.bsi_base_id;
    435 
    436 		rs->sr_err = entry_schema_check( op, &r, NULL, 0, 0, NULL,
    437 			&rs->sr_text, textbuf, sizeof( textbuf ) );
    438 		if ( rs->sr_err != LDAP_SUCCESS ) {
    439 			Debug( LDAP_DEBUG_TRACE, "   backsql_modrdn(\"%s\"): "
    440 				"entry failed schema check -- aborting\n",
    441 				r.e_name.bv_val );
    442 			e = NULL;
    443 			goto done;
    444 		}
    445 	}
    446 
    447 done:;
    448 	if ( e != NULL ) {
    449 		if ( !access_allowed( op, e, slap_schema.si_ad_entry, NULL,
    450 					ACL_DISCLOSE, NULL ) )
    451 		{
    452 			rs->sr_err = LDAP_NO_SUCH_OBJECT;
    453 			rs->sr_text = NULL;
    454 			rs->sr_matched = NULL;
    455 			if ( rs->sr_ref ) {
    456 				ber_bvarray_free( rs->sr_ref );
    457 				rs->sr_ref = NULL;
    458 			}
    459 		}
    460 	}
    461 
    462 	/*
    463 	 * Commit only if all operations succeed
    464 	 */
    465 	if ( sth != SQL_NULL_HSTMT ) {
    466 		SQLUSMALLINT	CompletionType = SQL_ROLLBACK;
    467 
    468 		if ( rs->sr_err == LDAP_SUCCESS && !op->o_noop ) {
    469 			CompletionType = SQL_COMMIT;
    470 		}
    471 
    472 		SQLTransact( SQL_NULL_HENV, dbh, CompletionType );
    473 	}
    474 
    475 	if ( op->o_noop && rs->sr_err == LDAP_SUCCESS ) {
    476 		rs->sr_err = LDAP_X_NO_OPERATION;
    477 	}
    478 
    479 	send_ldap_result( op, rs );
    480 	slap_graduate_commit_csn( op );
    481 
    482 	if ( !BER_BVISNULL( &realnew_dn ) && realnew_dn.bv_val != op->orr_newDN.bv_val ) {
    483 		ch_free( realnew_dn.bv_val );
    484 	}
    485 
    486 	if ( !BER_BVISNULL( &e_id.eid_ndn ) ) {
    487 		(void)backsql_free_entryID( &e_id, 0, op->o_tmpmemctx );
    488 	}
    489 
    490 	if ( !BER_BVISNULL( &n_id.eid_ndn ) ) {
    491 		(void)backsql_free_entryID( &n_id, 0, op->o_tmpmemctx );
    492 	}
    493 
    494 	if ( !BER_BVISNULL( &r.e_nname ) ) {
    495 		backsql_entry_clean( op, &r );
    496 	}
    497 
    498 	if ( !BER_BVISNULL( &p.e_nname ) ) {
    499 		backsql_entry_clean( op, &p );
    500 	}
    501 
    502 	if ( !BER_BVISNULL( &n.e_nname ) ) {
    503 		backsql_entry_clean( op, &n );
    504 	}
    505 
    506 	if ( rs->sr_ref ) {
    507 		ber_bvarray_free( rs->sr_ref );
    508 		rs->sr_ref = NULL;
    509 	}
    510 
    511 	Debug( LDAP_DEBUG_TRACE, "<==backsql_modrdn()\n" );
    512 
    513 	return rs->sr_err;
    514 }
    515 
    516