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