LCOV - code coverage report
Current view: top level - source4/dsdb/samdb/ldb_modules - dirsync.c (source / functions) Hit Total Coverage
Test: coverage report for abartlet/fix-coverage dd10fb34 Lines: 442 577 76.6 %
Date: 2021-09-23 10:06:22 Functions: 6 7 85.7 %

          Line data    Source code
       1             : /*
       2             :    SAMDB control module
       3             : 
       4             :    Copyright (C) Matthieu Patou <mat@matws.net> 2011
       5             : 
       6             :    This program is free software; you can redistribute it and/or modify
       7             :    it under the terms of the GNU General Public License as published by
       8             :    the Free Software Foundation; either version 3 of the License, or
       9             :    (at your option) any later version.
      10             : 
      11             :    This program is distributed in the hope that it will be useful,
      12             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      13             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      14             :    GNU General Public License for more details.
      15             : 
      16             :    You should have received a copy of the GNU General Public License
      17             :    along with this program.  If not, see <http://www.gnu.org/licenses/>.
      18             : */
      19             : 
      20             : 
      21             : #include "includes.h"
      22             : #include "ldb/include/ldb.h"
      23             : #include "ldb/include/ldb_errors.h"
      24             : #include "ldb/include/ldb_module.h"
      25             : #include "libcli/security/security.h"
      26             : #include "librpc/gen_ndr/drsblobs.h"
      27             : #include "librpc/gen_ndr/ndr_drsblobs.h"
      28             : #include "librpc/ndr/libndr.h"
      29             : #include "dsdb/samdb/samdb.h"
      30             : #include "dsdb/samdb/ldb_modules/util.h"
      31             : #include "lib/util/smb_strtox.h"
      32             : 
      33             : #define LDAP_DIRSYNC_OBJECT_SECURITY            0x01
      34             : #define LDAP_DIRSYNC_ANCESTORS_FIRST_ORDER      0x800
      35             : #define LDAP_DIRSYNC_PUBLIC_DATA_ONLY           0x2000
      36             : #define LDAP_DIRSYNC_INCREMENTAL_VALUES         0x80000000
      37             : 
      38             : 
      39             : struct dirsync_context {
      40             :         struct ldb_module *module;
      41             :         struct ldb_request *req;
      42             : 
      43             :         /*
      44             :          * We keep a track of the number of attributes that we
      45             :          * add just for the need of the implementation
      46             :          * it will be usefull to track then entries that needs not to
      47             :          * be returned because there is no real change
      48             :          */
      49             : 
      50             :         unsigned int nbDefaultAttrs;
      51             :         uint64_t highestUSN;
      52             :         uint64_t fromreqUSN;
      53             :         uint32_t cursor_size;
      54             :         bool noextended;
      55             :         int extended_type;
      56             :         bool linkIncrVal;
      57             :         bool localonly;
      58             :         bool partial;
      59             :         bool assystem;
      60             :         int functional_level;
      61             :         const struct GUID *our_invocation_id;
      62             :         const struct dsdb_schema *schema;
      63             :         struct ldb_dn *nc_root;
      64             :         struct drsuapi_DsReplicaCursor *cursors;
      65             : };
      66             : 
      67             : 
      68        8159 : static int dirsync_filter_entry(struct ldb_request *req,
      69             :                                         struct ldb_message *msg,
      70             :                                         struct ldb_control **controls,
      71             :                                         struct dirsync_context *dsc,
      72             :                                         bool referral)
      73             : {
      74             :         struct ldb_context *ldb;
      75        8159 :         uint64_t val = 0;
      76             :         enum ndr_err_code ndr_err;
      77             :         uint32_t n;
      78             :         int i;
      79             :         unsigned int size, j;
      80        8159 :         struct ldb_val *replMetaData = NULL;
      81             :         struct replPropertyMetaDataBlob rmd;
      82             :         const struct dsdb_attribute *attr;
      83        8159 :         const char **listAttr = NULL;
      84        8159 :         bool namereturned = false;
      85        8159 :         bool nameasked = false;
      86             :         NTSTATUS status;
      87             :         /* Ajustment for the added attributes, it will reduce the number of
      88             :          * expected to be here attributes*/
      89        8159 :         unsigned int delta = 0;
      90        8159 :         const char **myaccept = NULL;
      91        8159 :         const char *emptyaccept[] = { NULL };
      92        8159 :         const char *extendedaccept[] = { "GUID", "SID", "WKGUID", NULL };
      93        8159 :         const char *rdn = NULL;
      94             :         struct ldb_message_element *el;
      95             :         struct ldb_message *newmsg;
      96        8159 :         bool keep = false;
      97             :         /*
      98             :          * Where we asked to do extended dn ?
      99             :          * if so filter out everything bug GUID, SID, WKGUID,
     100             :          * if not filter out everything (just keep the dn).
     101             :          */
     102        8159 :         if ( dsc->noextended == true ) {
     103        6379 :                 myaccept = emptyaccept;
     104             :         } else {
     105        1780 :                 myaccept = extendedaccept;
     106             :         }
     107        8159 :         ldb = ldb_module_get_ctx(dsc->module);
     108             : 
     109        8159 :         if (msg->num_elements == 0) {
     110             :                 /*
     111             :                         * Entry that we don't really have access to
     112             :                         */
     113           0 :                 return LDB_SUCCESS;
     114             :         }
     115        8159 :         ldb_dn_extended_filter(msg->dn, myaccept);
     116             : 
     117             :         /*
     118             :         * If the RDN starts with CN then the CN attribute is never returned
     119             :         */
     120        8159 :         rdn = ldb_dn_get_rdn_name(msg->dn);
     121             : 
     122             :         /*
     123             :          * if objectGUID is asked and we are dealing for the referrals entries and
     124             :          * the usn searched is 0 then we didn't count the objectGUID as an automatically
     125             :          * returned attribute, do to so we increament delta.
     126             :          */
     127        8173 :         if (referral == true &&
     128          26 :                         ldb_attr_in_list(req->op.search.attrs, "objectGUID") &&
     129          12 :                         dsc->fromreqUSN == 0) {
     130          12 :                 delta++;
     131             :         }
     132             : 
     133             : 
     134             :         /*
     135             :          * In terms of big O notation this is not the best algorithm,
     136             :          * but we try our best not to make the worse one.
     137             :          * We are obliged to run through the n message's elements
     138             :          * and through the p elements of the replPropertyMetaData.
     139             :          *
     140             :          * It turns out that we are crawling twice the message's elements
     141             :          * the first crawl is to remove the non replicated and generated
     142             :          * attributes. The second one is to remove attributes that haven't
     143             :          * a USN > as the requested one.
     144             :          *
     145             :          * In the second crawl we are reading the list of elements in the
     146             :          * replPropertyMetaData for each remaining replicated attribute.
     147             :          * In order to keep the list small
     148             :          *
     149             :          * We have a O(n'*p') complexity, in worse case n' = n and p' = p
     150             :          * but in most case n' = n/2 (at least half of returned attributes
     151             :          * are not replicated or generated) and p' is small as we
     152             :          * list only the attribute that have been modified since last interogation
     153             :          *
     154             :          */
     155        8159 :         newmsg = ldb_msg_new(dsc->req);
     156        8159 :         if (newmsg == NULL) {
     157           0 :                 return ldb_oom(ldb);
     158             :         }
     159      148850 :         for (i = msg->num_elements - 1; i >= 0; i--) {
     160      140691 :                 if (ldb_attr_cmp(msg->elements[i].name, "uSNChanged") == 0) {
     161        8159 :                         int error = 0;
     162             :                         /* Read the USN it will used at the end of the filtering
     163             :                          * to update the max USN in the cookie if we
     164             :                          * decide to keep this entry
     165             :                          */
     166        8159 :                         val = smb_strtoull(
     167        8159 :                                 (const char*)msg->elements[i].values[0].data,
     168             :                                 NULL,
     169             :                                 0,
     170             :                                 &error,
     171             :                                 SMB_STR_STANDARD);
     172        8159 :                         if (error != 0) {
     173           0 :                                 ldb_set_errstring(ldb,
     174             :                                                   "Failed to convert USN");
     175           0 :                                 return ldb_module_done(dsc->req,
     176             :                                                        NULL,
     177             :                                                        NULL,
     178             :                                                        LDB_ERR_OPERATIONS_ERROR);
     179             :                         }
     180        8159 :                         continue;
     181             :                 }
     182             : 
     183      132532 :                 if (ldb_attr_cmp(msg->elements[i].name,
     184             :                                                 "replPropertyMetaData") == 0) {
     185        7939 :                         replMetaData = (talloc_steal(dsc, &msg->elements[i].values[0]));
     186        7939 :                         continue;
     187             :                 }
     188             :         }
     189             : 
     190        8159 :         if (replMetaData == NULL) {
     191         220 :                 bool guidfound = false;
     192             : 
     193             :                 /*
     194             :                  * We are in the case of deleted object where we don't have the
     195             :                  * right to read it.
     196             :                  */
     197         220 :                 if (!ldb_msg_find_attr_as_uint(msg, "isDeleted", 0)) {
     198             :                         /*
     199             :                          * This is not a deleted item and we don't
     200             :                          * have the replPropertyMetaData.
     201             :                          * Do not return it
     202             :                          */
     203         220 :                         return LDB_SUCCESS;
     204             :                 }
     205           0 :                 newmsg->dn = ldb_dn_new(newmsg, ldb, "");
     206           0 :                 if (newmsg->dn == NULL) {
     207           0 :                         return ldb_oom(ldb);
     208             :                 }
     209             : 
     210           0 :                 el = ldb_msg_find_element(msg, "objectGUID");
     211           0 :                 if ( el != NULL) {
     212           0 :                         guidfound = true;
     213             :                 }
     214             :                 /*
     215             :                  * We expect to find the GUID in the object,
     216             :                  * if it turns out not to be the case sometime
     217             :                  * well will uncomment the code bellow
     218             :                  */
     219           0 :                 SMB_ASSERT(guidfound == true);
     220             :                 /*
     221             :                 if (guidfound == false) {
     222             :                         struct GUID guid;
     223             :                         struct ldb_val *new_val;
     224             :                         DATA_BLOB guid_blob;
     225             : 
     226             :                         tmp[0] = '\0';
     227             :                         txt = strrchr(txt, ':');
     228             :                         if (txt == NULL) {
     229             :                                 return ldb_module_done(dsc->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
     230             :                         }
     231             :                         txt++;
     232             : 
     233             :                         status = GUID_from_string(txt, &guid);
     234             :                         if (!NT_STATUS_IS_OK(status)) {
     235             :                                 return ldb_module_done(dsc->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
     236             :                         }
     237             : 
     238             :                         status = GUID_to_ndr_blob(&guid, msg, &guid_blob);
     239             :                         if (!NT_STATUS_IS_OK(status)) {
     240             :                                 return ldb_module_done(dsc->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
     241             :                         }
     242             : 
     243             :                         new_val = talloc(msg, struct ldb_val);
     244             :                         if (new_val == NULL) {
     245             :                                 return ldb_oom(ldb);
     246             :                         }
     247             :                         new_val->data = talloc_steal(new_val, guid_blob.data);
     248             :                         new_val->length = guid_blob.length;
     249             :                         if (ldb_msg_add_value(msg, "objectGUID", new_val, NULL) != 0) {
     250             :                                 return ldb_module_done(dsc->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
     251             :                         }
     252             :                 }
     253             :                 */
     254           0 :                 ldb_msg_add(newmsg, el, LDB_FLAG_MOD_ADD);
     255           0 :                 talloc_steal(newmsg->elements, el->name);
     256           0 :                 talloc_steal(newmsg->elements, el->values);
     257             : 
     258           0 :                 talloc_steal(newmsg->elements, msg);
     259           0 :                 return ldb_module_send_entry(dsc->req, msg, controls);
     260             :         }
     261             : 
     262        7939 :         ndr_err = ndr_pull_struct_blob(replMetaData, dsc, &rmd,
     263             :                 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
     264        7939 :         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
     265           0 :                 ldb_set_errstring(ldb, "Unable to unmarshall replPropertyMetaData");
     266           0 :                 return ldb_module_done(dsc->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
     267             :         }
     268       15723 :         if (ldb_attr_in_list(req->op.search.attrs, "name") ||
     269        7784 :                         ldb_attr_in_list(req->op.search.attrs, "*")) {
     270        6127 :                 nameasked = true;
     271             :         }
     272             : 
     273             :         /*
     274             :                 * If we don't have an USN and no updateness array then we skip the
     275             :                 * test phase this is an optimisation for the case when you
     276             :                 * first query the DC without a cookie.
     277             :                 * As this query is most probably the one
     278             :                 * that will return the biggest answer, skipping this part
     279             :                 * will really save time.
     280             :                 */
     281        7939 :         if (ldb_dn_compare(dsc->nc_root, msg->dn) == 0) {
     282             :                 /* If we have name then we expect to have parentGUID,
     283             :                  * it will not be the case for the root of the NC
     284             :                  */
     285          14 :                 delta++;
     286             :         }
     287             : 
     288        7939 :         if (dsc->fromreqUSN > 0 || dsc->cursors != NULL) {
     289          53 :                 j = 0;
     290             :                 /*
     291             :                 * Allocate an array of size(replMetaData) of char*
     292             :                 * we know that it will be oversized but it's a short lived element
     293             :                 */
     294          53 :                 listAttr = talloc_array(msg, const char*, rmd.ctr.ctr1.count + 1);
     295          53 :                 if (listAttr == NULL) {
     296           0 :                         return ldb_oom(ldb);
     297             :                 }
     298        1448 :                 for (n=0; n < rmd.ctr.ctr1.count; n++) {
     299        1395 :                         struct replPropertyMetaData1 *omd = &rmd.ctr.ctr1.array[n];
     300        1395 :                         if (omd->local_usn > dsc->fromreqUSN) {
     301         252 :                                 const struct dsdb_attribute *a = dsdb_attribute_by_attributeID_id(dsc->schema,
     302         252 :                                                                                 omd->attid);
     303         252 :                                 if (!dsc->localonly) {
     304           0 :                                         struct drsuapi_DsReplicaCursor *tab = dsc->cursors;
     305             :                                         uint32_t l;
     306           0 :                                         for (l=0; l < dsc->cursor_size; l++) {
     307           0 :                                                 if (GUID_equal(&tab[l].source_dsa_invocation_id, &omd->originating_invocation_id) &&
     308           0 :                                                                 tab[l].highest_usn >= omd->originating_usn) {
     309             :                                                         /*
     310             :                                                          * If we have in the uptodateness vector an entry
     311             :                                                          * with the same invocation id as the originating invocation
     312             :                                                          * and if the usn in the vector is greater or equal to
     313             :                                                          * the one in originating_usn, then it means that this entry
     314             :                                                          * has already been sent (from another DC) to the client
     315             :                                                          * no need to resend it one more time.
     316             :                                                          */
     317           0 :                                                         goto skip;
     318             :                                                 }
     319             :                                         }
     320             :                                         /* If we are here it's because we have a usn > (max(usn of vectors))*/
     321             :                                 }
     322         252 :                                 if (namereturned == false &&
     323          28 :                                                 nameasked == true &&
     324          28 :                                                 ldb_attr_cmp(a->lDAPDisplayName, "name") == 0) {
     325           6 :                                         namereturned = true;
     326           6 :                                         if (ldb_dn_compare(dsc->nc_root, msg->dn) == 0) {
     327           0 :                                                 delta++;
     328             :                                         }
     329             :                                 }
     330         252 :                                 listAttr[j] = a->lDAPDisplayName;
     331         252 :                                 j++;
     332         252 : skip:
     333         252 :                                 continue;
     334             :                         }
     335             :                 }
     336          53 :                 size = j;
     337             :         } else {
     338        7886 :                 size = 0;
     339        9816 :                 if (ldb_attr_in_list(req->op.search.attrs, "*") ||
     340        1930 :                                 ldb_attr_in_list(req->op.search.attrs, "name")) {
     341        6111 :                         namereturned = true;
     342             :                 }
     343             :         }
     344             : 
     345             : 
     346             :         /*
     347             :          * Let's loop around the remaining elements
     348             :          * to see which one are in the listAttr.
     349             :          * If they are in this array it means that
     350             :          * their localusn > usn from the request (in the cookie)
     351             :          * if not we remove the attribute.
     352             :          */
     353      144035 :         for (i = msg->num_elements - 1; i >= 0; i--) {
     354             :                 const char *ldapattrname;
     355             : 
     356      136096 :                 el = &(msg->elements[i]);
     357      136096 :                 ldapattrname = el->name;
     358             : 
     359      136096 :                 attr = dsdb_attribute_by_lDAPDisplayName(dsc->schema,
     360             :                                 el->name);
     361      136096 :                 if (attr == NULL) {
     362           0 :                         continue;
     363             :                 }
     364             : 
     365      136096 :                 keep = false;
     366             : 
     367      136096 :                 if (attr->linkID & 1) {
     368             :                         /*
     369             :                          * Attribute is a backlink so let's remove it
     370             :                          */
     371         108 :                         continue;
     372             :                 }
     373             : 
     374      135988 :                 if (ldb_attr_cmp(msg->elements[i].name,
     375             :                                                 "replPropertyMetaData") == 0) {
     376        7939 :                         continue;
     377             :                 }
     378             : 
     379      128049 :                 if ((attr->systemFlags & (DS_FLAG_ATTR_NOT_REPLICATED | DS_FLAG_ATTR_IS_CONSTRUCTED))) {
     380       77783 :                         if (ldb_attr_cmp(attr->lDAPDisplayName, "objectGUID") != 0 &&
     381       35564 :                                         ldb_attr_cmp(attr->lDAPDisplayName, "parentGUID") != 0) {
     382             :                                 /*
     383             :                                  * Attribute is constructed or not replicated, let's get rid of it
     384             :                                  */
     385       27639 :                                 continue;
     386             :                         } else {
     387             :                                 /* Let's keep the attribute that we forced to be added
     388             :                                  * even if they are not in the replicationMetaData
     389             :                                  * or are just generated
     390             :                                  */
     391       18216 :                                 if (namereturned == false &&
     392        3636 :                                         (ldb_attr_cmp(attr->lDAPDisplayName, "parentGUID") == 0)) {
     393        1814 :                                         delta++;
     394        1814 :                                         continue;
     395             :                                 }
     396       14050 :                                 if (ldb_msg_add(newmsg, el, LDB_FLAG_MOD_ADD) != LDB_SUCCESS) {
     397           0 :                                         return ldb_error(ldb,
     398             :                                                 LDB_ERR_OPERATIONS_ERROR,
     399             :                                                 "Unable to add attribute");
     400             :                                 }
     401       14050 :                                 talloc_steal(newmsg->elements, el->name);
     402       14050 :                                 talloc_steal(newmsg->elements, el->values);
     403       14050 :                                 continue;
     404             :                         }
     405             :                 }
     406             : 
     407       84546 :                 if (ldb_attr_cmp(msg->elements[i].name, rdn) == 0) {
     408             :                         /*
     409             :                          * We have an attribute that is the same as the start of the RDN
     410             :                          * (ie. attribute CN with rdn CN=).
     411             :                          */
     412        5974 :                         continue;
     413             :                 }
     414             : 
     415       78572 :                 if (ldb_attr_cmp(attr->lDAPDisplayName, "instanceType") == 0) {
     416        7939 :                         if (ldb_msg_add(newmsg, el, LDB_FLAG_MOD_ADD) != LDB_SUCCESS) {
     417           0 :                                 return ldb_error(ldb,
     418             :                                                 LDB_ERR_OPERATIONS_ERROR,
     419             :                                                 "Unable to add attribute");
     420             :                         }
     421        7939 :                         talloc_steal(newmsg->elements, el->name);
     422        7939 :                         talloc_steal(newmsg->elements, el->values);
     423        7939 :                         continue;
     424             :                 }
     425             :                 /* For links, when our functional level > windows 2000
     426             :                  * we use the RMD_LOCAL_USN information to decide whether
     427             :                  * we return the attribute or not.
     428             :                  * For windows 2000 this information is in the replPropertyMetaData
     429             :                  * so it will be handled like any other replicated attribute
     430             :                  */
     431             : 
     432      138774 :                 if (dsc->functional_level > DS_DOMAIN_FUNCTION_2000 &&
     433       70633 :                                 attr->linkID != 0 ) {
     434             :                         int k;
     435             :                         /*
     436             :                          * Elements for incremental changes on linked attributes
     437             :                          */
     438         116 :                         struct ldb_message_element *el_incr_add = NULL;
     439         116 :                         struct ldb_message_element *el_incr_del = NULL;
     440             :                         /*
     441             :                          * Attribute is a forwardlink so let's remove it
     442             :                          */
     443             : 
     444         364 :                         for (k = el->num_values -1; k >= 0; k--) {
     445             :                                 char *dn_ln;
     446         248 :                                 uint32_t flags = 0;
     447         248 :                                 uint32_t tmp_usn = 0;
     448         248 :                                 uint32_t tmp_usn2 = 0;
     449         248 :                                 struct GUID invocation_id = GUID_zero();
     450         248 :                                 struct dsdb_dn *dn = dsdb_dn_parse(msg, ldb, &el->values[k], attr->syntax->ldap_oid);
     451             :                                 struct ldb_dn *copydn;
     452         248 :                                 if (dn == NULL) {
     453           0 :                                         ldb_set_errstring(ldb, "Cannot parse DN");
     454           0 :                                         return ldb_module_done(dsc->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
     455             :                                 }
     456             : 
     457         248 :                                 copydn = ldb_dn_copy(msg, dn->dn);
     458         248 :                                 if (copydn == NULL) {
     459           0 :                                         ldb_oom(ldb);
     460             :                                 }
     461             : 
     462         248 :                                 status = dsdb_get_extended_dn_uint32(dn->dn, &tmp_usn, "RMD_LOCAL_USN");
     463         248 :                                 if (!NT_STATUS_IS_OK(status)) {
     464           0 :                                         talloc_free(dn);
     465           0 :                                         return ldb_module_done(dsc->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
     466             :                                 }
     467         248 :                                 status = dsdb_get_extended_dn_guid(dn->dn,  &invocation_id, "RMD_INVOCID");
     468         248 :                                 if (!NT_STATUS_IS_OK(status)) {
     469           0 :                                         talloc_free(dn);
     470           0 :                                         return ldb_module_done(dsc->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
     471             :                                 }
     472             : 
     473         248 :                                 status = dsdb_get_extended_dn_uint32(dn->dn, &flags, "RMD_FLAGS");
     474         248 :                                 if (!NT_STATUS_IS_OK(status)) {
     475           0 :                                         talloc_free(dn);
     476           0 :                                         return ldb_module_done(dsc->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
     477             :                                 }
     478             : 
     479         248 :                                 status = dsdb_get_extended_dn_uint32(dn->dn, &tmp_usn2, "RMD_ORIGINATING_USN");
     480         248 :                                 if (!NT_STATUS_IS_OK(status)) {
     481           0 :                                         talloc_free(dn);
     482           0 :                                         return ldb_module_done(dsc->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
     483             :                                 }
     484             : 
     485         248 :                                 ldb_dn_extended_filter(dn->dn, myaccept);
     486         248 :                                 dn_ln = dsdb_dn_get_extended_linearized(dn, dn,
     487             :                                                         dsc->extended_type);
     488         248 :                                 if (dn_ln == NULL)
     489             :                                 {
     490           0 :                                         talloc_free(dn);
     491           0 :                                         ldb_set_errstring(ldb, "Cannot linearize dn");
     492           0 :                                         return ldb_module_done(dsc->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
     493             :                                 }
     494             : 
     495         248 :                                 talloc_free(el->values[k].data);
     496         248 :                                 el->values[k].data = (uint8_t*)talloc_steal(el->values, dn_ln);
     497         248 :                                 if (el->values[k].data == NULL) {
     498           0 :                                         talloc_free(dn);
     499           0 :                                         return ldb_module_done(dsc->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
     500             :                                 }
     501         248 :                                 el->values[k].length = strlen(dn_ln);
     502             : 
     503             : 
     504         248 :                                 if (tmp_usn > dsc->fromreqUSN) {
     505         224 :                                         if (!dsc->localonly) {
     506         215 :                                                 struct drsuapi_DsReplicaCursor *tab = dsc->cursors;
     507             :                                                 uint32_t l;
     508             : 
     509         215 :                                                 for (l=0; l < dsc->cursor_size; l++) {
     510           0 :                                                         if (GUID_equal(&tab[l].source_dsa_invocation_id, &invocation_id) &&
     511           0 :                                                                         tab[l].highest_usn >= tmp_usn2) {
     512             :                                                                 /*
     513             :                                                                 * If we have in the uptodateness vector an entry
     514             :                                                                 * with the same invocation id as the originating invocation
     515             :                                                                 * and if the usn in the vector is greater or equal to
     516             :                                                                 * the one in originating_usn, then it means that this entry
     517             :                                                                 * has already been sent (from another DC) to the client
     518             :                                                                 * no need to resend it one more time.
     519             :                                                                 */
     520           0 :                                                                 goto skip_link;
     521             :                                                         }
     522             :                                                 }
     523             :                                                 /* If we are here it's because we have a usn > (max(usn of vectors))*/
     524         215 :                                                 keep = true;
     525             :                                         } else {
     526           9 :                                                 keep = true;
     527             :                                         }
     528             :                                 /* If we are here it's because the link is more recent than either any
     529             :                                  * originating usn or local usn
     530             :                                  */
     531             : 
     532         224 :                                         if (dsc->linkIncrVal == true) {
     533             :                                                 struct ldb_message_element *tmpel;
     534           9 :                                                 if (flags & DSDB_RMD_FLAG_DELETED) {
     535             :                                                         /* We have to check that the inactive link still point to an existing object */
     536             :                                                         struct GUID guid;
     537             :                                                         struct ldb_dn *tdn;
     538             :                                                         int ret;
     539             : 
     540           4 :                                                         status = dsdb_get_extended_dn_guid(copydn, &guid, "GUID");
     541           4 :                                                         if (!NT_STATUS_IS_OK(status)) {
     542           0 :                                                                 DEBUG(0,(__location__ " Unable to extract GUID in linked attribute '%s' in '%s'\n",
     543             :                                                                         el->name, ldb_dn_get_linearized(copydn)));
     544           0 :                                                                 return ldb_operr(ldb);
     545             :                                                         }
     546           4 :                                                         ret = dsdb_module_dn_by_guid(dsc->module, newmsg, &guid, &tdn, req);
     547           4 :                                                         if (ret == LDB_ERR_NO_SUCH_OBJECT) {
     548           0 :                                                                 DEBUG(2, (" Search of guid %s returned 0 objects, skipping it !\n",
     549             :                                                                                         GUID_string(newmsg, &guid)));
     550           0 :                                                                 continue;
     551           4 :                                                         } else if (ret != LDB_SUCCESS) {
     552           0 :                                                                 DEBUG(0, (__location__ " Search of guid %s failed with error code %d\n",
     553             :                                                                                         GUID_string(newmsg, &guid),
     554             :                                                                                         ret));
     555           0 :                                                                 continue;
     556             :                                                         }
     557           4 :                                                         tmpel = el_incr_del;
     558             :                                                 } else {
     559           5 :                                                         tmpel = el_incr_add;
     560             :                                                 }
     561             : 
     562           9 :                                                 if (tmpel == NULL) {
     563           5 :                                                         tmpel = talloc_zero(newmsg, struct ldb_message_element);
     564           5 :                                                         if (tmpel == NULL) {
     565           0 :                                                                 return ldb_oom(ldb);
     566             :                                                         }
     567           5 :                                                         tmpel->values = talloc_array(tmpel, struct ldb_val, 1);
     568           5 :                                                         if (tmpel->values == NULL) {
     569           0 :                                                                 return ldb_oom(ldb);
     570             :                                                         }
     571           5 :                                                         if (flags & DSDB_RMD_FLAG_DELETED) {
     572           3 :                                                                 tmpel->name = talloc_asprintf(tmpel,
     573             :                                                                                 "%s;range=0-0",
     574             :                                                                                 el->name);
     575             :                                                         }
     576             :                                                         else {
     577           2 :                                                                 tmpel->name = talloc_asprintf(tmpel,
     578             :                                                                                 "%s;range=1-1",
     579             :                                                                                 el->name);
     580             :                                                         }
     581           5 :                                                         if (tmpel->name == NULL) {
     582           0 :                                                                 return ldb_oom(ldb);
     583             :                                                         }
     584           5 :                                                         tmpel->num_values = 1;
     585             :                                                 } else {
     586           4 :                                                         tmpel->num_values += 1;
     587           4 :                                                         tmpel->values = talloc_realloc(tmpel,
     588             :                                                                                                 tmpel->values,
     589             :                                                                                                 struct ldb_val,
     590             :                                                                                                 tmpel->num_values);
     591           4 :                                                         if (tmpel->values == NULL) {
     592           0 :                                                                 return ldb_oom(ldb);
     593             :                                                         }
     594             :                                                 }
     595           9 :                                                 tmpel->values[tmpel->num_values -1].data =talloc_steal(tmpel->values, el->values[k].data);
     596           9 :                                                 tmpel->values[tmpel->num_values -1].length = el->values[k].length;
     597             : 
     598           9 :                                                 if (flags & DSDB_RMD_FLAG_DELETED) {
     599           4 :                                                         el_incr_del = tmpel;
     600             :                                                 } else {
     601           5 :                                                         el_incr_add = tmpel;
     602             :                                                 }
     603             :                                         }
     604             :                                 }
     605             : 
     606         248 :                                 if (dsc->linkIncrVal == false) {
     607         225 :                                         if (flags & DSDB_RMD_FLAG_DELETED) {
     608          28 :                                                 ARRAY_DEL_ELEMENT(
     609             :                                                         el->values,
     610             :                                                         k,
     611             :                                                         el->num_values);
     612          28 :                                                 el->num_values--;
     613             :                                         }
     614             :                                 }
     615         468 : skip_link:
     616         248 :                                 talloc_free(dn);
     617             : 
     618             :                         }
     619         116 :                         if (keep == true) {
     620         116 :                                 if (dsc->linkIncrVal == false) {
     621         111 :                                         if (ldb_msg_add(newmsg, el, LDB_FLAG_MOD_ADD) != LDB_SUCCESS) {
     622           0 :                                                 return ldb_error(ldb,
     623             :                                                         LDB_ERR_OPERATIONS_ERROR,
     624             :                                                         "Unable to add attribute");
     625             :                                         }
     626         111 :                                         talloc_steal(newmsg->elements, el->name);
     627         111 :                                         talloc_steal(newmsg->elements, el->values);
     628             :                                 } else {
     629           5 :                                         if (el_incr_del) {
     630           3 :                                                 if (ldb_msg_add(newmsg, el_incr_del, LDB_FLAG_MOD_ADD))
     631           0 :                                                         return ldb_error(ldb,
     632             :                                                                 LDB_ERR_OPERATIONS_ERROR,
     633             :                                                                 "Unable to add attribute");
     634             :                                         }
     635           5 :                                         if (el_incr_add) {
     636           2 :                                                 if (ldb_msg_add(newmsg, el_incr_add, LDB_FLAG_MOD_ADD))
     637           0 :                                                         return ldb_error(ldb,
     638             :                                                                 LDB_ERR_OPERATIONS_ERROR,
     639             :                                                                 "Unable to add attribute");
     640             :                                         }
     641             :                                 }
     642             :                         }
     643         116 :                         continue;
     644             :                 }
     645             : 
     646       70517 :                 if (listAttr) {
     647        1843 :                         for (j=0; j<size; j++) {
     648             :                         /*
     649             :                                 * We mark attribute that has already been seen well
     650             :                                 * as seen. So that after attribute that are still in
     651             :                                 * listAttr are attributes that has been modified after
     652             :                                 * the requested USN but not present in the attributes
     653             :                                 * returned by the ldb search.
     654             :                                 * That is to say attributes that have been removed
     655             :                                 */
     656        1496 :                                 if (listAttr[j] && ldb_attr_cmp(listAttr[j], ldapattrname) == 0) {
     657         130 :                                         listAttr[j] = NULL;
     658         130 :                                         keep = true;
     659         130 :                                         continue;
     660             :                                 }
     661             :                         }
     662             :                 } else {
     663       70170 :                         keep = true;
     664             :                 }
     665             : 
     666       70517 :                 if (keep == true) {
     667       70300 :                         if (ldb_msg_add(newmsg, el, LDB_FLAG_MOD_ADD) != LDB_SUCCESS) {
     668           0 :                                 return ldb_error(ldb,
     669             :                                         LDB_ERR_OPERATIONS_ERROR,
     670             :                                         "Unable to add attribute");
     671             :                         }
     672       70300 :                         talloc_steal(newmsg->elements, el->name);
     673       70300 :                         talloc_steal(newmsg->elements, el->values);
     674       70300 :                         continue;
     675             :                 }
     676             :         }
     677        7939 :         talloc_steal(newmsg->elements, msg);
     678             : 
     679             :         /*
     680             :          * Here we run through the list of attributes returned
     681             :          * in the propertyMetaData.
     682             :          * Entries of this list have usn > requested_usn,
     683             :          * entries that are also present in the message have been
     684             :          * replaced by NULL, so at this moment the list contains
     685             :          * only elements that have a usn > requested_usn and that
     686             :          * haven't been seen. It's attributes that were removed.
     687             :          * We add them to the message like empty elements.
     688             :          */
     689        8191 :         for (j=0; j<size; j++) {
     690         374 :                 if (listAttr[j] && (
     691         230 :                                 ldb_attr_in_list(req->op.search.attrs, "*") ||
     692         146 :                                 ldb_attr_in_list(req->op.search.attrs, listAttr[j])) &&
     693          82 :                                 (ldb_attr_cmp(listAttr[j], rdn) != 0) &&
     694          44 :                                 (ldb_attr_cmp(listAttr[j], "instanceType") != 0)) {
     695          42 :                         ldb_msg_add_empty(newmsg, listAttr[j], LDB_FLAG_MOD_DELETE, NULL);
     696             :                 }
     697             :         }
     698        7939 :         talloc_free(listAttr);
     699             : 
     700        7939 :         if ((newmsg->num_elements - ( dsc->nbDefaultAttrs - delta)) > 0) {
     701             :                 /*
     702             :                  * After cleaning attributes there is still some attributes that were not added just
     703             :                  * for the purpose of the control (objectGUID, instanceType, ...)
     704             :                  */
     705             : 
     706        7927 :                 newmsg->dn = talloc_steal(newmsg, msg->dn);
     707        7927 :                 if (val > dsc->highestUSN) {
     708         474 :                         dsc->highestUSN = val;
     709             :                 }
     710        7927 :                 return ldb_module_send_entry(dsc->req, newmsg, controls);
     711             :         } else {
     712          12 :                 talloc_free(newmsg);
     713          12 :                 return LDB_SUCCESS;
     714             :         }
     715             : }
     716             : 
     717             : 
     718         449 : static int dirsync_create_vector(struct ldb_request *req,
     719             :                                         struct ldb_reply *ares,
     720             :                                         struct dirsync_context *dsc,
     721             :                                         struct ldapControlDirSyncCookie *cookie,
     722             :                                         struct ldb_context *ldb)
     723             : {
     724             :         struct ldb_result *resVector;
     725         449 :         const char* attrVector[] = {"replUpToDateVector", NULL };
     726             :         uint64_t highest_usn;
     727         449 :         uint32_t count = 1;
     728             :         int ret;
     729             :         struct drsuapi_DsReplicaCursor *tab;
     730             : 
     731         449 :         ret = ldb_sequence_number(ldb, LDB_SEQ_HIGHEST_SEQ, &highest_usn);
     732         449 :         if (ret != LDB_SUCCESS) {
     733           0 :                 return ldb_error(ldb, LDB_ERR_OPERATIONS_ERROR, "Unable to get highest USN from current NC");
     734             :         }
     735             : 
     736             :         /* If we have a full answer then the highest USN
     737             :          * is not the highest USN from the result set but the
     738             :          * highest of the naming context, unless the sequence is not updated yet.
     739             :          */
     740         449 :         if (highest_usn > dsc->highestUSN) {
     741         328 :                 dsc->highestUSN = highest_usn;
     742             :         }
     743             : 
     744             : 
     745         449 :         ret = dsdb_module_search_dn(dsc->module, dsc, &resVector,
     746             :                         dsc->nc_root,
     747             :                         attrVector,
     748             :                         DSDB_FLAG_NEXT_MODULE, req);
     749         449 :         if (ret != LDB_SUCCESS) {
     750           0 :                 return ldb_error(ldb, LDB_ERR_OPERATIONS_ERROR,
     751             :                                  "Unable to get replUpToDateVector for current NC");
     752             :         }
     753             : 
     754         449 :         if (resVector->count != 0) {
     755             :                 DATA_BLOB blob;
     756             :                 uint32_t i;
     757         449 :                 struct ldb_message_element *el = ldb_msg_find_element(resVector->msgs[0], "replUpToDateVector");
     758         449 :                 if (el) {
     759             :                         enum ndr_err_code ndr_err;
     760             :                         struct replUpToDateVectorBlob utd;
     761           0 :                         blob.data = el->values[0].data;
     762           0 :                         blob.length = el->values[0].length;
     763           0 :                         ndr_err = ndr_pull_struct_blob(&blob, dsc, &utd,
     764             :                                                 (ndr_pull_flags_fn_t)ndr_pull_replUpToDateVectorBlob);
     765             : 
     766           0 :                         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
     767           0 :                                 return ldb_error(ldb, LDB_ERR_OPERATIONS_ERROR,
     768             :                                                 "Unable to pull replUpToDateVectorBlob structure");
     769             :                         }
     770             : 
     771             : 
     772           0 :                         count += utd.ctr.ctr2.count;
     773           0 :                         tab = talloc_array(cookie, struct drsuapi_DsReplicaCursor, count);
     774           0 :                         if (tab == NULL) {
     775           0 :                                 return ldb_oom(ldb);
     776             :                         }
     777           0 :                         for (i=1; i < count; i++) {
     778           0 :                                 memset(&tab[i], 0, sizeof(struct drsuapi_DsReplicaCursor));
     779           0 :                                 tab[i].highest_usn = utd.ctr.ctr2.cursors[i-1].highest_usn;
     780           0 :                                 tab[i].source_dsa_invocation_id = utd.ctr.ctr2.cursors[i-1].source_dsa_invocation_id;
     781             :                         }
     782             :                 } else {
     783         449 :                         tab = talloc_array(cookie, struct drsuapi_DsReplicaCursor, count);
     784         449 :                         if (tab == NULL) {
     785           0 :                                 return ldb_oom(ldb);
     786             :                         }
     787             :                 }
     788             :         } else {
     789             :                 /*
     790             :                  * No replUpToDateVector ? it happens quite often (1 DC,
     791             :                  * other DCs didn't update ...
     792             :                  */
     793           0 :                 tab = talloc_array(cookie, struct drsuapi_DsReplicaCursor, count);
     794           0 :                 if (tab == NULL) {
     795           0 :                         return ldb_oom(ldb);
     796             :                 }
     797             :         }
     798             :         /* Our vector is always the first */
     799         449 :         tab[0].highest_usn = dsc->highestUSN;
     800         449 :         tab[0].source_dsa_invocation_id = *(dsc->our_invocation_id);
     801             : 
     802             : 
     803             :         /* We have to add the updateness vector that we have*/
     804             :         /* Version is always 1 in dirsync cookies */
     805         449 :         cookie->blob.extra.uptodateness_vector.version = 1;
     806         449 :         cookie->blob.extra.uptodateness_vector.reserved = 0;
     807         449 :         cookie->blob.extra.uptodateness_vector.ctr.ctr1.count = count;
     808         449 :         cookie->blob.extra.uptodateness_vector.ctr.ctr1.reserved = 0;
     809         449 :         cookie->blob.extra.uptodateness_vector.ctr.ctr1.cursors = tab;
     810             : 
     811         449 :         return LDB_SUCCESS;
     812             : }
     813             : 
     814        9937 : static int dirsync_search_callback(struct ldb_request *req, struct ldb_reply *ares)
     815             : {
     816             :         int ret;
     817             :         struct dirsync_context *dsc;
     818             :         struct ldb_result *res, *res2;
     819             :         struct ldb_dirsync_control *control;
     820             :         struct ldapControlDirSyncCookie *cookie;
     821             :         struct ldb_context *ldb;
     822             :         struct ldb_dn *dn;
     823             :         struct ldb_val *val;
     824             :         DATA_BLOB *blob;
     825             :         NTTIME now;
     826        9937 :         const char *attrs[] = { "objectGUID", NULL };
     827             :         enum ndr_err_code ndr_err;
     828             :         char *tmp;
     829             :         uint32_t flags;
     830             : 
     831        9937 :         dsc = talloc_get_type_abort(req->context, struct dirsync_context);
     832        9937 :         ldb = ldb_module_get_ctx(dsc->module);
     833        9937 :         if (!ares) {
     834           0 :                 return ldb_module_done(dsc->req, NULL, NULL,
     835             :                                        LDB_ERR_OPERATIONS_ERROR);
     836             :         }
     837        9937 :         if (ares->error != LDB_SUCCESS) {
     838           0 :                 return ldb_module_done(dsc->req, ares->controls,
     839             :                                        ares->response, ares->error);
     840             :         }
     841             : 
     842        9937 :         switch (ares->type) {
     843        8145 :         case LDB_REPLY_ENTRY:
     844        8145 :                 return dirsync_filter_entry(req, ares->message, ares->controls, dsc, false);
     845             : 
     846        1343 :         case LDB_REPLY_REFERRAL:
     847             :                 /* Skip the ldap(s):// so up to 8 chars,
     848             :                  * we don't care to be precise as the goal is to be in
     849             :                  * the name of DC, then we search the next '/'
     850             :                  * as it will be the last char before the DN of the referal
     851             :                  */
     852        1343 :                 if (strncmp(ares->referral, "ldap://", 7) == 0) {
     853        1140 :                         tmp = ares->referral + 7;
     854         203 :                 } else if (strncmp(ares->referral, "ldaps://", 8) == 0) {
     855         203 :                         tmp = ares->referral + 8;
     856             :                 } else {
     857           0 :                         return ldb_operr(ldb);
     858             :                 }
     859             : 
     860        1343 :                 tmp = strchr(tmp, '/');
     861        1343 :                 if (tmp == NULL) {
     862           0 :                         return ldb_operr(ldb);
     863             :                 }
     864        1343 :                 tmp++;
     865             : 
     866        1343 :                 dn = ldb_dn_new(dsc, ldb, tmp);
     867        1343 :                 if (dn == NULL) {
     868           0 :                         return ldb_oom(ldb);
     869             :                 }
     870             : 
     871        1343 :                 flags = DSDB_FLAG_NEXT_MODULE |
     872             :                         DSDB_SEARCH_SHOW_DELETED |
     873             :                         DSDB_SEARCH_SHOW_EXTENDED_DN;
     874             : 
     875        1343 :                 if (dsc->assystem) {
     876         440 :                         flags = flags | DSDB_FLAG_AS_SYSTEM;
     877             :                 }
     878             : 
     879        1343 :                 ret = dsdb_module_search_tree(dsc->module, dsc, &res,
     880             :                                         dn, LDB_SCOPE_BASE,
     881             :                                         req->op.search.tree,
     882             :                                         req->op.search.attrs,
     883             :                                         flags, req);
     884             : 
     885        1343 :                 if (ret != LDB_SUCCESS) {
     886           0 :                         talloc_free(dn);
     887           0 :                         return ret;
     888             :                 }
     889             : 
     890        1343 :                 if (res->count > 1) {
     891           0 :                         char *ldbmsg = talloc_asprintf(dn, "LDB returned more than result for dn: %s", tmp);
     892           0 :                         if (ldbmsg) {
     893           0 :                                 ldb_set_errstring(ldb, ldbmsg);
     894             :                         }
     895           0 :                         talloc_free(dn);
     896           0 :                         return ldb_module_done(dsc->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
     897        1343 :                 } else if (res->count == 0) {
     898             :                         /* if nothing is returned then it means that we don't
     899             :                         * have access to it.
     900             :                         */
     901        1329 :                         return LDB_SUCCESS;
     902             :                 }
     903             : 
     904          14 :                 talloc_free(dn);
     905             :                 /*
     906             :                  * Fetch the objectGUID of the root of current NC
     907             :                  */
     908          14 :                 ret = dsdb_module_search_dn(dsc->module, dsc, &res2,
     909             :                                         req->op.search.base,
     910             :                                         attrs,
     911             :                                         DSDB_FLAG_NEXT_MODULE, req);
     912             : 
     913          14 :                 if (ret != LDB_SUCCESS) {
     914           0 :                         return ret;
     915             :                 }
     916          14 :                 if (res2->msgs[0]->num_elements != 1) {
     917           0 :                         ldb_set_errstring(ldb,
     918             :                                           "More than 1 attribute returned while looking for objectGUID");
     919           0 :                         return ldb_module_done(dsc->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
     920             :                 }
     921             : 
     922          14 :                 val = res2->msgs[0]->elements[0].values;
     923          14 :                 ret = ldb_msg_add_value(res->msgs[0], "parentGUID", val, NULL);
     924             :                 /*
     925             :                  * It *very* important to steal otherwise as val is in a subcontext
     926             :                  * related to res2, when the value will be one more time stolen
     927             :                  * it's elements[x].values that will be stolen, so it's important to
     928             :                  * recreate the context hierrachy as if it was done from a ldb_request
     929             :                  */
     930          14 :                 talloc_steal(res->msgs[0]->elements[0].values, val);
     931          14 :                 if (ret != LDB_SUCCESS) {
     932           0 :                         return ret;
     933             :                 }
     934          14 :                 return dirsync_filter_entry(req, res->msgs[0], res->controls, dsc, true);
     935             : 
     936         449 :         case LDB_REPLY_DONE:
     937             :                 /*
     938             :                  * Let's add our own control
     939             :                  */
     940             : 
     941         449 :                 control = talloc_zero(ares->controls, struct ldb_dirsync_control);
     942         449 :                 if (control == NULL) {
     943           0 :                         return ldb_oom(ldb);
     944             :                 }
     945             : 
     946             :                 /*
     947             :                  * When outputing flags is used to say more results.
     948             :                  * For the moment we didn't honnor the size info */
     949             : 
     950         449 :                 control->flags = 0;
     951             : 
     952             :                 /*
     953             :                  * max_attribute is unused cf. 3.1.1.3.4.1.3 LDAP_SERVER_DIRSYNC_OID in MS-ADTS
     954             :                  */
     955             : 
     956         449 :                 control->max_attributes = 0;
     957         449 :                 cookie = talloc_zero(control, struct ldapControlDirSyncCookie);
     958         449 :                 if (cookie == NULL) {
     959           0 :                         return ldb_oom(ldb);
     960             :                 }
     961             : 
     962         449 :                 if (!dsc->partial) {
     963         449 :                         ret = dirsync_create_vector(req, ares, dsc, cookie, ldb);
     964         449 :                         if (ret != LDB_SUCCESS) {
     965           0 :                                 return ldb_module_done(dsc->req, NULL, NULL, ret);
     966             :                         }
     967             :                 }
     968             : 
     969         449 :                 unix_to_nt_time(&now, time(NULL));
     970         449 :                 cookie->blob.time = now;
     971         449 :                 cookie->blob.highwatermark.highest_usn = dsc->highestUSN;
     972         449 :                 cookie->blob.highwatermark.tmp_highest_usn = dsc->highestUSN;
     973         449 :                 cookie->blob.guid1 = *(dsc->our_invocation_id);
     974             : 
     975         449 :                 blob = talloc_zero(control, DATA_BLOB);
     976         449 :                 if (blob == NULL) {
     977           0 :                         return ldb_oom(ldb);
     978             :                 }
     979             : 
     980         449 :                 ndr_err = ndr_push_struct_blob(blob, blob, cookie,
     981             :                                                 (ndr_push_flags_fn_t)ndr_push_ldapControlDirSyncCookie);
     982             : 
     983         449 :                 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
     984           0 :                         ldb_set_errstring(ldb, "Can't marshall ldapControlDirSyncCookie struct");
     985           0 :                         return ldb_module_done(dsc->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
     986             :                 }
     987         449 :                 control->cookie = (char *)blob->data;
     988         449 :                 control->cookie_len = blob->length;
     989         449 :                 ldb_reply_add_control(ares, LDB_CONTROL_DIRSYNC_OID, true, control);
     990             : 
     991         449 :                 return ldb_module_done(dsc->req, ares->controls,
     992             :                                        ares->response, LDB_SUCCESS);
     993             : 
     994             :         }
     995           0 :         return LDB_SUCCESS;
     996             : }
     997             : 
     998    14384136 : static int dirsync_ldb_search(struct ldb_module *module, struct ldb_request *req)
     999             : {
    1000             :         struct ldb_control *control;
    1001             :         struct ldb_result *acl_res;
    1002             :         struct ldb_dirsync_control *dirsync_ctl;
    1003    14384136 :         struct ldb_control *extended = NULL;
    1004             :         struct ldb_request *down_req;
    1005             :         struct dirsync_context *dsc;
    1006             :         struct ldb_context *ldb;
    1007    14384136 :         struct ldb_parse_tree *new_tree = req->op.search.tree;
    1008    14384136 :         uint32_t flags = 0;
    1009             :         enum ndr_err_code ndr_err;
    1010             :         DATA_BLOB blob;
    1011             :         const char **attrs;
    1012             :         int ret;
    1013             : 
    1014             : 
    1015    14384136 :         if (ldb_dn_is_special(req->op.search.base)) {
    1016     5005754 :                 return ldb_next_request(module, req);
    1017             :         }
    1018             : 
    1019             :         /*
    1020             :          * check if there's a dirsync control
    1021             :          */
    1022     9378382 :         control = ldb_request_get_control(req, LDB_CONTROL_DIRSYNC_OID);
    1023     9378382 :         if (control == NULL) {
    1024             :                 /* not found go on */
    1025     9377918 :                 return ldb_next_request(module, req);
    1026             :         }
    1027             : 
    1028         464 :         ldb = ldb_module_get_ctx(module);
    1029             :         /*
    1030             :          * This control must always be critical otherwise we return PROTOCOL error
    1031             :          */
    1032         464 :         if (!control->critical) {
    1033           0 :                 return ldb_operr(ldb);
    1034             :         }
    1035             : 
    1036         464 :         dsc = talloc_zero(req, struct dirsync_context);
    1037         464 :         if (dsc == NULL) {
    1038           0 :                 return ldb_oom(ldb);
    1039             :         }
    1040         464 :         dsc->module = module;
    1041         464 :         dsc->req = req;
    1042         464 :         dsc->nbDefaultAttrs = 0;
    1043             : 
    1044             : 
    1045         464 :         dirsync_ctl = talloc_get_type(control->data, struct ldb_dirsync_control);
    1046         464 :         if (dirsync_ctl == NULL) {
    1047           0 :                 return ldb_error(ldb, LDB_ERR_PROTOCOL_ERROR, "No data in dirsync control");
    1048             :         }
    1049             : 
    1050         464 :         ret = dsdb_find_nc_root(ldb, dsc, req->op.search.base, &dsc->nc_root);
    1051         464 :         if (ret != LDB_SUCCESS) {
    1052           0 :                 return ret;
    1053             :         }
    1054             : 
    1055         464 :         if (ldb_dn_compare(dsc->nc_root, req->op.search.base) != 0) {
    1056          10 :                 if (dirsync_ctl->flags & LDAP_DIRSYNC_OBJECT_SECURITY) {
    1057           4 :                         return ldb_error(ldb, LDB_ERR_UNWILLING_TO_PERFORM,
    1058             :                                  "DN is not one of the naming context");
    1059             :                 }
    1060             :                 else {
    1061           6 :                         return ldb_error(ldb, LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS,
    1062             :                                  "dN is not one of the naming context");
    1063             :                 }
    1064             :         }
    1065             : 
    1066         454 :         if (!(dirsync_ctl->flags & LDAP_DIRSYNC_OBJECT_SECURITY)) {
    1067             :                 struct dom_sid *sid;
    1068         153 :                 struct security_descriptor *sd = NULL;
    1069         153 :                 const char *acl_attrs[] = { "nTSecurityDescriptor", "objectSid", NULL };
    1070             :                 /*
    1071             :                  * If we don't have the flag and if we have the "replicate directory change" granted
    1072             :                  * then we upgrade ourself to system to not be blocked by the acl
    1073             :                  */
    1074             :                 /* FIXME we won't check the replicate directory change filtered attribute set
    1075             :                  * it should be done so that if attr is not empty then we check that the user
    1076             :                  * has also this right
    1077             :                  */
    1078             : 
    1079             :                 /*
    1080             :                  * First change to system to get the SD of the root of current NC
    1081             :                  * if we don't the acl_read will forbid us the right to read it ...
    1082             :                  */
    1083         153 :                 ret = dsdb_module_search_dn(module, dsc, &acl_res,
    1084             :                                         req->op.search.base,
    1085             :                                         acl_attrs,
    1086             :                                         DSDB_FLAG_NEXT_MODULE|DSDB_FLAG_AS_SYSTEM, req);
    1087             : 
    1088         153 :                 if (ret != LDB_SUCCESS) {
    1089           5 :                         return ret;
    1090             :                 }
    1091             : 
    1092         153 :                 sid = samdb_result_dom_sid(dsc, acl_res->msgs[0], "objectSid");
    1093             :                 /* sid can be null ... */
    1094         153 :                 ret = dsdb_get_sd_from_ldb_message(ldb_module_get_ctx(module), acl_res, acl_res->msgs[0], &sd);
    1095             : 
    1096         153 :                 if (ret != LDB_SUCCESS) {
    1097           0 :                         return ret;
    1098             :                 }
    1099         153 :                 ret = acl_check_extended_right(dsc, sd, acl_user_token(module), GUID_DRS_GET_CHANGES, SEC_ADS_CONTROL_ACCESS, sid);
    1100             : 
    1101         153 :                 if (ret == LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS) {
    1102           5 :                         return ret;
    1103             :                 }
    1104         148 :                 dsc->assystem = true;
    1105         148 :                 ret = ldb_request_add_control(req, LDB_CONTROL_AS_SYSTEM_OID, false, NULL);
    1106             : 
    1107         148 :                 if (ret != LDB_SUCCESS) {
    1108           0 :                         return ret;
    1109             :                 }
    1110         148 :                 talloc_free(acl_res);
    1111             :         } else {
    1112         301 :                 flags |= DSDB_ACL_CHECKS_DIRSYNC_FLAG;
    1113             : 
    1114         301 :                 if (ret != LDB_SUCCESS) {
    1115           0 :                         return ret;
    1116             :                 }
    1117             : 
    1118             :         }
    1119             : 
    1120         449 :         dsc->functional_level = dsdb_functional_level(ldb);
    1121             : 
    1122         449 :         if (req->op.search.attrs) {
    1123         308 :                 attrs = ldb_attr_list_copy(dsc, req->op.search.attrs);
    1124         308 :                 if (attrs == NULL) {
    1125           0 :                         return ldb_oom(ldb);
    1126             :                 }
    1127             :                 /*
    1128             :                 * Check if we have only "dn" as attribute, if so then
    1129             :                 * treat as if "*" was requested
    1130             :                 */
    1131         308 :                 if (attrs && attrs[0]) {
    1132         308 :                         if (ldb_attr_cmp(attrs[0], "dn") == 0 && !attrs[1]) {
    1133           2 :                                 attrs = talloc_array(dsc, const char*, 2);
    1134           2 :                                 if (attrs == NULL) {
    1135           0 :                                         return ldb_oom(ldb);
    1136             :                                 }
    1137           2 :                                 attrs[0] = "*";
    1138           2 :                                 attrs[1] = NULL;
    1139             :                         }
    1140             :                 }
    1141             :                 /*
    1142             :                  * When returning all the attributes return also the SD as
    1143             :                  * Windws do so.
    1144             :                  */
    1145         308 :                 if (ldb_attr_in_list(attrs, "*")) {
    1146          97 :                         struct ldb_sd_flags_control *sdctr = talloc_zero(dsc, struct ldb_sd_flags_control);
    1147          97 :                         sdctr->secinfo_flags = 0xF;
    1148          97 :                         ret = ldb_request_add_control(req, LDB_CONTROL_SD_FLAGS_OID, false, sdctr);
    1149          97 :                         if (ret != LDB_SUCCESS) {
    1150           0 :                                 return ret;
    1151             :                         }
    1152          97 :                         attrs = ldb_attr_list_copy_add(dsc, attrs, "parentGUID");
    1153          97 :                         if (attrs == NULL) {
    1154           0 :                                 return ldb_oom(ldb);
    1155             :                         }
    1156          97 :                         attrs = ldb_attr_list_copy_add(dsc, attrs, "replPropertyMetaData");
    1157          97 :                         if (attrs == NULL) {
    1158           0 :                                 return ldb_oom(ldb);
    1159             :                         }
    1160             :                         /*
    1161             :                         * When no attributes are asked we in anycase expect at least 3 attributes:
    1162             :                         * * instanceType
    1163             :                         * * objectGUID
    1164             :                         * * parentGUID
    1165             :                         */
    1166             : 
    1167          97 :                         dsc->nbDefaultAttrs = 3;
    1168             :                 } else {
    1169             :                         /*
    1170             :                          * We will need this two attributes in the callback
    1171             :                          */
    1172         211 :                         attrs = ldb_attr_list_copy_add(dsc, attrs, "usnChanged");
    1173         211 :                         if (attrs == NULL) {
    1174           0 :                                 return ldb_operr(ldb);
    1175             :                         }
    1176         211 :                         attrs = ldb_attr_list_copy_add(dsc, attrs, "replPropertyMetaData");
    1177         211 :                         if (attrs == NULL) {
    1178           0 :                                 return ldb_operr(ldb);
    1179             :                         }
    1180             : 
    1181         211 :                         if (!ldb_attr_in_list(attrs, "instanceType")) {
    1182         209 :                                 attrs = ldb_attr_list_copy_add(dsc, attrs, "instanceType");
    1183         209 :                                 if (attrs == NULL) {
    1184           0 :                                         return ldb_operr(ldb);
    1185             :                                 }
    1186         209 :                                 dsc->nbDefaultAttrs++;
    1187             :                         }
    1188             : 
    1189         211 :                         if (!ldb_attr_in_list(attrs, "objectGUID")) {
    1190         207 :                                 attrs = ldb_attr_list_copy_add(dsc, attrs, "objectGUID");
    1191         207 :                                 if (attrs == NULL) {
    1192           0 :                                         return ldb_operr(ldb);
    1193             :                                 }
    1194             :                         }
    1195             :                         /*
    1196             :                          * Always increment the number of asked attributes as we don't care if objectGUID was asked
    1197             :                          * or not for counting the number of "real" attributes returned.
    1198             :                          */
    1199         211 :                         dsc->nbDefaultAttrs++;
    1200             : 
    1201         211 :                         if (!ldb_attr_in_list(attrs, "parentGUID")) {
    1202         209 :                                 attrs = ldb_attr_list_copy_add(dsc, attrs, "parentGUID");
    1203         209 :                                 if (attrs == NULL) {
    1204           0 :                                         return ldb_operr(ldb);
    1205             :                                 }
    1206             :                         }
    1207         211 :                         dsc->nbDefaultAttrs++;
    1208             : 
    1209             :                 }
    1210             :         } else {
    1211         141 :                 struct ldb_sd_flags_control *sdctr = talloc_zero(dsc, struct ldb_sd_flags_control);
    1212         141 :                 sdctr->secinfo_flags = 0xF;
    1213         141 :                 ret = ldb_request_add_control(req, LDB_CONTROL_SD_FLAGS_OID, false, sdctr);
    1214         141 :                 attrs = talloc_array(dsc, const char*, 4);
    1215         141 :                 if (attrs == NULL) {
    1216           0 :                         return ldb_operr(ldb);
    1217             :                 }
    1218         141 :                 attrs[0] = "*";
    1219         141 :                 attrs[1] = "parentGUID";
    1220         141 :                 attrs[2] = "replPropertyMetaData";
    1221         141 :                 attrs[3] = NULL;
    1222         141 :                 if (ret != LDB_SUCCESS) {
    1223           0 :                         return ret;
    1224             :                 }
    1225             :                 /*
    1226             :                  * When no attributes are asked we in anycase expect at least 3 attributes:
    1227             :                  * * instanceType
    1228             :                  * * objectGUID
    1229             :                  * * parentGUID
    1230             :                  */
    1231             : 
    1232         141 :                 dsc->nbDefaultAttrs = 3;
    1233             :         }
    1234             : 
    1235             :         /* check if there's an extended dn control */
    1236         449 :         extended = ldb_request_get_control(req, LDB_CONTROL_EXTENDED_DN_OID);
    1237         449 :         if (extended != NULL) {
    1238          82 :                 struct ldb_extended_dn_control *extended_ctrl = NULL;
    1239             : 
    1240          82 :                 if (extended->data != NULL) {
    1241          80 :                         extended_ctrl = talloc_get_type(extended->data,
    1242             :                                                 struct ldb_extended_dn_control);
    1243             :                 }
    1244          82 :                 if (extended_ctrl != NULL) {
    1245          80 :                         dsc->extended_type = extended_ctrl->type;
    1246             :                 }
    1247             :         } else {
    1248         367 :                 ret = ldb_request_add_control(req, LDB_CONTROL_EXTENDED_DN_OID, false, NULL);
    1249         367 :                 if (ret != LDB_SUCCESS) {
    1250           0 :                         return ret;
    1251             :                 }
    1252         367 :                 dsc->noextended = true;
    1253             :         }
    1254             : 
    1255         449 :         if (ldb_request_get_control(req, LDB_CONTROL_REVEAL_INTERNALS) == NULL) {
    1256         449 :                 ret = ldb_request_add_control(req, LDB_CONTROL_REVEAL_INTERNALS, false, NULL);
    1257         449 :                 if (ret != LDB_SUCCESS) {
    1258           0 :                         return ret;
    1259             :                 }
    1260             :         }
    1261             : 
    1262         449 :         if (ldb_request_get_control(req, LDB_CONTROL_SHOW_RECYCLED_OID) == NULL) {
    1263         449 :                 ret = ldb_request_add_control(req, LDB_CONTROL_SHOW_RECYCLED_OID, false, NULL);
    1264         449 :                 if (ret != LDB_SUCCESS) {
    1265           0 :                         return ret;
    1266             :                 }
    1267             :         }
    1268             : 
    1269         449 :         if (ldb_request_get_control(req, LDB_CONTROL_SHOW_DELETED_OID) == NULL) {
    1270         447 :                 ret = ldb_request_add_control(req, LDB_CONTROL_SHOW_DELETED_OID, false, NULL);
    1271         447 :                 if (ret != LDB_SUCCESS) {
    1272           0 :                         return ret;
    1273             :                 }
    1274             :         }
    1275             : 
    1276         449 :         if (dirsync_ctl->flags & LDAP_DIRSYNC_INCREMENTAL_VALUES) {
    1277           5 :                 dsc->linkIncrVal = true;
    1278             :         } else {
    1279         444 :                 dsc->linkIncrVal = false;
    1280             :         }
    1281             : 
    1282         449 :         dsc->our_invocation_id = samdb_ntds_invocation_id(ldb);
    1283         449 :         if (dsc->our_invocation_id == NULL) {
    1284           0 :                 return ldb_operr(ldb);
    1285             :         }
    1286             : 
    1287         449 :         if (dirsync_ctl->cookie_len > 0) {
    1288             :                 struct ldapControlDirSyncCookie cookie;
    1289             : 
    1290          97 :                 blob.data = (uint8_t *)dirsync_ctl->cookie;
    1291          97 :                 blob.length = dirsync_ctl->cookie_len;
    1292          97 :                 ndr_err = ndr_pull_struct_blob(&blob, dsc, &cookie,
    1293             :                                                 (ndr_pull_flags_fn_t)ndr_pull_ldapControlDirSyncCookie);
    1294             : 
    1295             :                 /* If we can't unmarshall the cookie into the correct structure we return
    1296             :                 * unsupported critical extension
    1297             :                 */
    1298          97 :                 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
    1299           0 :                         return ldb_error(ldb, LDB_ERR_UNSUPPORTED_CRITICAL_EXTENSION,
    1300             :                                          "Unable to unmarshall cookie as a ldapControlDirSyncCookie structure");
    1301             :                 }
    1302             : 
    1303             :                 /*
    1304             :                 * Let's search for the max usn within the cookie
    1305             :                 */
    1306          97 :                 if (GUID_equal(&(cookie.blob.guid1), dsc->our_invocation_id)) {
    1307             :                         /*
    1308             :                          * Ok, it's our invocation ID so we can treat the demand
    1309             :                          * Let's take the highest usn from (tmp)highest_usn
    1310             :                          */
    1311          95 :                         dsc->fromreqUSN = cookie.blob.highwatermark.tmp_highest_usn;
    1312          95 :                         dsc->localonly = true;
    1313             : 
    1314          95 :                         if (cookie.blob.highwatermark.highest_usn > cookie.blob.highwatermark.tmp_highest_usn) {
    1315           0 :                                 dsc->fromreqUSN = cookie.blob.highwatermark.highest_usn;
    1316             :                         }
    1317             :                 } else {
    1318           2 :                         dsc->localonly = false;
    1319             :                 }
    1320         169 :                 if (cookie.blob.extra_length > 0 &&
    1321          97 :                                 cookie.blob.extra.uptodateness_vector.ctr.ctr1.count > 0) {
    1322             :                         struct drsuapi_DsReplicaCursor cursor;
    1323             :                         uint32_t p;
    1324         194 :                         for (p=0; p < cookie.blob.extra.uptodateness_vector.ctr.ctr1.count; p++) {
    1325          97 :                                 cursor = cookie.blob.extra.uptodateness_vector.ctr.ctr1.cursors[p];
    1326          97 :                                 if (GUID_equal( &(cursor.source_dsa_invocation_id), dsc->our_invocation_id)) {
    1327          97 :                                         if (cursor.highest_usn > dsc->fromreqUSN) {
    1328           2 :                                                 dsc->fromreqUSN = cursor.highest_usn;
    1329             :                                         }
    1330             :                                 }
    1331             :                         }
    1332          97 :                         dsc->cursors = talloc_steal(dsc,
    1333             :                                         cookie.blob.extra.uptodateness_vector.ctr.ctr1.cursors);
    1334          97 :                         if (dsc->cursors == NULL) {
    1335           0 :                                 return ldb_oom(ldb);
    1336             :                         }
    1337          97 :                         dsc->cursor_size = p;
    1338             :                 }
    1339             :         }
    1340             : 
    1341         449 :         DEBUG(4, ("Dirsync: searching with min usn > %llu\n",
    1342             :                                 (long long unsigned int)dsc->fromreqUSN));
    1343         449 :         if (dsc->fromreqUSN > 0) {
    1344             :                 /* FIXME it would be better to use PRId64 */
    1345          97 :                 char *expression = talloc_asprintf(dsc, "(&%s(uSNChanged>=%llu))",
    1346             :                                                         ldb_filter_from_tree(dsc,
    1347          97 :                                                              req->op.search.tree),
    1348          97 :                                                         (long long unsigned int)(dsc->fromreqUSN + 1));
    1349             : 
    1350          97 :                 if (expression == NULL) {
    1351           0 :                         return ldb_oom(ldb);
    1352             :                 }
    1353          97 :                 new_tree = ldb_parse_tree(req, expression);
    1354          97 :                 if (new_tree == NULL) {
    1355           0 :                         return ldb_error(ldb, LDB_ERR_OPERATIONS_ERROR,
    1356             :                                         "Problem while parsing tree");
    1357             :                 }
    1358             : 
    1359             :         }
    1360             :         /*
    1361             :          * Mark dirsync control as uncritical (done)
    1362             :          *
    1363             :          * We need this so ranged_results knows how to behave with
    1364             :          * dirsync
    1365             :          */
    1366         449 :         control->critical = false;
    1367         449 :         dsc->schema = dsdb_get_schema(ldb, dsc);
    1368             :         /*
    1369             :          * At the beginning we make the hypothesis that we will return a
    1370             :          * complete result set.
    1371             :          */
    1372             : 
    1373         449 :         dsc->partial = false;
    1374             : 
    1375             :         /*
    1376             :          * 3.1.1.3.4.1.3 of MS-ADTS.pdf specify that if the scope is not subtree
    1377             :          * we treat the search as if subtree was specified
    1378             :          */
    1379             : 
    1380         449 :         ret = ldb_build_search_req_ex(&down_req, ldb, dsc,
    1381             :                                       req->op.search.base,
    1382             :                                       LDB_SCOPE_SUBTREE,
    1383             :                                       new_tree,
    1384             :                                       attrs,
    1385             :                                       req->controls,
    1386             :                                       dsc, dirsync_search_callback,
    1387             :                                       req);
    1388         449 :         ldb_req_set_custom_flags(down_req, flags);
    1389         449 :         LDB_REQ_SET_LOCATION(down_req);
    1390         449 :         if (ret != LDB_SUCCESS) {
    1391           0 :                 return ret;
    1392             :         }
    1393             :         /* perform the search */
    1394         449 :         return ldb_next_request(module, down_req);
    1395             : }
    1396             : 
    1397      132453 : static int dirsync_ldb_init(struct ldb_module *module)
    1398             : {
    1399             :         int ret;
    1400             : 
    1401      132453 :         ret = ldb_mod_register_control(module, LDB_CONTROL_DIRSYNC_OID);
    1402      132453 :         if (ret != LDB_SUCCESS) {
    1403           0 :                 ldb_debug(ldb_module_get_ctx(module), LDB_DEBUG_ERROR,
    1404             :                         "dirsync: Unable to register control with rootdse!\n");
    1405           0 :                 return ldb_operr(ldb_module_get_ctx(module));
    1406             :         }
    1407             : 
    1408      132453 :         return ldb_next_init(module);
    1409             : }
    1410             : 
    1411             : static const struct ldb_module_ops ldb_dirsync_ldb_module_ops = {
    1412             :         .name              = "dirsync",
    1413             :         .search            = dirsync_ldb_search,
    1414             :         .init_context      = dirsync_ldb_init,
    1415             : };
    1416             : 
    1417             : /*
    1418             :   initialise the module
    1419             :  */
    1420        5536 : _PUBLIC_ int ldb_dirsync_module_init(const char *version)
    1421             : {
    1422             :         int ret;
    1423        5536 :         LDB_MODULE_CHECK_VERSION(version);
    1424        5536 :         ret = ldb_register_module(&ldb_dirsync_ldb_module_ops);
    1425        5536 :         return ret;
    1426             : }

Generated by: LCOV version 1.13