xref: /netbsd-src/external/bsd/openldap/dist/servers/slapd/back-ndb/add.cpp (revision e670fd5c413e99c2f6a37901bb21c537fcd322d2)
14e6df137Slukem /* add.cpp - ldap NDB back-end add routine */
2d11b170bStron /* $OpenLDAP$ */
34e6df137Slukem /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
44e6df137Slukem  *
5*e670fd5cSchristos  * Copyright 2008-2021 The OpenLDAP Foundation.
64e6df137Slukem  * All rights reserved.
74e6df137Slukem  *
84e6df137Slukem  * Redistribution and use in source and binary forms, with or without
94e6df137Slukem  * modification, are permitted only as authorized by the OpenLDAP
104e6df137Slukem  * Public License.
114e6df137Slukem  *
124e6df137Slukem  * A copy of this license is available in the file LICENSE in the
134e6df137Slukem  * top-level directory of the distribution or, alternatively, at
144e6df137Slukem  * <http://www.OpenLDAP.org/license.html>.
154e6df137Slukem  */
164e6df137Slukem /* ACKNOWLEDGEMENTS:
174e6df137Slukem  * This work was initially developed by Howard Chu for inclusion
184e6df137Slukem  * in OpenLDAP Software. This work was sponsored by MySQL.
194e6df137Slukem  */
204e6df137Slukem 
214e6df137Slukem #include "portable.h"
224e6df137Slukem 
234e6df137Slukem #include <stdio.h>
244e6df137Slukem #include <ac/string.h>
254e6df137Slukem 
264e6df137Slukem #include "back-ndb.h"
274e6df137Slukem 
284e6df137Slukem extern "C" int
ndb_back_add(Operation * op,SlapReply * rs)294e6df137Slukem ndb_back_add(Operation *op, SlapReply *rs )
304e6df137Slukem {
314e6df137Slukem 	struct ndb_info *ni = (struct ndb_info *) op->o_bd->be_private;
324e6df137Slukem 	Entry		p = {0};
334e6df137Slukem 	Attribute	poc;
344e6df137Slukem 	char textbuf[SLAP_TEXT_BUFLEN];
354e6df137Slukem 	size_t textlen = sizeof textbuf;
364e6df137Slukem 	AttributeDescription *children = slap_schema.si_ad_children;
374e6df137Slukem 	AttributeDescription *entry = slap_schema.si_ad_entry;
384e6df137Slukem 	NdbArgs NA;
394e6df137Slukem 	NdbRdns rdns;
404e6df137Slukem 	struct berval matched;
414e6df137Slukem 	struct berval pdn, pndn;
424e6df137Slukem 
434e6df137Slukem 	int		num_retries = 0;
444e6df137Slukem 	int		success;
454e6df137Slukem 
464e6df137Slukem 	LDAPControl **postread_ctrl = NULL;
474e6df137Slukem 	LDAPControl *ctrls[SLAP_MAX_RESPONSE_CONTROLS];
484e6df137Slukem 	int num_ctrls = 0;
494e6df137Slukem 
504e6df137Slukem 	Debug(LDAP_DEBUG_ARGS, "==> " LDAP_XSTRING(ndb_back_add) ": %s\n",
514e6df137Slukem 		op->oq_add.rs_e->e_name.bv_val, 0, 0);
524e6df137Slukem 
534e6df137Slukem 	ctrls[num_ctrls] = 0;
54d11b170bStron 	NA.txn = NULL;
554e6df137Slukem 
564e6df137Slukem 	/* check entry's schema */
574e6df137Slukem 	rs->sr_err = entry_schema_check( op, op->oq_add.rs_e, NULL,
584e6df137Slukem 		get_relax(op), 1, NULL, &rs->sr_text, textbuf, textlen );
594e6df137Slukem 	if ( rs->sr_err != LDAP_SUCCESS ) {
604e6df137Slukem 		Debug( LDAP_DEBUG_TRACE,
614e6df137Slukem 			LDAP_XSTRING(ndb_back_add) ": entry failed schema check: "
624e6df137Slukem 			"%s (%d)\n", rs->sr_text, rs->sr_err, 0 );
634e6df137Slukem 		goto return_results;
644e6df137Slukem 	}
654e6df137Slukem 
664e6df137Slukem 	/* add opattrs to shadow as well, only missing attrs will actually
674e6df137Slukem 	 * be added; helps compatibility with older OL versions */
684e6df137Slukem 	rs->sr_err = slap_add_opattrs( op, &rs->sr_text, textbuf, textlen, 1 );
694e6df137Slukem 	if ( rs->sr_err != LDAP_SUCCESS ) {
704e6df137Slukem 		Debug( LDAP_DEBUG_TRACE,
714e6df137Slukem 			LDAP_XSTRING(ndb_back_add) ": entry failed op attrs add: "
724e6df137Slukem 			"%s (%d)\n", rs->sr_text, rs->sr_err, 0 );
734e6df137Slukem 		goto return_results;
744e6df137Slukem 	}
754e6df137Slukem 
764e6df137Slukem 	/* Get our NDB handle */
774e6df137Slukem 	rs->sr_err = ndb_thread_handle( op, &NA.ndb );
784e6df137Slukem 
794e6df137Slukem 	/*
804e6df137Slukem 	 * Get the parent dn and see if the corresponding entry exists.
814e6df137Slukem 	 */
824e6df137Slukem 	if ( be_issuffix( op->o_bd, &op->oq_add.rs_e->e_nname ) ) {
834e6df137Slukem 		pdn = slap_empty_bv;
844e6df137Slukem 		pndn = slap_empty_bv;
854e6df137Slukem 	} else {
864e6df137Slukem 		dnParent( &op->ora_e->e_name, &pdn );
874e6df137Slukem 		dnParent( &op->ora_e->e_nname, &pndn );
884e6df137Slukem 	}
894e6df137Slukem 	p.e_name = op->ora_e->e_name;
904e6df137Slukem 	p.e_nname = op->ora_e->e_nname;
914e6df137Slukem 
924e6df137Slukem 	op->ora_e->e_id = NOID;
934e6df137Slukem 	rdns.nr_num = 0;
944e6df137Slukem 	NA.rdns = &rdns;
954e6df137Slukem 
964e6df137Slukem 	if( 0 ) {
974e6df137Slukem retry:	/* transaction retry */
984e6df137Slukem 		NA.txn->close();
994e6df137Slukem 		NA.txn = NULL;
1004e6df137Slukem 		if ( op->o_abandon ) {
1014e6df137Slukem 			rs->sr_err = SLAPD_ABANDON;
1024e6df137Slukem 			goto return_results;
1034e6df137Slukem 		}
1044e6df137Slukem 		ndb_trans_backoff( ++num_retries );
1054e6df137Slukem 	}
1064e6df137Slukem 
1074e6df137Slukem 	NA.txn = NA.ndb->startTransaction();
1084e6df137Slukem 	rs->sr_text = NULL;
1094e6df137Slukem 	if( !NA.txn ) {
1104e6df137Slukem 		Debug( LDAP_DEBUG_TRACE,
1114e6df137Slukem 			LDAP_XSTRING(ndb_back_add) ": startTransaction failed: %s (%d)\n",
1124e6df137Slukem 			NA.ndb->getNdbError().message, NA.ndb->getNdbError().code, 0 );
1134e6df137Slukem 		rs->sr_err = LDAP_OTHER;
1144e6df137Slukem 		rs->sr_text = "internal error";
1154e6df137Slukem 		goto return_results;
1164e6df137Slukem 	}
1174e6df137Slukem 
1184e6df137Slukem 	/* get entry or parent */
1194e6df137Slukem 	NA.e = &p;
1204e6df137Slukem 	NA.ocs = NULL;
1214e6df137Slukem 	rs->sr_err = ndb_entry_get_info( op, &NA, 0, &matched );
1224e6df137Slukem 	switch( rs->sr_err ) {
1234e6df137Slukem 	case 0:
1244e6df137Slukem 		rs->sr_err = LDAP_ALREADY_EXISTS;
1254e6df137Slukem 		goto return_results;
1264e6df137Slukem 	case LDAP_NO_SUCH_OBJECT:
1274e6df137Slukem 		break;
1284e6df137Slukem #if 0
1294e6df137Slukem 	case DB_LOCK_DEADLOCK:
1304e6df137Slukem 	case DB_LOCK_NOTGRANTED:
1314e6df137Slukem 		goto retry;
1324e6df137Slukem #endif
1334e6df137Slukem 	case LDAP_BUSY:
1344e6df137Slukem 		rs->sr_text = "ldap server busy";
1354e6df137Slukem 		goto return_results;
1364e6df137Slukem 	default:
1374e6df137Slukem 		rs->sr_err = LDAP_OTHER;
1384e6df137Slukem 		rs->sr_text = "internal error";
1394e6df137Slukem 		goto return_results;
1404e6df137Slukem 	}
1414e6df137Slukem 
1424e6df137Slukem 	if ( NA.ocs ) {
1434e6df137Slukem 		int i;
1444e6df137Slukem 		for ( i=0; !BER_BVISNULL( &NA.ocs[i] ); i++ );
1454e6df137Slukem 		poc.a_numvals = i;
1464e6df137Slukem 		poc.a_desc = slap_schema.si_ad_objectClass;
1474e6df137Slukem 		poc.a_vals = NA.ocs;
1484e6df137Slukem 		poc.a_nvals = poc.a_vals;
1494e6df137Slukem 		poc.a_next = NULL;
1504e6df137Slukem 		p.e_attrs = &poc;
1514e6df137Slukem 	}
1524e6df137Slukem 
1534e6df137Slukem 	if ( ber_bvstrcasecmp( &pndn, &matched ) ) {
1544e6df137Slukem 		rs->sr_matched = matched.bv_val;
1554e6df137Slukem 		Debug( LDAP_DEBUG_TRACE,
1564e6df137Slukem 			LDAP_XSTRING(ndb_back_add) ": parent "
1574e6df137Slukem 			"does not exist\n", 0, 0, 0 );
1584e6df137Slukem 
1594e6df137Slukem 		rs->sr_text = "parent does not exist";
1604e6df137Slukem 		rs->sr_err = LDAP_NO_SUCH_OBJECT;
1614e6df137Slukem 		if ( p.e_attrs && is_entry_referral( &p )) {
1624e6df137Slukem is_ref:			p.e_attrs = NULL;
1634e6df137Slukem 			ndb_entry_get_data( op, &NA, 0 );
1644e6df137Slukem 			rs->sr_ref = get_entry_referrals( op, &p );
1654e6df137Slukem 			rs->sr_err = LDAP_REFERRAL;
1664e6df137Slukem 			rs->sr_flags = REP_REF_MUSTBEFREED;
1674e6df137Slukem 			attrs_free( p.e_attrs );
1684e6df137Slukem 			p.e_attrs = NULL;
1694e6df137Slukem 		}
1704e6df137Slukem 		goto return_results;
1714e6df137Slukem 	}
1724e6df137Slukem 
1734e6df137Slukem 	p.e_name = pdn;
1744e6df137Slukem 	p.e_nname = pndn;
1754e6df137Slukem 	rs->sr_err = access_allowed( op, &p,
1764e6df137Slukem 		children, NULL, ACL_WADD, NULL );
1774e6df137Slukem 
1784e6df137Slukem 	if ( ! rs->sr_err ) {
1794e6df137Slukem 		Debug( LDAP_DEBUG_TRACE,
1804e6df137Slukem 			LDAP_XSTRING(ndb_back_add) ": no write access to parent\n",
1814e6df137Slukem 			0, 0, 0 );
1824e6df137Slukem 		rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
1834e6df137Slukem 		rs->sr_text = "no write access to parent";
1844e6df137Slukem 		goto return_results;
1854e6df137Slukem 	}
1864e6df137Slukem 
1874e6df137Slukem 	if ( NA.ocs ) {
1884e6df137Slukem 		if ( is_entry_subentry( &p )) {
1894e6df137Slukem 			/* parent is a subentry, don't allow add */
1904e6df137Slukem 			Debug( LDAP_DEBUG_TRACE,
1914e6df137Slukem 				LDAP_XSTRING(ndb_back_add) ": parent is subentry\n",
1924e6df137Slukem 				0, 0, 0 );
1934e6df137Slukem 			rs->sr_err = LDAP_OBJECT_CLASS_VIOLATION;
1944e6df137Slukem 			rs->sr_text = "parent is a subentry";
1954e6df137Slukem 			goto return_results;
1964e6df137Slukem 		}
1974e6df137Slukem 
1984e6df137Slukem 		if ( is_entry_alias( &p ) ) {
1994e6df137Slukem 			/* parent is an alias, don't allow add */
2004e6df137Slukem 			Debug( LDAP_DEBUG_TRACE,
2014e6df137Slukem 				LDAP_XSTRING(ndb_back_add) ": parent is alias\n",
2024e6df137Slukem 				0, 0, 0 );
2034e6df137Slukem 			rs->sr_err = LDAP_ALIAS_PROBLEM;
2044e6df137Slukem 			rs->sr_text = "parent is an alias";
2054e6df137Slukem 			goto return_results;
2064e6df137Slukem 		}
2074e6df137Slukem 
2084e6df137Slukem 		if ( is_entry_referral( &p ) ) {
2094e6df137Slukem 			/* parent is a referral, don't allow add */
2104e6df137Slukem 			rs->sr_matched = p.e_name.bv_val;
2114e6df137Slukem 			goto is_ref;
2124e6df137Slukem 		}
2134e6df137Slukem 	}
2144e6df137Slukem 
2154e6df137Slukem 	rs->sr_err = access_allowed( op, op->ora_e,
2164e6df137Slukem 		entry, NULL, ACL_WADD, NULL );
2174e6df137Slukem 
2184e6df137Slukem 	if ( ! rs->sr_err ) {
2194e6df137Slukem 		Debug( LDAP_DEBUG_TRACE,
2204e6df137Slukem 			LDAP_XSTRING(ndb_back_add) ": no write access to entry\n",
2214e6df137Slukem 			0, 0, 0 );
2224e6df137Slukem 		rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
2234e6df137Slukem 		rs->sr_text = "no write access to entry";
2244e6df137Slukem 		goto return_results;;
2254e6df137Slukem 	}
2264e6df137Slukem 
2274e6df137Slukem 	/*
2284e6df137Slukem 	 * Check ACL for attribute write access
2294e6df137Slukem 	 */
2304e6df137Slukem 	if (!acl_check_modlist(op, op->ora_e, op->ora_modlist)) {
2314e6df137Slukem 		Debug( LDAP_DEBUG_TRACE,
2324e6df137Slukem 			LDAP_XSTRING(bdb_add) ": no write access to attribute\n",
2334e6df137Slukem 			0, 0, 0 );
2344e6df137Slukem 		rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
2354e6df137Slukem 		rs->sr_text = "no write access to attribute";
2364e6df137Slukem 		goto return_results;;
2374e6df137Slukem 	}
2384e6df137Slukem 
2394e6df137Slukem 
2404e6df137Slukem 	/* acquire entry ID */
2414e6df137Slukem 	if ( op->ora_e->e_id == NOID ) {
2424e6df137Slukem 		rs->sr_err = ndb_next_id( op->o_bd, NA.ndb, &op->ora_e->e_id );
2434e6df137Slukem 		if( rs->sr_err != 0 ) {
2444e6df137Slukem 			Debug( LDAP_DEBUG_TRACE,
2454e6df137Slukem 				LDAP_XSTRING(ndb_back_add) ": next_id failed (%d)\n",
2464e6df137Slukem 				rs->sr_err, 0, 0 );
2474e6df137Slukem 			rs->sr_err = LDAP_OTHER;
2484e6df137Slukem 			rs->sr_text = "internal error";
2494e6df137Slukem 			goto return_results;
2504e6df137Slukem 		}
2514e6df137Slukem 	}
2524e6df137Slukem 
2534e6df137Slukem 	if ( matched.bv_val )
2544e6df137Slukem 		rdns.nr_num++;
2554e6df137Slukem 	NA.e = op->ora_e;
2564e6df137Slukem 	/* dn2id index */
2574e6df137Slukem 	rs->sr_err = ndb_entry_put_info( op->o_bd, &NA, 0 );
2584e6df137Slukem 	if ( rs->sr_err ) {
2594e6df137Slukem 		Debug( LDAP_DEBUG_TRACE,
2604e6df137Slukem 			LDAP_XSTRING(ndb_back_add) ": ndb_entry_put_info failed (%d)\n",
2614e6df137Slukem 			rs->sr_err, 0, 0 );
2624e6df137Slukem 		rs->sr_text = "internal error";
2634e6df137Slukem 		goto return_results;
2644e6df137Slukem 	}
2654e6df137Slukem 
2664e6df137Slukem 	/* id2entry index */
2674e6df137Slukem 	rs->sr_err = ndb_entry_put_data( op->o_bd, &NA );
2684e6df137Slukem 	if ( rs->sr_err ) {
2694e6df137Slukem 		Debug( LDAP_DEBUG_TRACE,
2704e6df137Slukem 			LDAP_XSTRING(ndb_back_add) ": ndb_entry_put_data failed (%d) %s(%d)\n",
2714e6df137Slukem 			rs->sr_err, NA.txn->getNdbError().message, NA.txn->getNdbError().code );
2724e6df137Slukem 		rs->sr_text = "internal error";
2734e6df137Slukem 		goto return_results;
2744e6df137Slukem 	}
2754e6df137Slukem 
2764e6df137Slukem 	/* post-read */
2774e6df137Slukem 	if( op->o_postread ) {
2784e6df137Slukem 		if( postread_ctrl == NULL ) {
2794e6df137Slukem 			postread_ctrl = &ctrls[num_ctrls++];
2804e6df137Slukem 			ctrls[num_ctrls] = NULL;
2814e6df137Slukem 		}
2824e6df137Slukem 		if ( slap_read_controls( op, rs, op->oq_add.rs_e,
2834e6df137Slukem 			&slap_post_read_bv, postread_ctrl ) )
2844e6df137Slukem 		{
2854e6df137Slukem 			Debug( LDAP_DEBUG_TRACE,
2864e6df137Slukem 				"<=- " LDAP_XSTRING(ndb_back_add) ": post-read "
2874e6df137Slukem 				"failed!\n", 0, 0, 0 );
2884e6df137Slukem 			if ( op->o_postread & SLAP_CONTROL_CRITICAL ) {
2894e6df137Slukem 				/* FIXME: is it correct to abort
2904e6df137Slukem 				 * operation if control fails? */
2914e6df137Slukem 				goto return_results;
2924e6df137Slukem 			}
2934e6df137Slukem 		}
2944e6df137Slukem 	}
2954e6df137Slukem 
2964e6df137Slukem 	if ( op->o_noop ) {
2974e6df137Slukem 		if (( rs->sr_err=NA.txn->execute( NdbTransaction::Rollback,
2984e6df137Slukem 			NdbOperation::AbortOnError, 1 )) != 0 ) {
2994e6df137Slukem 			rs->sr_text = "txn (no-op) failed";
3004e6df137Slukem 		} else {
3014e6df137Slukem 			rs->sr_err = LDAP_X_NO_OPERATION;
3024e6df137Slukem 		}
3034e6df137Slukem 
3044e6df137Slukem 	} else {
3054e6df137Slukem 		if(( rs->sr_err=NA.txn->execute( NdbTransaction::Commit,
3064e6df137Slukem 			NdbOperation::AbortOnError, 1 )) != 0 ) {
3074e6df137Slukem 			rs->sr_text = "txn_commit failed";
3084e6df137Slukem 		} else {
3094e6df137Slukem 			rs->sr_err = LDAP_SUCCESS;
3104e6df137Slukem 		}
3114e6df137Slukem 	}
3124e6df137Slukem 
3134e6df137Slukem 	if ( rs->sr_err != LDAP_SUCCESS && rs->sr_err != LDAP_X_NO_OPERATION ) {
3144e6df137Slukem 		Debug( LDAP_DEBUG_TRACE,
3154e6df137Slukem 			LDAP_XSTRING(ndb_back_add) ": %s : %s (%d)\n",
3164e6df137Slukem 			rs->sr_text, NA.txn->getNdbError().message, NA.txn->getNdbError().code );
3174e6df137Slukem 		rs->sr_err = LDAP_OTHER;
3184e6df137Slukem 		goto return_results;
3194e6df137Slukem 	}
3204e6df137Slukem 	NA.txn->close();
3214e6df137Slukem 	NA.txn = NULL;
3224e6df137Slukem 
3234e6df137Slukem 	Debug(LDAP_DEBUG_TRACE,
3244e6df137Slukem 		LDAP_XSTRING(ndb_back_add) ": added%s id=%08lx dn=\"%s\"\n",
3254e6df137Slukem 		op->o_noop ? " (no-op)" : "",
3264e6df137Slukem 		op->oq_add.rs_e->e_id, op->oq_add.rs_e->e_dn );
3274e6df137Slukem 
3284e6df137Slukem 	rs->sr_text = NULL;
3294e6df137Slukem 	if( num_ctrls ) rs->sr_ctrls = ctrls;
3304e6df137Slukem 
3314e6df137Slukem return_results:
3324e6df137Slukem 	success = rs->sr_err;
3334e6df137Slukem 	send_ldap_result( op, rs );
3344e6df137Slukem 	slap_graduate_commit_csn( op );
3354e6df137Slukem 
3364e6df137Slukem 	if( NA.txn != NULL ) {
3374e6df137Slukem 		NA.txn->execute( Rollback );
3384e6df137Slukem 		NA.txn->close();
3394e6df137Slukem 	}
3404e6df137Slukem 
3414e6df137Slukem 	if( postread_ctrl != NULL && (*postread_ctrl) != NULL ) {
3424e6df137Slukem 		slap_sl_free( (*postread_ctrl)->ldctl_value.bv_val, op->o_tmpmemctx );
3434e6df137Slukem 		slap_sl_free( *postread_ctrl, op->o_tmpmemctx );
3444e6df137Slukem 	}
3454e6df137Slukem 
3464e6df137Slukem 	return rs->sr_err;
3474e6df137Slukem }
348