1 /* $NetBSD: add.c,v 1.3 2021/08/14 16:15:00 christos Exp $ */ 2 3 /* add.c - ldap mdb back-end add routine */ 4 /* $OpenLDAP$ */ 5 /* This work is part of OpenLDAP Software <http://www.openldap.org/>. 6 * 7 * Copyright 2000-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 19 #include <sys/cdefs.h> 20 __RCSID("$NetBSD: add.c,v 1.3 2021/08/14 16:15:00 christos Exp $"); 21 22 #include "portable.h" 23 24 #include <stdio.h> 25 #include <ac/string.h> 26 27 #include "back-mdb.h" 28 29 int 30 mdb_add(Operation *op, SlapReply *rs ) 31 { 32 struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private; 33 struct berval pdn; 34 Entry *p = NULL, *oe = op->ora_e; 35 char textbuf[SLAP_TEXT_BUFLEN]; 36 size_t textlen = sizeof textbuf; 37 AttributeDescription *children = slap_schema.si_ad_children; 38 AttributeDescription *entry = slap_schema.si_ad_entry; 39 MDB_txn *txn = NULL; 40 MDB_cursor *mc = NULL; 41 MDB_cursor *mcd; 42 ID eid, pid = 0; 43 mdb_op_info opinfo = {{{ 0 }}}, *moi = &opinfo; 44 int subentry; 45 int numads = mdb->mi_numads; 46 47 int success; 48 49 LDAPControl **postread_ctrl = NULL; 50 LDAPControl *ctrls[SLAP_MAX_RESPONSE_CONTROLS]; 51 int num_ctrls = 0; 52 53 Debug(LDAP_DEBUG_ARGS, "==> " LDAP_XSTRING(mdb_add) ": %s\n", 54 op->ora_e->e_name.bv_val ); 55 56 ctrls[num_ctrls] = 0; 57 58 /* check entry's schema */ 59 rs->sr_err = entry_schema_check( op, op->ora_e, NULL, 60 get_relax(op), 1, NULL, &rs->sr_text, textbuf, textlen ); 61 if ( rs->sr_err != LDAP_SUCCESS ) { 62 Debug( LDAP_DEBUG_TRACE, 63 LDAP_XSTRING(mdb_add) ": entry failed schema check: " 64 "%s (%d)\n", rs->sr_text, rs->sr_err ); 65 goto return_results; 66 } 67 68 /* begin transaction */ 69 rs->sr_err = mdb_opinfo_get( op, mdb, 0, &moi ); 70 rs->sr_text = NULL; 71 if( rs->sr_err != 0 ) { 72 Debug( LDAP_DEBUG_TRACE, 73 LDAP_XSTRING(mdb_add) ": txn_begin failed: %s (%d)\n", 74 mdb_strerror(rs->sr_err), rs->sr_err ); 75 rs->sr_err = LDAP_OTHER; 76 rs->sr_text = "internal error"; 77 goto return_results; 78 } 79 txn = moi->moi_txn; 80 81 /* add opattrs to shadow as well, only missing attrs will actually 82 * be added; helps compatibility with older OL versions */ 83 rs->sr_err = slap_add_opattrs( op, &rs->sr_text, textbuf, textlen, 1 ); 84 if ( rs->sr_err != LDAP_SUCCESS ) { 85 Debug( LDAP_DEBUG_TRACE, 86 LDAP_XSTRING(mdb_add) ": entry failed op attrs add: " 87 "%s (%d)\n", rs->sr_text, rs->sr_err ); 88 goto return_results; 89 } 90 91 if ( get_assert( op ) && 92 ( test_filter( op, op->ora_e, get_assertion( op )) != LDAP_COMPARE_TRUE )) 93 { 94 rs->sr_err = LDAP_ASSERTION_FAILED; 95 goto return_results; 96 } 97 98 subentry = is_entry_subentry( op->ora_e ); 99 100 /* 101 * Get the parent dn and see if the corresponding entry exists. 102 */ 103 if ( be_issuffix( op->o_bd, &op->ora_e->e_nname ) ) { 104 pdn = slap_empty_bv; 105 } else { 106 dnParent( &op->ora_e->e_nname, &pdn ); 107 } 108 109 rs->sr_err = mdb_cursor_open( txn, mdb->mi_dn2id, &mcd ); 110 if( rs->sr_err != 0 ) { 111 Debug( LDAP_DEBUG_TRACE, 112 LDAP_XSTRING(mdb_add) ": mdb_cursor_open failed (%d)\n", 113 rs->sr_err ); 114 rs->sr_err = LDAP_OTHER; 115 rs->sr_text = "internal error"; 116 goto return_results; 117 } 118 119 /* get entry or parent */ 120 rs->sr_err = mdb_dn2entry( op, txn, mcd, &op->ora_e->e_nname, &p, NULL, 1 ); 121 switch( rs->sr_err ) { 122 case 0: 123 rs->sr_err = LDAP_ALREADY_EXISTS; 124 mdb_entry_return( op, p ); 125 p = NULL; 126 goto return_results; 127 case MDB_NOTFOUND: 128 break; 129 case LDAP_BUSY: 130 rs->sr_text = "ldap server busy"; 131 goto return_results; 132 default: 133 rs->sr_err = LDAP_OTHER; 134 rs->sr_text = "internal error"; 135 goto return_results; 136 } 137 138 if ( !p ) 139 p = (Entry *)&slap_entry_root; 140 141 if ( !bvmatch( &pdn, &p->e_nname ) ) { 142 rs->sr_matched = ber_strdup_x( p->e_name.bv_val, 143 op->o_tmpmemctx ); 144 if ( p != (Entry *)&slap_entry_root && is_entry_referral( p )) { 145 BerVarray ref = get_entry_referrals( op, p ); 146 rs->sr_ref = referral_rewrite( ref, &p->e_name, 147 &op->o_req_dn, LDAP_SCOPE_DEFAULT ); 148 ber_bvarray_free( ref ); 149 } else { 150 rs->sr_ref = NULL; 151 } 152 if ( p != (Entry *)&slap_entry_root ) 153 mdb_entry_return( op, p ); 154 p = NULL; 155 Debug( LDAP_DEBUG_TRACE, 156 LDAP_XSTRING(mdb_add) ": parent " 157 "does not exist\n" ); 158 159 rs->sr_err = LDAP_REFERRAL; 160 rs->sr_flags = REP_MATCHED_MUSTBEFREED | REP_REF_MUSTBEFREED; 161 goto return_results; 162 } 163 164 rs->sr_err = access_allowed( op, p, 165 children, NULL, ACL_WADD, NULL ); 166 167 if ( ! rs->sr_err ) { 168 if ( p != (Entry *)&slap_entry_root ) 169 mdb_entry_return( op, p ); 170 p = NULL; 171 172 Debug( LDAP_DEBUG_TRACE, 173 LDAP_XSTRING(mdb_add) ": no write access to parent\n" ); 174 rs->sr_err = LDAP_INSUFFICIENT_ACCESS; 175 rs->sr_text = "no write access to parent"; 176 goto return_results;; 177 } 178 179 if ( p != (Entry *)&slap_entry_root ) { 180 if ( is_entry_subentry( p ) ) { 181 mdb_entry_return( op, p ); 182 p = NULL; 183 /* parent is a subentry, don't allow add */ 184 Debug( LDAP_DEBUG_TRACE, 185 LDAP_XSTRING(mdb_add) ": parent is subentry\n" ); 186 rs->sr_err = LDAP_OBJECT_CLASS_VIOLATION; 187 rs->sr_text = "parent is a subentry"; 188 goto return_results;; 189 } 190 191 if ( is_entry_alias( p ) ) { 192 mdb_entry_return( op, p ); 193 p = NULL; 194 /* parent is an alias, don't allow add */ 195 Debug( LDAP_DEBUG_TRACE, 196 LDAP_XSTRING(mdb_add) ": parent is alias\n" ); 197 rs->sr_err = LDAP_ALIAS_PROBLEM; 198 rs->sr_text = "parent is an alias"; 199 goto return_results;; 200 } 201 202 if ( is_entry_referral( p ) ) { 203 BerVarray ref = get_entry_referrals( op, p ); 204 /* parent is a referral, don't allow add */ 205 rs->sr_matched = ber_strdup_x( p->e_name.bv_val, 206 op->o_tmpmemctx ); 207 rs->sr_ref = referral_rewrite( ref, &p->e_name, 208 &op->o_req_dn, LDAP_SCOPE_DEFAULT ); 209 ber_bvarray_free( ref ); 210 mdb_entry_return( op, p ); 211 p = NULL; 212 Debug( LDAP_DEBUG_TRACE, 213 LDAP_XSTRING(mdb_add) ": parent is referral\n" ); 214 215 rs->sr_err = LDAP_REFERRAL; 216 rs->sr_flags = REP_MATCHED_MUSTBEFREED | REP_REF_MUSTBEFREED; 217 goto return_results; 218 } 219 220 } 221 222 if ( subentry ) { 223 /* FIXME: */ 224 /* parent must be an administrative point of the required kind */ 225 } 226 227 /* free parent */ 228 if ( p != (Entry *)&slap_entry_root ) { 229 pid = p->e_id; 230 if ( p->e_nname.bv_len ) { 231 struct berval ppdn; 232 233 /* ITS#5326: use parent's DN if differs from provided one */ 234 dnParent( &op->ora_e->e_name, &ppdn ); 235 if ( !dn_match( &p->e_name, &ppdn ) ) { 236 struct berval rdn; 237 struct berval newdn; 238 239 dnRdn( &op->ora_e->e_name, &rdn ); 240 241 build_new_dn( &newdn, &p->e_name, &rdn, NULL ); 242 if ( op->ora_e->e_name.bv_val != op->o_req_dn.bv_val ) 243 ber_memfree( op->ora_e->e_name.bv_val ); 244 op->ora_e->e_name = newdn; 245 246 /* FIXME: should check whether 247 * dnNormalize(newdn) == e->e_nname ... */ 248 } 249 } 250 251 mdb_entry_return( op, p ); 252 } 253 p = NULL; 254 255 rs->sr_err = access_allowed( op, op->ora_e, 256 entry, NULL, ACL_WADD, NULL ); 257 258 if ( ! rs->sr_err ) { 259 Debug( LDAP_DEBUG_TRACE, 260 LDAP_XSTRING(mdb_add) ": no write access to entry\n" ); 261 rs->sr_err = LDAP_INSUFFICIENT_ACCESS; 262 rs->sr_text = "no write access to entry"; 263 goto return_results;; 264 } 265 266 /* 267 * Check ACL for attribute write access 268 */ 269 if (!acl_check_modlist(op, oe, op->ora_modlist)) { 270 Debug( LDAP_DEBUG_TRACE, 271 LDAP_XSTRING(mdb_add) ": no write access to attribute\n" ); 272 rs->sr_err = LDAP_INSUFFICIENT_ACCESS; 273 rs->sr_text = "no write access to attribute"; 274 goto return_results;; 275 } 276 277 rs->sr_err = mdb_cursor_open( txn, mdb->mi_id2entry, &mc ); 278 if( rs->sr_err != 0 ) { 279 Debug( LDAP_DEBUG_TRACE, 280 LDAP_XSTRING(mdb_add) ": mdb_cursor_open failed (%d)\n", 281 rs->sr_err ); 282 rs->sr_err = LDAP_OTHER; 283 rs->sr_text = "internal error"; 284 goto return_results; 285 } 286 287 rs->sr_err = mdb_next_id( op->o_bd, mc, &eid ); 288 if( rs->sr_err != 0 ) { 289 Debug( LDAP_DEBUG_TRACE, 290 LDAP_XSTRING(mdb_add) ": next_id failed (%d)\n", 291 rs->sr_err ); 292 rs->sr_err = LDAP_OTHER; 293 rs->sr_text = "internal error"; 294 goto return_results; 295 } 296 op->ora_e->e_id = eid; 297 298 /* dn2id index */ 299 rs->sr_err = mdb_dn2id_add( op, mcd, mcd, pid, 1, 1, op->ora_e ); 300 mdb_cursor_close( mcd ); 301 if ( rs->sr_err != 0 ) { 302 Debug( LDAP_DEBUG_TRACE, 303 LDAP_XSTRING(mdb_add) ": dn2id_add failed: %s (%d)\n", 304 mdb_strerror(rs->sr_err), rs->sr_err ); 305 306 switch( rs->sr_err ) { 307 case MDB_KEYEXIST: 308 rs->sr_err = LDAP_ALREADY_EXISTS; 309 break; 310 default: 311 rs->sr_err = LDAP_OTHER; 312 } 313 goto return_results; 314 } 315 316 /* attribute indexes */ 317 rs->sr_err = mdb_index_entry_add( op, txn, op->ora_e ); 318 if ( rs->sr_err != LDAP_SUCCESS ) { 319 Debug( LDAP_DEBUG_TRACE, 320 LDAP_XSTRING(mdb_add) ": index_entry_add failed\n" ); 321 rs->sr_err = LDAP_OTHER; 322 rs->sr_text = "index generation failed"; 323 goto return_results; 324 } 325 326 /* id2entry index */ 327 rs->sr_err = mdb_id2entry_add( op, txn, mc, op->ora_e ); 328 if ( rs->sr_err != 0 ) { 329 Debug( LDAP_DEBUG_TRACE, 330 LDAP_XSTRING(mdb_add) ": id2entry_add failed\n" ); 331 if ( rs->sr_err == LDAP_ADMINLIMIT_EXCEEDED ) { 332 rs->sr_text = "entry is too big"; 333 } else { 334 rs->sr_err = LDAP_OTHER; 335 rs->sr_text = "entry store failed"; 336 } 337 goto return_results; 338 } 339 340 /* post-read */ 341 if( op->o_postread ) { 342 if( postread_ctrl == NULL ) { 343 postread_ctrl = &ctrls[num_ctrls++]; 344 ctrls[num_ctrls] = NULL; 345 } 346 if ( slap_read_controls( op, rs, op->ora_e, 347 &slap_post_read_bv, postread_ctrl ) ) 348 { 349 Debug( LDAP_DEBUG_TRACE, 350 "<=- " LDAP_XSTRING(mdb_add) ": post-read " 351 "failed!\n" ); 352 if ( op->o_postread & SLAP_CONTROL_CRITICAL ) { 353 /* FIXME: is it correct to abort 354 * operation if control fails? */ 355 goto return_results; 356 } 357 } 358 } 359 360 if ( moi == &opinfo ) { 361 LDAP_SLIST_REMOVE( &op->o_extra, &opinfo.moi_oe, OpExtra, oe_next ); 362 opinfo.moi_oe.oe_key = NULL; 363 if ( op->o_noop ) { 364 mdb->mi_numads = numads; 365 mdb_txn_abort( txn ); 366 rs->sr_err = LDAP_X_NO_OPERATION; 367 txn = NULL; 368 goto return_results; 369 } 370 371 rs->sr_err = mdb_txn_commit( txn ); 372 txn = NULL; 373 if ( rs->sr_err != 0 ) { 374 mdb->mi_numads = numads; 375 rs->sr_text = "txn_commit failed"; 376 Debug( LDAP_DEBUG_ANY, 377 LDAP_XSTRING(mdb_add) ": %s : %s (%d)\n", 378 rs->sr_text, mdb_strerror(rs->sr_err), rs->sr_err ); 379 rs->sr_err = LDAP_OTHER; 380 goto return_results; 381 } 382 } 383 384 Debug(LDAP_DEBUG_TRACE, 385 LDAP_XSTRING(mdb_add) ": added%s id=%08lx dn=\"%s\"\n", 386 op->o_noop ? " (no-op)" : "", 387 op->ora_e->e_id, op->ora_e->e_dn ); 388 389 rs->sr_text = NULL; 390 if( num_ctrls ) rs->sr_ctrls = ctrls; 391 392 return_results: 393 success = rs->sr_err; 394 send_ldap_result( op, rs ); 395 396 if( moi == &opinfo ) { 397 if( txn != NULL ) { 398 mdb->mi_numads = numads; 399 mdb_txn_abort( txn ); 400 } 401 if ( opinfo.moi_oe.oe_key ) { 402 LDAP_SLIST_REMOVE( &op->o_extra, &opinfo.moi_oe, OpExtra, oe_next ); 403 } 404 } else { 405 moi->moi_ref--; 406 } 407 408 if( success == LDAP_SUCCESS ) { 409 #if 0 410 if ( mdb->bi_txn_cp_kbyte ) { 411 TXN_CHECKPOINT( mdb->bi_dbenv, 412 mdb->bi_txn_cp_kbyte, mdb->bi_txn_cp_min, 0 ); 413 } 414 #endif 415 } 416 417 slap_graduate_commit_csn( op ); 418 419 if( postread_ctrl != NULL && (*postread_ctrl) != NULL ) { 420 slap_sl_free( (*postread_ctrl)->ldctl_value.bv_val, op->o_tmpmemctx ); 421 slap_sl_free( *postread_ctrl, op->o_tmpmemctx ); 422 } 423 return rs->sr_err; 424 } 425