Back to index

openldap  2.4.31
modify.c
Go to the documentation of this file.
00001 /* $OpenLDAP$ */
00002 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
00003  *
00004  * Copyright 1998-2012 The OpenLDAP Foundation.
00005  * All rights reserved.
00006  *
00007  * Redistribution and use in source and binary forms, with or without
00008  * modification, are permitted only as authorized by the OpenLDAP
00009  * Public License.
00010  *
00011  * A copy of this license is available in the file LICENSE in the
00012  * top-level directory of the distribution or, alternatively, at
00013  * <http://www.OpenLDAP.org/license.html>.
00014  */
00015 /* Portions Copyright (c) 1995 Regents of the University of Michigan.
00016  * All rights reserved.
00017  *
00018  * Redistribution and use in source and binary forms are permitted
00019  * provided that this notice is preserved and that due credit is given
00020  * to the University of Michigan at Ann Arbor. The name of the University
00021  * may not be used to endorse or promote products derived from this
00022  * software without specific prior written permission. This software
00023  * is provided ``as is'' without express or implied warranty.
00024  */
00025 
00026 #include "portable.h"
00027 
00028 #include <stdio.h>
00029 
00030 #include <ac/socket.h>
00031 #include <ac/string.h>
00032 #include <ac/time.h>
00033 
00034 #include "slap.h"
00035 #include "lutil.h"
00036 
00037 
00038 int
00039 do_modify(
00040     Operation *op,
00041     SlapReply *rs )
00042 {
00043        struct berval dn = BER_BVNULL;
00044        char          textbuf[ SLAP_TEXT_BUFLEN ];
00045        size_t        textlen = sizeof( textbuf );
00046 #ifdef LDAP_DEBUG
00047        Modifications *tmp;
00048 #endif
00049 
00050        Debug( LDAP_DEBUG_TRACE, "%s do_modify\n",
00051               op->o_log_prefix, 0, 0 );
00052        /*
00053         * Parse the modify request.  It looks like this:
00054         *
00055         *     ModifyRequest := [APPLICATION 6] SEQUENCE {
00056         *            name   DistinguishedName,
00057         *            mods   SEQUENCE OF SEQUENCE {
00058         *                   operation     ENUMERATED {
00059         *                          add    (0),
00060         *                          delete (1),
00061         *                          replace       (2)
00062         *                   },
00063         *                   modification  SEQUENCE {
00064         *                          type   AttributeType,
00065         *                          values SET OF AttributeValue
00066         *                   }
00067         *            }
00068         *     }
00069         */
00070 
00071        if ( ber_scanf( op->o_ber, "{m" /*}*/, &dn ) == LBER_ERROR ) {
00072               Debug( LDAP_DEBUG_ANY, "%s do_modify: ber_scanf failed\n",
00073                      op->o_log_prefix, 0, 0 );
00074               send_ldap_discon( op, rs, LDAP_PROTOCOL_ERROR, "decoding error" );
00075               return SLAPD_DISCONNECT;
00076        }
00077 
00078        Debug( LDAP_DEBUG_ARGS, "%s do_modify: dn (%s)\n",
00079               op->o_log_prefix, dn.bv_val, 0 );
00080 
00081        rs->sr_err = slap_parse_modlist( op, rs, op->o_ber, &op->oq_modify );
00082        if ( rs->sr_err != LDAP_SUCCESS ) {
00083               Debug( LDAP_DEBUG_ANY, "%s do_modify: slap_parse_modlist failed err=%d msg=%s\n",
00084                      op->o_log_prefix, rs->sr_err, rs->sr_text );
00085               send_ldap_result( op, rs );
00086               goto cleanup;
00087        }
00088 
00089        if( get_ctrls( op, rs, 1 ) != LDAP_SUCCESS ) {
00090               Debug( LDAP_DEBUG_ANY, "%s do_modify: get_ctrls failed\n",
00091                      op->o_log_prefix, 0, 0 );
00092               /* get_ctrls has sent results.     Now clean up. */
00093               goto cleanup;
00094        }
00095 
00096        rs->sr_err = dnPrettyNormal( NULL, &dn, &op->o_req_dn, &op->o_req_ndn,
00097               op->o_tmpmemctx );
00098        if( rs->sr_err != LDAP_SUCCESS ) {
00099               Debug( LDAP_DEBUG_ANY, "%s do_modify: invalid dn (%s)\n",
00100                      op->o_log_prefix, dn.bv_val, 0 );
00101               send_ldap_error( op, rs, LDAP_INVALID_DN_SYNTAX, "invalid DN" );
00102               goto cleanup;
00103        }
00104 
00105        op->orm_no_opattrs = 0;
00106 
00107 #ifdef LDAP_DEBUG
00108        Debug( LDAP_DEBUG_ARGS, "%s modifications:\n",
00109                      op->o_log_prefix, 0, 0 );
00110 
00111        for ( tmp = op->orm_modlist; tmp != NULL; tmp = tmp->sml_next ) {
00112               Debug( LDAP_DEBUG_ARGS, "\t%s: %s\n",
00113                      tmp->sml_op == LDAP_MOD_ADD ? "add" :
00114                             (tmp->sml_op == LDAP_MOD_INCREMENT ? "increment" :
00115                             (tmp->sml_op == LDAP_MOD_DELETE ? "delete" :
00116                                    "replace")), tmp->sml_type.bv_val, 0 );
00117 
00118               if ( tmp->sml_values == NULL ) {
00119                      Debug( LDAP_DEBUG_ARGS, "%s\n",
00120                         "\t\tno values", NULL, NULL );
00121               } else if ( BER_BVISNULL( &tmp->sml_values[ 0 ] ) ) {
00122                      Debug( LDAP_DEBUG_ARGS, "%s\n",
00123                         "\t\tzero values", NULL, NULL );
00124               } else if ( BER_BVISNULL( &tmp->sml_values[ 1 ] ) ) {
00125                      Debug( LDAP_DEBUG_ARGS, "%s, length %ld\n",
00126                         "\t\tone value", (long) tmp->sml_values[0].bv_len, NULL );
00127               } else {
00128                      Debug( LDAP_DEBUG_ARGS, "%s\n",
00129                         "\t\tmultiple values", NULL, NULL );
00130               }
00131        }
00132 
00133        if ( StatslogTest( LDAP_DEBUG_STATS ) ) {
00134               char abuf[BUFSIZ/2], *ptr = abuf;
00135               int len = 0;
00136 
00137               Statslog( LDAP_DEBUG_STATS, "%s MOD dn=\"%s\"\n",
00138                      op->o_log_prefix, op->o_req_dn.bv_val, 0, 0, 0 );
00139 
00140               for ( tmp = op->orm_modlist; tmp != NULL; tmp = tmp->sml_next ) {
00141                      if (len + 1 + tmp->sml_type.bv_len > sizeof(abuf)) {
00142                             Statslog( LDAP_DEBUG_STATS, "%s MOD attr=%s\n",
00143                                 op->o_log_prefix, abuf, 0, 0, 0 );
00144 
00145                             len = 0;
00146                             ptr = abuf;
00147 
00148                             if( 1 + tmp->sml_type.bv_len > sizeof(abuf)) {
00149                                    Statslog( LDAP_DEBUG_STATS, "%s MOD attr=%s\n",
00150                                           op->o_log_prefix, tmp->sml_type.bv_val, 0, 0, 0 );
00151                                    continue;
00152                             }
00153                      }
00154                      if (len) {
00155                             *ptr++ = ' ';
00156                             len++;
00157                      }
00158                      ptr = lutil_strcopy(ptr, tmp->sml_type.bv_val);
00159                      len += tmp->sml_type.bv_len;
00160               }
00161               if (len) {
00162                      Statslog( LDAP_DEBUG_STATS, "%s MOD attr=%s\n",
00163                             op->o_log_prefix, abuf, 0, 0, 0 );
00164               }
00165        }
00166 #endif /* LDAP_DEBUG */
00167 
00168        rs->sr_err = slap_mods_check( op, op->orm_modlist,
00169               &rs->sr_text, textbuf, textlen, NULL );
00170 
00171        if ( rs->sr_err != LDAP_SUCCESS ) {
00172               send_ldap_result( op, rs );
00173               goto cleanup;
00174        }
00175 
00176        op->o_bd = frontendDB;
00177        rs->sr_err = frontendDB->be_modify( op, rs );
00178 
00179 #ifdef LDAP_X_TXN
00180        if( rs->sr_err == LDAP_X_TXN_SPECIFY_OKAY ) {
00181               /* skip cleanup */
00182               return rs->sr_err;
00183        }
00184 #endif
00185 
00186 cleanup:
00187        op->o_tmpfree( op->o_req_dn.bv_val, op->o_tmpmemctx );
00188        op->o_tmpfree( op->o_req_ndn.bv_val, op->o_tmpmemctx );
00189        if ( op->orm_modlist != NULL ) slap_mods_free( op->orm_modlist, 1 );
00190 
00191        return rs->sr_err;
00192 }
00193 
00194 int
00195 fe_op_modify( Operation *op, SlapReply *rs )
00196 {
00197        BackendDB     *op_be, *bd = op->o_bd;
00198        char          textbuf[ SLAP_TEXT_BUFLEN ];
00199        size_t        textlen = sizeof( textbuf );
00200        
00201        if ( BER_BVISEMPTY( &op->o_req_ndn ) ) {
00202               Debug( LDAP_DEBUG_ANY, "%s do_modify: root dse!\n",
00203                      op->o_log_prefix, 0, 0 );
00204               send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM,
00205                      "modify upon the root DSE not supported" );
00206               goto cleanup;
00207 
00208        } else if ( bvmatch( &op->o_req_ndn, &frontendDB->be_schemandn ) ) {
00209               Debug( LDAP_DEBUG_ANY, "%s do_modify: subschema subentry!\n",
00210                      op->o_log_prefix, 0, 0 );
00211               send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM,
00212                      "modification of subschema subentry not supported" );
00213               goto cleanup;
00214        }
00215 
00216        /*
00217         * We could be serving multiple database backends.  Select the
00218         * appropriate one, or send a referral to our "referral server"
00219         * if we don't hold it.
00220         */
00221        op->o_bd = select_backend( &op->o_req_ndn, 1 );
00222        if ( op->o_bd == NULL ) {
00223               op->o_bd = bd;
00224               rs->sr_ref = referral_rewrite( default_referral,
00225                      NULL, &op->o_req_dn, LDAP_SCOPE_DEFAULT );
00226               if ( !rs->sr_ref ) {
00227                      rs->sr_ref = default_referral;
00228               }
00229 
00230               if ( rs->sr_ref != NULL ) {
00231                      rs->sr_err = LDAP_REFERRAL;
00232                      send_ldap_result( op, rs );
00233 
00234                      if ( rs->sr_ref != default_referral ) {
00235                             ber_bvarray_free( rs->sr_ref );
00236                      }
00237 
00238               } else {
00239                      send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM,
00240                             "no global superior knowledge" );
00241               }
00242               goto cleanup;
00243        }
00244 
00245        /* If we've got a glued backend, check the real backend */
00246        op_be = op->o_bd;
00247        if ( SLAP_GLUE_INSTANCE( op->o_bd )) {
00248               op->o_bd = select_backend( &op->o_req_ndn, 0 );
00249        }
00250 
00251        /* check restrictions */
00252        if ( backend_check_restrictions( op, rs, NULL ) != LDAP_SUCCESS ) {
00253               send_ldap_result( op, rs );
00254               goto cleanup;
00255        }
00256 
00257        /* check for referrals */
00258        if ( backend_check_referrals( op, rs ) != LDAP_SUCCESS ) {
00259               goto cleanup;
00260        }
00261 
00262        rs->sr_err = slap_mods_obsolete_check( op, op->orm_modlist,
00263               &rs->sr_text, textbuf, textlen );
00264        if ( rs->sr_err != LDAP_SUCCESS ) {
00265               send_ldap_result( op, rs );
00266               goto cleanup;
00267        }
00268 
00269        /* check for modify/increment support */
00270        if ( op->orm_increment && !SLAP_INCREMENT( op->o_bd ) ) {
00271               send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM,
00272                      "modify/increment not supported in context" );
00273               goto cleanup;
00274        }
00275 
00276        /*
00277         * do the modify if 1 && (2 || 3)
00278         * 1) there is a modify function implemented in this backend;
00279         * 2) this backend is master for what it holds;
00280         * 3) it's a replica and the dn supplied is the update_ndn.
00281         */
00282        if ( op->o_bd->be_modify ) {
00283               /* do the update here */
00284               int repl_user = be_isupdate( op );
00285 
00286               /*
00287                * Multimaster slapd does not have to check for replicator dn
00288                * because it accepts each modify request
00289                */
00290               if ( !SLAP_SINGLE_SHADOW(op->o_bd) || repl_user ) {
00291                      int update = !BER_BVISEMPTY( &op->o_bd->be_update_ndn );
00292 
00293                      op->o_bd = op_be;
00294 
00295                      if ( !update ) {
00296                             rs->sr_err = slap_mods_no_user_mod_check( op, op->orm_modlist,
00297                                    &rs->sr_text, textbuf, textlen );
00298                             if ( rs->sr_err != LDAP_SUCCESS ) {
00299                                    send_ldap_result( op, rs );
00300                                    goto cleanup;
00301                             }
00302                      }
00303                      op->o_bd->be_modify( op, rs );
00304 
00305               } else { /* send a referral */
00306                      BerVarray defref = op->o_bd->be_update_refs
00307                             ? op->o_bd->be_update_refs : default_referral;
00308                      if ( defref != NULL ) {
00309                             rs->sr_ref = referral_rewrite( defref,
00310                                    NULL, &op->o_req_dn,
00311                                    LDAP_SCOPE_DEFAULT );
00312                             if ( rs->sr_ref == NULL ) {
00313                                    /* FIXME: must duplicate, because
00314                                     * overlays may muck with it */
00315                                    rs->sr_ref = defref;
00316                             }
00317                             rs->sr_err = LDAP_REFERRAL;
00318                             send_ldap_result( op, rs );
00319                             if ( rs->sr_ref != defref ) {
00320                                    ber_bvarray_free( rs->sr_ref );
00321                             }
00322 
00323                      } else {
00324                             send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM,
00325                                    "shadow context; no update referral" );
00326                      }
00327               }
00328 
00329        } else {
00330               send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM,
00331                   "operation not supported within namingContext" );
00332        }
00333 
00334 cleanup:;
00335        op->o_bd = bd;
00336        return rs->sr_err;
00337 }
00338 
00339 /*
00340  * Obsolete constraint checking.
00341  */
00342 int
00343 slap_mods_obsolete_check(
00344        Operation *op,
00345        Modifications *ml,
00346        const char **text,
00347        char *textbuf,
00348        size_t textlen )
00349 {
00350        if( get_relax( op ) ) return LDAP_SUCCESS;
00351 
00352        for ( ; ml != NULL; ml = ml->sml_next ) {
00353               if ( is_at_obsolete( ml->sml_desc->ad_type ) &&
00354                      (( ml->sml_op != LDAP_MOD_REPLACE &&
00355                             ml->sml_op != LDAP_MOD_DELETE ) ||
00356                                    ml->sml_values != NULL ))
00357               {
00358                      /*
00359                       * attribute is obsolete,
00360                       * only allow replace/delete with no values
00361                       */
00362                      snprintf( textbuf, textlen,
00363                             "%s: attribute is obsolete",
00364                             ml->sml_type.bv_val );
00365                      *text = textbuf;
00366                      return LDAP_CONSTRAINT_VIOLATION;
00367               }
00368        }
00369 
00370        return LDAP_SUCCESS;
00371 }
00372 
00373 /*
00374  * No-user-modification constraint checking.
00375  */
00376 int
00377 slap_mods_no_user_mod_check(
00378        Operation *op,
00379        Modifications *ml,
00380        const char **text,
00381        char *textbuf,
00382        size_t textlen )
00383 {
00384        for ( ; ml != NULL; ml = ml->sml_next ) {
00385               if ( !is_at_no_user_mod( ml->sml_desc->ad_type ) ) {
00386                      continue;
00387               }
00388 
00389               if ( ml->sml_flags & SLAP_MOD_INTERNAL ) {
00390                      continue;
00391               }
00392 
00393               if ( get_relax( op ) ) {
00394                      if ( ml->sml_desc->ad_type->sat_flags & SLAP_AT_MANAGEABLE ) {
00395                             ml->sml_flags |= SLAP_MOD_MANAGING;
00396                             continue;
00397                      }
00398 
00399                      /* attribute not manageable */
00400                      snprintf( textbuf, textlen,
00401                             "%s: no-user-modification attribute not manageable",
00402                             ml->sml_type.bv_val );
00403 
00404               } else {
00405                      /* user modification disallowed */
00406                      snprintf( textbuf, textlen,
00407                             "%s: no user modification allowed",
00408                             ml->sml_type.bv_val );
00409               }
00410 
00411               *text = textbuf;
00412               return LDAP_CONSTRAINT_VIOLATION;
00413        }
00414 
00415        return LDAP_SUCCESS;
00416 }
00417 
00418 int
00419 slap_mods_no_repl_user_mod_check(
00420        Operation *op,
00421        Modifications *ml,
00422        const char **text,
00423        char *textbuf,
00424        size_t textlen )
00425 {
00426        Modifications *mods;
00427        Modifications *modp;
00428 
00429        for ( mods = ml; mods != NULL; mods = mods->sml_next ) {
00430               assert( mods->sml_op == LDAP_MOD_ADD );
00431 
00432               /* check doesn't already appear */
00433               for ( modp = ml; modp != NULL; modp = modp->sml_next ) {
00434                      if ( mods->sml_desc == modp->sml_desc && mods != modp ) {
00435                             snprintf( textbuf, textlen,
00436                                    "attribute '%s' provided more than once",
00437                                    mods->sml_desc->ad_cname.bv_val );
00438                             *text = textbuf;
00439                             return LDAP_TYPE_OR_VALUE_EXISTS;
00440                      }
00441               }
00442        }
00443 
00444        return LDAP_SUCCESS;
00445 }
00446 
00447 /*
00448  * Do basic attribute type checking and syntax validation.
00449  */
00450 int slap_mods_check(
00451        Operation *op,
00452        Modifications *ml,
00453        const char **text,
00454        char *textbuf,
00455        size_t textlen,
00456        void *ctx )
00457 {
00458        int rc;
00459 
00460        for( ; ml != NULL; ml = ml->sml_next ) {
00461               AttributeDescription *ad = NULL;
00462 
00463               /* convert to attribute description */
00464               if ( ml->sml_desc == NULL ) {
00465                      rc = slap_bv2ad( &ml->sml_type, &ml->sml_desc, text );
00466                      if( rc != LDAP_SUCCESS ) {
00467                             if ( get_no_schema_check( op )) {
00468                                    rc = slap_bv2undef_ad( &ml->sml_type, &ml->sml_desc,
00469                                           text, 0 );
00470                             }
00471                      }
00472                      if( rc != LDAP_SUCCESS ) {
00473                             snprintf( textbuf, textlen, "%s: %s",
00474                                    ml->sml_type.bv_val, *text );
00475                             *text = textbuf;
00476                             return rc;
00477                      }
00478               }
00479 
00480               ad = ml->sml_desc;
00481 
00482               if( slap_syntax_is_binary( ad->ad_type->sat_syntax )
00483                      && !slap_ad_is_binary( ad ))
00484               {
00485                      /* attribute requires binary transfer */
00486                      snprintf( textbuf, textlen,
00487                             "%s: requires ;binary transfer",
00488                             ml->sml_type.bv_val );
00489                      *text = textbuf;
00490                      return LDAP_UNDEFINED_TYPE;
00491               }
00492 
00493               if( !slap_syntax_is_binary( ad->ad_type->sat_syntax )
00494                      && slap_ad_is_binary( ad ))
00495               {
00496                      /* attribute does not require binary transfer */
00497                      snprintf( textbuf, textlen,
00498                             "%s: disallows ;binary transfer",
00499                             ml->sml_type.bv_val );
00500                      *text = textbuf;
00501                      return LDAP_UNDEFINED_TYPE;
00502               }
00503 
00504               if( slap_ad_is_tag_range( ad )) {
00505                      /* attribute requires binary transfer */
00506                      snprintf( textbuf, textlen,
00507                             "%s: inappropriate use of tag range option",
00508                             ml->sml_type.bv_val );
00509                      *text = textbuf;
00510                      return LDAP_UNDEFINED_TYPE;
00511               }
00512 
00513 #if 0
00514               if ( is_at_obsolete( ad->ad_type ) &&
00515                      (( ml->sml_op != LDAP_MOD_REPLACE &&
00516                             ml->sml_op != LDAP_MOD_DELETE ) ||
00517                                    ml->sml_values != NULL ))
00518               {
00519                      /*
00520                       * attribute is obsolete,
00521                       * only allow replace/delete with no values
00522                       */
00523                      snprintf( textbuf, textlen,
00524                             "%s: attribute is obsolete",
00525                             ml->sml_type.bv_val );
00526                      *text = textbuf;
00527                      return LDAP_CONSTRAINT_VIOLATION;
00528               }
00529 #endif
00530 
00531               if ( ml->sml_op == LDAP_MOD_INCREMENT &&
00532 #ifdef SLAPD_REAL_SYNTAX
00533                      !is_at_syntax( ad->ad_type, SLAPD_REAL_SYNTAX ) &&
00534 #endif
00535                      !is_at_syntax( ad->ad_type, SLAPD_INTEGER_SYNTAX ) )
00536               {
00537                      /*
00538                       * attribute values must be INTEGER or REAL
00539                       */
00540                      snprintf( textbuf, textlen,
00541                             "%s: attribute syntax inappropriate for increment",
00542                             ml->sml_type.bv_val );
00543                      *text = textbuf;
00544                      return LDAP_CONSTRAINT_VIOLATION;
00545               }
00546 
00547               /*
00548                * check values
00549                */
00550               if( ml->sml_values != NULL ) {
00551                      ber_len_t nvals;
00552                      slap_syntax_validate_func *validate =
00553                             ad->ad_type->sat_syntax->ssyn_validate;
00554                      slap_syntax_transform_func *pretty =
00555                             ad->ad_type->sat_syntax->ssyn_pretty;
00556  
00557                      if( !pretty && !validate ) {
00558                             *text = "no validator for syntax";
00559                             snprintf( textbuf, textlen,
00560                                    "%s: no validator for syntax %s",
00561                                    ml->sml_type.bv_val,
00562                                    ad->ad_type->sat_syntax->ssyn_oid );
00563                             *text = textbuf;
00564                             return LDAP_INVALID_SYNTAX;
00565                      }
00566 
00567                      /*
00568                       * check that each value is valid per syntax
00569                       *     and pretty if appropriate
00570                       */
00571                      for ( nvals = 0; !BER_BVISNULL( &ml->sml_values[nvals] ); nvals++ ) {
00572                             struct berval pval;
00573 
00574                             if ( pretty ) {
00575                                    rc = ordered_value_pretty( ad,
00576                                           &ml->sml_values[nvals], &pval, ctx );
00577                             } else {
00578                                    rc = ordered_value_validate( ad,
00579                                           &ml->sml_values[nvals], ml->sml_op );
00580                             }
00581 
00582                             if( rc != 0 ) {
00583                                    snprintf( textbuf, textlen,
00584                                           "%s: value #%ld invalid per syntax",
00585                                           ml->sml_type.bv_val, (long) nvals );
00586                                    *text = textbuf;
00587                                    return LDAP_INVALID_SYNTAX;
00588                             }
00589 
00590                             if( pretty ) {
00591                                    ber_memfree_x( ml->sml_values[nvals].bv_val, ctx );
00592                                    ml->sml_values[nvals] = pval;
00593                             }
00594                      }
00595                      ml->sml_values[nvals].bv_len = 0;
00596                      ml->sml_numvals = nvals;
00597 
00598                      /*
00599                       * a rough single value check... an additional check is needed
00600                       * to catch add of single value to existing single valued attribute
00601                       */
00602                      if ((ml->sml_op == LDAP_MOD_ADD || ml->sml_op == LDAP_MOD_REPLACE)
00603                             && nvals > 1 && is_at_single_value( ad->ad_type ))
00604                      {
00605                             snprintf( textbuf, textlen,
00606                                    "%s: multiple values provided",
00607                                    ml->sml_type.bv_val );
00608                             *text = textbuf;
00609                             return LDAP_CONSTRAINT_VIOLATION;
00610                      }
00611 
00612                      /* if the type has a normalizer, generate the
00613                       * normalized values. otherwise leave them NULL.
00614                       *
00615                       * this is different from the rule for attributes
00616                       * in an entry - in an attribute list, the normalized
00617                       * value is set equal to the non-normalized value
00618                       * when there is no normalizer.
00619                       */
00620                      if( nvals && ad->ad_type->sat_equality &&
00621                             ad->ad_type->sat_equality->smr_normalize )
00622                      {
00623                             ml->sml_nvalues = ber_memalloc_x(
00624                                    (nvals+1)*sizeof(struct berval), ctx );
00625 
00626                             for ( nvals = 0; !BER_BVISNULL( &ml->sml_values[nvals] ); nvals++ ) {
00627                                    rc = ordered_value_normalize(
00628                                           SLAP_MR_VALUE_OF_ATTRIBUTE_SYNTAX,
00629                                           ad,
00630                                           ad->ad_type->sat_equality,
00631                                           &ml->sml_values[nvals], &ml->sml_nvalues[nvals], ctx );
00632                                    if ( rc ) {
00633                                           Debug( LDAP_DEBUG_ANY,
00634                                                  "<= str2entry NULL (ssyn_normalize %d)\n",
00635                                                  rc, 0, 0 );
00636                                           snprintf( textbuf, textlen,
00637                                                  "%s: value #%ld normalization failed",
00638                                                  ml->sml_type.bv_val, (long) nvals );
00639                                           *text = textbuf;
00640                                           BER_BVZERO( &ml->sml_nvalues[nvals] );
00641                                           return rc;
00642                                    }
00643                             }
00644 
00645                             BER_BVZERO( &ml->sml_nvalues[nvals] );
00646                      }
00647 
00648                      /* check for duplicates, but ignore Deletes.
00649                       */
00650                      if( nvals > 1 && ml->sml_op != LDAP_MOD_DELETE ) {
00651                             int i;
00652                             rc = slap_sort_vals( ml, text, &i, ctx );
00653                             if ( rc == LDAP_TYPE_OR_VALUE_EXISTS ) {
00654                                    /* value exists already */
00655                                    snprintf( textbuf, textlen,
00656                                           "%s: value #%d provided more than once",
00657                                           ml->sml_desc->ad_cname.bv_val, i );
00658                                    *text = textbuf;
00659                             }
00660                             if ( rc )
00661                                    return rc;
00662                      }
00663               } else {
00664                      ml->sml_numvals = 0;
00665               }
00666        }
00667 
00668        return LDAP_SUCCESS;
00669 }
00670 
00671 /* Sort a set of values. An (Attribute *) may be used interchangeably here
00672  * instead of a (Modifications *) structure.
00673  *
00674  * Uses Quicksort + Insertion sort for small arrays
00675  */
00676 
00677 int
00678 slap_sort_vals(
00679        Modifications *ml,
00680        const char **text,
00681        int *dup,
00682        void *ctx )
00683 {
00684        AttributeDescription *ad;
00685        MatchingRule *mr;
00686        int istack[sizeof(int)*16];
00687        int i, j, k, l, ir, jstack, match, *ix, itmp, nvals, rc = LDAP_SUCCESS;
00688        int is_norm;
00689        struct berval a, *cv;
00690 
00691 #define SMALL 8
00692 #define       SWAP(a,b,tmp) tmp=(a);(a)=(b);(b)=tmp
00693 #define       COMP(a,b)     match=0; rc = ordered_value_match( &match, \
00694                                           ad, mr, SLAP_MR_EQUALITY \
00695                                                         | SLAP_MR_VALUE_OF_ASSERTION_SYNTAX \
00696                                                         | SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH \
00697                                                         | SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH, \
00698                                                         &(a), &(b), text );
00699 
00700 #define       IX(x)  ix[x]
00701 #define       EXCH(x,y)     SWAP(ix[x],ix[y],itmp)
00702 #define       SETA(x)       itmp = ix[x]; a = cv[itmp]
00703 #define       GETA(x)       ix[x] = itmp;
00704 #define       SET(x,y)      ix[x] = ix[y]
00705 
00706        ad = ml->sml_desc;
00707        nvals = ml->sml_numvals;
00708        if ( nvals <= 1 )
00709               goto ret;
00710 
00711        /* For Modifications, sml_nvalues is NULL if normalization wasn't needed.
00712         * For Attributes, sml_nvalues == sml_values when normalization isn't needed.
00713         */
00714        if ( ml->sml_nvalues && ml->sml_nvalues != ml->sml_values ) {
00715               cv = ml->sml_nvalues;
00716               is_norm = 1;
00717        } else {
00718               cv = ml->sml_values;
00719               is_norm = 0;
00720        }
00721 
00722        if ( ad == slap_schema.si_ad_objectClass )
00723               mr = NULL;    /* shortcut matching */
00724        else
00725               mr = ad->ad_type->sat_equality;
00726 
00727        /* record indices to preserve input ordering */
00728        ix = slap_sl_malloc( nvals * sizeof(int), ctx );
00729        for (i=0; i<nvals; i++) ix[i] = i;
00730 
00731        ir = nvals-1;
00732        l = 0;
00733        jstack = 0;
00734 
00735        for(;;) {
00736               if (ir - l < SMALL) {       /* Insertion sort */
00737                      match=1;
00738                      for (j=l+1;j<=ir;j++) {
00739                             SETA(j);
00740                             for (i=j-1;i>=0;i--) {
00741                                    COMP(cv[IX(i)], a);
00742                                    if ( match <= 0 )
00743                                           break;
00744                                    SET(i+1,i);
00745                             }
00746                             GETA(i+1);
00747                             if ( match == 0 ) goto done;
00748                      }
00749                      if ( jstack == 0 ) break;
00750                      if ( match == 0 ) break;
00751                      ir = istack[jstack--];
00752                      l = istack[jstack--];
00753               } else {
00754                      k = (l + ir) >> 1;   /* Choose median of left, center, right */
00755                      EXCH(k, l+1);
00756                      COMP( cv[IX(l)], cv[IX(ir)] );
00757                      if ( match > 0 ) {
00758                             EXCH(l, ir);
00759                      } else if ( match == 0 ) {
00760                             i = ir;
00761                             break;
00762                      }
00763                      COMP( cv[IX(l+1)], cv[IX(ir)] );
00764                      if ( match > 0 ) {
00765                             EXCH(l+1, ir);
00766                      } else if ( match == 0 ) {
00767                             i = ir;
00768                             break;
00769                      }
00770                      COMP( cv[IX(l)], cv[IX(l+1)] );
00771                      if ( match > 0 ) {
00772                             EXCH(l, l+1);
00773                      } else if ( match == 0 ) {
00774                             i = l;
00775                             break;
00776                      }
00777                      i = l+1;
00778                      j = ir;
00779                      a = cv[IX(i)];
00780                      for(;;) {
00781                             do {
00782                                    i++;
00783                                    COMP( cv[IX(i)], a );
00784                             } while( match < 0 );
00785                             while( match > 0 ) {
00786                                    j--;
00787                                    COMP( cv[IX(j)], a );
00788                             }
00789                             if (j < i) {
00790                                    match = 1;
00791                                    break;
00792                             }
00793                             if ( match == 0 ) {
00794                                    i = l+1;
00795                                    break;
00796                             }
00797                             EXCH(i,j);
00798                      }
00799                      if ( match == 0 )
00800                             break;
00801                      EXCH(l+1,j);
00802                      jstack += 2;
00803                      if (ir-i+1 >= j) {
00804                             istack[jstack] = ir;
00805                             istack[jstack-1] = i;
00806                             ir = j;
00807                      } else {
00808                             istack[jstack] = j;
00809                             istack[jstack-1] = l;
00810                             l = i;
00811                      }
00812               }
00813        }
00814        done:
00815        if ( match == 0 && i >= 0 )
00816               *dup = ix[i];
00817 
00818        /* For sorted attributes, put the values in index order */
00819        if ( rc == LDAP_SUCCESS && match &&
00820               ( ad->ad_type->sat_flags & SLAP_AT_SORTED_VAL )) {
00821               BerVarray tmpv = slap_sl_malloc( sizeof( struct berval ) * nvals, ctx );
00822               for ( i = 0; i<nvals; i++ )
00823                      tmpv[i] = cv[ix[i]];
00824               for ( i = 0; i<nvals; i++ )
00825                      cv[i] = tmpv[i];
00826               /* Check if the non-normalized array needs to move too */
00827               if ( is_norm ) {
00828                      cv = ml->sml_values;
00829                      for ( i = 0; i<nvals; i++ )
00830                             tmpv[i] = cv[ix[i]];
00831                      for ( i = 0; i<nvals; i++ )
00832                             cv[i] = tmpv[i];
00833               }
00834               slap_sl_free( tmpv, ctx );
00835        }
00836 
00837        slap_sl_free( ix, ctx );
00838 
00839        if ( rc == LDAP_SUCCESS && match == 0 ) {
00840               /* value exists already */
00841               assert( i >= 0 );
00842               assert( i < nvals );
00843               rc = LDAP_TYPE_OR_VALUE_EXISTS;
00844        }
00845  ret:
00846        return rc;
00847 }
00848 
00849 /* Enter with bv->bv_len = sizeof buffer, returns with
00850  * actual length of string
00851  */
00852 void slap_timestamp( time_t *tm, struct berval *bv )
00853 {
00854        struct tm ltm;
00855 
00856        ldap_pvt_gmtime( tm, &ltm );
00857 
00858        bv->bv_len = lutil_gentime( bv->bv_val, bv->bv_len, &ltm );
00859 }
00860 
00861 /* Called for all modify and modrdn ops. If the current op was replicated
00862  * from elsewhere, all of the attrs should already be present.
00863  */
00864 void slap_mods_opattrs(
00865        Operation *op,
00866        Modifications **modsp,
00867        int manage_ctxcsn )
00868 {
00869        struct berval name, timestamp, csn = BER_BVNULL;
00870        struct berval nname;
00871        char timebuf[ LDAP_LUTIL_GENTIME_BUFSIZE ];
00872        char csnbuf[ LDAP_PVT_CSNSTR_BUFSIZE ];
00873        Modifications *mod, **modtail, *modlast;
00874        int gotcsn = 0, gotmname = 0, gotmtime = 0;
00875 
00876        if ( SLAP_LASTMOD( op->o_bd ) && !op->orm_no_opattrs ) {
00877               char *ptr;
00878               timestamp.bv_val = timebuf;
00879               for ( modtail = modsp; *modtail; modtail = &(*modtail)->sml_next ) {
00880                      if ( (*modtail)->sml_op != LDAP_MOD_ADD &&
00881                             (*modtail)->sml_op != SLAP_MOD_SOFTADD &&
00882                             (*modtail)->sml_op != SLAP_MOD_ADD_IF_NOT_PRESENT &&
00883                             (*modtail)->sml_op != LDAP_MOD_REPLACE )
00884                      {
00885                             continue;
00886                      }
00887 
00888                      if ( (*modtail)->sml_desc == slap_schema.si_ad_entryCSN )
00889                      {
00890                             csn = (*modtail)->sml_values[0];
00891                             gotcsn = 1;
00892 
00893                      } else if ( (*modtail)->sml_desc == slap_schema.si_ad_modifiersName )
00894                      {
00895                             gotmname = 1;
00896 
00897                      } else if ( (*modtail)->sml_desc == slap_schema.si_ad_modifyTimestamp )
00898                      {
00899                             gotmtime = 1;
00900                      }
00901               }
00902 
00903               if ( BER_BVISEMPTY( &op->o_csn )) {
00904                      if ( !gotcsn ) {
00905                             csn.bv_val = csnbuf;
00906                             csn.bv_len = sizeof( csnbuf );
00907                             slap_get_csn( op, &csn, manage_ctxcsn );
00908 
00909                      } else {
00910                             if ( manage_ctxcsn ) {
00911                                    slap_queue_csn( op, &csn );
00912                             }
00913                      }
00914 
00915               } else {
00916                      csn = op->o_csn;
00917               }
00918 
00919               ptr = ber_bvchr( &csn, '#' );
00920               if ( ptr ) {
00921                      timestamp.bv_len = STRLENOF("YYYYMMDDHHMMSSZ");
00922                      AC_MEMCPY( timebuf, csn.bv_val, timestamp.bv_len );
00923                      timebuf[timestamp.bv_len-1] = 'Z';
00924                      timebuf[timestamp.bv_len] = '\0';
00925 
00926               } else {
00927                      time_t now = slap_get_time();
00928 
00929                      timestamp.bv_len = sizeof(timebuf);
00930 
00931                      slap_timestamp( &now, &timestamp );
00932               }
00933 
00934               if ( BER_BVISEMPTY( &op->o_dn ) ) {
00935                      BER_BVSTR( &name, SLAPD_ANONYMOUS );
00936                      nname = name;
00937 
00938               } else {
00939                      name = op->o_dn;
00940                      nname = op->o_ndn;
00941               }
00942 
00943               if ( !gotcsn ) {
00944                      mod = (Modifications *) ch_malloc( sizeof( Modifications ) );
00945                      mod->sml_op = LDAP_MOD_REPLACE;
00946                      mod->sml_flags = SLAP_MOD_INTERNAL;
00947                      mod->sml_next = NULL;
00948                      BER_BVZERO( &mod->sml_type );
00949                      mod->sml_desc = slap_schema.si_ad_entryCSN;
00950                      mod->sml_numvals = 1;
00951                      mod->sml_values = (BerVarray) ch_malloc( 2 * sizeof( struct berval ) );
00952                      ber_dupbv( &mod->sml_values[0], &csn );
00953                      BER_BVZERO( &mod->sml_values[1] );
00954                      assert( !BER_BVISNULL( &mod->sml_values[0] ) );
00955                      mod->sml_nvalues = NULL;
00956                      *modtail = mod;
00957                      modlast = mod;
00958                      modtail = &mod->sml_next;
00959               }
00960 
00961               if ( !gotmname ) {
00962                      mod = (Modifications *) ch_malloc( sizeof( Modifications ) );
00963                      mod->sml_op = LDAP_MOD_REPLACE;
00964                      mod->sml_flags = SLAP_MOD_INTERNAL;
00965                      mod->sml_next = NULL;
00966                      BER_BVZERO( &mod->sml_type );
00967                      mod->sml_desc = slap_schema.si_ad_modifiersName;
00968                      mod->sml_numvals = 1;
00969                      mod->sml_values = (BerVarray) ch_malloc( 2 * sizeof( struct berval ) );
00970                      ber_dupbv( &mod->sml_values[0], &name );
00971                      BER_BVZERO( &mod->sml_values[1] );
00972                      assert( !BER_BVISNULL( &mod->sml_values[0] ) );
00973                      mod->sml_nvalues =
00974                             (BerVarray) ch_malloc( 2 * sizeof( struct berval ) );
00975                      ber_dupbv( &mod->sml_nvalues[0], &nname );
00976                      BER_BVZERO( &mod->sml_nvalues[1] );
00977                      assert( !BER_BVISNULL( &mod->sml_nvalues[0] ) );
00978                      *modtail = mod;
00979                      modtail = &mod->sml_next;
00980               }
00981 
00982               if ( !gotmtime ) {
00983                      mod = (Modifications *) ch_malloc( sizeof( Modifications ) );
00984                      mod->sml_op = LDAP_MOD_REPLACE;
00985                      mod->sml_flags = SLAP_MOD_INTERNAL;
00986                      mod->sml_next = NULL;
00987                      BER_BVZERO( &mod->sml_type );
00988                      mod->sml_desc = slap_schema.si_ad_modifyTimestamp;
00989                      mod->sml_numvals = 1;
00990                      mod->sml_values = (BerVarray) ch_malloc( 2 * sizeof( struct berval ) );
00991                      ber_dupbv( &mod->sml_values[0], &timestamp );
00992                      BER_BVZERO( &mod->sml_values[1] );
00993                      assert( !BER_BVISNULL( &mod->sml_values[0] ) );
00994                      mod->sml_nvalues = NULL;
00995                      *modtail = mod;
00996                      modtail = &mod->sml_next;
00997               }
00998        }
00999 }
01000 
01001 int
01002 slap_parse_modlist(
01003        Operation *op,
01004        SlapReply *rs,
01005        BerElement *ber,
01006        req_modify_s *ms )
01007 {
01008        ber_tag_t     tag;
01009        ber_len_t     len;
01010        char          *last;
01011        Modifications **modtail = &ms->rs_mods.rs_modlist;
01012 
01013        ms->rs_mods.rs_modlist = NULL;
01014        ms->rs_increment = 0;
01015 
01016        rs->sr_err = LDAP_SUCCESS;
01017 
01018        /* collect modifications & save for later */
01019        for ( tag = ber_first_element( ber, &len, &last );
01020               tag != LBER_DEFAULT;
01021               tag = ber_next_element( ber, &len, last ) )
01022        {
01023               ber_int_t mop;
01024               Modifications tmp, *mod;
01025 
01026               tmp.sml_nvalues = NULL;
01027 
01028               if ( ber_scanf( ber, "{e{m[W]}}", &mop,
01029                   &tmp.sml_type, &tmp.sml_values ) == LBER_ERROR )
01030               {
01031                      rs->sr_text = "decoding modlist error";
01032                      rs->sr_err = LDAP_PROTOCOL_ERROR;
01033                      goto done;
01034               }
01035 
01036               mod = (Modifications *) ch_malloc( sizeof(Modifications) );
01037               mod->sml_op = mop;
01038               mod->sml_flags = 0;
01039               mod->sml_type = tmp.sml_type;
01040               mod->sml_values = tmp.sml_values;
01041               mod->sml_nvalues = NULL;
01042               mod->sml_desc = NULL;
01043               mod->sml_next = NULL;
01044               *modtail = mod;
01045 
01046               switch( mop ) {
01047               case LDAP_MOD_ADD:
01048                      if ( mod->sml_values == NULL ) {
01049                             rs->sr_text = "modify/add operation requires values";
01050                             rs->sr_err = LDAP_PROTOCOL_ERROR;
01051                             goto done;
01052                      }
01053 
01054                      /* fall through */
01055 
01056               case LDAP_MOD_DELETE:
01057               case LDAP_MOD_REPLACE:
01058                      break;
01059 
01060               case LDAP_MOD_INCREMENT:
01061                      if( op->o_protocol >= LDAP_VERSION3 ) {
01062                             ms->rs_increment++;
01063                             if ( mod->sml_values == NULL ) {
01064                                    rs->sr_text = "modify/increment operation requires value";
01065                                    rs->sr_err = LDAP_PROTOCOL_ERROR;
01066                                    goto done;
01067                             }
01068 
01069                             if ( !BER_BVISNULL( &mod->sml_values[ 1 ] ) ) {
01070                                    rs->sr_text = "modify/increment operation requires single value";
01071                                    rs->sr_err = LDAP_PROTOCOL_ERROR;
01072                                    goto done;
01073                             }
01074 
01075                             break;
01076                      }
01077                      /* fall thru */
01078 
01079               default:
01080                      rs->sr_text = "unrecognized modify operation";
01081                      rs->sr_err = LDAP_PROTOCOL_ERROR;
01082                      goto done;
01083               }
01084 
01085               modtail = &mod->sml_next;
01086        }
01087        *modtail = NULL;
01088 
01089 done:
01090        if ( rs->sr_err != LDAP_SUCCESS ) {
01091               slap_mods_free( ms->rs_mods.rs_modlist, 1 );
01092               ms->rs_mods.rs_modlist = NULL;
01093               ms->rs_increment = 0;
01094        }
01095 
01096        return rs->sr_err;
01097 }
01098