LCOV - code coverage report
Current view: top level - source4/dsdb/samdb/ldb_modules - dirsync.c (source / functions) Hit Total Coverage
Test: coverage report for vadcx-master-patch-75612 fe003de8 Lines: 445 621 71.7 %
Date: 2024-02-29 22:57:05 Functions: 6 6 100.0 %

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

Generated by: LCOV version 1.14