Back to index

openldap  2.4.31
dupent.c
Go to the documentation of this file.
00001 /* dupent.c - LDAP Control for a Duplicate Entry Representation of Search Results */
00002 /* $OpenLDAP$ */
00003 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
00004  *
00005  * Copyright 2006-2012 The OpenLDAP Foundation.
00006  * All rights reserved.
00007  *
00008  * Redistribution and use in source and binary forms, with or without
00009  * modification, are permitted only as authorized by the OpenLDAP
00010  * Public License.
00011  *
00012  * A copy of this license is available in the file LICENSE in the
00013  * top-level directory of the distribution or, alternatively, at
00014  * <http://www.OpenLDAP.org/license.html>.
00015  */
00016 /* ACKNOWLEDGEMENTS:
00017  * This work was initially developed by Pierangelo Masarati for inclusion
00018  * in OpenLDAP Software.
00019  */
00020 
00021 /*
00022  * LDAP Control for a Duplicate Entry Representation of Search Results
00023  * <draft-ietf-ldapext-ldapv3-dupent-08.txt> (EXPIRED)
00024  * <http://tools.ietf.org/id/draft-ietf-ldapext-ldapv3-dupent-08.txt>
00025  */
00026 
00027 #include "portable.h"
00028 
00029 /* define SLAPD_OVER_DUPENT=2 to build as run-time loadable module */
00030 #ifdef SLAPD_OVER_DUPENT
00031 
00032 /*
00033  * The macros
00034  *
00035  * LDAP_CONTROL_DUPENT_REQUEST            "2.16.840.1.113719.1.27.101.1"
00036  * LDAP_CONTROL_DUPENT_RESPONSE           "2.16.840.1.113719.1.27.101.2"
00037  * LDAP_CONTROL_DUPENT_ENTRY              "2.16.840.1.113719.1.27.101.3"
00038  *
00039  * are already defined in <ldap.h>
00040  */
00041 
00042 /*
00043  * support for no attrs and "*" in AttributeDescriptionList is missing
00044  */
00045 
00046 #include "slap.h"
00047 #include "ac/string.h"
00048 
00049 #define o_dupent                   o_ctrlflag[dupent_cid]
00050 #define o_ctrldupent        o_controls[dupent_cid]
00051 
00052 static int dupent_cid;
00053 static slap_overinst dupent;
00054 
00055 typedef struct dupent_t {
00056        AttributeName *ds_an;
00057        ber_len_t            ds_nattrs;
00058        slap_mask_t          ds_flags;
00059        ber_int_t            ds_paa;
00060 } dupent_t;
00061 
00062 static int
00063 dupent_parseCtrl (
00064        Operation *op,
00065        SlapReply *rs,
00066        LDAPControl *ctrl )
00067 {
00068        ber_tag_t tag;
00069        BerElementBuffer berbuf;
00070        BerElement *ber = (BerElement *)&berbuf;
00071        ber_len_t len;
00072        BerVarray AttributeDescriptionList = NULL;
00073        ber_len_t cnt = sizeof(struct berval);
00074        ber_len_t off = 0;
00075        ber_int_t PartialApplicationAllowed = 1;
00076        dupent_t *ds = NULL;
00077        int i;
00078 
00079        if ( op->o_dupent != SLAP_CONTROL_NONE ) {
00080               rs->sr_text = "Dupent control specified multiple times";
00081               return LDAP_PROTOCOL_ERROR;
00082        }
00083 
00084        if ( BER_BVISNULL( &ctrl->ldctl_value ) ) {
00085               rs->sr_text = "Dupent control value is absent";
00086               return LDAP_PROTOCOL_ERROR;
00087        }
00088 
00089        if ( BER_BVISEMPTY( &ctrl->ldctl_value ) ) {
00090               rs->sr_text = "Dupent control value is empty";
00091               return LDAP_PROTOCOL_ERROR;
00092        }
00093 
00094        ber_init2( ber, &ctrl->ldctl_value, 0 );
00095 
00096        /*
00097 
00098    DuplicateEntryRequest ::= SEQUENCE { 
00099         AttributeDescriptionList, -- from [RFC2251] 
00100         PartialApplicationAllowed BOOLEAN DEFAULT TRUE } 
00101 
00102         AttributeDescriptionList ::= SEQUENCE OF 
00103                 AttributeDescription 
00104     
00105         AttributeDescription ::= LDAPString 
00106     
00107         attributeDescription = AttributeType [ ";" <options> ] 
00108 
00109         */
00110 
00111        tag = ber_skip_tag( ber, &len );
00112        if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX;
00113        if ( ber_scanf( ber, "{M}", &AttributeDescriptionList, &cnt, off )
00114               == LBER_ERROR )
00115        {
00116               rs->sr_text = "Dupent control: dupentSpec decoding error";
00117               rs->sr_err = LDAP_PROTOCOL_ERROR;
00118               goto done;
00119        }
00120        tag = ber_skip_tag( ber, &len );
00121        if ( tag == LBER_BOOLEAN ) {
00122               /* NOTE: PartialApplicationAllowed is ignored, since the control
00123                * can always be honored
00124                */
00125               if ( ber_scanf( ber, "b", &PartialApplicationAllowed ) == LBER_ERROR )
00126               {
00127                      rs->sr_text = "Dupent control: dupentSpec decoding error";
00128                      rs->sr_err = LDAP_PROTOCOL_ERROR;
00129                      goto done;
00130               }
00131               tag = ber_skip_tag( ber, &len );
00132        }
00133        if ( len || tag != LBER_DEFAULT ) {
00134               rs->sr_text = "Dupent control: dupentSpec decoding error";
00135               rs->sr_err = LDAP_PROTOCOL_ERROR;
00136               goto done;
00137        }
00138 
00139        ds = (dupent_t *)op->o_tmpcalloc( 1,
00140               sizeof(dupent_t) + sizeof(AttributeName)*cnt,
00141               op->o_tmpmemctx );
00142 
00143        ds->ds_paa = PartialApplicationAllowed;
00144 
00145        if ( cnt == 0 ) {
00146               ds->ds_flags |= SLAP_USERATTRS_YES;
00147 
00148        } else {
00149               int c;
00150 
00151               ds->ds_an = (AttributeName *)&ds[ 1 ];
00152 
00153               for ( i = 0, c = 0; i < cnt; i++ ) {
00154                      const char *text;
00155                      int j;
00156                      int rc;
00157                      AttributeDescription *ad = NULL;
00158 
00159                      if ( bvmatch( &AttributeDescriptionList[i],
00160                             slap_bv_all_user_attrs ) )
00161                      {
00162                             if ( ds->ds_flags & SLAP_USERATTRS_YES ) {
00163                                    rs->sr_text = "Dupent control: AttributeDescription decoding error";
00164                                    rs->sr_err = LDAP_PROTOCOL_ERROR;
00165                                    goto done;
00166                             }
00167 
00168                             ds->ds_flags |= SLAP_USERATTRS_YES;
00169                             continue;
00170                      }
00171 
00172                      rc = slap_bv2ad( &AttributeDescriptionList[i], &ad, &text );
00173                      if ( rc != LDAP_SUCCESS ) {
00174                             continue;
00175                      }
00176 
00177                      ds->ds_an[c].an_desc = ad;
00178                      ds->ds_an[c].an_name = ad->ad_cname;
00179 
00180                      /* FIXME: not specified; consider this an error, just in case */
00181                      for ( j = 0; j < c; j++ ) {
00182                             if ( ds->ds_an[c].an_desc == ds->ds_an[j].an_desc ) {
00183                                    rs->sr_text = "Dupent control: AttributeDescription must be unique within AttributeDescriptionList";
00184                                    rs->sr_err = LDAP_PROTOCOL_ERROR;
00185                                    goto done;
00186                             }
00187                      }
00188 
00189                      c++;
00190               }
00191 
00192               ds->ds_nattrs = c;
00193 
00194               if ( ds->ds_flags & SLAP_USERATTRS_YES ) {
00195                      /* purge user attrs */
00196                      for ( i = 0; i < ds->ds_nattrs;  ) {
00197                             if ( is_at_operational( ds->ds_an[i].an_desc->ad_type ) ) {
00198                                    i++;
00199                                    continue;
00200                             }
00201 
00202                             ds->ds_nattrs--;
00203                             if ( i < ds->ds_nattrs ) {
00204                                    ds->ds_an[i] = ds->ds_an[ds->ds_nattrs];
00205                             }
00206                      }
00207               }
00208        }
00209 
00210        op->o_ctrldupent = (void *)ds;
00211 
00212        op->o_dupent = ctrl->ldctl_iscritical
00213               ? SLAP_CONTROL_CRITICAL
00214               : SLAP_CONTROL_NONCRITICAL;
00215 
00216        rs->sr_err = LDAP_SUCCESS;
00217 
00218 done:;
00219        if ( rs->sr_err != LDAP_SUCCESS ) {
00220               op->o_tmpfree( ds, op->o_tmpmemctx );
00221        }
00222 
00223        if ( AttributeDescriptionList != NULL ) {
00224               ber_memfree_x( AttributeDescriptionList, op->o_tmpmemctx );
00225        }
00226 
00227        return rs->sr_err;
00228 }
00229 
00230 typedef struct dupent_cb_t {
00231        slap_overinst *dc_on;
00232        dupent_t             *dc_ds;
00233        int           dc_skip;
00234 } dupent_cb_t;
00235 
00236 typedef struct valnum_t {
00237        Attribute *ap;
00238        Attribute a;
00239        struct berval vals[2];
00240        struct berval nvals[2];
00241        int cnt;
00242 } valnum_t;
00243 
00244 static int
00245 dupent_response_done( Operation *op, SlapReply *rs )
00246 {
00247        BerElementBuffer     berbuf;
00248        BerElement                  *ber = (BerElement *) &berbuf;
00249        struct berval        ctrlval;
00250        LDAPControl                 *ctrl, *ctrlsp[2];
00251 
00252        ber_init2( ber, NULL, LBER_USE_DER );
00253 
00254        /*
00255 
00256       DuplicateEntryResponseDone ::= SEQUENCE { 
00257          resultCode,     -- From [RFC2251] 
00258          errorMessage    [0] LDAPString OPTIONAL, 
00259          attribute       [1] AttributeDescription OPTIONAL } 
00260 
00261         */
00262 
00263        ber_printf( ber, "{i}", rs->sr_err );
00264        if ( ber_flatten2( ber, &ctrlval, 0 ) == -1 ) {
00265               ber_free_buf( ber );
00266               if ( op->o_dupent == SLAP_CONTROL_CRITICAL ) {
00267                      return LDAP_CONSTRAINT_VIOLATION;
00268               }
00269               return SLAP_CB_CONTINUE;
00270        }
00271 
00272        ctrl = op->o_tmpcalloc( 1,
00273               sizeof( LDAPControl ) + ctrlval.bv_len + 1,
00274               op->o_tmpmemctx );
00275        ctrl->ldctl_value.bv_val = (char *)&ctrl[ 1 ];
00276        ctrl->ldctl_oid = LDAP_CONTROL_DUPENT_RESPONSE;
00277        ctrl->ldctl_iscritical = 0;
00278        ctrl->ldctl_value.bv_len = ctrlval.bv_len;
00279        AC_MEMCPY( ctrl->ldctl_value.bv_val, ctrlval.bv_val, ctrlval.bv_len );
00280        ctrl->ldctl_value.bv_val[ ctrl->ldctl_value.bv_len ] = '\0';
00281 
00282        ber_free_buf( ber );
00283 
00284        ctrlsp[0] = ctrl;
00285        ctrlsp[1] = NULL;
00286        slap_add_ctrls( op, rs, ctrlsp );
00287 
00288        return SLAP_CB_CONTINUE;
00289 }
00290 
00291 static int
00292 dupent_response_entry_1level(
00293        Operation *op,
00294        SlapReply *rs,
00295        Entry *e,
00296        valnum_t *valnum,
00297        int nattrs,
00298        int level )
00299 {
00300        int i, rc = LDAP_SUCCESS;
00301 
00302        for ( i = 0; i < valnum[level].ap->a_numvals; i++ ) {
00303               LDAPControl   *ctrl = NULL, *ctrlsp[2];
00304 
00305               valnum[level].a.a_vals[0] = valnum[level].ap->a_vals[i];
00306               if ( valnum[level].ap->a_nvals != valnum[level].ap->a_vals ) {
00307                      valnum[level].a.a_nvals[0] = valnum[level].ap->a_nvals[i];
00308               }
00309 
00310               if ( level < nattrs - 1 ) {
00311                      rc = dupent_response_entry_1level( op, rs,
00312                             e, valnum, nattrs, level + 1 );
00313                      if ( rc != LDAP_SUCCESS ) {
00314                             break;
00315                      }
00316 
00317                      continue;
00318               }
00319 
00320               /* NOTE: add the control all times, under the assumption
00321                * send_search_entry() honors the REP_CTRLS_MUSTBEFREED
00322                * set by slap_add_ctrls(); this is not true (ITS#6629)
00323                */
00324               ctrl = op->o_tmpcalloc( 1, sizeof( LDAPControl ), op->o_tmpmemctx );
00325               ctrl->ldctl_oid = LDAP_CONTROL_DUPENT_ENTRY;
00326               ctrl->ldctl_iscritical = 0;
00327 
00328               ctrlsp[0] = ctrl;
00329               ctrlsp[1] = NULL;
00330               slap_add_ctrls( op, rs, ctrlsp );
00331 
00332               /* do the real send */
00333               rs->sr_entry = e;
00334               rc = send_search_entry( op, rs );
00335               if ( rc != LDAP_SUCCESS ) {
00336                      break;
00337               }
00338        }
00339 
00340        return rc;
00341 }
00342 
00343 static int
00344 dupent_attr_prepare( dupent_t *ds, Entry *e, valnum_t *valnum, int nattrs, int c, Attribute **app, Attribute **ap_listp )
00345 {
00346        valnum[c].ap = *app;
00347        *app = (*app)->a_next;
00348 
00349        valnum[c].ap->a_next = *ap_listp;
00350        *ap_listp = valnum[c].ap;
00351 
00352        valnum[c].a = *valnum[c].ap;
00353        if ( c < nattrs - 1 ) {
00354               valnum[c].a.a_next = &valnum[c + 1].a;
00355        } else {
00356               valnum[c].a.a_next = NULL;
00357        }
00358        valnum[c].a.a_numvals = 1;
00359        valnum[c].a.a_vals = valnum[c].vals;
00360        BER_BVZERO( &valnum[c].vals[1] );
00361        if ( valnum[c].ap->a_nvals != valnum[c].ap->a_vals ) {
00362               valnum[c].a.a_nvals = valnum[c].nvals;
00363               BER_BVZERO( &valnum[c].nvals[1] );
00364        } else {
00365               valnum[c].a.a_nvals = valnum[c].a.a_vals;
00366        }
00367 }
00368 
00369 static int
00370 dupent_response_entry( Operation *op, SlapReply *rs )
00371 {
00372        dupent_cb_t   *dc = (dupent_cb_t *)op->o_callback->sc_private;
00373        int                  nattrs = 0;
00374        valnum_t      *valnum = NULL;
00375        Attribute     **app, *ap_list = NULL;
00376        int                  i, c;
00377        Entry         *e = NULL;
00378        int                  rc;
00379 
00380        assert( rs->sr_type == REP_SEARCH );
00381 
00382        for ( i = 0; i < dc->dc_ds->ds_nattrs; i++ ) {
00383               Attribute *ap;
00384 
00385               ap = attr_find( rs->sr_entry->e_attrs,
00386                      dc->dc_ds->ds_an[ i ].an_desc );
00387               if ( ap && ap->a_numvals > 1 ) {
00388                      nattrs++;
00389               }
00390        }
00391 
00392        if ( dc->dc_ds->ds_flags & SLAP_USERATTRS_YES ) {
00393               Attribute *ap;
00394 
00395               for ( ap = rs->sr_entry->e_attrs; ap != NULL; ap = ap->a_next ) {
00396                      if ( !is_at_operational( ap->a_desc->ad_type ) && ap->a_numvals > 1 ) {
00397                             nattrs++;
00398                      }
00399               }
00400        }
00401 
00402        if ( !nattrs ) {
00403               return SLAP_CB_CONTINUE;
00404        }
00405 
00406        rs_entry2modifiable( op, rs, dc->dc_on );
00407        rs->sr_flags &= ~(REP_ENTRY_MODIFIABLE | REP_ENTRY_MUSTBEFREED);
00408        e = rs->sr_entry;
00409 
00410        valnum = op->o_tmpcalloc( sizeof(valnum_t), nattrs, op->o_tmpmemctx );
00411 
00412        for ( c = 0, i = 0; i < dc->dc_ds->ds_nattrs; i++ ) {
00413               for ( app = &e->e_attrs; *app != NULL; app = &(*app)->a_next ) {
00414                      if ( (*app)->a_desc == dc->dc_ds->ds_an[ i ].an_desc ) {
00415                             break;
00416                      }
00417               }
00418 
00419               if ( *app != NULL && (*app)->a_numvals > 1 ) {
00420                      assert( c < nattrs );
00421                      dupent_attr_prepare( dc->dc_ds, e, valnum, nattrs, c, app, &ap_list );
00422                      c++;
00423               }
00424        }
00425 
00426        if ( dc->dc_ds->ds_flags & SLAP_USERATTRS_YES ) {
00427               for ( app = &e->e_attrs; *app != NULL; app = &(*app)->a_next ) {
00428                      if ( !is_at_operational( (*app)->a_desc->ad_type ) && (*app)->a_numvals > 1 ) {
00429                             assert( c < nattrs );
00430                             dupent_attr_prepare( dc->dc_ds, e, valnum, nattrs, c, app, &ap_list );
00431                             c++;
00432                      }
00433               }
00434        }
00435 
00436        for ( app = &e->e_attrs; *app != NULL; app = &(*app)->a_next )
00437               /* goto tail */ ;
00438 
00439        *app = &valnum[0].a;
00440 
00441        /* NOTE: since send_search_entry() does not honor the
00442         * REP_CTRLS_MUSTBEFREED flag set by slap_add_ctrls(),
00443         * the control could be added here once for all (ITS#6629)
00444         */
00445 
00446        dc->dc_skip = 1;
00447        rc = dupent_response_entry_1level( op, rs, e, valnum, nattrs, 0 );
00448        dc->dc_skip = 0;
00449 
00450        *app = ap_list;
00451 
00452        entry_free( e );
00453 
00454        op->o_tmpfree( valnum, op->o_tmpmemctx );
00455 
00456        return rc;
00457 }
00458 
00459 static int
00460 dupent_response( Operation *op, SlapReply *rs )
00461 {
00462        dupent_cb_t   *dc = (dupent_cb_t *)op->o_callback->sc_private;
00463 
00464        if ( dc->dc_skip ) {
00465               return SLAP_CB_CONTINUE;
00466        }
00467        
00468        switch ( rs->sr_type ) {
00469        case REP_RESULT:
00470               return dupent_response_done( op, rs );
00471 
00472        case REP_SEARCH:
00473               return dupent_response_entry( op, rs );
00474 
00475        case REP_SEARCHREF:
00476               break;
00477 
00478        default:
00479               assert( 0 );
00480        }
00481 
00482        return SLAP_CB_CONTINUE;
00483 }
00484 
00485 static int
00486 dupent_cleanup( Operation *op, SlapReply *rs )
00487 {
00488        if ( rs->sr_type == REP_RESULT || rs->sr_err == SLAPD_ABANDON ) {
00489               op->o_tmpfree( op->o_callback, op->o_tmpmemctx );
00490               op->o_callback = NULL;
00491 
00492               op->o_tmpfree( op->o_ctrldupent, op->o_tmpmemctx );
00493               op->o_ctrldupent = NULL;
00494        }
00495 
00496        return SLAP_CB_CONTINUE;
00497 }
00498 
00499 static int
00500 dupent_op_search( Operation *op, SlapReply *rs )
00501 {
00502        if ( op->o_dupent != SLAP_CONTROL_NONE ) {
00503               slap_callback *sc;
00504               dupent_cb_t *dc;
00505 
00506               sc = op->o_tmpcalloc( 1, sizeof( slap_callback ) + sizeof( dupent_cb_t ), op->o_tmpmemctx );
00507 
00508               dc = (dupent_cb_t *)&sc[ 1 ];
00509               dc->dc_on = (slap_overinst *)op->o_bd->bd_info;
00510               dc->dc_ds = (dupent_t *)op->o_ctrldupent;
00511               dc->dc_skip = 0;
00512 
00513               sc->sc_response = dupent_response;
00514               sc->sc_cleanup = dupent_cleanup;
00515               sc->sc_private = (void *)dc;
00516 
00517               sc->sc_next = op->o_callback->sc_next;
00518                 op->o_callback->sc_next = sc;
00519        }
00520        
00521        return SLAP_CB_CONTINUE;
00522 }
00523 
00524 #if SLAPD_OVER_DUPENT == SLAPD_MOD_DYNAMIC
00525 static
00526 #endif /* SLAPD_OVER_DUPENT == SLAPD_MOD_DYNAMIC */
00527 int
00528 dupent_initialize( void )
00529 {
00530        int rc;
00531 
00532        rc = register_supported_control( LDAP_CONTROL_DUPENT,
00533               SLAP_CTRL_SEARCH, NULL,
00534               dupent_parseCtrl, &dupent_cid );
00535        if ( rc != LDAP_SUCCESS ) {
00536               Debug( LDAP_DEBUG_ANY,
00537                      "dupent_initialize: Failed to register control (%d)\n",
00538                      rc, 0, 0 );
00539               return -1;
00540        }
00541 
00542        dupent.on_bi.bi_type = "dupent";
00543 
00544        dupent.on_bi.bi_op_search = dupent_op_search;
00545 
00546        return overlay_register( &dupent );
00547 }
00548 
00549 #if SLAPD_OVER_DUPENT == SLAPD_MOD_DYNAMIC
00550 int
00551 init_module( int argc, char *argv[] )
00552 {
00553        return dupent_initialize();
00554 }
00555 #endif /* SLAPD_OVER_DUPENT == SLAPD_MOD_DYNAMIC */
00556 
00557 #endif /* SLAPD_OVER_DUPENT */