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