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