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