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