1/*	$NetBSD: modify.c,v 1.3 2021/08/14 16:15:01 christos Exp $	*/
2
3/* $OpenLDAP$ */
4/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5 *
6 * Copyright 1999-2021 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: modify.c,v 1.3 2021/08/14 16:15:01 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
37int
38backsql_modify( Operation *op, SlapReply *rs )
39{
40	backsql_info		*bi = (backsql_info*)op->o_bd->be_private;
41	SQLHDBC 		dbh = SQL_NULL_HDBC;
42	backsql_oc_map_rec	*oc = NULL;
43	backsql_srch_info	bsi = { 0 };
44	Entry			m = { 0 }, *e = NULL;
45	int			manageDSAit = get_manageDSAit( op );
46	SQLUSMALLINT		CompletionType = SQL_ROLLBACK;
47
48	/*
49	 * FIXME: in case part of the operation cannot be performed
50	 * (missing mapping, SQL write fails or so) the entire operation
51	 * should be rolled-back
52	 */
53	Debug( LDAP_DEBUG_TRACE, "==>backsql_modify(): modifying entry \"%s\"\n",
54		op->o_req_ndn.bv_val );
55
56	rs->sr_err = backsql_get_db_conn( op, &dbh );
57	if ( rs->sr_err != LDAP_SUCCESS ) {
58		Debug( LDAP_DEBUG_TRACE, "   backsql_modify(): "
59			"could not get connection handle - exiting\n" );
60		/*
61		 * FIXME: we don't want to send back
62		 * excessively detailed messages
63		 */
64		rs->sr_text = ( rs->sr_err == LDAP_OTHER )
65			? "SQL-backend error" : NULL;
66		goto done;
67	}
68
69	bsi.bsi_e = &m;
70	rs->sr_err = backsql_init_search( &bsi, &op->o_req_ndn,
71			LDAP_SCOPE_BASE,
72			(time_t)(-1), NULL, dbh, op, rs,
73			slap_anlist_all_attributes,
74			( BACKSQL_ISF_MATCHED | BACKSQL_ISF_GET_ENTRY | BACKSQL_ISF_GET_OC ) );
75	switch ( rs->sr_err ) {
76	case LDAP_SUCCESS:
77		break;
78
79	case LDAP_REFERRAL:
80		if ( manageDSAit && !BER_BVISNULL( &bsi.bsi_e->e_nname ) &&
81				dn_match( &op->o_req_ndn, &bsi.bsi_e->e_nname ) )
82		{
83			rs->sr_err = LDAP_SUCCESS;
84			rs->sr_text = NULL;
85			rs->sr_matched = NULL;
86			if ( rs->sr_ref ) {
87				ber_bvarray_free( rs->sr_ref );
88				rs->sr_ref = NULL;
89			}
90			break;
91		}
92		e = &m;
93		/* fallthru */
94
95	default:
96		Debug( LDAP_DEBUG_TRACE, "backsql_modify(): "
97			"could not retrieve modifyDN ID - no such entry\n" );
98		if ( !BER_BVISNULL( &m.e_nname ) ) {
99			/* FIXME: should always be true! */
100			e = &m;
101
102		} else {
103			e = NULL;
104		}
105		goto done;
106	}
107
108	Debug( LDAP_DEBUG_TRACE, "   backsql_modify(): "
109		"modifying entry \"%s\" (id=" BACKSQL_IDFMT ")\n",
110		bsi.bsi_base_id.eid_dn.bv_val,
111		BACKSQL_IDARG(bsi.bsi_base_id.eid_id) );
112
113	if ( get_assert( op ) &&
114			( test_filter( op, &m, get_assertion( op ) )
115			  != LDAP_COMPARE_TRUE ))
116	{
117		rs->sr_err = LDAP_ASSERTION_FAILED;
118		e = &m;
119		goto done;
120	}
121
122	slap_mods_opattrs( op, &op->orm_modlist, 1 );
123
124	assert( bsi.bsi_base_id.eid_oc != NULL );
125	oc = bsi.bsi_base_id.eid_oc;
126
127	if ( !acl_check_modlist( op, &m, op->orm_modlist ) ) {
128		rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
129		e = &m;
130		goto done;
131	}
132
133	rs->sr_err = backsql_modify_internal( op, rs, dbh, oc,
134			&bsi.bsi_base_id, op->orm_modlist );
135	if ( rs->sr_err != LDAP_SUCCESS ) {
136		e = &m;
137		goto do_transact;
138	}
139
140	if ( BACKSQL_CHECK_SCHEMA( bi ) ) {
141		char		textbuf[ SLAP_TEXT_BUFLEN ] = { '\0' };
142
143		backsql_entry_clean( op, &m );
144
145		bsi.bsi_e = &m;
146		rs->sr_err = backsql_id2entry( &bsi, &bsi.bsi_base_id );
147		if ( rs->sr_err != LDAP_SUCCESS ) {
148			e = &m;
149			goto do_transact;
150		}
151
152		rs->sr_err = entry_schema_check( op, &m, NULL, 0, 0, NULL,
153			&rs->sr_text, textbuf, sizeof( textbuf ) );
154		if ( rs->sr_err != LDAP_SUCCESS ) {
155			Debug( LDAP_DEBUG_TRACE, "   backsql_modify(\"%s\"): "
156				"entry failed schema check -- aborting\n",
157				m.e_name.bv_val );
158			e = NULL;
159			goto do_transact;
160		}
161	}
162
163do_transact:;
164	/*
165	 * Commit only if all operations succeed
166	 */
167	if ( rs->sr_err == LDAP_SUCCESS && !op->o_noop ) {
168		assert( e == NULL );
169		CompletionType = SQL_COMMIT;
170	}
171
172	SQLTransact( SQL_NULL_HENV, dbh, CompletionType );
173
174done:;
175	if ( e != NULL ) {
176		if ( !access_allowed( op, e, slap_schema.si_ad_entry, NULL,
177					ACL_DISCLOSE, NULL ) )
178		{
179			rs->sr_err = LDAP_NO_SUCH_OBJECT;
180			rs->sr_text = NULL;
181			rs->sr_matched = NULL;
182			if ( rs->sr_ref ) {
183				ber_bvarray_free( rs->sr_ref );
184				rs->sr_ref = NULL;
185			}
186		}
187	}
188
189	if ( op->o_noop && rs->sr_err == LDAP_SUCCESS ) {
190		rs->sr_err = LDAP_X_NO_OPERATION;
191	}
192
193	send_ldap_result( op, rs );
194	slap_graduate_commit_csn( op );
195
196	if ( !BER_BVISNULL( &bsi.bsi_base_id.eid_ndn ) ) {
197		(void)backsql_free_entryID( &bsi.bsi_base_id, 0, op->o_tmpmemctx );
198	}
199
200	if ( !BER_BVISNULL( &m.e_nname ) ) {
201		backsql_entry_clean( op, &m );
202	}
203
204	if ( bsi.bsi_attrs != NULL ) {
205		op->o_tmpfree( bsi.bsi_attrs, op->o_tmpmemctx );
206	}
207
208	if ( rs->sr_ref ) {
209		ber_bvarray_free( rs->sr_ref );
210		rs->sr_ref = NULL;
211	}
212
213	Debug( LDAP_DEBUG_TRACE, "<==backsql_modify()\n" );
214
215	return rs->sr_err;
216}
217
218