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