]> git.sur5r.net Git - openldap/blob - servers/slapd/modify.c
Rework Modify statslog too
[openldap] / servers / slapd / modify.c
1 /* $OpenLDAP$ */
2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
3  *
4  * Copyright 1998-2015 The OpenLDAP Foundation.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted only as authorized by the OpenLDAP
9  * Public License.
10  *
11  * A copy of this license is available in the file LICENSE in the
12  * top-level directory of the distribution or, alternatively, at
13  * <http://www.OpenLDAP.org/license.html>.
14  */
15 /* Portions Copyright (c) 1995 Regents of the University of Michigan.
16  * All rights reserved.
17  *
18  * Redistribution and use in source and binary forms are permitted
19  * provided that this notice is preserved and that due credit is given
20  * to the University of Michigan at Ann Arbor. The name of the University
21  * may not be used to endorse or promote products derived from this
22  * software without specific prior written permission. This software
23  * is provided ``as is'' without express or implied warranty.
24  */
25
26 #include "portable.h"
27
28 #include <stdio.h>
29
30 #include <ac/socket.h>
31 #include <ac/string.h>
32 #include <ac/time.h>
33
34 #include "slap.h"
35 #include "lutil.h"
36
37
38 int
39 do_modify(
40     Operation   *op,
41     SlapReply   *rs )
42 {
43         struct berval dn = BER_BVNULL;
44         char            textbuf[ SLAP_TEXT_BUFLEN ];
45         size_t          textlen = sizeof( textbuf );
46 #ifdef LDAP_DEBUG
47         Modifications   *tmp;
48 #endif
49
50         Debug( LDAP_DEBUG_TRACE, "%s do_modify\n",
51                 op->o_log_prefix, 0, 0 );
52         /*
53          * Parse the modify request.  It looks like this:
54          *
55          *      ModifyRequest := [APPLICATION 6] SEQUENCE {
56          *              name    DistinguishedName,
57          *              mods    SEQUENCE OF SEQUENCE {
58          *                      operation       ENUMERATED {
59          *                              add     (0),
60          *                              delete  (1),
61          *                              replace (2)
62          *                      },
63          *                      modification    SEQUENCE {
64          *                              type    AttributeType,
65          *                              values  SET OF AttributeValue
66          *                      }
67          *              }
68          *      }
69          */
70
71         if ( ber_scanf( op->o_ber, "{m" /*}*/, &dn ) == LBER_ERROR ) {
72                 Debug( LDAP_DEBUG_ANY, "%s do_modify: ber_scanf failed\n",
73                         op->o_log_prefix, 0, 0 );
74                 send_ldap_discon( op, rs, LDAP_PROTOCOL_ERROR, "decoding error" );
75                 return SLAPD_DISCONNECT;
76         }
77
78         Debug( LDAP_DEBUG_ARGS, "%s do_modify: dn (%s)\n",
79                 op->o_log_prefix, dn.bv_val, 0 );
80
81         rs->sr_err = slap_parse_modlist( op, rs, op->o_ber, &op->oq_modify );
82         if ( rs->sr_err != LDAP_SUCCESS ) {
83                 Debug( LDAP_DEBUG_ANY, "%s do_modify: slap_parse_modlist failed err=%d msg=%s\n",
84                         op->o_log_prefix, rs->sr_err, rs->sr_text );
85                 send_ldap_result( op, rs );
86                 goto cleanup;
87         }
88
89         if( get_ctrls( op, rs, 1 ) != LDAP_SUCCESS ) {
90                 Debug( LDAP_DEBUG_ANY, "%s do_modify: get_ctrls failed\n",
91                         op->o_log_prefix, 0, 0 );
92                 /* get_ctrls has sent results.  Now clean up. */
93                 goto cleanup;
94         }
95
96         rs->sr_err = dnPrettyNormal( NULL, &dn, &op->o_req_dn, &op->o_req_ndn,
97                 op->o_tmpmemctx );
98         if( rs->sr_err != LDAP_SUCCESS ) {
99                 Debug( LDAP_DEBUG_ANY, "%s do_modify: invalid dn (%s)\n",
100                         op->o_log_prefix, dn.bv_val, 0 );
101                 send_ldap_error( op, rs, LDAP_INVALID_DN_SYNTAX, "invalid DN" );
102                 goto cleanup;
103         }
104
105         op->orm_no_opattrs = 0;
106
107 #ifdef LDAP_DEBUG
108         Debug( LDAP_DEBUG_ARGS, "%s modifications:\n",
109                         op->o_log_prefix, 0, 0 );
110
111         for ( tmp = op->orm_modlist; tmp != NULL; tmp = tmp->sml_next ) {
112                 Debug( LDAP_DEBUG_ARGS, "\t%s: %s\n",
113                         tmp->sml_op == LDAP_MOD_ADD ? "add" :
114                                 (tmp->sml_op == LDAP_MOD_INCREMENT ? "increment" :
115                                 (tmp->sml_op == LDAP_MOD_DELETE ? "delete" :
116                                         "replace")), tmp->sml_type.bv_val, 0 );
117
118                 if ( tmp->sml_values == NULL ) {
119                         Debug( LDAP_DEBUG_ARGS, "%s\n",
120                            "\t\tno values", NULL, NULL );
121                 } else if ( BER_BVISNULL( &tmp->sml_values[ 0 ] ) ) {
122                         Debug( LDAP_DEBUG_ARGS, "%s\n",
123                            "\t\tzero values", NULL, NULL );
124                 } else if ( BER_BVISNULL( &tmp->sml_values[ 1 ] ) ) {
125                         Debug( LDAP_DEBUG_ARGS, "%s, length %ld\n",
126                            "\t\tone value", (long) tmp->sml_values[0].bv_len, NULL );
127                 } else {
128                         Debug( LDAP_DEBUG_ARGS, "%s\n",
129                            "\t\tmultiple values", NULL, NULL );
130                 }
131         }
132
133         if ( StatslogTest( LDAP_DEBUG_STATS ) ) {
134                 char *ptr;
135                 unsigned len = 0, alen;
136
137                 ptr = op->o_logptr;
138                 ptr += snprintf( ptr, LOGBUFSIZ - (ptr - op->o_logbuf),
139                         " MOD dn=\"%s\"\n",
140                         op->o_req_dn.bv_val );
141
142                 len = ptr - op->o_logbuf;
143                 if ( op->orm_modlist ) {
144                         ptr += snprintf( ptr, LOGBUFSIZ - len, " MOD attr=" );
145                         len = ptr - op->o_logbuf;
146                 }
147                 for ( tmp = op->orm_modlist; tmp != NULL; tmp = tmp->sml_next ) {
148                         alen = tmp->sml_type.bv_len;
149                         if ( alen >= 256 )
150                                 alen = 255;
151                         if (len + 1 + alen >= LOGBUFSIZ ) {
152                                 if ( ldap_debug & LDAP_DEBUG_STATS )
153                                         lutil_debug( ldap_debug, LDAP_DEBUG_STATS, "%s", op->o_log_prefix );
154                                 if ( ldap_syslog & LDAP_DEBUG_STATS )
155                                         sendlog( op->o_logbuf, ptr - op->o_logbuf );
156                                 ptr = lutil_strcopy( op->o_logptr, " MOD attr=" );
157                                 len = ptr - op->o_logbuf;
158                         }
159                         ptr = lutil_strncopy( ptr, tmp->sml_type.bv_val, alen );
160                         len += alen;
161                         *ptr++ = ' ';
162                         *ptr = '\0';
163                 }
164                 if ( ptr != op->o_logptr ) {
165                         if ( ldap_debug & LDAP_DEBUG_STATS )
166                                 lutil_debug( ldap_debug, LDAP_DEBUG_STATS, "%s", op->o_log_prefix );
167                         if ( ldap_syslog & LDAP_DEBUG_STATS )
168                                 sendlog( op->o_logbuf, ptr - op->o_logbuf );
169                         *op->o_logptr = '\0';
170                 }
171         }
172 #endif  /* LDAP_DEBUG */
173
174         rs->sr_err = slap_mods_check( op, op->orm_modlist,
175                 &rs->sr_text, textbuf, textlen, NULL );
176
177         if ( rs->sr_err != LDAP_SUCCESS ) {
178                 send_ldap_result( op, rs );
179                 goto cleanup;
180         }
181
182         op->o_bd = frontendDB;
183         rs->sr_err = frontendDB->be_modify( op, rs );
184
185 #ifdef LDAP_X_TXN
186         if( rs->sr_err == LDAP_X_TXN_SPECIFY_OKAY ) {
187                 /* skip cleanup */
188                 return rs->sr_err;
189         }
190 #endif
191
192 cleanup:
193         op->o_tmpfree( op->o_req_dn.bv_val, op->o_tmpmemctx );
194         op->o_tmpfree( op->o_req_ndn.bv_val, op->o_tmpmemctx );
195         if ( op->orm_modlist != NULL ) slap_mods_free( op->orm_modlist, 1 );
196
197         return rs->sr_err;
198 }
199
200 int
201 fe_op_modify( Operation *op, SlapReply *rs )
202 {
203         BackendDB       *op_be, *bd = op->o_bd;
204         char            textbuf[ SLAP_TEXT_BUFLEN ];
205         size_t          textlen = sizeof( textbuf );
206         
207         if ( BER_BVISEMPTY( &op->o_req_ndn ) ) {
208                 Debug( LDAP_DEBUG_ANY, "%s do_modify: root dse!\n",
209                         op->o_log_prefix, 0, 0 );
210                 send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM,
211                         "modify upon the root DSE not supported" );
212                 goto cleanup;
213
214         } else if ( bvmatch( &op->o_req_ndn, &frontendDB->be_schemandn ) ) {
215                 Debug( LDAP_DEBUG_ANY, "%s do_modify: subschema subentry!\n",
216                         op->o_log_prefix, 0, 0 );
217                 send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM,
218                         "modification of subschema subentry not supported" );
219                 goto cleanup;
220         }
221
222         /*
223          * We could be serving multiple database backends.  Select the
224          * appropriate one, or send a referral to our "referral server"
225          * if we don't hold it.
226          */
227         op->o_bd = select_backend( &op->o_req_ndn, 1 );
228         if ( op->o_bd == NULL ) {
229                 op->o_bd = bd;
230                 rs->sr_ref = referral_rewrite( default_referral,
231                         NULL, &op->o_req_dn, LDAP_SCOPE_DEFAULT );
232                 if ( !rs->sr_ref ) {
233                         rs->sr_ref = default_referral;
234                 }
235
236                 if ( rs->sr_ref != NULL ) {
237                         rs->sr_err = LDAP_REFERRAL;
238                         send_ldap_result( op, rs );
239
240                         if ( rs->sr_ref != default_referral ) {
241                                 ber_bvarray_free( rs->sr_ref );
242                         }
243
244                 } else {
245                         send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM,
246                                 "no global superior knowledge" );
247                 }
248                 goto cleanup;
249         }
250
251         /* If we've got a glued backend, check the real backend */
252         op_be = op->o_bd;
253         if ( SLAP_GLUE_INSTANCE( op->o_bd )) {
254                 op->o_bd = select_backend( &op->o_req_ndn, 0 );
255         }
256
257         /* check restrictions */
258         if ( backend_check_restrictions( op, rs, NULL ) != LDAP_SUCCESS ) {
259                 send_ldap_result( op, rs );
260                 goto cleanup;
261         }
262
263         /* check for referrals */
264         if ( backend_check_referrals( op, rs ) != LDAP_SUCCESS ) {
265                 goto cleanup;
266         }
267
268         rs->sr_err = slap_mods_obsolete_check( op, op->orm_modlist,
269                 &rs->sr_text, textbuf, textlen );
270         if ( rs->sr_err != LDAP_SUCCESS ) {
271                 send_ldap_result( op, rs );
272                 goto cleanup;
273         }
274
275         /* check for modify/increment support */
276         if ( op->orm_increment && !SLAP_INCREMENT( op->o_bd ) ) {
277                 send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM,
278                         "modify/increment not supported in context" );
279                 goto cleanup;
280         }
281
282         /*
283          * do the modify if 1 && (2 || 3)
284          * 1) there is a modify function implemented in this backend;
285          * 2) this backend is master for what it holds;
286          * 3) it's a replica and the dn supplied is the update_ndn.
287          */
288         if ( op->o_bd->be_modify ) {
289                 /* do the update here */
290                 int repl_user = be_isupdate( op );
291
292                 /*
293                  * Multimaster slapd does not have to check for replicator dn
294                  * because it accepts each modify request
295                  */
296                 if ( !SLAP_SINGLE_SHADOW(op->o_bd) || repl_user ) {
297                         int update = !BER_BVISEMPTY( &op->o_bd->be_update_ndn );
298
299                         op->o_bd = op_be;
300
301                         if ( !update ) {
302                                 rs->sr_err = slap_mods_no_user_mod_check( op, op->orm_modlist,
303                                         &rs->sr_text, textbuf, textlen );
304                                 if ( rs->sr_err != LDAP_SUCCESS ) {
305                                         send_ldap_result( op, rs );
306                                         goto cleanup;
307                                 }
308                         }
309                         op->o_bd->be_modify( op, rs );
310
311                 } else { /* send a referral */
312                         BerVarray defref = op->o_bd->be_update_refs
313                                 ? op->o_bd->be_update_refs : default_referral;
314                         if ( defref != NULL ) {
315                                 rs->sr_ref = referral_rewrite( defref,
316                                         NULL, &op->o_req_dn,
317                                         LDAP_SCOPE_DEFAULT );
318                                 if ( rs->sr_ref == NULL ) {
319                                         /* FIXME: must duplicate, because
320                                          * overlays may muck with it */
321                                         rs->sr_ref = defref;
322                                 }
323                                 rs->sr_err = LDAP_REFERRAL;
324                                 send_ldap_result( op, rs );
325                                 if ( rs->sr_ref != defref ) {
326                                         ber_bvarray_free( rs->sr_ref );
327                                 }
328
329                         } else {
330                                 send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM,
331                                         "shadow context; no update referral" );
332                         }
333                 }
334
335         } else {
336                 send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM,
337                     "operation not supported within namingContext" );
338         }
339
340 cleanup:;
341         op->o_bd = bd;
342         return rs->sr_err;
343 }
344
345 /*
346  * Obsolete constraint checking.
347  */
348 int
349 slap_mods_obsolete_check(
350         Operation *op,
351         Modifications *ml,
352         const char **text,
353         char *textbuf,
354         size_t textlen )
355 {
356         if( get_relax( op ) ) return LDAP_SUCCESS;
357
358         for ( ; ml != NULL; ml = ml->sml_next ) {
359                 if ( is_at_obsolete( ml->sml_desc->ad_type ) &&
360                         (( ml->sml_op != LDAP_MOD_REPLACE &&
361                                 ml->sml_op != LDAP_MOD_DELETE ) ||
362                                         ml->sml_values != NULL ))
363                 {
364                         /*
365                          * attribute is obsolete,
366                          * only allow replace/delete with no values
367                          */
368                         snprintf( textbuf, textlen,
369                                 "%s: attribute is obsolete",
370                                 ml->sml_type.bv_val );
371                         *text = textbuf;
372                         return LDAP_CONSTRAINT_VIOLATION;
373                 }
374         }
375
376         return LDAP_SUCCESS;
377 }
378
379 /*
380  * No-user-modification constraint checking.
381  */
382 int
383 slap_mods_no_user_mod_check(
384         Operation *op,
385         Modifications *ml,
386         const char **text,
387         char *textbuf,
388         size_t textlen )
389 {
390         for ( ; ml != NULL; ml = ml->sml_next ) {
391                 if ( !is_at_no_user_mod( ml->sml_desc->ad_type ) ) {
392                         continue;
393                 }
394
395                 if ( ml->sml_flags & SLAP_MOD_INTERNAL ) {
396                         continue;
397                 }
398
399                 if ( get_relax( op ) ) {
400                         if ( ml->sml_desc->ad_type->sat_flags & SLAP_AT_MANAGEABLE ) {
401                                 ml->sml_flags |= SLAP_MOD_MANAGING;
402                                 continue;
403                         }
404
405                         /* attribute not manageable */
406                         snprintf( textbuf, textlen,
407                                 "%s: no-user-modification attribute not manageable",
408                                 ml->sml_type.bv_val );
409
410                 } else {
411                         /* user modification disallowed */
412                         snprintf( textbuf, textlen,
413                                 "%s: no user modification allowed",
414                                 ml->sml_type.bv_val );
415                 }
416
417                 *text = textbuf;
418                 return LDAP_CONSTRAINT_VIOLATION;
419         }
420
421         return LDAP_SUCCESS;
422 }
423
424 int
425 slap_mods_no_repl_user_mod_check(
426         Operation *op,
427         Modifications *ml,
428         const char **text,
429         char *textbuf,
430         size_t textlen )
431 {
432         Modifications *mods;
433         Modifications *modp;
434
435         for ( mods = ml; mods != NULL; mods = mods->sml_next ) {
436                 assert( mods->sml_op == LDAP_MOD_ADD );
437
438                 /* check doesn't already appear */
439                 for ( modp = ml; modp != NULL; modp = modp->sml_next ) {
440                         if ( mods->sml_desc == modp->sml_desc && mods != modp ) {
441                                 snprintf( textbuf, textlen,
442                                         "attribute '%s' provided more than once",
443                                         mods->sml_desc->ad_cname.bv_val );
444                                 *text = textbuf;
445                                 return LDAP_TYPE_OR_VALUE_EXISTS;
446                         }
447                 }
448         }
449
450         return LDAP_SUCCESS;
451 }
452
453 /*
454  * Do basic attribute type checking and syntax validation.
455  */
456 int slap_mods_check(
457         Operation *op,
458         Modifications *ml,
459         const char **text,
460         char *textbuf,
461         size_t textlen,
462         void *ctx )
463 {
464         int rc;
465
466         for( ; ml != NULL; ml = ml->sml_next ) {
467                 AttributeDescription *ad = NULL;
468
469                 /* convert to attribute description */
470                 if ( ml->sml_desc == NULL ) {
471                         rc = slap_bv2ad( &ml->sml_type, &ml->sml_desc, text );
472                         if( rc != LDAP_SUCCESS ) {
473                                 if ( get_no_schema_check( op )) {
474                                         rc = slap_bv2undef_ad( &ml->sml_type, &ml->sml_desc,
475                                                 text, 0 );
476                                 }
477                         }
478                         if( rc != LDAP_SUCCESS ) {
479                                 snprintf( textbuf, textlen, "%s: %s",
480                                         ml->sml_type.bv_val, *text );
481                                 *text = textbuf;
482                                 return rc;
483                         }
484                 }
485
486                 ad = ml->sml_desc;
487
488                 if( slap_syntax_is_binary( ad->ad_type->sat_syntax )
489                         && !slap_ad_is_binary( ad ))
490                 {
491                         /* attribute requires binary transfer */
492                         snprintf( textbuf, textlen,
493                                 "%s: requires ;binary transfer",
494                                 ml->sml_type.bv_val );
495                         *text = textbuf;
496                         return LDAP_UNDEFINED_TYPE;
497                 }
498
499                 if( !slap_syntax_is_binary( ad->ad_type->sat_syntax )
500                         && slap_ad_is_binary( ad ))
501                 {
502                         /* attribute does not require binary transfer */
503                         snprintf( textbuf, textlen,
504                                 "%s: disallows ;binary transfer",
505                                 ml->sml_type.bv_val );
506                         *text = textbuf;
507                         return LDAP_UNDEFINED_TYPE;
508                 }
509
510                 if( slap_ad_is_tag_range( ad )) {
511                         /* attribute requires binary transfer */
512                         snprintf( textbuf, textlen,
513                                 "%s: inappropriate use of tag range option",
514                                 ml->sml_type.bv_val );
515                         *text = textbuf;
516                         return LDAP_UNDEFINED_TYPE;
517                 }
518
519 #if 0
520                 if ( is_at_obsolete( ad->ad_type ) &&
521                         (( ml->sml_op != LDAP_MOD_REPLACE &&
522                                 ml->sml_op != LDAP_MOD_DELETE ) ||
523                                         ml->sml_values != NULL ))
524                 {
525                         /*
526                          * attribute is obsolete,
527                          * only allow replace/delete with no values
528                          */
529                         snprintf( textbuf, textlen,
530                                 "%s: attribute is obsolete",
531                                 ml->sml_type.bv_val );
532                         *text = textbuf;
533                         return LDAP_CONSTRAINT_VIOLATION;
534                 }
535 #endif
536
537                 if ( ml->sml_op == LDAP_MOD_INCREMENT &&
538 #ifdef SLAPD_REAL_SYNTAX
539                         !is_at_syntax( ad->ad_type, SLAPD_REAL_SYNTAX ) &&
540 #endif
541                         !is_at_syntax( ad->ad_type, SLAPD_INTEGER_SYNTAX ) )
542                 {
543                         /*
544                          * attribute values must be INTEGER or REAL
545                          */
546                         snprintf( textbuf, textlen,
547                                 "%s: attribute syntax inappropriate for increment",
548                                 ml->sml_type.bv_val );
549                         *text = textbuf;
550                         return LDAP_CONSTRAINT_VIOLATION;
551                 }
552
553                 /*
554                  * check values
555                  */
556                 if( ml->sml_values != NULL ) {
557                         ber_len_t nvals;
558                         slap_syntax_validate_func *validate =
559                                 ad->ad_type->sat_syntax->ssyn_validate;
560                         slap_syntax_transform_func *pretty =
561                                 ad->ad_type->sat_syntax->ssyn_pretty;
562  
563                         if( !pretty && !validate ) {
564                                 *text = "no validator for syntax";
565                                 snprintf( textbuf, textlen,
566                                         "%s: no validator for syntax %s",
567                                         ml->sml_type.bv_val,
568                                         ad->ad_type->sat_syntax->ssyn_oid );
569                                 *text = textbuf;
570                                 return LDAP_INVALID_SYNTAX;
571                         }
572
573                         /*
574                          * check that each value is valid per syntax
575                          *      and pretty if appropriate
576                          */
577                         for ( nvals = 0; !BER_BVISNULL( &ml->sml_values[nvals] ); nvals++ ) {
578                                 struct berval pval;
579
580                                 if ( pretty ) {
581                                         rc = ordered_value_pretty( ad,
582                                                 &ml->sml_values[nvals], &pval, ctx );
583                                 } else {
584                                         rc = ordered_value_validate( ad,
585                                                 &ml->sml_values[nvals], ml->sml_op );
586                                 }
587
588                                 if( rc != 0 ) {
589                                         snprintf( textbuf, textlen,
590                                                 "%s: value #%ld invalid per syntax",
591                                                 ml->sml_type.bv_val, (long) nvals );
592                                         *text = textbuf;
593                                         return LDAP_INVALID_SYNTAX;
594                                 }
595
596                                 if( pretty ) {
597                                         ber_memfree_x( ml->sml_values[nvals].bv_val, ctx );
598                                         ml->sml_values[nvals] = pval;
599                                 }
600                         }
601                         ml->sml_values[nvals].bv_len = 0;
602                         ml->sml_numvals = nvals;
603
604                         /*
605                          * a rough single value check... an additional check is needed
606                          * to catch add of single value to existing single valued attribute
607                          */
608                         if ((ml->sml_op == LDAP_MOD_ADD || ml->sml_op == LDAP_MOD_REPLACE)
609                                 && nvals > 1 && is_at_single_value( ad->ad_type ))
610                         {
611                                 snprintf( textbuf, textlen,
612                                         "%s: multiple values provided",
613                                         ml->sml_type.bv_val );
614                                 *text = textbuf;
615                                 return LDAP_CONSTRAINT_VIOLATION;
616                         }
617
618                         /* if the type has a normalizer, generate the
619                          * normalized values. otherwise leave them NULL.
620                          *
621                          * this is different from the rule for attributes
622                          * in an entry - in an attribute list, the normalized
623                          * value is set equal to the non-normalized value
624                          * when there is no normalizer.
625                          */
626                         if( nvals && ad->ad_type->sat_equality &&
627                                 ad->ad_type->sat_equality->smr_normalize )
628                         {
629                                 ml->sml_nvalues = ber_memalloc_x(
630                                         (nvals+1)*sizeof(struct berval), ctx );
631
632                                 for ( nvals = 0; !BER_BVISNULL( &ml->sml_values[nvals] ); nvals++ ) {
633                                         rc = ordered_value_normalize(
634                                                 SLAP_MR_VALUE_OF_ATTRIBUTE_SYNTAX,
635                                                 ad,
636                                                 ad->ad_type->sat_equality,
637                                                 &ml->sml_values[nvals], &ml->sml_nvalues[nvals], ctx );
638                                         if ( rc ) {
639                                                 Debug( LDAP_DEBUG_ANY,
640                                                         "<= str2entry NULL (ssyn_normalize %d)\n",
641                                                         rc, 0, 0 );
642                                                 snprintf( textbuf, textlen,
643                                                         "%s: value #%ld normalization failed",
644                                                         ml->sml_type.bv_val, (long) nvals );
645                                                 *text = textbuf;
646                                                 BER_BVZERO( &ml->sml_nvalues[nvals] );
647                                                 return rc;
648                                         }
649                                 }
650
651                                 BER_BVZERO( &ml->sml_nvalues[nvals] );
652                         }
653
654                         /* check for duplicates, but ignore Deletes.
655                          */
656                         if( nvals > 1 && ml->sml_op != LDAP_MOD_DELETE ) {
657                                 int i;
658                                 rc = slap_sort_vals( ml, text, &i, ctx );
659                                 if ( rc == LDAP_TYPE_OR_VALUE_EXISTS ) {
660                                         /* value exists already */
661                                         snprintf( textbuf, textlen,
662                                                 "%s: value #%d provided more than once",
663                                                 ml->sml_desc->ad_cname.bv_val, i );
664                                         *text = textbuf;
665                                 }
666                                 if ( rc )
667                                         return rc;
668                         }
669                 } else {
670                         ml->sml_numvals = 0;
671                 }
672         }
673
674         return LDAP_SUCCESS;
675 }
676
677 /* Sort a set of values. An (Attribute *) may be used interchangeably here
678  * instead of a (Modifications *) structure.
679  *
680  * Uses Quicksort + Insertion sort for small arrays
681  */
682
683 int
684 slap_sort_vals(
685         Modifications *ml,
686         const char **text,
687         int *dup,
688         void *ctx )
689 {
690         AttributeDescription *ad;
691         MatchingRule *mr;
692         int istack[sizeof(int)*16];
693         int i, j, k, l, ir, jstack, match, *ix, itmp, nvals, rc = LDAP_SUCCESS;
694         int is_norm;
695         struct berval a, *cv;
696
697 #define SMALL   8
698 #define SWAP(a,b,tmp)   tmp=(a);(a)=(b);(b)=tmp
699 #define COMP(a,b)       match=0; rc = ordered_value_match( &match, \
700                                                 ad, mr, SLAP_MR_EQUALITY \
701                                                                 | SLAP_MR_VALUE_OF_ASSERTION_SYNTAX \
702                                                                 | SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH \
703                                                                 | SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH, \
704                                                                 &(a), &(b), text );
705
706 #define IX(x)   ix[x]
707 #define EXCH(x,y)       SWAP(ix[x],ix[y],itmp)
708 #define SETA(x) itmp = ix[x]; a = cv[itmp]
709 #define GETA(x) ix[x] = itmp;
710 #define SET(x,y)        ix[x] = ix[y]
711
712         ad = ml->sml_desc;
713         nvals = ml->sml_numvals;
714         if ( nvals <= 1 )
715                 goto ret;
716
717         /* For Modifications, sml_nvalues is NULL if normalization wasn't needed.
718          * For Attributes, sml_nvalues == sml_values when normalization isn't needed.
719          */
720         if ( ml->sml_nvalues && ml->sml_nvalues != ml->sml_values ) {
721                 cv = ml->sml_nvalues;
722                 is_norm = 1;
723         } else {
724                 cv = ml->sml_values;
725                 is_norm = 0;
726         }
727
728         if ( ad == slap_schema.si_ad_objectClass )
729                 mr = NULL;      /* shortcut matching */
730         else
731                 mr = ad->ad_type->sat_equality;
732
733         /* record indices to preserve input ordering */
734         ix = slap_sl_malloc( nvals * sizeof(int), ctx );
735         for (i=0; i<nvals; i++) ix[i] = i;
736
737         ir = nvals-1;
738         l = 0;
739         jstack = 0;
740
741         for(;;) {
742                 if (ir - l < SMALL) {   /* Insertion sort */
743                         match=1;
744                         for (j=l+1;j<=ir;j++) {
745                                 SETA(j);
746                                 for (i=j-1;i>=0;i--) {
747                                         COMP(cv[IX(i)], a);
748                                         if ( match <= 0 )
749                                                 break;
750                                         SET(i+1,i);
751                                 }
752                                 GETA(i+1);
753                                 if ( match == 0 ) goto done;
754                         }
755                         if ( jstack == 0 ) break;
756                         ir = istack[jstack--];
757                         l = istack[jstack--];
758                 } else {
759                         k = (l + ir) >> 1;      /* Choose median of left, center, right */
760                         EXCH(k, l+1);
761                         COMP( cv[IX(l)], cv[IX(ir)] );
762                         if ( match > 0 ) {
763                                 EXCH(l, ir);
764                         } else if ( match == 0 ) {
765                                 i = ir;
766                                 break;
767                         }
768                         COMP( cv[IX(l+1)], cv[IX(ir)] );
769                         if ( match > 0 ) {
770                                 EXCH(l+1, ir);
771                         } else if ( match == 0 ) {
772                                 i = ir;
773                                 break;
774                         }
775                         COMP( cv[IX(l)], cv[IX(l+1)] );
776                         if ( match > 0 ) {
777                                 EXCH(l, l+1);
778                         } else if ( match == 0 ) {
779                                 i = l;
780                                 break;
781                         }
782                         i = l+1;
783                         j = ir;
784                         a = cv[IX(i)];
785                         for(;;) {
786                                 do {
787                                         i++;
788                                         COMP( cv[IX(i)], a );
789                                 } while( match < 0 );
790                                 while( match > 0 ) {
791                                         j--;
792                                         COMP( cv[IX(j)], a );
793                                 }
794                                 if (j < i) {
795                                         match = 1;
796                                         break;
797                                 }
798                                 if ( match == 0 ) {
799                                         i = l+1;
800                                         break;
801                                 }
802                                 EXCH(i,j);
803                         }
804                         if ( match == 0 )
805                                 break;
806                         EXCH(l+1,j);
807                         jstack += 2;
808                         if (ir-i+1 > j-l) {
809                                 istack[jstack] = ir;
810                                 istack[jstack-1] = i;
811                                 ir = j;
812                         } else {
813                                 istack[jstack] = j;
814                                 istack[jstack-1] = l;
815                                 l = i;
816                         }
817                 }
818         }
819         done:
820         if ( match == 0 && i >= 0 )
821                 *dup = ix[i];
822
823         /* For sorted attributes, put the values in index order */
824         if ( rc == LDAP_SUCCESS && match &&
825                 ( ad->ad_type->sat_flags & SLAP_AT_SORTED_VAL )) {
826                 BerVarray tmpv = slap_sl_malloc( sizeof( struct berval ) * nvals, ctx );
827                 for ( i = 0; i<nvals; i++ )
828                         tmpv[i] = cv[ix[i]];
829                 for ( i = 0; i<nvals; i++ )
830                         cv[i] = tmpv[i];
831                 /* Check if the non-normalized array needs to move too */
832                 if ( is_norm ) {
833                         cv = ml->sml_values;
834                         for ( i = 0; i<nvals; i++ )
835                                 tmpv[i] = cv[ix[i]];
836                         for ( i = 0; i<nvals; i++ )
837                                 cv[i] = tmpv[i];
838                 }
839                 slap_sl_free( tmpv, ctx );
840         }
841
842         slap_sl_free( ix, ctx );
843
844         if ( rc == LDAP_SUCCESS && match == 0 ) {
845                 /* value exists already */
846                 assert( i >= 0 );
847                 assert( i < nvals );
848                 rc = LDAP_TYPE_OR_VALUE_EXISTS;
849         }
850  ret:
851         return rc;
852 }
853
854 /* Enter with bv->bv_len = sizeof buffer, returns with
855  * actual length of string
856  */
857 void slap_timestamp( time_t *tm, struct berval *bv )
858 {
859         struct tm ltm;
860
861         ldap_pvt_gmtime( tm, &ltm );
862
863         bv->bv_len = lutil_gentime( bv->bv_val, bv->bv_len, &ltm );
864 }
865
866 /* Called for all modify and modrdn ops. If the current op was replicated
867  * from elsewhere, all of the attrs should already be present.
868  */
869 void slap_mods_opattrs(
870         Operation *op,
871         Modifications **modsp,
872         int manage_ctxcsn )
873 {
874         struct berval name, timestamp, csn = BER_BVNULL;
875         struct berval nname;
876         char timebuf[ LDAP_LUTIL_GENTIME_BUFSIZE ];
877         char csnbuf[ LDAP_PVT_CSNSTR_BUFSIZE ];
878         Modifications *mod, **modtail, *modlast;
879         int gotcsn = 0, gotmname = 0, gotmtime = 0;
880
881         if ( SLAP_LASTMOD( op->o_bd ) && !op->orm_no_opattrs ) {
882                 char *ptr;
883                 timestamp.bv_val = timebuf;
884                 for ( modtail = modsp; *modtail; modtail = &(*modtail)->sml_next ) {
885                         if ( (*modtail)->sml_op != LDAP_MOD_ADD &&
886                                 (*modtail)->sml_op != SLAP_MOD_SOFTADD &&
887                                 (*modtail)->sml_op != SLAP_MOD_ADD_IF_NOT_PRESENT &&
888                                 (*modtail)->sml_op != LDAP_MOD_REPLACE )
889                         {
890                                 continue;
891                         }
892
893                         if ( (*modtail)->sml_desc == slap_schema.si_ad_entryCSN )
894                         {
895                                 csn = (*modtail)->sml_values[0];
896                                 gotcsn = 1;
897
898                         } else if ( (*modtail)->sml_desc == slap_schema.si_ad_modifiersName )
899                         {
900                                 gotmname = 1;
901
902                         } else if ( (*modtail)->sml_desc == slap_schema.si_ad_modifyTimestamp )
903                         {
904                                 gotmtime = 1;
905                         }
906                 }
907
908                 if ( BER_BVISEMPTY( &op->o_csn )) {
909                         if ( !gotcsn ) {
910                                 csn.bv_val = csnbuf;
911                                 csn.bv_len = sizeof( csnbuf );
912                                 slap_get_csn( op, &csn, manage_ctxcsn );
913
914                         } else {
915                                 if ( manage_ctxcsn ) {
916                                         slap_queue_csn( op, &csn );
917                                 }
918                         }
919
920                 } else {
921                         csn = op->o_csn;
922                 }
923
924                 ptr = ber_bvchr( &csn, '#' );
925                 if ( ptr ) {
926                         timestamp.bv_len = STRLENOF("YYYYMMDDHHMMSSZ");
927                         AC_MEMCPY( timebuf, csn.bv_val, timestamp.bv_len );
928                         timebuf[timestamp.bv_len-1] = 'Z';
929                         timebuf[timestamp.bv_len] = '\0';
930
931                 } else {
932                         time_t now = slap_get_time();
933
934                         timestamp.bv_len = sizeof(timebuf);
935
936                         slap_timestamp( &now, &timestamp );
937                 }
938
939                 if ( BER_BVISEMPTY( &op->o_dn ) ) {
940                         BER_BVSTR( &name, SLAPD_ANONYMOUS );
941                         nname = name;
942
943                 } else {
944                         name = op->o_dn;
945                         nname = op->o_ndn;
946                 }
947
948                 if ( !gotcsn ) {
949                         mod = (Modifications *) ch_malloc( sizeof( Modifications ) );
950                         mod->sml_op = LDAP_MOD_REPLACE;
951                         mod->sml_flags = SLAP_MOD_INTERNAL;
952                         mod->sml_next = NULL;
953                         BER_BVZERO( &mod->sml_type );
954                         mod->sml_desc = slap_schema.si_ad_entryCSN;
955                         mod->sml_numvals = 1;
956                         mod->sml_values = (BerVarray) ch_malloc( 2 * sizeof( struct berval ) );
957                         ber_dupbv( &mod->sml_values[0], &csn );
958                         BER_BVZERO( &mod->sml_values[1] );
959                         assert( !BER_BVISNULL( &mod->sml_values[0] ) );
960                         mod->sml_nvalues = NULL;
961                         *modtail = mod;
962                         modlast = mod;
963                         modtail = &mod->sml_next;
964                 }
965
966                 if ( !gotmname ) {
967                         mod = (Modifications *) ch_malloc( sizeof( Modifications ) );
968                         mod->sml_op = LDAP_MOD_REPLACE;
969                         mod->sml_flags = SLAP_MOD_INTERNAL;
970                         mod->sml_next = NULL;
971                         BER_BVZERO( &mod->sml_type );
972                         mod->sml_desc = slap_schema.si_ad_modifiersName;
973                         mod->sml_numvals = 1;
974                         mod->sml_values = (BerVarray) ch_malloc( 2 * sizeof( struct berval ) );
975                         ber_dupbv( &mod->sml_values[0], &name );
976                         BER_BVZERO( &mod->sml_values[1] );
977                         assert( !BER_BVISNULL( &mod->sml_values[0] ) );
978                         mod->sml_nvalues =
979                                 (BerVarray) ch_malloc( 2 * sizeof( struct berval ) );
980                         ber_dupbv( &mod->sml_nvalues[0], &nname );
981                         BER_BVZERO( &mod->sml_nvalues[1] );
982                         assert( !BER_BVISNULL( &mod->sml_nvalues[0] ) );
983                         *modtail = mod;
984                         modtail = &mod->sml_next;
985                 }
986
987                 if ( !gotmtime ) {
988                         mod = (Modifications *) ch_malloc( sizeof( Modifications ) );
989                         mod->sml_op = LDAP_MOD_REPLACE;
990                         mod->sml_flags = SLAP_MOD_INTERNAL;
991                         mod->sml_next = NULL;
992                         BER_BVZERO( &mod->sml_type );
993                         mod->sml_desc = slap_schema.si_ad_modifyTimestamp;
994                         mod->sml_numvals = 1;
995                         mod->sml_values = (BerVarray) ch_malloc( 2 * sizeof( struct berval ) );
996                         ber_dupbv( &mod->sml_values[0], &timestamp );
997                         BER_BVZERO( &mod->sml_values[1] );
998                         assert( !BER_BVISNULL( &mod->sml_values[0] ) );
999                         mod->sml_nvalues = NULL;
1000                         *modtail = mod;
1001                         modtail = &mod->sml_next;
1002                 }
1003         }
1004 }
1005
1006 int
1007 slap_parse_modlist(
1008         Operation *op,
1009         SlapReply *rs,
1010         BerElement *ber,
1011         req_modify_s *ms )
1012 {
1013         ber_tag_t       tag;
1014         ber_len_t       len;
1015         char            *last;
1016         Modifications   **modtail = &ms->rs_mods.rs_modlist;
1017
1018         ms->rs_mods.rs_modlist = NULL;
1019         ms->rs_increment = 0;
1020
1021         rs->sr_err = LDAP_SUCCESS;
1022
1023         /* collect modifications & save for later */
1024         for ( tag = ber_first_element( ber, &len, &last );
1025                 tag != LBER_DEFAULT;
1026                 tag = ber_next_element( ber, &len, last ) )
1027         {
1028                 ber_int_t mop;
1029                 Modifications tmp, *mod;
1030
1031                 tmp.sml_nvalues = NULL;
1032
1033                 if ( ber_scanf( ber, "{e{m[W]}}", &mop,
1034                     &tmp.sml_type, &tmp.sml_values ) == LBER_ERROR )
1035                 {
1036                         rs->sr_text = "decoding modlist error";
1037                         rs->sr_err = LDAP_PROTOCOL_ERROR;
1038                         goto done;
1039                 }
1040
1041                 mod = (Modifications *) ch_malloc( sizeof(Modifications) );
1042                 mod->sml_op = mop;
1043                 mod->sml_flags = 0;
1044                 mod->sml_type = tmp.sml_type;
1045                 mod->sml_values = tmp.sml_values;
1046                 mod->sml_nvalues = NULL;
1047                 mod->sml_desc = NULL;
1048                 mod->sml_next = NULL;
1049                 *modtail = mod;
1050
1051                 switch( mop ) {
1052                 case LDAP_MOD_ADD:
1053                         if ( mod->sml_values == NULL ) {
1054                                 rs->sr_text = "modify/add operation requires values";
1055                                 rs->sr_err = LDAP_PROTOCOL_ERROR;
1056                                 goto done;
1057                         }
1058
1059                         /* fall through */
1060
1061                 case LDAP_MOD_DELETE:
1062                 case LDAP_MOD_REPLACE:
1063                         break;
1064
1065                 case LDAP_MOD_INCREMENT:
1066                         if( op->o_protocol >= LDAP_VERSION3 ) {
1067                                 ms->rs_increment++;
1068                                 if ( mod->sml_values == NULL ) {
1069                                         rs->sr_text = "modify/increment operation requires value";
1070                                         rs->sr_err = LDAP_PROTOCOL_ERROR;
1071                                         goto done;
1072                                 }
1073
1074                                 if ( !BER_BVISNULL( &mod->sml_values[ 1 ] ) ) {
1075                                         rs->sr_text = "modify/increment operation requires single value";
1076                                         rs->sr_err = LDAP_PROTOCOL_ERROR;
1077                                         goto done;
1078                                 }
1079
1080                                 break;
1081                         }
1082                         /* fall thru */
1083
1084                 default:
1085                         rs->sr_text = "unrecognized modify operation";
1086                         rs->sr_err = LDAP_PROTOCOL_ERROR;
1087                         goto done;
1088                 }
1089
1090                 modtail = &mod->sml_next;
1091         }
1092         *modtail = NULL;
1093
1094 done:
1095         if ( rs->sr_err != LDAP_SUCCESS ) {
1096                 slap_mods_free( ms->rs_mods.rs_modlist, 1 );
1097                 ms->rs_mods.rs_modlist = NULL;
1098                 ms->rs_increment = 0;
1099         }
1100
1101         return rs->sr_err;
1102 }
1103