xref: /netbsd-src/external/bsd/openldap/dist/servers/slapd/back-wt/add.c (revision 549b59ed3ccf0d36d3097190a0db27b770f3a839)
1 /*	$NetBSD: add.c,v 1.2 2021/08/14 16:15:02 christos Exp $	*/
2 
3 /* OpenLDAP WiredTiger backend */
4 /* $OpenLDAP$ */
5 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
6  *
7  * Copyright 2002-2021 The OpenLDAP Foundation.
8  * All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted only as authorized by the OpenLDAP
12  * Public License.
13  *
14  * A copy of this license is available in the file LICENSE in the
15  * top-level directory of the distribution or, alternatively, at
16  * <http://www.OpenLDAP.org/license.html>.
17  */
18 /* ACKNOWLEDGEMENTS:
19  * This work was developed by HAMANO Tsukasa <hamano@osstech.co.jp>
20  * based on back-bdb for inclusion in OpenLDAP Software.
21  * WiredTiger is a product of MongoDB Inc.
22  */
23 
24 #include <sys/cdefs.h>
25 __RCSID("$NetBSD: add.c,v 1.2 2021/08/14 16:15:02 christos Exp $");
26 
27 #include "portable.h"
28 
29 #include <stdio.h>
30 #include "back-wt.h"
31 #include "slap-config.h"
32 
33 int
wt_add(Operation * op,SlapReply * rs)34 wt_add( Operation *op, SlapReply *rs )
35 {
36     struct wt_info *wi = (struct wt_info *) op->o_bd->be_private;
37 	struct berval   pdn;
38 	char textbuf[SLAP_TEXT_BUFLEN];
39 	size_t textlen = sizeof textbuf;
40 	AttributeDescription *children = slap_schema.si_ad_children;
41 	AttributeDescription *entry = slap_schema.si_ad_entry;
42 	ID eid;
43 	int num_retries = 0;
44 	int success;
45 	LDAPControl **postread_ctrl = NULL;
46 	LDAPControl *ctrls[SLAP_MAX_RESPONSE_CONTROLS];
47 	int num_ctrls = 0;
48 	wt_ctx *wc;
49 	Entry *e = NULL;
50 	Entry *p = NULL;
51 	ID pid;
52 	int rc;
53 
54     Debug( LDAP_DEBUG_ARGS, "==> " LDAP_XSTRING(wt_add) ": %s\n",
55 		   op->ora_e->e_name.bv_val );
56 
57 	ctrls[num_ctrls] = 0;
58 
59 	/* check entry's schema */
60 	rs->sr_err = entry_schema_check(
61 		op, op->ora_e, NULL,
62 		get_relax(op), 1, NULL, &rs->sr_text, textbuf, textlen );
63     if ( rs->sr_err != LDAP_SUCCESS ) {
64         Debug( LDAP_DEBUG_TRACE,
65 			   LDAP_XSTRING(wt_add)
66 			   ": entry failed schema check: %s (%d)\n",
67 			   rs->sr_text, rs->sr_err );
68         goto return_results;
69     }
70 
71     /* add opattrs to shadow as well, only missing attrs will actually
72      * be added; helps compatibility with older OL versions */
73     rs->sr_err = slap_add_opattrs( op, &rs->sr_text, textbuf, textlen, 1 );
74     if ( rs->sr_err != LDAP_SUCCESS ) {
75         Debug( LDAP_DEBUG_TRACE,
76 			   LDAP_XSTRING(wt_add)
77 			   ": entry failed op attrs add: %s (%d)\n",
78 			   rs->sr_text, rs->sr_err );
79         goto return_results;
80     }
81 
82     if ( get_assert( op ) &&
83 		 ( test_filter( op, op->ora_e, get_assertion( op ))
84 		   != LDAP_COMPARE_TRUE ))
85     {
86         rs->sr_err = LDAP_ASSERTION_FAILED;
87         goto return_results;
88     }
89 
90 	/* Not used
91 	 * subentry = is_entry_subentry( op->ora_e );
92 	 */
93 
94     /*
95      * Get the parent dn and see if the corresponding entry exists.
96      */
97     if ( be_issuffix( op->o_bd, &op->ora_e->e_nname ) ) {
98         pdn = slap_empty_bv;
99     } else {
100         dnParent( &op->ora_e->e_nname, &pdn );
101     }
102 
103 	wc = wt_ctx_get(op, wi);
104 	if( !wc ){
105         Debug( LDAP_DEBUG_ANY,
106 			   LDAP_XSTRING(wt_add)
107 			   ": wt_ctx_get failed\n" );
108 		rs->sr_err = LDAP_OTHER;
109 		rs->sr_text = "internal error";
110         send_ldap_result( op, rs );
111         return rs->sr_err;
112 	}
113 
114 	rc = wt_dn2entry(op->o_bd, wc, &op->o_req_ndn, &e);
115 	switch( rc ) {
116 	case 0:
117 		rs->sr_err = LDAP_ALREADY_EXISTS;
118 		goto return_results;
119 		break;
120 	case WT_NOTFOUND:
121 		break;
122 	default:
123 		/* TODO: retry handling */
124         Debug( LDAP_DEBUG_ANY,
125 			   LDAP_XSTRING(wt_add)
126 			   ": error at wt_dn2entry() rc=%d\n",
127 			   rc );
128 		rs->sr_err = LDAP_OTHER;
129 		rs->sr_text = "internal error";
130 		goto return_results;
131 	}
132 
133     /* get parent entry */
134 	rc = wt_dn2pentry(op->o_bd, wc, &op->o_req_ndn, &p);
135 	switch( rc ){
136 	case 0:
137 	case WT_NOTFOUND:
138 		break;
139 	default:
140         Debug( LDAP_DEBUG_ANY,
141 			   LDAP_XSTRING(wt_add)
142 			   ": error at wt_dn2pentry() rc=%d\n",
143 			   rc );
144 		rs->sr_err = LDAP_OTHER;
145 		rs->sr_text = "internal error";
146 		goto return_results;
147 	}
148 
149 	if ( !p )
150 		p = (Entry *)&slap_entry_root;
151 
152 	if ( !bvmatch( &pdn, &p->e_nname ) ) {
153 		rs->sr_matched = ber_strdup_x( p->e_name.bv_val,
154 									   op->o_tmpmemctx );
155 		if ( p != (Entry *)&slap_entry_root ) {
156 			rs->sr_ref = is_entry_referral( p )
157 				? get_entry_referrals( op, p )
158 				: NULL;
159 			wt_entry_return( p );
160 		} else {
161 			rs->sr_ref = NULL;
162 		}
163 		p = NULL;
164         Debug( LDAP_DEBUG_TRACE,
165 			   LDAP_XSTRING(wt_add) ": parent "
166 			   "does not exist\n" );
167         rs->sr_err = LDAP_REFERRAL;
168         rs->sr_flags = REP_MATCHED_MUSTBEFREED | REP_REF_MUSTBEFREED;
169         goto return_results;
170 	}
171 
172 	rs->sr_err = access_allowed( op, p,
173 								 children, NULL, ACL_WADD, NULL );
174 	if ( ! rs->sr_err ) {
175 		/*
176 		if ( p != (Entry *)&slap_entry_root )
177 			wt_entry_return( op, p );
178 		*/
179 		p = NULL;
180 
181 		Debug( LDAP_DEBUG_TRACE,
182 			   LDAP_XSTRING(wt_add) ": no write access to parent\n" );
183 		rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
184 		rs->sr_text = "no write access to parent";
185 		goto return_results;;
186 	}
187 
188 	if ( p != (Entry *)&slap_entry_root ) {
189 		if ( is_entry_subentry( p ) ) {
190 			wt_entry_return( p );
191 			p = NULL;
192 			/* parent is a subentry, don't allow add */
193 			Debug( LDAP_DEBUG_TRACE,
194 				   LDAP_XSTRING(wt_add) ": parent is subentry\n" );
195 			rs->sr_err = LDAP_OBJECT_CLASS_VIOLATION;
196 			rs->sr_text = "parent is a subentry";
197 			goto return_results;;
198 		}
199 
200 		if ( is_entry_alias( p ) ) {
201 			wt_entry_return( p );
202 			p = NULL;
203 			/* parent is an alias, don't allow add */
204 			Debug( LDAP_DEBUG_TRACE,
205 				   LDAP_XSTRING(wt_add) ": parent is alias\n" );
206 			rs->sr_err = LDAP_ALIAS_PROBLEM;
207 			rs->sr_text = "parent is an alias";
208 			goto return_results;;
209 		}
210 
211 		if ( is_entry_referral( p ) ) {
212 			BerVarray ref = get_entry_referrals( op, p );
213 			/* parent is a referral, don't allow add */
214 			rs->sr_matched = ber_strdup_x( p->e_name.bv_val,
215 										   op->o_tmpmemctx );
216 			rs->sr_ref = referral_rewrite( ref, &p->e_name,
217 										   &op->o_req_dn, LDAP_SCOPE_DEFAULT );
218 			ber_bvarray_free( ref );
219 			wt_entry_return( p );
220 			p = NULL;
221 			Debug( LDAP_DEBUG_TRACE,
222 				   LDAP_XSTRING(wt_add) ": parent is referral\n" );
223 
224 			rs->sr_err = LDAP_REFERRAL;
225 			rs->sr_flags = REP_MATCHED_MUSTBEFREED | REP_REF_MUSTBEFREED;
226 			goto return_results;
227 		}
228 	}
229 
230 #if 0
231 	if ( subentry ) {
232 		/* FIXME: */
233 		/* parent must be an administrative point of the required kind */
234 	}
235 #endif
236 
237 	/* free parent */
238 	if ( p != (Entry *)&slap_entry_root ) {
239 		pid = p->e_id;
240 		if ( p->e_nname.bv_len ) {
241 			struct berval ppdn;
242 
243 			/* ITS#5326: use parent's DN if differs from provided one */
244 			dnParent( &op->ora_e->e_name, &ppdn );
245 			if ( !dn_match( &p->e_name, &ppdn ) ) {
246 				struct berval rdn;
247 				struct berval newdn;
248 
249 				dnRdn( &op->ora_e->e_name, &rdn );
250 
251 				build_new_dn( &newdn, &p->e_name, &rdn, NULL );
252 				if ( op->ora_e->e_name.bv_val != op->o_req_dn.bv_val )
253 					ber_memfree( op->ora_e->e_name.bv_val );
254 				op->ora_e->e_name = newdn;
255 
256 				/* FIXME: should check whether
257                  * dnNormalize(newdn) == e->e_nname ... */
258 			}
259 		}
260 
261 		wt_entry_return( p );
262 	}
263 	p = NULL;
264 
265 	rs->sr_err = access_allowed( op, op->ora_e,
266 								 entry, NULL, ACL_WADD, NULL );
267 
268 	if ( ! rs->sr_err ) {
269 		Debug( LDAP_DEBUG_TRACE,
270 			   LDAP_XSTRING(wt_add) ": no write access to entry\n" );
271 		rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
272 		rs->sr_text = "no write access to entry";
273 		goto return_results;
274 	}
275 
276 	/*
277 	 * Check ACL for attribute write access
278 	 */
279 	if (!acl_check_modlist(op, op->ora_e, op->ora_modlist)) {
280 		Debug( LDAP_DEBUG_TRACE,
281 			   LDAP_XSTRING(wt_add) ": no write access to attribute\n" );
282 		rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
283 		rs->sr_text = "no write access to attribute";
284 		goto return_results;
285 	}
286 
287 	rc = wc->session->begin_transaction(wc->session, NULL);
288 	if( rc ) {
289 		Debug( LDAP_DEBUG_TRACE,
290 			   LDAP_XSTRING(wt_add) ": begin_transaction failed: %s (%d)\n",
291 			   wiredtiger_strerror(rc), rc );
292 		rs->sr_err = LDAP_OTHER;
293 		rs->sr_text = "begin_transaction failed";
294 		goto return_results;
295 	}
296 	Debug( LDAP_DEBUG_TRACE, LDAP_XSTRING(wt_add) ": session id: %p\n",
297 		   wc->session );
298 
299 	wt_next_id( op->o_bd, &eid );
300 	op->ora_e->e_id = eid;
301 
302 	rc = wt_dn2id_add( op, wc->session, pid, op->ora_e );
303 	if( rc ){
304 		Debug( LDAP_DEBUG_TRACE,
305 			   LDAP_XSTRING(wt_add)
306 			   ": dn2id_add failed: %s (%d)\n",
307 			   wiredtiger_strerror(rc), rc );
308 		switch( rc ) {
309 		case WT_DUPLICATE_KEY:
310 			rs->sr_err = LDAP_ALREADY_EXISTS;
311 			break;
312 		default:
313 			rs->sr_err = LDAP_OTHER;
314 		}
315 		wc->session->rollback_transaction(wc->session, NULL);
316 		goto return_results;
317 	}
318 
319 	rc = wt_id2entry_add( op, wc->session, op->ora_e );
320 	if ( rc ) {
321 		Debug( LDAP_DEBUG_TRACE,
322 			   LDAP_XSTRING(wt_add)
323 			   ": id2entry_add failed: %s (%d)\n",
324 			   wiredtiger_strerror(rc), rc );
325 		if ( rc == LDAP_ADMINLIMIT_EXCEEDED ) {
326 			rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
327 			rs->sr_text = "entry is too big";
328 		} else {
329 			rs->sr_err = LDAP_OTHER;
330 			rs->sr_text = "entry store failed";
331 		}
332 		wc->session->rollback_transaction(wc->session, NULL);
333 		goto return_results;
334 	}
335 
336 	/* add indices */
337 	rc = wt_index_entry_add( op, wc, op->ora_e );
338 	if ( rc ) {
339 		Debug(LDAP_DEBUG_TRACE,
340 			  "<== " LDAP_XSTRING(wt_add)
341 			  ": index add failed: %s (%d)\n",
342 			  wiredtiger_strerror(rc), rc );
343 		rs->sr_err = LDAP_OTHER;
344 		rs->sr_text = "index add failed";
345 		wc->session->rollback_transaction(wc->session, NULL);
346 		goto return_results;
347 	}
348 
349 	rc = wc->session->commit_transaction(wc->session, NULL);
350 	if( rc ) {
351 		Debug( LDAP_DEBUG_TRACE,
352 			   "<== " LDAP_XSTRING(wt_add)
353 			   ": commit_transaction failed: %s (%d)\n",
354 			   wiredtiger_strerror(rc), rc );
355 		rs->sr_err = LDAP_OTHER;
356 		rs->sr_text = "commit_transaction failed";
357 		goto return_results;
358 	}
359 
360 	rs->sr_err = LDAP_SUCCESS;
361 
362 	/* post-read */
363 	if( op->o_postread ) {
364 		if( postread_ctrl == NULL ) {
365 			postread_ctrl = &ctrls[num_ctrls++];
366 			ctrls[num_ctrls] = NULL;
367 		}
368 		if ( slap_read_controls( op, rs, op->ora_e,
369 								 &slap_post_read_bv, postread_ctrl ) )
370 		{
371 			Debug( LDAP_DEBUG_TRACE,
372 				   "<=- " LDAP_XSTRING(wt_add) ": post-read "
373 				   "failed!\n" );
374 			if ( op->o_postread & SLAP_CONTROL_CRITICAL ) {
375 				/* FIXME: is it correct to abort
376                  * operation if control fails? */
377 				goto return_results;
378 			}
379 		}
380 	}
381 
382 	Debug(LDAP_DEBUG_TRACE,
383 		  LDAP_XSTRING(wt_add) ": added%s id=%08lx dn=\"%s\"\n",
384 		  op->o_noop ? " (no-op)" : "",
385 		  op->ora_e->e_id, op->ora_e->e_dn );
386 
387 return_results:
388 	success = rs->sr_err;
389 	send_ldap_result( op, rs );
390 
391 	slap_graduate_commit_csn( op );
392 
393 	if( postread_ctrl != NULL && (*postread_ctrl) != NULL ) {
394         slap_sl_free( (*postread_ctrl)->ldctl_value.bv_val, op->o_tmpmemctx );
395         slap_sl_free( *postread_ctrl, op->o_tmpmemctx );
396     }
397     return rs->sr_err;
398 }
399 
400 /*
401  * Local variables:
402  * indent-tabs-mode: t
403  * tab-width: 4
404  * c-basic-offset: 4
405  * End:
406  */
407