1*cf1d77f7Schristos /*	$NetBSD: addpartial-overlay.c,v 1.3 2021/08/14 16:14:50 christos Exp $	*/
24e6df137Slukem 
34e6df137Slukem /* addpartial-overlay.c */
433197c6aStron /* $OpenLDAP$ */
54e6df137Slukem /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
62de962bdSlukem  *
7*cf1d77f7Schristos  * Copyright 2004-2021 The OpenLDAP Foundation.
84e6df137Slukem  * Portions Copyright (C) 2004 Virginia Tech, David Hawes.
92de962bdSlukem  * All rights reserved.
102de962bdSlukem  *
112de962bdSlukem  * Redistribution and use in source and binary forms, with or without
122de962bdSlukem  * modification, are permitted only as authorized by the OpenLDAP
132de962bdSlukem  * Public License.
142de962bdSlukem  *
152de962bdSlukem  * A copy of this license is available in file LICENSE in the
162de962bdSlukem  * top-level directory of the distribution or, alternatively, at
172de962bdSlukem  * http://www.OpenLDAP.org/license.html.
184e6df137Slukem  */
194e6df137Slukem /* ACKNOLEDGEDMENTS:
204e6df137Slukem  * This work was initially developed by David Hawes of Virginia Tech
214e6df137Slukem  * for inclusion in OpenLDAP Software.
224e6df137Slukem  */
234e6df137Slukem /* addpartial-overlay
242de962bdSlukem  *
252de962bdSlukem  * This is an OpenLDAP overlay that intercepts ADD requests, determines if a
262de962bdSlukem  * change has actually taken place for that record, and then performs a modify
272de962bdSlukem  * request for those values that have changed (modified, added, deleted).  If
282de962bdSlukem  * the record has not changed in any way, it is ignored.  If the record does not
292de962bdSlukem  * exist, the record falls through to the normal add mechanism.  This overlay is
302de962bdSlukem  * useful for replicating from sources that are not LDAPs where it is easier to
312de962bdSlukem  * build entire records than to determine the changes (i.e. a database).
322de962bdSlukem  */
332de962bdSlukem 
348bd9f7cdSchristos #include <sys/cdefs.h>
35*cf1d77f7Schristos __RCSID("$NetBSD: addpartial-overlay.c,v 1.3 2021/08/14 16:14:50 christos Exp $");
368bd9f7cdSchristos 
372de962bdSlukem #include "portable.h"
382de962bdSlukem #include "slap.h"
392de962bdSlukem 
402de962bdSlukem static int collect_error_msg_cb( Operation *op, SlapReply *rs);
412de962bdSlukem 
422de962bdSlukem static slap_overinst addpartial;
432de962bdSlukem 
442de962bdSlukem /**
452de962bdSlukem  *  The meat of the overlay.  Search for the record, determine changes, take
462de962bdSlukem  *  action or fall through.
472de962bdSlukem  */
addpartial_add(Operation * op,SlapReply * rs)482de962bdSlukem static int addpartial_add( Operation *op, SlapReply *rs)
492de962bdSlukem {
502de962bdSlukem     Operation nop = *op;
512de962bdSlukem     Entry *toAdd = NULL;
524e6df137Slukem     Entry *found = NULL;
532de962bdSlukem     slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
542de962bdSlukem     int rc;
552de962bdSlukem 
562de962bdSlukem     toAdd = op->oq_add.rs_e;
572de962bdSlukem 
582de962bdSlukem     Debug(LDAP_DEBUG_TRACE, "%s: toAdd->e_nname.bv_val: %s\n",
59*cf1d77f7Schristos           addpartial.on_bi.bi_type, toAdd->e_nname.bv_val );
602de962bdSlukem 
612de962bdSlukem     /* if the user doesn't have access, fall through to the normal ADD */
622de962bdSlukem     if(!access_allowed(op, toAdd, slap_schema.si_ad_entry,
632de962bdSlukem                        NULL, ACL_WRITE, NULL))
642de962bdSlukem     {
652de962bdSlukem         return SLAP_CB_CONTINUE;
662de962bdSlukem     }
672de962bdSlukem 
684e6df137Slukem     rc = overlay_entry_get_ov(&nop, &nop.o_req_ndn, NULL, NULL, 0, &found, on);
692de962bdSlukem 
702de962bdSlukem     if(rc != LDAP_SUCCESS)
714e6df137Slukem     {
724e6df137Slukem         Debug(LDAP_DEBUG_TRACE,
734e6df137Slukem               "%s: no entry found, falling through to normal add\n",
74*cf1d77f7Schristos               addpartial.on_bi.bi_type );
752de962bdSlukem         return SLAP_CB_CONTINUE;
764e6df137Slukem     }
772de962bdSlukem     else
782de962bdSlukem     {
79*cf1d77f7Schristos         Debug(LDAP_DEBUG_TRACE, "%s: found the dn\n", addpartial.on_bi.bi_type );
802de962bdSlukem 
812de962bdSlukem         if(found)
822de962bdSlukem         {
832de962bdSlukem             Attribute *attr = NULL;
842de962bdSlukem             Attribute *at = NULL;
852de962bdSlukem             int ret;
862de962bdSlukem             Modifications *mods = NULL;
872de962bdSlukem             Modifications **modtail = &mods;
882de962bdSlukem             Modifications *mod = NULL;
892de962bdSlukem 
902de962bdSlukem             Debug(LDAP_DEBUG_TRACE, "%s: have an entry!\n",
91*cf1d77f7Schristos                   addpartial.on_bi.bi_type );
922de962bdSlukem 
932de962bdSlukem            /* determine if the changes are in the found entry */
942de962bdSlukem             for(attr = toAdd->e_attrs; attr; attr = attr->a_next)
952de962bdSlukem             {
962de962bdSlukem                 if(attr->a_desc->ad_type->sat_atype.at_usage != 0) continue;
972de962bdSlukem 
982de962bdSlukem                 at = attr_find(found->e_attrs, attr->a_desc);
992de962bdSlukem                 if(!at)
1002de962bdSlukem                 {
1012de962bdSlukem                     Debug(LDAP_DEBUG_TRACE, "%s: Attribute %s not found!\n",
1022de962bdSlukem                           addpartial.on_bi.bi_type,
103*cf1d77f7Schristos                           attr->a_desc->ad_cname.bv_val );
1042de962bdSlukem                     mod = (Modifications *) ch_malloc(sizeof(
1052de962bdSlukem                                                             Modifications));
1062de962bdSlukem                     mod->sml_flags = 0;
1072de962bdSlukem                     mod->sml_op = LDAP_MOD_REPLACE | LDAP_MOD_BVALUES;
1082de962bdSlukem                     mod->sml_op &= LDAP_MOD_OP;
1092de962bdSlukem                     mod->sml_next = NULL;
1102de962bdSlukem                     mod->sml_desc = attr->a_desc;
1114e6df137Slukem                     mod->sml_type = attr->a_desc->ad_cname;
1122de962bdSlukem                     mod->sml_values = attr->a_vals;
1132de962bdSlukem                     mod->sml_nvalues = attr->a_nvals;
1142de962bdSlukem                     mod->sml_numvals = attr->a_numvals;
1152de962bdSlukem                     *modtail = mod;
1162de962bdSlukem                     modtail = &mod->sml_next;
1172de962bdSlukem                 }
1182de962bdSlukem                 else
1192de962bdSlukem                 {
1202de962bdSlukem                     MatchingRule *mr = attr->a_desc->ad_type->sat_equality;
1212de962bdSlukem                     struct berval *bv;
1222de962bdSlukem                     const char *text;
1232de962bdSlukem                     int acount , bcount;
1242de962bdSlukem                     Debug(LDAP_DEBUG_TRACE, "%s: Attribute %s found\n",
1252de962bdSlukem                           addpartial.on_bi.bi_type,
126*cf1d77f7Schristos                           attr->a_desc->ad_cname.bv_val );
1272de962bdSlukem 
1282de962bdSlukem                     for(bv = attr->a_vals, acount = 0; bv->bv_val != NULL;
1292de962bdSlukem                         bv++, acount++)
1302de962bdSlukem                     {
1312de962bdSlukem                         /* count num values for attr */
1322de962bdSlukem                     }
1332de962bdSlukem                     for(bv = at->a_vals, bcount = 0; bv->bv_val != NULL;
1342de962bdSlukem                         bv++, bcount++)
1352de962bdSlukem                     {
1362de962bdSlukem                         /* count num values for attr */
1372de962bdSlukem                     }
1382de962bdSlukem                     if(acount != bcount)
1392de962bdSlukem                     {
1402de962bdSlukem                         Debug(LDAP_DEBUG_TRACE, "%s: acount != bcount, %s\n",
1412de962bdSlukem                               addpartial.on_bi.bi_type,
142*cf1d77f7Schristos                               "replace all" );
1432de962bdSlukem                         mod = (Modifications *) ch_malloc(sizeof(
1442de962bdSlukem                                                                 Modifications));
1452de962bdSlukem                         mod->sml_flags = 0;
1462de962bdSlukem                         mod->sml_op = LDAP_MOD_REPLACE | LDAP_MOD_BVALUES;
1472de962bdSlukem                         mod->sml_op &= LDAP_MOD_OP;
1482de962bdSlukem                         mod->sml_next = NULL;
1492de962bdSlukem                         mod->sml_desc = attr->a_desc;
1504e6df137Slukem                         mod->sml_type = attr->a_desc->ad_cname;
1512de962bdSlukem                         mod->sml_values = attr->a_vals;
1522de962bdSlukem                         mod->sml_nvalues = attr->a_nvals;
1532de962bdSlukem                         mod->sml_numvals = attr->a_numvals;
1542de962bdSlukem                         *modtail = mod;
1552de962bdSlukem                         modtail = &mod->sml_next;
1562de962bdSlukem                         continue;
1572de962bdSlukem                     }
1582de962bdSlukem 
1592de962bdSlukem                     for(bv = attr->a_vals; bv->bv_val != NULL; bv++)
1602de962bdSlukem                     {
1612de962bdSlukem                         struct berval *v;
1622de962bdSlukem                         ret = -1;
1632de962bdSlukem 
1642de962bdSlukem                         for(v = at->a_vals; v->bv_val != NULL; v++)
1652de962bdSlukem                         {
1662de962bdSlukem                             int r;
1672de962bdSlukem                             if(mr && ((r = value_match(&ret, attr->a_desc, mr,
1682de962bdSlukem                                            SLAP_MR_VALUE_OF_ASSERTION_SYNTAX,
1692de962bdSlukem                                            bv, v, &text)) == 0))
1702de962bdSlukem                             {
1712de962bdSlukem                                 if(ret == 0)
1722de962bdSlukem                                     break;
1732de962bdSlukem                             }
1742de962bdSlukem                             else
1752de962bdSlukem                             {
1762de962bdSlukem                                 Debug(LDAP_DEBUG_TRACE,
1772de962bdSlukem                                       "%s: \tvalue DNE, r: %d \n",
1782de962bdSlukem                                       addpartial.on_bi.bi_type,
179*cf1d77f7Schristos                                       r );
1802de962bdSlukem                                 ret = strcmp(bv->bv_val, v->bv_val);
1812de962bdSlukem                                 if(ret == 0)
1822de962bdSlukem                                     break;
1832de962bdSlukem                             }
1842de962bdSlukem                         }
1852de962bdSlukem 
1862de962bdSlukem                         if(ret == 0)
1872de962bdSlukem                         {
1882de962bdSlukem                             Debug(LDAP_DEBUG_TRACE,
1892de962bdSlukem                                   "%s: \tvalue %s exists, ret: %d\n",
1902de962bdSlukem                                   addpartial.on_bi.bi_type, bv->bv_val, ret);
1912de962bdSlukem                         }
1922de962bdSlukem                         else
1932de962bdSlukem                         {
1942de962bdSlukem                             Debug(LDAP_DEBUG_TRACE,
1952de962bdSlukem                                   "%s: \tvalue %s DNE, ret: %d\n",
1962de962bdSlukem                                   addpartial.on_bi.bi_type, bv->bv_val, ret);
1972de962bdSlukem                             mod = (Modifications *) ch_malloc(sizeof(
1982de962bdSlukem                                                                 Modifications));
1992de962bdSlukem                             mod->sml_flags = 0;
2002de962bdSlukem                             mod->sml_op = LDAP_MOD_REPLACE | LDAP_MOD_BVALUES;
2012de962bdSlukem                             mod->sml_op &= LDAP_MOD_OP;
2022de962bdSlukem                             mod->sml_next = NULL;
2032de962bdSlukem                             mod->sml_desc = attr->a_desc;
2044e6df137Slukem                             mod->sml_type = attr->a_desc->ad_cname;
2052de962bdSlukem                             mod->sml_values = attr->a_vals;
2062de962bdSlukem                             mod->sml_nvalues = attr->a_nvals;
2072de962bdSlukem                             mod->sml_numvals = attr->a_numvals;
2082de962bdSlukem                             *modtail = mod;
2092de962bdSlukem                             modtail = &mod->sml_next;
2102de962bdSlukem                             break;
2112de962bdSlukem                         }
2122de962bdSlukem                     }
2132de962bdSlukem                 }
2142de962bdSlukem             }
2152de962bdSlukem 
2162de962bdSlukem             /* determine if any attributes were deleted */
2172de962bdSlukem             for(attr = found->e_attrs; attr; attr = attr->a_next)
2182de962bdSlukem             {
2192de962bdSlukem                 if(attr->a_desc->ad_type->sat_atype.at_usage != 0) continue;
2202de962bdSlukem 
2212de962bdSlukem                 at = NULL;
2222de962bdSlukem                 at = attr_find(toAdd->e_attrs, attr->a_desc);
2232de962bdSlukem                 if(!at)
2242de962bdSlukem                 {
2252de962bdSlukem                     Debug(LDAP_DEBUG_TRACE,
2262de962bdSlukem                           "%s: Attribute %s not found in new entry!!!\n",
2272de962bdSlukem                           addpartial.on_bi.bi_type,
228*cf1d77f7Schristos                           attr->a_desc->ad_cname.bv_val );
2292de962bdSlukem                     mod = (Modifications *) ch_malloc(sizeof(
2302de962bdSlukem                                                         Modifications));
2312de962bdSlukem                     mod->sml_flags = 0;
2322de962bdSlukem                     mod->sml_op = LDAP_MOD_REPLACE;
2332de962bdSlukem                     mod->sml_next = NULL;
2342de962bdSlukem                     mod->sml_desc = attr->a_desc;
2354e6df137Slukem                     mod->sml_type = attr->a_desc->ad_cname;
2362de962bdSlukem                     mod->sml_values = NULL;
2372de962bdSlukem                     mod->sml_nvalues = NULL;
2382de962bdSlukem                     mod->sml_numvals = 0;
2392de962bdSlukem                     *modtail = mod;
2402de962bdSlukem                     modtail = &mod->sml_next;
2412de962bdSlukem                 }
2422de962bdSlukem                 else
2432de962bdSlukem                 {
2442de962bdSlukem                     Debug(LDAP_DEBUG_TRACE,
2452de962bdSlukem                           "%s: Attribute %s found in new entry\n",
2462de962bdSlukem                           addpartial.on_bi.bi_type,
247*cf1d77f7Schristos                           at->a_desc->ad_cname.bv_val );
2482de962bdSlukem                 }
2492de962bdSlukem             }
2502de962bdSlukem 
2514e6df137Slukem             overlay_entry_release_ov(&nop, found, 0, on);
2524e6df137Slukem 
2532de962bdSlukem             if(mods)
2542de962bdSlukem             {
2552de962bdSlukem                 Modifications *m = NULL;
2564e6df137Slukem                 Modifications *toDel;
2572de962bdSlukem                 int modcount;
2582de962bdSlukem                 slap_callback nullcb = { NULL, collect_error_msg_cb,
2592de962bdSlukem                                          NULL, NULL };
2604e6df137Slukem 
2614e6df137Slukem                 Debug(LDAP_DEBUG_TRACE, "%s: mods to do...\n",
262*cf1d77f7Schristos                       addpartial.on_bi.bi_type );
2632de962bdSlukem 
2642de962bdSlukem                 nop.o_tag = LDAP_REQ_MODIFY;
2652de962bdSlukem                 nop.orm_modlist = mods;
2664e6df137Slukem                 nop.orm_no_opattrs = 0;
2672de962bdSlukem                 nop.o_callback = &nullcb;
2682de962bdSlukem                 nop.o_bd->bd_info = (BackendInfo *) on->on_info;
2692de962bdSlukem 
2702de962bdSlukem                 for(m = mods, modcount = 0; m; m = m->sml_next,
2712de962bdSlukem                     modcount++)
2722de962bdSlukem                 {
2732de962bdSlukem                     /* count number of mods */
2742de962bdSlukem                 }
2752de962bdSlukem 
2762de962bdSlukem                 Debug(LDAP_DEBUG_TRACE, "%s: number of mods: %d\n",
277*cf1d77f7Schristos                       addpartial.on_bi.bi_type, modcount );
2782de962bdSlukem 
2794e6df137Slukem                 if(nop.o_bd->be_modify)
2804e6df137Slukem                 {
28133197c6aStron                     SlapReply nrs = { REP_RESULT };
2822de962bdSlukem                     rc = (nop.o_bd->be_modify)(&nop, &nrs);
2834e6df137Slukem                 }
2842de962bdSlukem 
2852de962bdSlukem                 if(rc == LDAP_SUCCESS)
2862de962bdSlukem                 {
2872de962bdSlukem                     Debug(LDAP_DEBUG_TRACE,
2882de962bdSlukem                           "%s: modify successful\n",
289*cf1d77f7Schristos                           addpartial.on_bi.bi_type );
2902de962bdSlukem                 }
2912de962bdSlukem                 else
2922de962bdSlukem                 {
2932de962bdSlukem                     Debug(LDAP_DEBUG_TRACE, "%s: modify unsuccessful: %d\n",
294*cf1d77f7Schristos                           addpartial.on_bi.bi_type, rc );
2952de962bdSlukem                     rs->sr_err = rc;
2964e6df137Slukem                     if(nullcb.sc_private)
2972de962bdSlukem                     {
2982de962bdSlukem                         rs->sr_text = nullcb.sc_private;
2992de962bdSlukem                     }
3002de962bdSlukem                 }
3012de962bdSlukem 
3022de962bdSlukem                 Debug(LDAP_DEBUG_TRACE, "%s: freeing mods...\n",
303*cf1d77f7Schristos                       addpartial.on_bi.bi_type );
3042de962bdSlukem 
3052de962bdSlukem                 for(toDel = mods; toDel; toDel = mods)
3062de962bdSlukem                 {
3072de962bdSlukem                     mods = mods->sml_next;
3082de962bdSlukem                     ch_free(toDel);
3092de962bdSlukem                 }
3102de962bdSlukem             }
3112de962bdSlukem             else
3122de962bdSlukem             {
3132de962bdSlukem                 Debug(LDAP_DEBUG_TRACE, "%s: no mods to process\n",
314*cf1d77f7Schristos                       addpartial.on_bi.bi_type );
3152de962bdSlukem             }
3162de962bdSlukem         }
3172de962bdSlukem         else
3182de962bdSlukem         {
3192de962bdSlukem             Debug(LDAP_DEBUG_TRACE, "%s: no entry!\n",
320*cf1d77f7Schristos                   addpartial.on_bi.bi_type );
3212de962bdSlukem         }
3222de962bdSlukem 
3232de962bdSlukem         op->o_callback = NULL;
3242de962bdSlukem         send_ldap_result( op, rs );
3252de962bdSlukem         ch_free((void *)rs->sr_text);
3262de962bdSlukem         rs->sr_text = NULL;
3272de962bdSlukem 
3282de962bdSlukem         return LDAP_SUCCESS;
3292de962bdSlukem     }
3302de962bdSlukem }
3312de962bdSlukem 
collect_error_msg_cb(Operation * op,SlapReply * rs)3322de962bdSlukem static int collect_error_msg_cb( Operation *op, SlapReply *rs)
3332de962bdSlukem {
3342de962bdSlukem     if(rs->sr_text)
3352de962bdSlukem     {
3362de962bdSlukem         op->o_callback->sc_private = (void *) ch_strdup(rs->sr_text);
3372de962bdSlukem     }
3382de962bdSlukem 
3392de962bdSlukem     return LDAP_SUCCESS;
3402de962bdSlukem }
3412de962bdSlukem 
addpartial_init()3422de962bdSlukem int addpartial_init()
3432de962bdSlukem {
3442de962bdSlukem     addpartial.on_bi.bi_type = "addpartial";
345*cf1d77f7Schristos 	addpartial.on_bi.bi_flags = SLAPO_BFLAG_SINGLE;
3462de962bdSlukem     addpartial.on_bi.bi_op_add = addpartial_add;
3472de962bdSlukem 
3482de962bdSlukem     return (overlay_register(&addpartial));
3492de962bdSlukem }
3502de962bdSlukem 
init_module(int argc,char * argv[])3512de962bdSlukem int init_module(int argc, char *argv[])
3522de962bdSlukem {
3532de962bdSlukem     return addpartial_init();
3542de962bdSlukem }
355