]> git.sur5r.net Git - openldap/blob - servers/slapd/back-ldbm/modify.c
Fix referral handling bug
[openldap] / servers / slapd / back-ldbm / modify.c
1 /* modify.c - ldbm backend modify routine */
2 /* $OpenLDAP$ */
3 /*
4  * Copyright 1998-2000 The OpenLDAP Foundation, All Rights Reserved.
5  * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
6  */
7
8 #include "portable.h"
9
10 #include <stdio.h>
11
12 #include <ac/string.h>
13 #include <ac/socket.h>
14 #include <ac/time.h>
15
16 #include "slap.h"
17 #include "back-ldbm.h"
18 #include "proto-back-ldbm.h"
19
20 static int add_values LDAP_P(( Entry *e, Modification *mod, char *dn ));
21 static int delete_values LDAP_P(( Entry *e, Modification *mod, char *dn ));
22 static int replace_values LDAP_P(( Entry *e, Modification *mod, char *dn ));
23
24 /* We need this function because of LDAP modrdn. If we do not 
25  * add this there would be a bunch of code replication here 
26  * and there and of course the likelihood of bugs increases.
27  * Juan C. Gomez (gomez@engr.sgi.com) 05/18/99
28  */ 
29
30 int ldbm_modify_internal(
31     Backend     *be,
32     Connection  *conn,
33     Operation   *op,
34     const char  *dn,
35     Modifications       *modlist,
36     Entry       *e,
37         const char **text 
38 )
39 {
40         int rc, err;
41         Modification    *mod;
42         Modifications   *ml;
43         Attribute       *save_attrs;
44
45 #ifdef NEW_LOGGING
46         LDAP_LOG(( "backend", LDAP_LEVEL_ENTRY,
47                    "ldbm_modify_internal: %s\n", dn ));
48 #else
49         Debug(LDAP_DEBUG_TRACE, "ldbm_modify_internal:\n", 0, 0, 0);
50 #endif
51
52
53         if ( !acl_check_modlist( be, conn, op, e, modlist )) {
54                 return LDAP_INSUFFICIENT_ACCESS;
55         }
56
57         save_attrs = e->e_attrs;
58         e->e_attrs = attrs_dup( e->e_attrs );
59
60         for ( ml = modlist; ml != NULL; ml = ml->sml_next ) {
61                 mod = &ml->sml_mod;
62
63                 switch ( mod->sm_op ) {
64                 case LDAP_MOD_ADD:
65 #ifdef NEW_LOGGING
66                         LDAP_LOG(( "backend", LDAP_LEVEL_DETAIL1,
67                                    "ldbm_modify_internal: add\n" ));
68 #else
69                         Debug(LDAP_DEBUG_ARGS, "ldbm_modify_internal: add\n", 0, 0, 0);
70 #endif
71
72                         err = add_values( e, mod, op->o_ndn );
73
74                         if( err != LDAP_SUCCESS ) {
75                                 *text = "modify: add values failed";
76 #ifdef NEW_LOGGING
77                                 LDAP_LOG(( "backend", LDAP_LEVEL_INFO,
78                                            "ldbm_modify_internal: failed %d (%s)\n",
79                                            err, *text ));
80 #else
81                                 Debug(LDAP_DEBUG_ARGS, "ldbm_modify_internal: %d %s\n",
82                                         err, *text, 0);
83 #endif
84                         }
85                         break;
86
87                 case LDAP_MOD_DELETE:
88 #ifdef NEW_LOGGING
89                         LDAP_LOG(( "backend", LDAP_LEVEL_DETAIL1,
90                                    "ldbm_modify_internal: delete\n" ));
91 #else
92                         Debug(LDAP_DEBUG_ARGS, "ldbm_modify_internal: delete\n", 0, 0, 0);
93 #endif
94
95                         err = delete_values( e, mod, op->o_ndn );
96                         assert( err != LDAP_TYPE_OR_VALUE_EXISTS );
97                         if( err != LDAP_SUCCESS ) {
98                                 *text = "modify: delete values failed";
99 #ifdef NEW_LOGGING
100                                 LDAP_LOG(( "backend", LDAP_LEVEL_INFO,
101                                            "ldbm_modify_internal: failed %d (%s)\n", err, *text ));
102 #else
103                                 Debug(LDAP_DEBUG_ARGS, "ldbm_modify_internal: %d %s\n",
104                                         err, *text, 0);
105 #endif
106                         }
107                         break;
108
109                 case LDAP_MOD_REPLACE:
110 #ifdef NEW_LOGGING
111                         LDAP_LOG(( "backend", LDAP_LEVEL_DETAIL1,
112                                    "ldbm_modify_internal:  replace\n" ));
113 #else
114                         Debug(LDAP_DEBUG_ARGS, "ldbm_modify_internal: replace\n", 0, 0, 0);
115 #endif
116
117                         err = replace_values( e, mod, op->o_ndn );
118                         assert( err != LDAP_TYPE_OR_VALUE_EXISTS );
119                         if( err != LDAP_SUCCESS ) {
120                                 *text = "modify: replace values failed";
121 #ifdef NEW_LOGGING
122                                 LDAP_LOG(( "backend", LDAP_LEVEL_INFO,
123                                            "ldbm_modify_internal: failed %d (%s)\n", err, *text ));
124 #else
125                                 Debug(LDAP_DEBUG_ARGS, "ldbm_modify_internal: %d %s\n",
126                                         err, *text, 0);
127 #endif
128
129                         }
130                         break;
131
132                 case SLAP_MOD_SOFTADD:
133 #ifdef NEW_LOGGING
134                         LDAP_LOG(( "backend", LDAP_LEVEL_DETAIL1,
135                                    "ldbm_modify_internal: softadd\n" ));
136 #else
137                         Debug(LDAP_DEBUG_ARGS, "ldbm_modify_internal: softadd\n", 0, 0, 0);
138 #endif
139
140                         /* Avoid problems in index_add_mods()
141                          * We need to add index if necessary.
142                          */
143                         mod->sm_op = LDAP_MOD_ADD;
144                         err = add_values( e, mod, op->o_ndn );
145
146                         if ( err == LDAP_TYPE_OR_VALUE_EXISTS ) {
147                                 err = LDAP_SUCCESS;
148                         }
149
150                         if( err != LDAP_SUCCESS ) {
151                                 *text = "modify: (soft)add values failed";
152 #ifdef NEW_LOGGING
153                                 LDAP_LOG(( "backend", LDAP_LEVEL_INFO,
154                                            "ldbm_modify_internal: failed %d (%s)\n", err, *text ));
155 #else
156                                 Debug(LDAP_DEBUG_ARGS, "ldbm_modify_internal: %d %s\n",
157                                         err, *text, 0);
158 #endif
159
160                         }
161                         break;
162
163                 default:
164 #ifdef NEW_LOGGING
165                         LDAP_LOG(( "backend", LDAP_LEVEL_ERR,
166                                    "ldbm_modify_internal: invalid op %d\n", mod->sm_op ));
167 #else
168                         Debug(LDAP_DEBUG_ANY, "ldbm_modify_internal: invalid op %d\n",
169                                 mod->sm_op, 0, 0);
170 #endif
171
172                         err = LDAP_OTHER;
173                         *text = "Invalid modify operation";
174 #ifdef NEW_LOGGING
175                         LDAP_LOG(( "backend", LDAP_LEVEL_INFO,
176                                    "ldbm_modify_internal: %d (%s)\n", err, *text ));
177 #else
178                         Debug(LDAP_DEBUG_ARGS, "ldbm_modify_internal: %d %s\n",
179                                 err, *text, 0);
180 #endif
181
182                 }
183
184                 if ( err != LDAP_SUCCESS ) {
185                         attrs_free( e->e_attrs );
186                         e->e_attrs = save_attrs;
187                         /* unlock entry, delete from cache */
188                         return err; 
189                 }
190         }
191
192         /* check for abandon */
193         ldap_pvt_thread_mutex_lock( &op->o_abandonmutex );
194         if ( op->o_abandon ) {
195                 attrs_free( e->e_attrs );
196                 e->e_attrs = save_attrs;
197                 ldap_pvt_thread_mutex_unlock( &op->o_abandonmutex );
198                 return SLAPD_ABANDON;
199         }
200         ldap_pvt_thread_mutex_unlock( &op->o_abandonmutex );
201
202         /* check that the entry still obeys the schema */
203         rc = entry_schema_check( e, save_attrs, text );
204         if ( rc != LDAP_SUCCESS ) {
205                 attrs_free( e->e_attrs );
206                 e->e_attrs = save_attrs;
207 #ifdef NEW_LOGGING
208                 LDAP_LOG(( "backend", LDAP_LEVEL_ERR,
209                            "ldbm_modify_internal: entry failed schema check: %s\n",
210                            *text ));
211 #else
212                 Debug( LDAP_DEBUG_ANY, "entry failed schema check: %s\n",
213                         *text, 0, 0 );
214 #endif
215
216                 return rc;
217         }
218
219         /* check for abandon */
220         ldap_pvt_thread_mutex_lock( &op->o_abandonmutex );
221         if ( op->o_abandon ) {
222                 attrs_free( e->e_attrs );
223                 e->e_attrs = save_attrs;
224                 ldap_pvt_thread_mutex_unlock( &op->o_abandonmutex );
225                 return SLAPD_ABANDON;
226         }
227         ldap_pvt_thread_mutex_unlock( &op->o_abandonmutex );
228
229         /* delete indices for old attributes */
230         index_entry_del( be, e, save_attrs);
231
232         /* add indices for new attributes */
233         index_entry_add( be, e, e->e_attrs);
234
235         attrs_free( save_attrs );
236
237         return LDAP_SUCCESS;
238 }
239
240
241 int
242 ldbm_back_modify(
243     Backend     *be,
244     Connection  *conn,
245     Operation   *op,
246     const char  *dn,
247     const char  *ndn,
248     Modifications       *modlist
249 )
250 {
251         int rc;
252         struct ldbminfo *li = (struct ldbminfo *) be->be_private;
253         Entry           *matched;
254         Entry           *e;
255         int             manageDSAit = get_manageDSAit( op );
256         const char *text = NULL;
257
258 #ifdef NEW_LOGGING
259         LDAP_LOG(( "backend", LDAP_LEVEL_ENTRY,
260                    "ldbm_back_modify: enter\n" ));
261 #else
262         Debug(LDAP_DEBUG_ARGS, "ldbm_back_modify:\n", 0, 0, 0);
263 #endif
264
265
266         /* acquire and lock entry */
267         if ( (e = dn2entry_w( be, ndn, &matched )) == NULL ) {
268                 char* matched_dn = NULL;
269                 struct berval **refs = NULL;
270
271                 if ( matched != NULL ) {
272                         matched_dn = ch_strdup( matched->e_dn );
273                         refs = is_entry_referral( matched )
274                                 ? get_entry_referrals( be, conn, op, matched )
275                                 : NULL;
276                         cache_return_entry_r( &li->li_cache, matched );
277                 } else {
278                         refs = default_referral;
279                 }
280
281                 send_ldap_result( conn, op, LDAP_REFERRAL,
282                         matched_dn, NULL, refs, NULL );
283
284                 if ( matched != NULL ) {
285                         ber_bvecfree( refs );
286                         free( matched_dn );
287                 }
288
289                 return( -1 );
290         }
291
292     if ( !manageDSAit && is_entry_referral( e ) ) {
293                 /* parent is a referral, don't allow add */
294                 /* parent is an alias, don't allow add */
295                 struct berval **refs = get_entry_referrals( be,
296                         conn, op, e );
297
298 #ifdef NEW_LOGGING
299                 LDAP_LOG(( "backend", LDAP_LEVEL_INFO,
300                            "ldbm_back_modify: entry (%s) is referral\n", ndn ));
301 #else
302                 Debug( LDAP_DEBUG_TRACE, "entry is referral\n", 0,
303                     0, 0 );
304 #endif
305
306
307                 send_ldap_result( conn, op, LDAP_REFERRAL,
308                     e->e_dn, NULL, refs, NULL );
309
310                 ber_bvecfree( refs );
311
312                 goto error_return;
313         }
314         
315         /* Modify the entry */
316         rc = ldbm_modify_internal( be, conn, op, ndn, modlist, e, &text );
317
318         if( rc != LDAP_SUCCESS ) {
319                 if( rc != SLAPD_ABANDON ) {
320                         send_ldap_result( conn, op, rc,
321                                 NULL, text, NULL, NULL );
322                 }
323
324                 goto error_return;
325         }
326
327         /* change the entry itself */
328         if ( id2entry_add( be, e ) != 0 ) {
329                 send_ldap_result( conn, op, LDAP_OTHER,
330                         NULL, "id2entry failure", NULL, NULL );
331                 goto error_return;
332         }
333
334         send_ldap_result( conn, op, LDAP_SUCCESS,
335                 NULL, NULL, NULL, NULL );
336
337         cache_return_entry_w( &li->li_cache, e );
338         return( 0 );
339
340 error_return:;
341         cache_return_entry_w( &li->li_cache, e );
342         return( -1 );
343 }
344
345 static int
346 add_values(
347     Entry       *e,
348     Modification        *mod,
349     char        *dn
350 )
351 {
352         int             i;
353         Attribute       *a;
354
355         /* char *desc = mod->sm_desc->ad_cname->bv_val; */
356         MatchingRule *mr = mod->sm_desc->ad_type->sat_equality;
357
358         a = attr_find( e->e_attrs, mod->sm_desc );
359
360         /* check if the values we're adding already exist */
361         if ( a != NULL ) {
362                 if( mr == NULL || !mr->smr_match ) {
363                         /* do not allow add of additional attribute
364                                 if no equality rule exists */
365                         return LDAP_INAPPROPRIATE_MATCHING;
366                 }
367
368                 for ( i = 0; mod->sm_bvalues[i] != NULL; i++ ) {
369                         int rc;
370                         int j;
371                         const char *text = NULL;
372                         struct berval *asserted;
373
374                         rc = value_normalize( mod->sm_desc,
375                                 SLAP_MR_EQUALITY,
376                                 mod->sm_bvalues[i],
377                                 &asserted,
378                                 &text );
379
380                         if( rc != LDAP_SUCCESS ) return rc;
381
382                         for ( j = 0; a->a_vals[j] != NULL; j++ ) {
383                                 int match;
384                                 int rc = value_match( &match, mod->sm_desc, mr,
385                                         SLAP_MR_MODIFY_MATCHING,
386                                         a->a_vals[j], asserted, &text );
387
388                                 if( rc == LDAP_SUCCESS && match == 0 ) {
389                                         ber_bvfree( asserted );
390                                         return LDAP_TYPE_OR_VALUE_EXISTS;
391                                 }
392                         }
393
394                         ber_bvfree( asserted );
395                 }
396         }
397
398         /* no - add them */
399         if( attr_merge( e, mod->sm_desc, mod->sm_bvalues ) != 0 ) {
400                 /* this should return result return of attr_merge */
401                 return LDAP_OTHER;
402         }
403
404         return LDAP_SUCCESS;
405 }
406
407 static int
408 delete_values(
409     Entry       *e,
410     Modification        *mod,
411     char        *dn
412 )
413 {
414         int             i, j, k, found;
415         Attribute       *a;
416         char *desc = mod->sm_desc->ad_cname->bv_val;
417         MatchingRule *mr = mod->sm_desc->ad_type->sat_equality;
418
419         /* delete the entire attribute */
420         if ( mod->sm_bvalues == NULL ) {
421 #ifdef NEW_LOGGING
422                 LDAP_LOG(( "backend", LDAP_LEVEL_ENTRY,
423                            "delete_values: removing entire attribute %s\n", desc ));
424 #else
425                 Debug( LDAP_DEBUG_ARGS, "removing entire attribute %s\n",
426                     desc, 0, 0 );
427 #endif
428
429                 return( attr_delete( &e->e_attrs, mod->sm_desc ) ?
430                     LDAP_NO_SUCH_ATTRIBUTE : LDAP_SUCCESS );
431         }
432
433         if( mr == NULL || !mr->smr_match ) {
434                 /* disallow specific attributes from being deleted if
435                         no equality rule */
436                 return LDAP_INAPPROPRIATE_MATCHING;
437         }
438
439         /* delete specific values - find the attribute first */
440         if ( (a = attr_find( e->e_attrs, mod->sm_desc )) == NULL ) {
441 #ifdef NEW_LOGGING
442                 LDAP_LOG(( "backend", LDAP_LEVEL_INFO,
443                            "ldap_modify_delete: Could not find attribute %s\n", desc ));
444 #else
445                 Debug( LDAP_DEBUG_ARGS, "ldap_modify_delete: "
446                         "could not find attribute %s\n",
447                     desc, 0, 0 );
448 #endif
449
450                 return( LDAP_NO_SUCH_ATTRIBUTE );
451         }
452
453         /* find each value to delete */
454         for ( i = 0; mod->sm_bvalues[i] != NULL; i++ ) {
455                 int rc;
456                 const char *text = NULL;
457
458                 struct berval *asserted;
459
460                 rc = value_normalize( mod->sm_desc,
461                         SLAP_MR_EQUALITY,
462                         mod->sm_bvalues[i],
463                         &asserted,
464                         &text );
465
466                 if( rc != LDAP_SUCCESS ) return rc;
467
468                 found = 0;
469                 for ( j = 0; a->a_vals[j] != NULL; j++ ) {
470                         int match;
471                         int rc = value_match( &match, mod->sm_desc, mr,
472                                 SLAP_MR_MODIFY_MATCHING,
473                                 a->a_vals[j], asserted, &text );
474
475                         if( rc == LDAP_SUCCESS && match != 0 ) {
476                                 continue;
477                         }
478
479                         /* found a matching value */
480                         found = 1;
481
482                         /* delete it */
483                         ber_bvfree( a->a_vals[j] );
484                         for ( k = j + 1; a->a_vals[k] != NULL; k++ ) {
485                                 a->a_vals[k - 1] = a->a_vals[k];
486                         }
487                         a->a_vals[k - 1] = NULL;
488
489                         break;
490                 }
491
492                 ber_bvfree( asserted );
493
494                 /* looked through them all w/o finding it */
495                 if ( ! found ) {
496 #ifdef NEW_LOGGING
497                         LDAP_LOG(( "backend", LDAP_LEVEL_ARGS,
498                                    "delete_values: could not find value for attr %s\n", desc )); 
499 #else
500                         Debug( LDAP_DEBUG_ARGS,
501                             "ldbm_modify_delete: could not find value for attr %s\n",
502                             desc, 0, 0 );
503 #endif
504
505                         return LDAP_NO_SUCH_ATTRIBUTE;
506                 }
507         }
508
509         /* if no values remain, delete the entire attribute */
510         if ( a->a_vals[0] == NULL ) {
511 #ifdef NEW_LOGGING
512                 LDAP_LOG(( "backend", LDAP_LEVEL_ENTRY,
513                            "delete_values: removing entire attribute %s\n", desc ));
514 #else
515                 Debug( LDAP_DEBUG_ARGS,
516                         "removing entire attribute %s\n",
517                         desc, 0, 0 );
518 #endif
519
520                 if ( attr_delete( &e->e_attrs, mod->sm_desc ) ) {
521                         return LDAP_NO_SUCH_ATTRIBUTE;
522                 }
523         }
524
525         return LDAP_SUCCESS;
526 }
527
528 static int
529 replace_values(
530     Entry       *e,
531     Modification        *mod,
532     char        *dn
533 )
534 {
535         int rc = attr_delete( &e->e_attrs, mod->sm_desc );
536
537         if( rc != LDAP_SUCCESS && rc != LDAP_NO_SUCH_ATTRIBUTE ) {
538                 return rc;
539         }
540
541         if ( mod->sm_bvalues != NULL &&
542                 attr_merge( e, mod->sm_desc, mod->sm_bvalues ) != 0 )
543         {
544                 return LDAP_OTHER;
545         }
546
547         return LDAP_SUCCESS;
548 }