LCOV - code coverage report
Current view: top level - source4/dsdb/samdb/ldb_modules - extended_dn_store.c (source / functions) Hit Total Coverage
Test: coverage report for abartlet/fix-coverage dd10fb34 Lines: 261 313 83.4 %
Date: 2021-09-23 10:06:22 Functions: 9 9 100.0 %

          Line data    Source code
       1             : /* 
       2             :    ldb database library
       3             : 
       4             :    Copyright (C) Simo Sorce 2005-2008
       5             :    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2007-2009
       6             : 
       7             :    This program is free software; you can redistribute it and/or modify
       8             :    it under the terms of the GNU General Public License as published by
       9             :    the Free Software Foundation; either version 3 of the License, or
      10             :    (at your option) any later version.
      11             :    
      12             :    This program is distributed in the hope that it will be useful,
      13             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      14             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      15             :    GNU General Public License for more details.
      16             :    
      17             :    You should have received a copy of the GNU General Public License
      18             :    along with this program.  If not, see <http://www.gnu.org/licenses/>.
      19             : */
      20             : 
      21             : /*
      22             :  *  Name: ldb
      23             :  *
      24             :  *  Component: ldb extended dn control module
      25             :  *
      26             :  *  Description: this module builds a special dn for returned search
      27             :  *  results nad creates the special DN in the backend store for new
      28             :  *  values.
      29             :  *
      30             :  *  This also has the curious result that we convert <SID=S-1-2-345>
      31             :  *  in an attribute value into a normal DN for the rest of the stack
      32             :  *  to process
      33             :  *
      34             :  *  Authors: Simo Sorce
      35             :  *           Andrew Bartlett
      36             :  */
      37             : 
      38             : #include "includes.h"
      39             : #include <ldb.h>
      40             : #include <ldb_errors.h>
      41             : #include <ldb_module.h>
      42             : #include "librpc/gen_ndr/ndr_misc.h"
      43             : #include "dsdb/samdb/samdb.h"
      44             : #include "libcli/security/security.h"
      45             : #include "dsdb/samdb/ldb_modules/util.h"
      46             : #include <time.h>
      47             : 
      48             : struct extended_dn_replace_list {
      49             :         struct extended_dn_replace_list *next;
      50             :         struct dsdb_dn *dsdb_dn;
      51             :         TALLOC_CTX *mem_ctx;
      52             :         struct ldb_val *replace_dn;
      53             :         struct extended_dn_context *ac;
      54             :         struct ldb_request *search_req;
      55             :         bool fpo_enabled;
      56             :         bool require_object;
      57             :         bool got_entry;
      58             : };
      59             : 
      60             : 
      61             : struct extended_dn_context {
      62             :         const struct dsdb_schema *schema;
      63             :         struct ldb_module *module;
      64             :         struct ldb_context *ldb;
      65             :         struct ldb_request *req;
      66             :         struct ldb_request *new_req;
      67             : 
      68             :         struct extended_dn_replace_list *ops;
      69             :         struct extended_dn_replace_list *cur;
      70             : 
      71             :         /*
      72             :          * Used by the FPO-enabled attribute validation.
      73             :          */
      74             :         struct dsdb_trust_routing_table *routing_table;
      75             : };
      76             : 
      77             : 
      78      768569 : static struct extended_dn_context *extended_dn_context_init(struct ldb_module *module,
      79             :                                                             struct ldb_request *req)
      80             : {
      81             :         struct extended_dn_context *ac;
      82      768569 :         struct ldb_context *ldb = ldb_module_get_ctx(module);
      83      768569 :         ac = talloc_zero(req, struct extended_dn_context);
      84      768569 :         if (ac == NULL) {
      85           0 :                 ldb_oom(ldb);
      86           0 :                 return NULL;
      87             :         }
      88             : 
      89      768569 :         ac->schema = dsdb_get_schema(ldb, ac);
      90      768569 :         ac->module = module;
      91      768569 :         ac->ldb = ldb;
      92      768569 :         ac->req = req;
      93             : 
      94      768569 :         return ac;
      95             : }
      96             : 
      97      130430 : static int extended_replace_dn(struct extended_dn_replace_list *os,
      98             :                                struct ldb_dn *dn)
      99             : {
     100      130430 :         struct dsdb_dn *dsdb_dn = NULL;
     101      130430 :         const char *str = NULL;
     102             : 
     103             :         /*
     104             :          * Rebuild with the string or binary 'extra part' the
     105             :          * DN may have had as a prefix
     106             :          */
     107      242021 :         dsdb_dn = dsdb_dn_construct(os, dn,
     108      125540 :                                     os->dsdb_dn->extra_part,
     109      130430 :                                     os->dsdb_dn->oid);
     110      130430 :         if (dsdb_dn == NULL) {
     111           0 :                 return ldb_module_operr(os->ac->module);
     112             :         }
     113             : 
     114      130430 :         str = dsdb_dn_get_extended_linearized(os->mem_ctx,
     115             :                                               dsdb_dn, 1);
     116      130430 :         if (str == NULL) {
     117           0 :                 return ldb_module_operr(os->ac->module);
     118             :         }
     119             : 
     120             :         /*
     121             :          * Replace the DN with the extended version of the DN
     122             :          * (ie, add SID and GUID)
     123             :          */
     124      130430 :         *os->replace_dn = data_blob_string_const(str);
     125      130430 :         os->got_entry = true;
     126      130430 :         return LDB_SUCCESS;
     127             : }
     128             : 
     129          38 : static int extended_dn_handle_fpo_attr(struct extended_dn_replace_list *os)
     130             : {
     131          38 :         struct dom_sid target_sid = { 0, };
     132          38 :         struct dom_sid target_domain = { 0, };
     133          38 :         struct ldb_message *fmsg = NULL;
     134          38 :         char *fsid = NULL;
     135          38 :         const struct dom_sid *domain_sid = NULL;
     136          38 :         struct ldb_dn *domain_dn = NULL;
     137          38 :         const struct lsa_TrustDomainInfoInfoEx *tdo = NULL;
     138          38 :         uint32_t trust_attributes = 0;
     139          38 :         const char *no_attrs[] = { NULL, };
     140          38 :         struct ldb_result *res = NULL;
     141             :         NTSTATUS status;
     142             :         bool match;
     143             :         bool ok;
     144             :         int ret;
     145             : 
     146             :         /*
     147             :          * DN doesn't exist yet
     148             :          *
     149             :          * Check if a foreign SID is specified,
     150             :          * which would trigger the creation
     151             :          * of a foreignSecurityPrincipal.
     152             :          */
     153          38 :         status = dsdb_get_extended_dn_sid(os->dsdb_dn->dn,
     154             :                                           &target_sid,
     155             :                                           "SID");
     156          38 :         if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
     157             :                 /*
     158             :                  * No SID specified
     159             :                  */
     160          15 :                 return dsdb_module_werror(os->ac->module,
     161             :                                           LDB_ERR_NO_SUCH_OBJECT,
     162             :                                           WERR_NO_SUCH_USER,
     163             :                                           "specified dn doesn't exist");
     164             :         }
     165          23 :         if (!NT_STATUS_IS_OK(status)) {
     166           0 :                 return ldb_module_operr(os->ac->module);
     167             :         }
     168          23 :         if (ldb_dn_get_extended_comp_num(os->dsdb_dn->dn) != 1) {
     169           0 :                 return dsdb_module_werror(os->ac->module,
     170             :                                           LDB_ERR_NO_SUCH_OBJECT,
     171             :                                           WERR_NO_SUCH_USER,
     172             :                                           "specified extended component other than SID");
     173             :         }
     174          23 :         if (ldb_dn_get_comp_num(os->dsdb_dn->dn) != 0) {
     175           0 :                 return dsdb_module_werror(os->ac->module,
     176             :                                           LDB_ERR_NO_SUCH_OBJECT,
     177             :                                           WERR_NO_SUCH_USER,
     178             :                                           "specified more the SID");
     179             :         }
     180             : 
     181          23 :         target_domain = target_sid;
     182          23 :         sid_split_rid(&target_domain, NULL);
     183             : 
     184          23 :         match = dom_sid_equal(&global_sid_Builtin, &target_domain);
     185          23 :         if (match) {
     186             :                 /*
     187             :                  * Non existing BUILTIN sid
     188             :                  */
     189           4 :                 return dsdb_module_werror(os->ac->module,
     190             :                                 LDB_ERR_NO_SUCH_OBJECT,
     191             :                                 WERR_NO_SUCH_MEMBER,
     192             :                                 "specified sid doesn't exist in BUILTIN");
     193             :         }
     194             : 
     195          19 :         domain_sid = samdb_domain_sid(os->ac->ldb);
     196          19 :         if (domain_sid == NULL) {
     197           0 :                 return ldb_module_operr(os->ac->module);
     198             :         }
     199          19 :         match = dom_sid_equal(domain_sid, &target_domain);
     200          19 :         if (match) {
     201             :                 /*
     202             :                  * Non existing SID in our domain.
     203             :                  */
     204           4 :                 return dsdb_module_werror(os->ac->module,
     205             :                                 LDB_ERR_UNWILLING_TO_PERFORM,
     206             :                                 WERR_DS_INVALID_GROUP_TYPE,
     207             :                                 "specified sid doesn't exist in domain");
     208             :         }
     209             : 
     210          15 :         if (os->ac->routing_table == NULL) {
     211          15 :                 status = dsdb_trust_routing_table_load(os->ac->ldb, os->ac,
     212          15 :                                                        &os->ac->routing_table);
     213          15 :                 if (!NT_STATUS_IS_OK(status)) {
     214           0 :                         return ldb_module_operr(os->ac->module);
     215             :                 }
     216             :         }
     217             : 
     218          15 :         tdo = dsdb_trust_domain_by_sid(os->ac->routing_table,
     219             :                                        &target_domain, NULL);
     220          15 :         if (tdo != NULL) {
     221          11 :                 trust_attributes = tdo->trust_attributes;
     222             :         }
     223             : 
     224          15 :         if (trust_attributes & LSA_TRUST_ATTRIBUTE_WITHIN_FOREST) {
     225           0 :                 return dsdb_module_werror(os->ac->module,
     226             :                                 LDB_ERR_UNWILLING_TO_PERFORM,
     227             :                                 WERR_DS_INVALID_GROUP_TYPE,
     228             :                                 "specified sid doesn't exist in forest");
     229             :         }
     230             : 
     231          15 :         fmsg = ldb_msg_new(os);
     232          15 :         if (fmsg == NULL) {
     233           0 :                 return ldb_module_oom(os->ac->module);
     234             :         }
     235             : 
     236          15 :         fsid = dom_sid_string(fmsg, &target_sid);
     237          15 :         if (fsid == NULL) {
     238           0 :                 return ldb_module_oom(os->ac->module);
     239             :         }
     240             : 
     241          15 :         domain_dn = ldb_get_default_basedn(os->ac->ldb);
     242          15 :         if (domain_dn == NULL) {
     243           0 :                 return ldb_module_operr(os->ac->module);
     244             :         }
     245             : 
     246          15 :         fmsg->dn = ldb_dn_copy(fmsg, domain_dn);
     247          15 :         if (fmsg->dn == NULL) {
     248           0 :                 return ldb_module_oom(os->ac->module);
     249             :         }
     250             : 
     251          15 :         ok = ldb_dn_add_child_fmt(fmsg->dn,
     252             :                                   "CN=%s,CN=ForeignSecurityPrincipals",
     253             :                                   fsid);
     254          15 :         if (!ok) {
     255           0 :                 return ldb_module_oom(os->ac->module);
     256             :         }
     257             : 
     258          15 :         ret = ldb_msg_add_string(fmsg, "objectClass", "foreignSecurityPrincipal");
     259          15 :         if (ret != LDB_SUCCESS) {
     260           0 :                 return ret;
     261             :         }
     262             : 
     263          15 :         ret = dsdb_module_add(os->ac->module, fmsg,
     264             :                               DSDB_FLAG_AS_SYSTEM |
     265             :                               DSDB_FLAG_NEXT_MODULE,
     266          15 :                               os->ac->req);
     267          15 :         if (ret != LDB_SUCCESS) {
     268           0 :                 return ret;
     269             :         }
     270             : 
     271          15 :         ret = dsdb_module_search_dn(os->ac->module, fmsg, &res,
     272             :                                     fmsg->dn, no_attrs,
     273             :                                     DSDB_FLAG_AS_SYSTEM |
     274             :                                     DSDB_FLAG_NEXT_MODULE |
     275             :                                     DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
     276          15 :                                     os->ac->req);
     277          15 :         if (ret != LDB_SUCCESS) {
     278           0 :                 return ret;
     279             :         }
     280             : 
     281             :         /*
     282             :          * dsdb_module_search_dn() garantees exactly one result message
     283             :          * on success.
     284             :          */
     285          15 :         ret = extended_replace_dn(os, res->msgs[0]->dn);
     286          15 :         TALLOC_FREE(fmsg);
     287          15 :         if (ret != LDB_SUCCESS) {
     288           0 :                 return ret;
     289             :         }
     290             : 
     291          15 :         return LDB_SUCCESS;
     292             : }
     293             : 
     294             : /* An extra layer of indirection because LDB does not allow the original request to be altered */
     295             : 
     296      117386 : static int extended_final_callback(struct ldb_request *req, struct ldb_reply *ares)
     297             : {
     298      117386 :         int ret = LDB_ERR_OPERATIONS_ERROR;
     299             :         struct extended_dn_context *ac;
     300      117386 :         ac = talloc_get_type(req->context, struct extended_dn_context);
     301             : 
     302      117386 :         if (ares->error != LDB_SUCCESS) {
     303         274 :                 ret = ldb_module_done(ac->req, ares->controls,
     304             :                                       ares->response, ares->error);
     305             :         } else {
     306      117112 :                 switch (ares->type) {
     307           0 :                 case LDB_REPLY_ENTRY:
     308             :                         
     309           0 :                         ret = ldb_module_send_entry(ac->req, ares->message, ares->controls);
     310           0 :                         break;
     311           1 :                 case LDB_REPLY_REFERRAL:
     312             :                         
     313           1 :                         ret = ldb_module_send_referral(ac->req, ares->referral);
     314           1 :                         break;
     315      117111 :                 case LDB_REPLY_DONE:
     316             :                         
     317      117111 :                         ret = ldb_module_done(ac->req, ares->controls,
     318             :                                               ares->response, ares->error);
     319      117111 :                         break;
     320             :                 }
     321             :         }
     322      117386 :         return ret;
     323             : }
     324             : 
     325      263689 : static int extended_replace_callback(struct ldb_request *req, struct ldb_reply *ares)
     326             : {
     327      263689 :         struct extended_dn_replace_list *os = talloc_get_type(req->context, 
     328             :                                                            struct extended_dn_replace_list);
     329             : 
     330      263689 :         if (!ares) {
     331           0 :                 return ldb_module_done(os->ac->req, NULL, NULL,
     332             :                                         LDB_ERR_OPERATIONS_ERROR);
     333             :         }
     334      263689 :         if (ares->error == LDB_ERR_NO_SUCH_OBJECT) {
     335        2859 :                 if (os->got_entry) {
     336             :                         /* This is in internal error... */
     337           0 :                         int ret = ldb_module_operr(os->ac->module);
     338           0 :                         return ldb_module_done(os->ac->req, NULL, NULL, ret);
     339             :                 }
     340             : 
     341        2859 :                 if (os->require_object && os->fpo_enabled) {
     342             :                         int ret;
     343             : 
     344          38 :                         ret = extended_dn_handle_fpo_attr(os);
     345          38 :                         if (ret != LDB_SUCCESS) {
     346          23 :                                 return ldb_module_done(os->ac->req, NULL, NULL,
     347             :                                                        ret);
     348             :                         }
     349             :                         /* os->got_entry is true at this point... */
     350             :                 }
     351             : 
     352        2836 :                 if (!os->got_entry && os->require_object) {
     353             :                         /*
     354             :                          * It's an error if the target doesn't exist,
     355             :                          * unless it's a delete.
     356             :                          */
     357          21 :                         int ret = dsdb_module_werror(os->ac->module,
     358             :                                                 LDB_ERR_CONSTRAINT_VIOLATION,
     359             :                                                 WERR_DS_NAME_REFERENCE_INVALID,
     360             :                                                 "Referenced object not found");
     361          21 :                         return ldb_module_done(os->ac->req, NULL, NULL, ret);
     362             :                 }
     363             : 
     364             :                 /* Don't worry too much about dangling references */
     365             : 
     366        2815 :                 ldb_reset_err_string(os->ac->ldb);
     367        2815 :                 if (os->next) {
     368             :                         struct extended_dn_replace_list *next;
     369             : 
     370        1815 :                         next = os->next;
     371             : 
     372        1815 :                         talloc_free(os);
     373             : 
     374        1815 :                         os = next;
     375        1815 :                         return ldb_next_request(os->ac->module, next->search_req);
     376             :                 } else {
     377             :                         /* Otherwise, we are done - let's run the
     378             :                          * request now we have swapped the DNs for the
     379             :                          * full versions */
     380        1000 :                         return ldb_next_request(os->ac->module, os->ac->new_req);
     381             :                 }
     382             :         }
     383      260830 :         if (ares->error != LDB_SUCCESS) {
     384           0 :                 return ldb_module_done(os->ac->req, ares->controls,
     385             :                                         ares->response, ares->error);
     386             :         }
     387             : 
     388             :         /* Only entries are interesting, and we only want the olddn */
     389      260830 :         switch (ares->type) {
     390      130415 :         case LDB_REPLY_ENTRY:
     391             :         {
     392             :                 /* This *must* be the right DN, as this is a base
     393             :                  * search.  We can't check, as it could be an extended
     394             :                  * DN, so a module below will resolve it */
     395             :                 int ret;
     396             : 
     397      130415 :                 ret = extended_replace_dn(os, ares->message->dn);
     398      130415 :                 if (ret != LDB_SUCCESS) {
     399           0 :                         return ldb_module_done(os->ac->req, NULL, NULL, ret);
     400             :                 }
     401             :                 /* os->got_entry is true at this point */
     402      125525 :                 break;
     403             :         }
     404           0 :         case LDB_REPLY_REFERRAL:
     405             :                 /* ignore */
     406           0 :                 break;
     407             : 
     408      130415 :         case LDB_REPLY_DONE:
     409             : 
     410      130415 :                 talloc_free(ares);
     411             : 
     412      130415 :                 if (!os->got_entry && os->require_object && os->fpo_enabled) {
     413             :                         int ret;
     414             : 
     415           0 :                         ret = extended_dn_handle_fpo_attr(os);
     416           0 :                         if (ret != LDB_SUCCESS) {
     417           0 :                                 return ldb_module_done(os->ac->req, NULL, NULL,
     418             :                                                        ret);
     419             :                         }
     420             :                         /* os->got_entry is true at this point... */
     421             :                 }
     422             : 
     423      130415 :                 if (!os->got_entry && os->require_object) {
     424             :                         /*
     425             :                          * It's an error if the target doesn't exist,
     426             :                          * unless it's a delete.
     427             :                          */
     428           0 :                         int ret = dsdb_module_werror(os->ac->module,
     429             :                                                  LDB_ERR_CONSTRAINT_VIOLATION,
     430             :                                                  WERR_DS_NAME_REFERENCE_INVALID,
     431             :                                                  "Referenced object not found");
     432           0 :                         return ldb_module_done(os->ac->req, NULL, NULL, ret);
     433             :                 }
     434             : 
     435             :                 /* Run the next search */
     436             : 
     437      130415 :                 if (os->next) {
     438             :                         struct extended_dn_replace_list *next;
     439             : 
     440       14132 :                         next = os->next;
     441             : 
     442       14132 :                         talloc_free(os);
     443             : 
     444       14132 :                         os = next;
     445       14132 :                         return ldb_next_request(os->ac->module, next->search_req);
     446             :                 } else {
     447             :                         /* Otherwise, we are done - let's run the
     448             :                          * request now we have swapped the DNs for the
     449             :                          * full versions */
     450      116283 :                         return ldb_next_request(os->ac->module, os->ac->new_req);
     451             :                 }
     452             :         }
     453             : 
     454      130415 :         talloc_free(ares);
     455      130415 :         return LDB_SUCCESS;
     456             : }
     457             : 
     458             : /* We have a 'normal' DN in the inbound request.  We need to find out
     459             :  * what the GUID and SID are on the DN it points to, so we can
     460             :  * construct an extended DN for storage.
     461             :  *
     462             :  * This creates a list of DNs to look up, and the plain DN to replace
     463             :  */
     464             : 
     465      265868 : static int extended_store_replace(struct extended_dn_context *ac,
     466             :                                   TALLOC_CTX *callback_mem_ctx,
     467             :                                   struct ldb_dn *self_dn,
     468             :                                   struct ldb_val *plain_dn,
     469             :                                   bool is_delete, 
     470             :                                   const struct dsdb_attribute *schema_attr)
     471             : {
     472      265868 :         const char *oid = schema_attr->syntax->ldap_oid;
     473             :         int ret;
     474             :         struct extended_dn_replace_list *os;
     475             :         static const char *attrs[] = {
     476             :                 "objectSid",
     477             :                 "objectGUID",
     478             :                 NULL
     479             :         };
     480      265868 :         uint32_t ctrl_flags = 0;
     481      265868 :         bool is_untrusted = ldb_req_is_untrusted(ac->req);
     482             : 
     483      265868 :         os = talloc_zero(ac, struct extended_dn_replace_list);
     484      265868 :         if (!os) {
     485           0 :                 return ldb_oom(ac->ldb);
     486             :         }
     487             : 
     488      265868 :         os->ac = ac;
     489             :         
     490      265868 :         os->mem_ctx = callback_mem_ctx;
     491             : 
     492      265868 :         os->dsdb_dn = dsdb_dn_parse(os, ac->ldb, plain_dn, oid);
     493      265868 :         if (!os->dsdb_dn || !ldb_dn_validate(os->dsdb_dn->dn)) {
     494          12 :                 talloc_free(os);
     495          24 :                 ldb_asprintf_errstring(ac->ldb,
     496          12 :                                        "could not parse %.*s as a %s DN", (int)plain_dn->length, plain_dn->data,
     497             :                                        oid);
     498          12 :                 return LDB_ERR_INVALID_DN_SYNTAX;
     499             :         }
     500             : 
     501      265856 :         if (self_dn != NULL) {
     502       45985 :                 ret = ldb_dn_compare(self_dn, os->dsdb_dn->dn);
     503       45985 :                 if (ret == 0) {
     504             :                         /*
     505             :                          * If this is a reference to the object
     506             :                          * itself during an 'add', we won't
     507             :                          * be able to find the object.
     508             :                          */
     509       30719 :                         talloc_free(os);
     510       30719 :                         return LDB_SUCCESS;
     511             :                 }
     512             :         }
     513             : 
     514      235137 :         if (is_delete && !ldb_dn_has_extended(os->dsdb_dn->dn)) {
     515             :                 /* NO need to figure this DN out, this element is
     516             :                  * going to be deleted anyway, and becuase it's not
     517             :                  * extended, we have enough information to do the
     518             :                  * delete */
     519      101859 :                 talloc_free(os);
     520      101859 :                 return LDB_SUCCESS;
     521             :         }
     522             :         
     523             :                 
     524      133278 :         os->replace_dn = plain_dn;
     525             : 
     526             :         /* The search request here might happen to be for an
     527             :          * 'extended' style DN, such as <GUID=abced...>.  The next
     528             :          * module in the stack will convert this into a normal DN for
     529             :          * processing */
     530      251977 :         ret = ldb_build_search_req(&os->search_req,
     531      133278 :                                    ac->ldb, os, os->dsdb_dn->dn, LDB_SCOPE_BASE, NULL, 
     532             :                                    attrs, NULL, os, extended_replace_callback,
     533             :                                    ac->req);
     534      133278 :         LDB_REQ_SET_LOCATION(os->search_req);
     535      133278 :         if (ret != LDB_SUCCESS) {
     536           0 :                 talloc_free(os);
     537           0 :                 return ret;
     538             :         }
     539             : 
     540             :         /*
     541             :          * By default we require the presence of the target.
     542             :          */
     543      133278 :         os->require_object = true;
     544             : 
     545             :         /*
     546             :          * Handle FPO-enabled attributes, see
     547             :          * [MS-ADTS] 3.1.1.5.2.3 Special Classes and Attributes:
     548             :          *
     549             :          *   FPO-enabled attributes: member, msDS-MembersForAzRole,
     550             :          *     msDS-NeverRevealGroup, msDS-NonMembers, msDS-RevealOnDemandGroup,
     551             :          *     msDS-ServiceAccount.
     552             :          *
     553             :          * Note there's no msDS-ServiceAccount in any schema (only
     554             :          * msDS-HostServiceAccount and that's not an FPO-enabled attribute
     555             :          * at least not in W2008R2)
     556             :          *
     557             :          * msDS-NonMembers always generates NOT_SUPPORTED against W2008R2.
     558             :          *
     559             :          * See also [MS-SAMR] 3.1.1.8.9 member.
     560             :          */
     561      133278 :         switch (schema_attr->attributeID_id) {
     562       12204 :         case DRSUAPI_ATTID_member:
     563             :         case DRSUAPI_ATTID_msDS_MembersForAzRole:
     564             :         case DRSUAPI_ATTID_msDS_NeverRevealGroup:
     565             :         case DRSUAPI_ATTID_msDS_RevealOnDemandGroup:
     566       12204 :                 os->fpo_enabled = true;
     567       12204 :                 break;
     568             : 
     569           4 :         case DRSUAPI_ATTID_msDS_HostServiceAccount:
     570             :                 /* This is NOT a FPO-enabled attribute */
     571           4 :                 break;
     572             : 
     573           4 :         case DRSUAPI_ATTID_msDS_NonMembers:
     574           4 :                 return dsdb_module_werror(os->ac->module,
     575             :                                           LDB_ERR_UNWILLING_TO_PERFORM,
     576             :                                           WERR_NOT_SUPPORTED,
     577             :                                           "msDS-NonMembers is not supported");
     578             :         }
     579             : 
     580      133274 :         if (schema_attr->linkID == 0) {
     581             :                 /*
     582             :                  * None linked attributes allow references
     583             :                  * to deleted objects.
     584             :                  */
     585      113029 :                 ctrl_flags |= DSDB_SEARCH_SHOW_RECYCLED;
     586             :         }
     587             : 
     588      133274 :         if (is_delete) {
     589             :                 /*
     590             :                  * On delete want to be able to
     591             :                  * find a deleted object, but
     592             :                  * it's not a problem if they doesn't
     593             :                  * exist.
     594             :                  */
     595         209 :                 ctrl_flags |= DSDB_SEARCH_SHOW_RECYCLED;
     596         209 :                 os->require_object = false;
     597             :         }
     598             : 
     599      133274 :         if (!is_untrusted) {
     600      120644 :                 struct ldb_control *ctrl = NULL;
     601             : 
     602             :                 /*
     603             :                  * During provision or dbcheck we may not find
     604             :                  * an object.
     605             :                  */
     606             : 
     607      120644 :                 ctrl = ldb_request_get_control(ac->req, LDB_CONTROL_RELAX_OID);
     608      120644 :                 if (ctrl != NULL) {
     609       16601 :                         os->require_object = false;
     610             :                 }
     611      120644 :                 ctrl = ldb_request_get_control(ac->req, DSDB_CONTROL_DBCHECK);
     612      120644 :                 if (ctrl != NULL) {
     613      101687 :                         os->require_object = false;
     614             :                 }
     615             :         }
     616             : 
     617      133274 :         ret = dsdb_request_add_controls(os->search_req,
     618             :                                         DSDB_FLAG_AS_SYSTEM |
     619             :                                         ctrl_flags |
     620             :                                         DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT);
     621      133274 :         if (ret != LDB_SUCCESS) {
     622           0 :                 talloc_free(os);
     623           0 :                 return ret;
     624             :         }
     625             : 
     626      133274 :         if (ac->ops) {
     627       15947 :                 ac->cur->next = os;
     628             :         } else {
     629      117327 :                 ac->ops = os;
     630             :         }
     631      133274 :         ac->cur = os;
     632             : 
     633      133274 :         return LDB_SUCCESS;
     634             : }
     635             : 
     636             : 
     637             : /* add */
     638      490196 : static int extended_dn_add(struct ldb_module *module, struct ldb_request *req)
     639             : {
     640             :         struct extended_dn_context *ac;
     641             :         int ret;
     642             :         unsigned int i, j;
     643             : 
     644      490196 :         if (ldb_dn_is_special(req->op.add.message->dn)) {
     645             :                 /* do not manipulate our control entries */
     646         508 :                 return ldb_next_request(module, req);
     647             :         }
     648             : 
     649      489688 :         ac = extended_dn_context_init(module, req);
     650      489688 :         if (!ac) {
     651           0 :                 return ldb_operr(ldb_module_get_ctx(module));
     652             :         }
     653             : 
     654      489688 :         if (!ac->schema) {
     655             :                 /* without schema, this doesn't make any sense */
     656           0 :                 talloc_free(ac);
     657           0 :                 return ldb_next_request(module, req);
     658             :         }
     659             : 
     660     5033675 :         for (i=0; i < req->op.add.message->num_elements; i++) {
     661     4620555 :                 const struct ldb_message_element *el = &req->op.add.message->elements[i];
     662     3588790 :                 const struct dsdb_attribute *schema_attr
     663     4620555 :                         = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
     664     4620555 :                 if (!schema_attr) {
     665         425 :                         continue;
     666             :                 }
     667             : 
     668             :                 /* We only setup an extended DN GUID on DN elements */
     669     4620130 :                 if (schema_attr->dn_format == DSDB_INVALID_DN) {
     670     4579616 :                         continue;
     671             :                 }
     672             : 
     673       40514 :                 if (schema_attr->attributeID_id == DRSUAPI_ATTID_distinguishedName) {
     674             :                         /* distinguishedName values are ignored */
     675          10 :                         continue;
     676             :                 }
     677             : 
     678             :                 /* Before we setup a procedure to modify the incoming message, we must copy it */
     679       40504 :                 if (!ac->new_req) {
     680       37365 :                         struct ldb_message *msg = ldb_msg_copy(ac, req->op.add.message);
     681       37365 :                         if (!msg) {
     682           0 :                                 return ldb_oom(ldb_module_get_ctx(module));
     683             :                         }
     684             :                    
     685       37365 :                         ret = ldb_build_add_req(&ac->new_req, ac->ldb, ac, msg, req->controls, ac, extended_final_callback, req);
     686       37365 :                         LDB_REQ_SET_LOCATION(ac->new_req);
     687       37365 :                         if (ret != LDB_SUCCESS) {
     688           0 :                                 return ret;
     689             :                         }
     690             :                 }
     691             :                 /* Re-calculate el */
     692       40504 :                 el = &ac->new_req->op.add.message->elements[i];
     693       86489 :                 for (j = 0; j < el->num_values; j++) {
     694       82105 :                         ret = extended_store_replace(ac, ac->new_req,
     695       45985 :                                                      req->op.add.message->dn,
     696       45985 :                                                      &el->values[j],
     697             :                                                      false, schema_attr);
     698       45985 :                         if (ret != LDB_SUCCESS) {
     699           0 :                                 return ret;
     700             :                         }
     701             :                 }
     702             :         }
     703             : 
     704             :         /* if no DNs were set continue */
     705      489688 :         if (ac->ops == NULL) {
     706      482901 :                 talloc_free(ac);
     707      482901 :                 return ldb_next_request(module, req);
     708             :         }
     709             : 
     710             :         /* start with the searches */
     711        6787 :         return ldb_next_request(module, ac->ops->search_req);
     712             : }
     713             : 
     714             : /* modify */
     715      279538 : static int extended_dn_modify(struct ldb_module *module, struct ldb_request *req)
     716             : {
     717             :         /* Look over list of modifications */
     718             :         /* Find if any are for linked attributes */
     719             :         /* Determine the effect of the modification */
     720             :         /* Apply the modify to the linked entry */
     721             : 
     722             :         unsigned int i, j;
     723             :         struct extended_dn_context *ac;
     724      279538 :         struct ldb_control *fix_links_control = NULL;
     725      279538 :         struct ldb_control *fix_link_sid_ctrl = NULL;
     726             :         int ret;
     727             : 
     728      279538 :         if (ldb_dn_is_special(req->op.mod.message->dn)) {
     729             :                 /* do not manipulate our control entries */
     730         657 :                 return ldb_next_request(module, req);
     731             :         }
     732             : 
     733      278881 :         ac = extended_dn_context_init(module, req);
     734      278881 :         if (!ac) {
     735           0 :                 return ldb_operr(ldb_module_get_ctx(module));
     736             :         }
     737             : 
     738      278881 :         if (!ac->schema) {
     739           0 :                 talloc_free(ac);
     740             :                 /* without schema, this doesn't make any sense */
     741           0 :                 return ldb_next_request(module, req);
     742             :         }
     743             : 
     744      278881 :         fix_links_control = ldb_request_get_control(req,
     745             :                                         DSDB_CONTROL_DBCHECK_FIX_DUPLICATE_LINKS);
     746      278881 :         if (fix_links_control != NULL) {
     747           2 :                 return ldb_next_request(module, req);
     748             :         }
     749             : 
     750      278879 :         fix_link_sid_ctrl = ldb_request_get_control(ac->req,
     751             :                                         DSDB_CONTROL_DBCHECK_FIX_LINK_DN_SID);
     752      278879 :         if (fix_link_sid_ctrl != NULL) {
     753           2 :                 return ldb_next_request(module, req);
     754             :         }
     755             : 
     756      701636 :         for (i=0; i < req->op.mod.message->num_elements; i++) {
     757      438847 :                 const struct ldb_message_element *el = &req->op.mod.message->elements[i];
     758      384974 :                 const struct dsdb_attribute *schema_attr
     759      438847 :                         = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
     760      438847 :                 if (!schema_attr) {
     761        1560 :                         continue;
     762             :                 }
     763             : 
     764             :                 /* We only setup an extended DN GUID on these particular DN objects */
     765      437287 :                 if (schema_attr->dn_format == DSDB_INVALID_DN) {
     766      219656 :                         continue;
     767             :                 }
     768             : 
     769      217631 :                 if (schema_attr->attributeID_id == DRSUAPI_ATTID_distinguishedName) {
     770             :                         /* distinguishedName values are ignored */
     771         277 :                         continue;
     772             :                 }
     773             : 
     774             :                 /* Before we setup a procedure to modify the incoming message, we must copy it */
     775      217354 :                 if (!ac->new_req) {
     776      110879 :                         struct ldb_message *msg = ldb_msg_copy(ac, req->op.mod.message);
     777      110879 :                         if (!msg) {
     778           0 :                                 talloc_free(ac);
     779           0 :                                 return ldb_oom(ac->ldb);
     780             :                         }
     781             :                    
     782      110879 :                         ret = ldb_build_mod_req(&ac->new_req, ac->ldb, ac, msg, req->controls, ac, extended_final_callback, req);
     783      110879 :                         LDB_REQ_SET_LOCATION(ac->new_req);
     784      110879 :                         if (ret != LDB_SUCCESS) {
     785           0 :                                 talloc_free(ac);
     786           0 :                                 return ret;
     787             :                         }
     788             :                 }
     789             :                 /* Re-calculate el */
     790      217354 :                 el = &ac->new_req->op.mod.message->elements[i];
     791             :                 /* For each value being added, we need to setup the lookups to fill in the extended DN */
     792      437221 :                 for (j = 0; j < el->num_values; j++) {
     793             :                         /* If we are just going to delete this
     794             :                          * element, only do a lookup if
     795             :                          * extended_store_replace determines it's an
     796             :                          * input of an extended DN */
     797      219883 :                         bool is_delete = (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_DELETE);
     798             : 
     799      420266 :                         ret = extended_store_replace(ac, ac->new_req,
     800             :                                                      NULL, /* self_dn to be ignored */
     801      219883 :                                                      &el->values[j],
     802             :                                                      is_delete, schema_attr);
     803      219883 :                         if (ret != LDB_SUCCESS) {
     804          16 :                                 talloc_free(ac);
     805          16 :                                 return ret;
     806             :                         }
     807             :                 }
     808             :         }
     809             : 
     810             :         /* if DNs were set continue */
     811      278861 :         if (ac->ops == NULL) {
     812      168321 :                 talloc_free(ac);
     813      168321 :                 return ldb_next_request(module, req);
     814             :         }
     815             : 
     816             :         /* start with the searches */
     817      110540 :         return ldb_next_request(module, ac->ops->search_req);
     818             : }
     819             : 
     820             : static const struct ldb_module_ops ldb_extended_dn_store_module_ops = {
     821             :         .name              = "extended_dn_store",
     822             :         .add               = extended_dn_add,
     823             :         .modify            = extended_dn_modify,
     824             : };
     825             : 
     826        5536 : int ldb_extended_dn_store_module_init(const char *version)
     827             : {
     828        5536 :         LDB_MODULE_CHECK_VERSION(version);
     829        5536 :         return ldb_register_module(&ldb_extended_dn_store_module_ops);
     830             : }

Generated by: LCOV version 1.13