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