static int constraint_check_count_violation( Modifications *m, Entry *target_entry, constraint *cp ) { BerVarray b = NULL; unsigned ce = 0; unsigned ca; int j; for ( j = 0; cp->ap[j]; j++ ) { /* Get this attribute count */ if ( target_entry ) ce = constraint_count_attr( target_entry, cp->ap[j] ); for( ; m; m = m->sml_next ) { if ( cp->ap[j] == m->sml_desc ) { ca = m->sml_numvals; switch ( m->sml_op ) { case LDAP_MOD_DELETE: case SLAP_MOD_SOFTDEL: if ( !ca || ca > ce ) { ce = 0; } else { /* No need to check for values' validity. Invalid values * cause the whole transaction to die anyway. */ ce -= ca; } break; case LDAP_MOD_ADD: case SLAP_MOD_SOFTADD: ce += ca; break; case LDAP_MOD_REPLACE: ce = ca; break; #if 0 /* TODO */ case handle SLAP_MOD_ADD_IF_NOT_PRESENT: #endif default: /* impossible! assert? */ return 1; } Debug(LDAP_DEBUG_TRACE, "==> constraint_check_count_violation ce = %u, " "ca = %u, cp->count = %lu\n", ce, ca, (unsigned long) cp->count); } } } return ( ce > cp->count ); }
static int constraint_update( Operation *op, SlapReply *rs ) { slap_overinst *on = (slap_overinst *) op->o_bd->bd_info; Backend *be = op->o_bd; constraint *c = on->on_bi.bi_private, *cp; Entry *target_entry = NULL, *target_entry_copy = NULL; Modifications *modlist, *m; BerVarray b = NULL; int i; struct berval rsv = BER_BVC("modify breaks constraint"); int rc; char *msg = NULL; if (get_relax(op)) { return SLAP_CB_CONTINUE; } switch ( op->o_tag ) { case LDAP_REQ_MODIFY: modlist = op->orm_modlist; break; case LDAP_REQ_MODRDN: modlist = op->orr_modlist; break; default: /* impossible! assert? */ return LDAP_OTHER; } Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE, "constraint_update()\n", 0,0,0); if ((m = modlist) == NULL) { op->o_bd->bd_info = (BackendInfo *)(on->on_info); send_ldap_error(op, rs, LDAP_INVALID_SYNTAX, "constraint_update() got null modlist"); return(rs->sr_err); } /* Do we need to count attributes? */ for(cp = c; cp; cp = cp->ap_next) { if (cp->count != 0 || cp->set || cp->restrict_lud != 0) { op->o_bd = on->on_info->oi_origdb; rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &target_entry ); op->o_bd = be; if (rc != 0 || target_entry == NULL) { Debug(LDAP_DEBUG_TRACE, "==> constraint_update rc = %d DN=\"%s\"%s\n", rc, op->o_req_ndn.bv_val, target_entry ? "" : " not found" ); if ( rc == 0 ) rc = LDAP_CONSTRAINT_VIOLATION; goto mod_violation; } break; } } rc = LDAP_CONSTRAINT_VIOLATION; for(;m; m = m->sml_next) { unsigned ce = 0; if (is_at_operational( m->sml_desc->ad_type )) continue; if ((( m->sml_op & LDAP_MOD_OP ) != LDAP_MOD_ADD) && (( m->sml_op & LDAP_MOD_OP ) != LDAP_MOD_REPLACE) && (( m->sml_op & LDAP_MOD_OP ) != LDAP_MOD_DELETE)) continue; /* we only care about ADD and REPLACE modifications */ /* and DELETE are used to track attribute count */ if ((( b = m->sml_values ) == NULL ) || (b[0].bv_val == NULL)) continue; /* Get this attribute count, if needed */ if (target_entry) ce = constraint_count_attr(target_entry, m->sml_desc); for(cp = c; cp; cp = cp->ap_next) { int j; for (j = 0; cp->ap[j]; j++) { if (cp->ap[j] == m->sml_desc) { break; } } if (cp->ap[j] == NULL) continue; if (cp->restrict_lud != NULL && constraint_check_restrict(op, cp, target_entry) == 0) { continue; } if (cp->count != 0) { unsigned ca; if (m->sml_op == LDAP_MOD_DELETE) ce = 0; for (ca = 0; b[ca].bv_val; ++ca); Debug(LDAP_DEBUG_TRACE, "==> constraint_update ce = %u, " "ca = %u, cp->count = %lu\n", ce, ca, (unsigned long) cp->count); if (m->sml_op == LDAP_MOD_ADD) { if (ca + ce > cp->count) { rc = LDAP_CONSTRAINT_VIOLATION; goto mod_violation; } } if (m->sml_op == LDAP_MOD_REPLACE) { if (ca > cp->count) { rc = LDAP_CONSTRAINT_VIOLATION; goto mod_violation; } ce = ca; } } /* DELETE are to be ignored beyond this point */ if (( m->sml_op & LDAP_MOD_OP ) == LDAP_MOD_DELETE) continue; for ( i = 0; b[i].bv_val; i++ ) { rc = constraint_violation( cp, &b[i], op, rs ); if ( rc ) { goto mod_violation; } } if (cp->set && target_entry) { if (target_entry_copy == NULL) { Modifications *ml; target_entry_copy = entry_dup(target_entry); /* if rename, set the new entry's name * (in normalized form only) */ if ( op->o_tag == LDAP_REQ_MODRDN ) { struct berval pdn, ndn = BER_BVNULL; if ( op->orr_nnewSup ) { pdn = *op->orr_nnewSup; } else { dnParent( &target_entry_copy->e_nname, &pdn ); } build_new_dn( &ndn, &pdn, &op->orr_nnewrdn, NULL ); ber_memfree( target_entry_copy->e_nname.bv_val ); target_entry_copy->e_nname = ndn; ber_bvreplace( &target_entry_copy->e_name, &ndn ); } /* apply modifications, in an attempt * to estimate what the entry would * look like in case all modifications * pass */ for ( ml = modlist; ml; ml = ml->sml_next ) { Modification *mod = &ml->sml_mod; const char *text; char textbuf[SLAP_TEXT_BUFLEN]; size_t textlen = sizeof(textbuf); int err; switch ( mod->sm_op ) { case LDAP_MOD_ADD: err = modify_add_values( target_entry_copy, mod, get_permissiveModify(op), &text, textbuf, textlen ); break; case LDAP_MOD_DELETE: err = modify_delete_values( target_entry_copy, mod, get_permissiveModify(op), &text, textbuf, textlen ); break; case LDAP_MOD_REPLACE: err = modify_replace_values( target_entry_copy, mod, get_permissiveModify(op), &text, textbuf, textlen ); break; case LDAP_MOD_INCREMENT: err = modify_increment_values( target_entry_copy, mod, get_permissiveModify(op), &text, textbuf, textlen ); break; case SLAP_MOD_SOFTADD: mod->sm_op = LDAP_MOD_ADD; err = modify_add_values( target_entry_copy, mod, get_permissiveModify(op), &text, textbuf, textlen ); mod->sm_op = SLAP_MOD_SOFTADD; if ( err == LDAP_TYPE_OR_VALUE_EXISTS ) { err = LDAP_SUCCESS; } break; case SLAP_MOD_SOFTDEL: mod->sm_op = LDAP_MOD_ADD; err = modify_delete_values( target_entry_copy, mod, get_permissiveModify(op), &text, textbuf, textlen ); mod->sm_op = SLAP_MOD_SOFTDEL; if ( err == LDAP_NO_SUCH_ATTRIBUTE ) { err = LDAP_SUCCESS; } break; case SLAP_MOD_ADD_IF_NOT_PRESENT: if ( attr_find( target_entry_copy->e_attrs, mod->sm_desc ) ) { err = LDAP_SUCCESS; break; } mod->sm_op = LDAP_MOD_ADD; err = modify_add_values( target_entry_copy, mod, get_permissiveModify(op), &text, textbuf, textlen ); mod->sm_op = SLAP_MOD_ADD_IF_NOT_PRESENT; break; default: err = LDAP_OTHER; break; } if ( err != LDAP_SUCCESS ) { rc = err; goto mod_violation; } } } if ( acl_match_set(&cp->val, op, target_entry_copy, NULL) == 0) { rc = LDAP_CONSTRAINT_VIOLATION; goto mod_violation; } } } } if (target_entry) { op->o_bd = on->on_info->oi_origdb; be_entry_release_r(op, target_entry); op->o_bd = be; } if (target_entry_copy) { entry_free(target_entry_copy); } return SLAP_CB_CONTINUE; mod_violation: /* violation */ if (target_entry) { op->o_bd = on->on_info->oi_origdb; be_entry_release_r(op, target_entry); op->o_bd = be; } if (target_entry_copy) { entry_free(target_entry_copy); } op->o_bd->bd_info = (BackendInfo *)(on->on_info); if ( rc == LDAP_CONSTRAINT_VIOLATION ) { msg = print_message( &rsv, m->sml_desc ); } send_ldap_error( op, rs, LDAP_CONSTRAINT_VIOLATION, msg ); ch_free(msg); return (rs->sr_err); }