]> git.sur5r.net Git - openldap/blob - servers/ldapd/modify.c
Update for Alpha3 from -devel as of OPENLDAP_DEVEL_981116.
[openldap] / servers / ldapd / modify.c
1 /*
2  * Copyright (c) 1990 Regents of the University of Michigan.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms are permitted
6  * provided that this notice is preserved and that due credit is given
7  * to the University of Michigan at Ann Arbor. The name of the University
8  * may not be used to endorse or promote products derived from this
9  * software without specific prior written permission. This software
10  * is provided ``as is'' without express or implied warranty.
11  */
12
13 #include "portable.h"
14
15 #include <stdio.h>
16
17 #include <ac/ctype.h>
18 #include <ac/socket.h>
19 #include <ac/string.h>          /* get SAFEMEMCPY */
20
21 #include <quipu/commonarg.h>
22 #include <quipu/attrvalue.h>
23 #include <quipu/ds_error.h>
24 #include <quipu/modify.h>
25 #include <quipu/dap2.h>
26 #include <quipu/dua.h>
27 extern IFP      merge_acl;
28
29 #include "lber.h"
30 #include "ldap.h"
31 #include "common.h"
32
33 static CommonArgs       common = default_common_args;
34
35 static int replace_mod( struct entrymod *, Attr_Sequence, Attr_Sequence );
36
37 #ifdef LDAP_COMPAT20
38 #define MODTAG  (ldap_compat == 20 ? OLD_LDAP_RES_MODIFY : LDAP_RES_MODIFY)
39 #else
40 #define MODTAG  LDAP_RES_MODIFY
41 #endif
42
43 int
44 do_modify(
45     Sockbuf     *clientsb,
46     struct msg  *m,
47     BerElement  *ber
48 )
49 {
50         char                    *dn;
51         char                    *last;
52         int                     rc;
53         unsigned long           tag, len;
54         LDAPMod                 *mods, *modtail;
55         struct ds_read_arg      ra;
56
57         Debug( LDAP_DEBUG_TRACE, "do_modify\n", 0, 0, 0 );
58
59         /*
60          * Parse the modify request.  It looks like this:
61          *      ModifyRequest := [APPLICATION 6] SEQUENCE {
62          *              name    DistinguishedName,
63          *              mods    SEQUENCE OF SEQUENCE {
64          *                      operation       ENUMERATED {
65          *                              add     (0),
66          *                              delete  (1),
67          *                              replace (2)
68          *                      },
69          *                      modification    SEQUENCE {
70          *                              type    AttributeType,
71          *                              values  SET OF AttributeValue
72          *                      }
73          *              }
74          *      }
75          * We then have to initiate a read of the entry to be modified.
76          * The actual modification is done by do_modify2(), after the
77          * read completes.
78          */
79
80 #if ISODEPACKAGE == IC
81 #if ICRELEASE > 2
82         DAS_ReadArgument_INIT( &ra );
83 #endif
84 #endif
85
86         if ( ber_scanf( ber, "{a", &dn ) == LBER_ERROR ) {
87                 Debug( LDAP_DEBUG_ANY, "ber_scanf failed\n", 0, 0, 0 );
88                 send_ldap_msgresult( clientsb, MODTAG, m,
89                     LDAP_PROTOCOL_ERROR, NULL, "" );
90                 return( 0 );
91         }
92
93         Debug( LDAP_DEBUG_ARGS, "do_modify: dn (%s)\n", dn, 0, 0 );
94
95         ra.rda_object = ldap_str2dn( dn );
96         free( dn );
97         if ( ra.rda_object == NULLDN ) {
98                 Debug( LDAP_DEBUG_ANY, "ldap_str2dn failed\n", 0, 0, 0 );
99                 send_ldap_msgresult( clientsb, MODTAG, m,
100                     LDAP_INVALID_DN_SYNTAX, NULL, "" );
101                 return( 0 );
102         }
103         ra.rda_eis.eis_allattributes = TRUE;
104         ra.rda_eis.eis_infotypes = EIS_ATTRIBUTESANDVALUES;
105         ra.rda_eis.eis_select = NULLATTR;
106
107         /* collect modifications & save for later */
108         mods = modtail = NULL;
109         for ( tag = ber_first_element( ber, &len, &last ); tag != LBER_DEFAULT;
110             tag = ber_next_element( ber, &len, last ) ) {
111                 LDAPMod *tmp;
112
113                 if ( (tmp = (LDAPMod *) calloc( 1, sizeof(LDAPMod) ))
114                     == NULL ) {
115                         send_ldap_msgresult( clientsb, MODTAG, m,
116                             LDAP_OPERATIONS_ERROR, NULL, "Malloc error" );
117                         return( 0 );
118                 }
119                         
120                 if ( ber_scanf( ber, "{i{a[V]}}", &tmp->mod_op, &tmp->mod_type,
121                     &tmp->mod_bvalues ) == LBER_ERROR ) {
122                         send_ldap_msgresult( clientsb, MODTAG, m,
123                             LDAP_PROTOCOL_ERROR, NULL, "" );
124                         return( 0 );
125                 }
126
127                 if ( mods == NULL ) {
128                         mods = tmp;
129                 } else {
130                         modtail->mod_next = tmp;
131                 }
132                 modtail = tmp;
133         }
134         m->m_mods = mods;
135
136         ra.rda_common = common; /* struct copy */
137
138         rc = initiate_dap_operation( OP_READ, m, &ra );
139
140         dn_free( ra.rda_object );
141
142         if ( rc != 0 ) {
143                 send_ldap_msgresult( clientsb, MODTAG, m, rc, NULL, "" );
144                 return( 0 );
145         }
146
147         return( 1 );
148 }
149
150 int
151 do_modify2(
152     Sockbuf                     *clientsb,
153     struct msg                  *m,
154     struct ds_read_result       *rr
155 )
156 {
157         struct ds_modifyentry_arg       ma;
158         struct entrymod                 *changetail = NULLMOD;
159         int                             rc;
160         LDAPMod                         *mods;
161
162         Debug( LDAP_DEBUG_TRACE, "do_modify2\n", 0, 0, 0 );
163
164 #if ISODEPACKAGE == IC
165 #if ICRELEASE > 2
166         DAS_ModifyEntryArgument_INIT( &ma );
167 #endif
168 #endif
169
170         ma.mea_changes = NULLMOD;
171         for ( mods = m->m_mods; mods != NULL; mods = mods->mod_next ) {
172                 struct entrymod *em;
173                 Attr_Sequence   as, new;
174
175                 if ( (em = (struct entrymod *) calloc( 1,
176                     sizeof(struct entrymod) )) == NULLMOD ) {
177                         send_ldap_msgresult( clientsb, MODTAG, m,
178                             LDAP_OPERATIONS_ERROR, NULL, "Malloc error" );
179                         return( 0 );
180                 }
181                 em->em_next = NULLMOD;
182
183                 if ( (new = get_as( clientsb, MODTAG, m,
184                     mods->mod_type, mods->mod_bvalues )) == NULLATTR )
185                         return( 0 );
186                 em->em_what = new;
187
188                 for ( as = rr->rdr_entry.ent_attr; as != NULLATTR;
189                     as = as->attr_link ) {
190                         if ( AttrT_cmp( new->attr_type, as->attr_type ) == 0 )
191                                 break;
192                 }
193
194                 if ( new->attr_value == NULLAV &&
195                     mods->mod_op != LDAP_MOD_DELETE ) {
196                         send_ldap_msgresult( clientsb, MODTAG, m,
197                             LDAP_INVALID_SYNTAX, NULL, "No values specified" );
198                         return( 0 );
199                 }
200
201                 switch ( mods->mod_op ) {
202                 case LDAP_MOD_ADD:
203                         Debug( LDAP_DEBUG_ARGS, "ADD:\n", 0, 0, 0 );
204
205                         if ( as == NULLATTR ) {
206                                 Debug( LDAP_DEBUG_ARGS, "\tattribute\n", 0, 0,
207                                     0 );
208                                 em->em_type = EM_ADDATTRIBUTE;
209                         } else {
210                                 Debug( LDAP_DEBUG_ARGS, "\tvalues\n", 0, 0, 0 );
211                                 em->em_type = EM_ADDVALUES;
212                         }
213                         break;
214
215                 case LDAP_MOD_DELETE:
216                         Debug( LDAP_DEBUG_ARGS, "DELETE:\n", 0, 0, 0 );
217
218                         if ( as == NULLATTR ) {
219                                 Debug( LDAP_DEBUG_ARGS,
220                                     "\tno existing attribute\n", 0, 0, 0 );
221                                 send_ldap_msgresult( clientsb, MODTAG,
222                                     m, LDAP_NO_SUCH_ATTRIBUTE, NULL, "" );
223                                 ems_free( em );
224                                 return( 0 );
225                         } else {
226                                 if ( new->attr_value == NULLAV ) {
227                                         Debug( LDAP_DEBUG_ARGS, "\tattribute\n",
228                                             0, 0, 0 );
229                                         em->em_type = EM_REMOVEATTRIBUTE;
230                                 } else {
231                                         if ( avs_cmp( new->attr_value,
232                                             as->attr_value ) == 0 ) {
233                                                 Debug( LDAP_DEBUG_ARGS,
234                                                     "\tattribute\n", 0, 0, 0 );
235                                                 em->em_type =
236                                                     EM_REMOVEATTRIBUTE;
237                                         } else {
238                                                 Debug( LDAP_DEBUG_ARGS,
239                                                     "\tvalues\n", 0, 0, 0 );
240                                                 em->em_type = EM_REMOVEVALUES;
241                                         }
242                                 }
243                         }
244                         break;
245
246                 case LDAP_MOD_REPLACE:
247                         Debug( LDAP_DEBUG_ARGS, "REPLACE:\n", 0, 0, 0 );
248
249                         if ( as == NULLATTR ) {
250                                 Debug( LDAP_DEBUG_ARGS, "\tattribute\n", 0, 0,
251                                     0 );
252                                 em->em_type = EM_ADDATTRIBUTE;
253                         } else {
254                                 if ( replace_mod( em, as, new ) < 0 ) {
255                                         return( 0 );
256                                 }
257                         }
258                         break;
259
260                 default:
261                         Debug( LDAP_DEBUG_ARGS, "UNKNOWN MOD:\n", 0, 0, 0 );
262
263                         send_ldap_msgresult( clientsb, MODTAG, m,
264                             LDAP_PROTOCOL_ERROR, NULL, "" );
265                         return( 0 );
266                         break;
267                 }
268
269                 if ( em->em_what == NULL ) {    /* ignore this mod */
270                         free( em );
271                 } else {
272                         if ( ma.mea_changes == NULLMOD ) {
273                                 ma.mea_changes = em;
274                         } else {
275                                 changetail->em_next = em;
276                         }
277                         changetail = em->em_next == NULLMOD ? em : em->em_next;
278                 }
279         }
280
281 #ifdef LDAP_DEBUG
282         if ( ldap_debug & LDAP_DEBUG_ARGS ) {
283                 struct entrymod *e;
284                 Attr_Sequence   as;
285                 AV_Sequence     val;
286                 PS              ps;
287
288                 ps = ps_alloc( std_open );
289                 std_setup( ps, stderr );
290
291                 fprintf( stderr, "Modify changes are:\n");
292                 for (e = ma.mea_changes; e; e = e->em_next) {
293                         switch (e->em_type) {
294                         case EM_ADDATTRIBUTE:
295                                 fprintf( stderr, "\tADD ATTRIBUTE\n");
296                                 break;
297                         case EM_REMOVEATTRIBUTE:
298                                 fprintf( stderr, "\tREMOVE ATTRIBUTE\n");
299                                 break;
300                         case EM_ADDVALUES:
301                                 fprintf( stderr, "\tADD VALUES\n");
302                                 break;
303                         case EM_REMOVEVALUES:
304                                 fprintf( stderr, "\tREMOVE VALUES\n");
305                                 break;
306                         default:
307                                 fprintf( stderr, "\tUNKNOWN\n");
308                                 break;
309                         }
310
311                         as = e->em_what;
312                         fprintf( stderr, "\t\ttype (" );
313                         AttrT_print( ps, as->attr_type, EDBOUT );
314                         fprintf( stderr, ")" );
315                         if ( e->em_type == EM_REMOVEATTRIBUTE ) {
316                                 fprintf( stderr, "\n" );
317                                 continue;
318                         }
319                         fprintf( stderr, " values" );
320                         for (val = as->attr_value; val; val = val->avseq_next) {
321                                 ps_print( ps, " (" );
322                                 AttrV_print( ps, &val->avseq_av, EDBOUT );
323                                 ps_print( ps, ")" );
324                         }
325                         fprintf( stderr, "\n" );
326                 }
327                 ps_free( ps );
328         }
329 #endif
330
331         if ( ma.mea_changes == NULLMOD ) {      /* nothing to do */
332                 send_ldap_msgresult( clientsb, MODTAG, m,
333                     LDAP_SUCCESS, NULL, "" );
334                 return( 0 );
335         }
336
337         ma.mea_object = rr->rdr_entry.ent_dn;
338         ma.mea_common = common; /* struct copy */
339
340         rc = initiate_dap_operation( OP_MODIFYENTRY, m, &ma );
341
342         ems_free( ma.mea_changes );
343
344         if ( rc != 0 ) {
345                 send_ldap_msgresult( clientsb, MODTAG, m, rc, NULL, "" );
346                 return( 0 );
347         }
348
349         return( 1 );
350 }
351
352 Attr_Sequence
353 get_as(
354     Sockbuf             *clientsb,
355     unsigned long       op,
356     struct msg          *m,
357     char                *type,
358     struct berval       **bvals
359 )
360 {
361         Attr_Sequence   as;
362         int             i;
363         short           syntax;
364
365         Debug( LDAP_DEBUG_TRACE, "get_as\n", 0, 0, 0 );
366
367         if ( (as = as_comp_new( NULLAttrT, NULLAV, NULLACL_INFO ))
368             == NULLATTR ) {
369                 send_ldap_msgresult( clientsb, op, m,
370                     LDAP_OPERATIONS_ERROR, NULL, "Malloc error" );
371                 return( NULLATTR );
372         }
373         as->attr_link = NULLATTR;
374         as->attr_value = NULLAV;
375         as->attr_acl = NULLACL_INFO;
376
377         if ( (as->attr_type = str2AttrT( type )) == NULLAttrT ) {
378               send_ldap_msgresult( clientsb, op, m, LDAP_UNDEFINED_TYPE,
379                   NULL, type );
380                 return( NULLATTR );
381         }
382
383         if ( bvals == NULL )
384                 return( as );
385
386         syntax = as->attr_type->oa_syntax;
387         for ( i = 0; bvals[i] != NULL; i++ ) {
388                 AttributeValue  av;
389                 int             t61str, ncomp;
390                 char            *sval, *s, *news, *n;
391
392                 if ( syntax == ldap_jpeg_syntax ||
393                     syntax == ldap_jpeg_nonfile_syntax ||
394                     syntax == ldap_octetstring_syntax ||
395                     syntax == ldap_audio_syntax ) {
396                         if (( av = bv_octet2AttrV( bvals[i] )) == NULLAttrV ) {
397                                 send_ldap_msgresult( clientsb, op, m,
398                                     LDAP_INVALID_SYNTAX, NULL, type );
399                                 as_free( as );
400                                 return( NULLATTR );
401                         }
402                 } else if ( syntax == ldap_photo_syntax ) {
403                         if (( av = bv_asn2AttrV( bvals[i] )) == NULLAttrV ) {
404                                 send_ldap_msgresult( clientsb, op, m,
405                                     LDAP_INVALID_SYNTAX, NULL, type );
406                                 as_free( as );
407                                 return( NULLATTR );
408                         }
409                 } else {
410
411                         if (( sval = malloc( bvals[i]->bv_len + 1 )) == NULL ) {
412                                 send_ldap_msgresult( clientsb, op, m,
413                                     LDAP_OPERATIONS_ERROR, NULL,
414                                   "Malloc error" );
415                                 return( NULLATTR );
416                         }
417                         SAFEMEMCPY( sval, bvals[i]->bv_val, bvals[i]->bv_len );
418                         sval[ bvals[i]->bv_len ] = '\0';
419
420                         /* dang quipu - there's no need for this! */
421                         if ( syntax == ldap_postaladdress_syntax ) {
422                                 t61str = 0;
423                                 ncomp = 1;
424                                 for ( s = sval; *s; s++ ) {
425                                         if ( *s == '$' ) {
426                                                 ncomp++;
427                                                 continue;
428                                         }
429 #define ist61(c)  (!isascii(c) || !isalnum(c) \
430                           && c != 047 && c != '(' && c != ')' \
431                           && c != '+' && c != '-' && c != '.' && c != ',' \
432                           && c != '/' && c != ':' && c != '=' && c != '?' \
433                           && c != ' ')
434                                         if ( ist61( *s ) )
435                                                 t61str = 1;
436                                 }
437 #define T61MARK         "{T.61}"
438 #define T61MARKLEN      6
439                                 if ( t61str ) {
440                                         news = malloc( strlen(sval) +
441                                             ncomp * T61MARKLEN + 1 );
442                                         strcpy( news, T61MARK );
443                                         for ( n = news + T61MARKLEN, s = sval;
444                                             *s; n++, s++ ) {
445                                                 *n = *s;
446                                                 if ( *s == '$' ) {
447                                                         strcpy( ++n, T61MARK );
448                                                         n += T61MARKLEN - 1;
449                                                 }
450                                         }
451                                         *n = '\0';
452                                         free( sval );
453                                         sval = news;
454                                 }
455
456                                 av = str_at2AttrV( sval, as->attr_type );
457                         } else if ( syntax == ldap_dn_syntax ) {
458                                 av = ldap_strdn2AttrV( sval );
459                         } else if ( i != 0 && syntax == ldap_acl_syntax ) {
460                                 (void) (*merge_acl)( as->attr_value, sval );
461                                 free( sval );
462                                 continue;
463                         } else {
464                                 av = ldap_str_at2AttrV( sval, as->attr_type );
465                         }
466
467                         if ( av == NULLAttrV ) {
468                                 send_ldap_msgresult( clientsb, op, m,
469                                     LDAP_INVALID_SYNTAX, NULL, sval );
470                                 free( sval );
471                                 as_free( as );
472                                 return( NULLATTR );
473                         }
474
475                         free( sval );
476                 }
477                 as->attr_value = avs_merge( as->attr_value,
478                     avs_comp_new( av ) );
479         }
480
481         return( as );
482 }
483
484 void
485 modify_result( Sockbuf *sb, struct msg *m )
486 {
487         send_ldap_msgresult( sb, MODTAG, m, LDAP_SUCCESS, NULL, "" );
488
489         return;
490 }
491
492 void
493 modlist_free( LDAPMod *mods )
494 {
495         LDAPMod *next = NULL;
496
497         for ( ; mods != NULL; mods = next ) {
498                 free( mods->mod_type );
499                 if ( mods->mod_bvalues != NULL )
500                         ber_bvecfree( mods->mod_bvalues );
501                 free( mods );
502         }
503 }
504
505 /*
506  * called when mod is replace to optimize by only deleting old values
507  * that are not in the new set and by only adding what isn't in old set
508  */
509
510 static int
511 replace_mod(
512     struct entrymod     *rem,
513     Attr_Sequence       oas,
514     Attr_Sequence       nas
515 )
516 {
517         AV_Sequence     oavs, navs, davs, prev_navs, tmp;
518 #ifdef LDAP_DEBUG
519         PS              ps;
520
521         ps = ps_alloc( std_open );
522         std_setup( ps, stderr );
523
524         if ( ldap_debug & LDAP_DEBUG_ARGS ) {
525                 ps_print( ps, "replace_mod(" );
526                 AttrT_print( ps, oas->attr_type, EDBOUT );
527                 ps_print( ps, ")\n" );
528         }
529 #endif
530
531         davs = NULL;
532         for ( oavs = oas->attr_value; oavs != NULL; oavs = oavs->avseq_next ) {
533 #ifdef LDAP_DEBUG
534                 if ( ldap_debug & LDAP_DEBUG_ARGS ) {
535                         ps_print( ps, "old value " );
536                         AttrV_print( ps, &oavs->avseq_av, EDBOUT );
537                         ps_print( ps, "\n" );
538                 }
539 #endif
540
541                 prev_navs = NULL;
542                 for ( navs = nas->attr_value; navs != NULL;
543                     prev_navs = navs, navs = navs->avseq_next ) {
544 #ifdef LDAP_DEBUG
545                         if ( ldap_debug & LDAP_DEBUG_ARGS ) {
546                                 ps_print( ps, "\tnew value " );
547                                 AttrV_print( ps, &navs->avseq_av, EDBOUT );
548                                 ps_print( ps, "\n" );
549                         }
550 #endif
551                         if ( AttrV_cmp( &oavs->avseq_av, &navs->avseq_av)
552                             == 0) {
553                                 break;
554                         }
555                 }
556
557                 if ( navs == NULL ) {   /* value to delete */
558 #ifdef LDAP_DEBUG
559                         if ( ldap_debug & LDAP_DEBUG_ARGS ) {
560                                 ps_print( ps, "value to delete " );
561                                 AttrV_print( ps, &oavs->avseq_av, EDBOUT );
562                                 ps_print( ps, "\n" );
563                         }
564 #endif
565                         if ( davs == NULL ) {
566                             davs = avs_comp_cpy( oavs );
567                         } else {
568                             tmp = avs_comp_cpy( oavs );
569                             tmp->avseq_next = davs;
570                             davs = tmp;
571                         }
572                 } else {                /* value to keep */
573 #ifdef LDAP_DEBUG
574                         if ( ldap_debug & LDAP_DEBUG_ARGS ) {
575                                 ps_print( ps, "value to leave alone " );
576                                 AttrV_print( ps, &oavs->avseq_av, EDBOUT );
577                                 ps_print( ps, "\n" );
578                         }
579 #endif
580                         if ( prev_navs == NULL ) {
581                             nas->attr_value = navs->avseq_next;
582                         } else {
583                             prev_navs->avseq_next = navs->avseq_next;
584                         }
585                         avs_comp_free( navs );
586                 }
587         }
588
589         if ( davs == NULL && nas->attr_value == NULL ) {
590 #ifdef LDAP_DEBUG
591                 if ( ldap_debug & LDAP_DEBUG_ARGS ) {
592                         ps_print( ps, "  nothing to do" );
593                 }
594 #endif
595                 rem->em_what = NULL;
596         } else {
597             /*  Must add new values before removing old values.
598              *  Otherwise, removing all existing values causes the
599              *  attribute to be removed such that subsequent add values
600              *  fail.
601              */
602                 if ( nas->attr_value != NULL ) {        /* add new values */
603 #ifdef LDAP_DEBUG
604                         if ( ldap_debug & LDAP_DEBUG_ARGS ) {
605                                 AttrT_print( ps, nas->attr_type, EDBOUT );
606                                 ps_print( ps, ": some to add\n" );
607                         }
608 #endif
609                         rem->em_type = EM_ADDVALUES;
610                         rem->em_what = nas;
611                         rem->em_next = NULLMOD;
612                 }
613
614                 if ( davs != NULL ) {   /* delete old values */
615 #ifdef LDAP_DEBUG
616                         if ( ldap_debug & LDAP_DEBUG_ARGS ) {
617                                 AttrT_print( ps, nas->attr_type, EDBOUT );
618                                 ps_print( ps, ": some to delete\n" );
619                         }
620 #endif
621                         if ( nas->attr_value != NULL ) {
622                                 rem->em_next = (struct entrymod *) calloc( 1,
623                                     sizeof(struct entrymod) );
624                                 rem = rem->em_next;
625                         }
626                         rem->em_type = EM_REMOVEVALUES;
627                         rem->em_what = as_comp_new( NULLAttrT, NULLAV,
628                             NULLACL_INFO );
629                         rem->em_what->attr_type = AttrT_cpy( nas->attr_type );
630                         rem->em_what->attr_value = davs;
631                 }
632         }
633
634         return( 0 );
635 }