LCOV - code coverage report
Current view: top level - source4/dsdb/samdb/ldb_modules - objectclass.c (source / functions) Hit Total Coverage
Test: coverage report for abartlet/fix-coverage dd10fb34 Lines: 503 608 82.7 %
Date: 2021-09-23 10:06:22 Functions: 18 19 94.7 %

          Line data    Source code
       1             : /* 
       2             :    ldb database library
       3             : 
       4             :    Copyright (C) Simo Sorce  2006-2008
       5             :    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005-2009
       6             :    Copyright (C) Matthias Dieter Wallnöfer 2010-2011
       7             : 
       8             :    This program is free software; you can redistribute it and/or modify
       9             :    it under the terms of the GNU General Public License as published by
      10             :    the Free Software Foundation; either version 3 of the License, or
      11             :    (at your option) any later version.
      12             :    
      13             :    This program is distributed in the hope that it will be useful,
      14             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      15             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      16             :    GNU General Public License for more details.
      17             :    
      18             :    You should have received a copy of the GNU General Public License
      19             :    along with this program.  If not, see <http://www.gnu.org/licenses/>.
      20             : */
      21             : 
      22             : /*
      23             :  *  Name: ldb
      24             :  *
      25             :  *  Component: objectClass sorting and constraint checking module
      26             :  *
      27             :  *  Description: 
      28             :  *  - sort the objectClass attribute into the class
      29             :  *    hierarchy and perform constraint checks (correct RDN name,
      30             :  *    valid parent),
      31             :  *  - fix DNs into 'standard' case
      32             :  *  - Add objectCategory and some other attribute defaults
      33             :  *
      34             :  *  Author: Andrew Bartlett
      35             :  */
      36             : 
      37             : 
      38             : #include "includes.h"
      39             : #include "ldb_module.h"
      40             : #include "dsdb/samdb/samdb.h"
      41             : #include "librpc/ndr/libndr.h"
      42             : #include "librpc/gen_ndr/ndr_security.h"
      43             : #include "libcli/security/security.h"
      44             : #include "auth/auth.h"
      45             : #include "param/param.h"
      46             : #include "../libds/common/flags.h"
      47             : #include "dsdb/samdb/ldb_modules/util.h"
      48             : 
      49             : #undef strcasecmp
      50             : 
      51             : struct oc_context {
      52             : 
      53             :         struct ldb_module *module;
      54             :         struct ldb_request *req;
      55             :         const struct dsdb_schema *schema;
      56             : 
      57             :         struct ldb_reply *search_res;
      58             :         struct ldb_reply *search_res2;
      59             : 
      60             :         int (*step_fn)(struct oc_context *);
      61             : };
      62             : 
      63      846128 : static struct oc_context *oc_init_context(struct ldb_module *module,
      64             :                                           struct ldb_request *req)
      65             : {
      66             :         struct ldb_context *ldb;
      67             :         struct oc_context *ac;
      68             : 
      69      846128 :         ldb = ldb_module_get_ctx(module);
      70             : 
      71      846128 :         ac = talloc_zero(req, struct oc_context);
      72      846128 :         if (ac == NULL) {
      73           0 :                 ldb_oom(ldb);
      74           0 :                 return NULL;
      75             :         }
      76             : 
      77      846128 :         ac->module = module;
      78      846128 :         ac->req = req;
      79      846128 :         ac->schema = dsdb_get_schema(ldb, ac);
      80             : 
      81      846128 :         return ac;
      82             : }
      83             : 
      84             : static int objectclass_do_add(struct oc_context *ac);
      85             : 
      86             : /*
      87             :  * This checks if we have unrelated object classes in our entry's "objectClass"
      88             :  * attribute. That means "unsatisfied" abstract classes (no concrete subclass)
      89             :  * or two or more disjunct structural ones.
      90             :  * If one of these conditions are true, blame.
      91             :  */
      92      489933 : static int check_unrelated_objectclasses(struct ldb_module *module,
      93             :                                         const struct dsdb_schema *schema,
      94             :                                         const struct dsdb_class *struct_objectclass,
      95             :                                         struct ldb_message_element *objectclass_element)
      96             : {
      97      489933 :         struct ldb_context *ldb = ldb_module_get_ctx(module);
      98             :         unsigned int i;
      99             :         bool found;
     100             : 
     101      489933 :         if (schema == NULL) {
     102           0 :                 return LDB_SUCCESS;
     103             :         }
     104             : 
     105     1440877 :         for (i = 0; i < objectclass_element->num_values; i++) {
     106     1027523 :                 const struct dsdb_class *tmp_class = dsdb_class_by_lDAPDisplayName_ldb_val(schema,
     107     1027523 :                                                                                            &objectclass_element->values[i]);
     108     1027523 :                 const struct dsdb_class *tmp_class2 = struct_objectclass;
     109             : 
     110             :                 /* Pointer comparison can be used due to the same schema str. */
     111     1181981 :                 if (tmp_class == NULL ||
     112      692053 :                     tmp_class == struct_objectclass ||
     113      953454 :                     tmp_class->objectClassCategory > 2 ||
     114      537484 :                     ldb_attr_cmp(tmp_class->lDAPDisplayName, "top") == 0) {
     115      979972 :                         continue;
     116             :                 }
     117             : 
     118       46247 :                 found = false;
     119      206773 :                 while (!found &&
     120       71805 :                        ldb_attr_cmp(tmp_class2->lDAPDisplayName, "top") != 0) {
     121       71800 :                         tmp_class2 = dsdb_class_by_lDAPDisplayName(schema,
     122       18457 :                                                                    tmp_class2->subClassOf);
     123       71800 :                         if (tmp_class2 == tmp_class) {
     124       47546 :                                 found = true;
     125             :                         }
     126             :                 }
     127       47551 :                 if (found) {
     128       47546 :                         continue;
     129             :                 }
     130             : 
     131           5 :                 ldb_asprintf_errstring(ldb,
     132             :                                        "objectclass: the objectclass '%s' seems to be unrelated to %s!",
     133           0 :                                        tmp_class->lDAPDisplayName,
     134           0 :                                        struct_objectclass->lDAPDisplayName);
     135           5 :                 return LDB_ERR_OBJECT_CLASS_VIOLATION;
     136             :         }
     137             : 
     138      413354 :         return LDB_SUCCESS;
     139             : }
     140             : 
     141     1113424 : static int get_search_callback(struct ldb_request *req, struct ldb_reply *ares)
     142             : {
     143             :         struct ldb_context *ldb;
     144             :         struct oc_context *ac;
     145             :         int ret;
     146             : 
     147     1113424 :         ac = talloc_get_type(req->context, struct oc_context);
     148     1113424 :         ldb = ldb_module_get_ctx(ac->module);
     149             : 
     150     1113424 :         if (!ares) {
     151           0 :                 return ldb_module_done(ac->req, NULL, NULL,
     152             :                                         LDB_ERR_OPERATIONS_ERROR);
     153             :         }
     154     1127372 :         if (ares->error != LDB_SUCCESS &&
     155       23572 :             ares->error != LDB_ERR_NO_SUCH_OBJECT) {
     156           2 :                 return ldb_module_done(ac->req, ares->controls,
     157             :                                         ares->response, ares->error);
     158             :         }
     159             : 
     160     1113422 :         ldb_reset_err_string(ldb);
     161             : 
     162     1113422 :         switch (ares->type) {
     163      544910 :         case LDB_REPLY_ENTRY:
     164      544910 :                 if (ac->search_res != NULL) {
     165           0 :                         ldb_set_errstring(ldb, "Too many results");
     166           0 :                         talloc_free(ares);
     167           0 :                         return ldb_module_done(ac->req, NULL, NULL,
     168             :                                                 LDB_ERR_OPERATIONS_ERROR);
     169             :                 }
     170             : 
     171      544910 :                 ac->search_res = talloc_steal(ac, ares);
     172      544910 :                 break;
     173             : 
     174           0 :         case LDB_REPLY_REFERRAL:
     175             :                 /* ignore */
     176           0 :                 talloc_free(ares);
     177           0 :                 break;
     178             : 
     179      568512 :         case LDB_REPLY_DONE:
     180      568512 :                 talloc_free(ares);
     181      568512 :                 ret = ac->step_fn(ac);
     182      568512 :                 if (ret != LDB_SUCCESS) {
     183       24107 :                         return ldb_module_done(ac->req, NULL, NULL, ret);
     184             :                 }
     185      467683 :                 break;
     186             :         }
     187             : 
     188      935891 :         return LDB_SUCCESS;
     189             : }
     190             : 
     191             : /* Fix up the DN to be in the standard form, taking particular care to match the parent DN
     192             : 
     193             :    This should mean that if the parent is:
     194             :     CN=Users,DC=samba,DC=example,DC=com
     195             :    and a proposed child is
     196             :     cn=Admins ,cn=USERS,dc=Samba,dc=example,dc=COM
     197             : 
     198             :    The resulting DN should be:
     199             : 
     200             :     CN=Admins,CN=Users,DC=samba,DC=example,DC=com
     201             :    
     202             :  */
     203      490488 : static int fix_dn(struct ldb_context *ldb,
     204             :                   TALLOC_CTX *mem_ctx,
     205             :                   struct ldb_dn *newdn, struct ldb_dn *parent_dn, 
     206             :                   struct ldb_dn **fixed_dn) 
     207             : {
     208             :         char *upper_rdn_attr;
     209             :         const struct ldb_val *rdn_val;
     210             : 
     211             :         /* Fix up the DN to be in the standard form, taking particular care to
     212             :          * match the parent DN */
     213      490488 :         *fixed_dn = ldb_dn_copy(mem_ctx, parent_dn);
     214      490488 :         if (*fixed_dn == NULL) {
     215           0 :                 return ldb_oom(ldb);
     216             :         }
     217             : 
     218             :         /* We need the attribute name in upper case */
     219      490488 :         upper_rdn_attr = strupper_talloc(*fixed_dn, 
     220             :                                          ldb_dn_get_rdn_name(newdn));
     221      490488 :         if (upper_rdn_attr == NULL) {
     222           0 :                 return ldb_oom(ldb);
     223             :         }
     224             : 
     225             :         /* Create a new child */
     226      490488 :         if (ldb_dn_add_child_fmt(*fixed_dn, "X=X") == false) {
     227           0 :                 return ldb_operr(ldb);
     228             :         }
     229             : 
     230      490488 :         rdn_val = ldb_dn_get_rdn_val(newdn);
     231      490488 :         if (rdn_val == NULL) {
     232           0 :                 return ldb_operr(ldb);
     233             :         }
     234             : 
     235             : #if 0
     236             :         /* the rules for rDN length constraints are more complex than
     237             :         this. Until we understand them we need to leave this
     238             :         constraint out. Otherwise we break replication, as windows
     239             :         does sometimes send us rDNs longer than 64 */
     240             :         if (!rdn_val || rdn_val->length > 64) {
     241             :                 DEBUG(2,(__location__ ": WARNING: rDN longer than 64 limit for '%s'\n", ldb_dn_get_linearized(newdn)));
     242             :         }
     243             : #endif
     244             : 
     245             : 
     246             :         /* And replace it with CN=foo (we need the attribute in upper case) */
     247      490488 :         return ldb_dn_set_component(*fixed_dn, 0, upper_rdn_attr, *rdn_val);
     248             : }
     249             : 
     250             : 
     251      490202 : static int objectclass_add(struct ldb_module *module, struct ldb_request *req)
     252             : {
     253             :         struct ldb_context *ldb;
     254             :         struct ldb_request *search_req;
     255             :         struct oc_context *ac;
     256             :         struct ldb_dn *parent_dn;
     257             :         const struct ldb_val *val;
     258             :         int ret;
     259             :         static const char * const parent_attrs[] = { "objectClass", NULL };
     260             : 
     261      490202 :         ldb = ldb_module_get_ctx(module);
     262             : 
     263      490202 :         ldb_debug(ldb, LDB_DEBUG_TRACE, "objectclass_add\n");
     264             : 
     265             :         /* do not manipulate our control entries */
     266      490202 :         if (ldb_dn_is_special(req->op.add.message->dn)) {
     267         508 :                 return ldb_next_request(module, req);
     268             :         }
     269             : 
     270             :         /* An add operation on the basedn without "NC-add" operation isn't
     271             :          * allowed. */
     272      489694 :         if (ldb_dn_compare(ldb_get_default_basedn(ldb), req->op.add.message->dn) == 0) {
     273             :                 unsigned int instanceType;
     274             : 
     275         122 :                 instanceType = ldb_msg_find_attr_as_uint(req->op.add.message,
     276             :                                                          "instanceType", 0);
     277         122 :                 if (!(instanceType & INSTANCE_TYPE_IS_NC_HEAD)) {
     278             :                         char *referral_uri;
     279             :                         /* When we are trying to readd the root basedn then
     280             :                          * this is denied, but with an interesting mechanism:
     281             :                          * there is generated a referral with the last
     282             :                          * component value as hostname. */
     283           1 :                         val = ldb_dn_get_component_val(req->op.add.message->dn,
     284           1 :                                                        ldb_dn_get_comp_num(req->op.add.message->dn) - 1);
     285           1 :                         if (val == NULL) {
     286           0 :                                 return ldb_operr(ldb);
     287             :                         }
     288           1 :                         referral_uri = talloc_asprintf(req, "ldap://%s/%s", val->data,
     289           1 :                                                        ldb_dn_get_linearized(req->op.add.message->dn));
     290           1 :                         if (referral_uri == NULL) {
     291           0 :                                 return ldb_module_oom(module);
     292             :                         }
     293             : 
     294           1 :                         return ldb_module_send_referral(req, referral_uri);
     295             :                 }
     296             :         }
     297             : 
     298      489693 :         ac = oc_init_context(module, req);
     299      489693 :         if (ac == NULL) {
     300           0 :                 return ldb_operr(ldb);
     301             :         }
     302             : 
     303             :         /* If there isn't a parent, just go on to the add processing */
     304      489693 :         if (ldb_dn_get_comp_num(ac->req->op.add.message->dn) == 1) {
     305           9 :                 return objectclass_do_add(ac);
     306             :         }
     307             : 
     308             :         /* get copy of parent DN */
     309      489684 :         parent_dn = ldb_dn_get_parent(ac, ac->req->op.add.message->dn);
     310      489684 :         if (parent_dn == NULL) {
     311           0 :                 return ldb_operr(ldb);
     312             :         }
     313             : 
     314      489684 :         ret = ldb_build_search_req(&search_req, ldb,
     315             :                                    ac, parent_dn, LDB_SCOPE_BASE,
     316             :                                    "(objectClass=*)", parent_attrs,
     317             :                                    NULL,
     318             :                                    ac, get_search_callback,
     319             :                                    req);
     320      489684 :         LDB_REQ_SET_LOCATION(search_req);
     321      489684 :         if (ret != LDB_SUCCESS) {
     322           0 :                 return ret;
     323             :         }
     324             : 
     325      489684 :         ret = dsdb_request_add_controls(search_req,
     326             :                                         DSDB_FLAG_AS_SYSTEM |
     327             :                                         DSDB_SEARCH_SHOW_RECYCLED);
     328      489684 :         if (ret != LDB_SUCCESS) {
     329           0 :                 return ret;
     330             :         }
     331             : 
     332      489684 :         ac->step_fn = objectclass_do_add;
     333             : 
     334      489684 :         return ldb_next_request(ac->module, search_req);
     335             : }
     336             : 
     337             : 
     338             : /*
     339             :   check if this is a special RODC nTDSDSA add
     340             :  */
     341          42 : static bool check_rodc_ntdsdsa_add(struct oc_context *ac,
     342             :                                    const struct dsdb_class *objectclass)
     343             : {
     344             :         struct ldb_control *rodc_control;
     345             : 
     346          42 :         if (ldb_attr_cmp(objectclass->lDAPDisplayName, "nTDSDSA") != 0) {
     347           1 :                 return false;
     348             :         }
     349          41 :         rodc_control = ldb_request_get_control(ac->req, LDB_CONTROL_RODC_DCPROMO_OID);
     350          41 :         if (!rodc_control) {
     351           0 :                 return false;
     352             :         }
     353             : 
     354          41 :         rodc_control->critical = false;
     355          41 :         return true;
     356             : }
     357             : 
     358      489693 : static int objectclass_do_add(struct oc_context *ac)
     359             : {
     360      489693 :         struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
     361             :         struct ldb_request *add_req;
     362             :         struct ldb_message_element *objectclass_element, *el;
     363             :         struct ldb_message *msg;
     364      489693 :         const char *rdn_name = NULL;
     365             :         char *value;
     366             :         const struct dsdb_class *objectclass;
     367             :         struct ldb_dn *objectcategory;
     368      489693 :         int32_t systemFlags = 0;
     369             :         unsigned int i, j;
     370             :         bool found;
     371             :         int ret;
     372             : 
     373      489693 :         msg = ldb_msg_copy_shallow(ac, ac->req->op.add.message);
     374      489693 :         if (msg == NULL) {
     375           0 :                 return ldb_module_oom(ac->module);
     376             :         }
     377             : 
     378             :         /* Check if we have a valid parent - this check is needed since
     379             :          * we don't get a LDB_ERR_NO_SUCH_OBJECT error. */
     380      489693 :         if (ac->search_res == NULL) {
     381             :                 unsigned int instanceType;
     382             : 
     383             :                 /* An add operation on partition DNs without "NC-add" operation
     384             :                  * isn't allowed. */
     385         122 :                 instanceType = ldb_msg_find_attr_as_uint(msg, "instanceType",
     386             :                                                          0);
     387         122 :                 if (!(instanceType & INSTANCE_TYPE_IS_NC_HEAD)) {
     388           1 :                         ldb_asprintf_errstring(ldb, "objectclass: Cannot add %s, parent does not exist!", 
     389             :                                                ldb_dn_get_linearized(msg->dn));
     390           1 :                         return LDB_ERR_NO_SUCH_OBJECT;
     391             :                 }
     392             : 
     393             :                 /* Don't keep any error messages - we've to add a partition */
     394         121 :                 ldb_set_errstring(ldb, NULL);
     395             :         } else {
     396             :                 /* Fix up the DN to be in the standard form, taking
     397             :                  * particular care to match the parent DN */
     398     1249863 :                 ret = fix_dn(ldb, msg,
     399      489571 :                              ac->req->op.add.message->dn,
     400      489571 :                              ac->search_res->message->dn,
     401             :                              &msg->dn);
     402      489571 :                 if (ret != LDB_SUCCESS) {
     403           0 :                         ldb_asprintf_errstring(ldb, "objectclass: Could not munge DN %s into normal form",
     404           0 :                                                ldb_dn_get_linearized(ac->req->op.add.message->dn));
     405           0 :                         return ret;
     406             :                 }
     407             :         }
     408             : 
     409      489692 :         if (ac->schema != NULL) {
     410      489692 :                 unsigned int linkID = 0;
     411             :                 /*
     412             :                  * Notice: by the normalization function call in "ldb_request()"
     413             :                  * case "LDB_ADD" we have always only *one* "objectClass"
     414             :                  * attribute at this stage!
     415             :                  */
     416             : 
     417      489692 :                 objectclass_element = ldb_msg_find_element(msg, "objectClass");
     418      489692 :                 if (!objectclass_element) {
     419           1 :                         ldb_asprintf_errstring(ldb, "objectclass: Cannot add %s, no objectclass specified!",
     420             :                                                ldb_dn_get_linearized(msg->dn));
     421           1 :                         return LDB_ERR_OBJECT_CLASS_VIOLATION;
     422             :                 }
     423      489691 :                 if (objectclass_element->num_values == 0) {
     424           1 :                         ldb_asprintf_errstring(ldb, "objectclass: Cannot add %s, at least one (structural) objectclass has to be specified!",
     425             :                                                ldb_dn_get_linearized(msg->dn));
     426           1 :                         return LDB_ERR_CONSTRAINT_VIOLATION;
     427             :                 }
     428             : 
     429             :                 /* Now do the sorting */
     430      489690 :                 ret = dsdb_sort_objectClass_attr(ldb, ac->schema,
     431             :                                                  objectclass_element, msg,
     432             :                                                  objectclass_element);
     433      489690 :                 if (ret != LDB_SUCCESS) {
     434           1 :                         return ret;
     435             :                 }
     436             : 
     437             :                 /*
     438             :                  * Get the new top-most structural object class and check for
     439             :                  * unrelated structural classes
     440             :                  */
     441      489689 :                 objectclass = dsdb_get_last_structural_class(ac->schema,
     442             :                                                              objectclass_element);
     443      489689 :                 if (objectclass == NULL) {
     444           1 :                         ldb_asprintf_errstring(ldb,
     445             :                                                "Failed to find a structural class for %s",
     446             :                                                ldb_dn_get_linearized(msg->dn));
     447           1 :                         return LDB_ERR_UNWILLING_TO_PERFORM;
     448             :                 }
     449             : 
     450      489688 :                 ret = check_unrelated_objectclasses(ac->module, ac->schema,
     451             :                                                     objectclass,
     452             :                                                     objectclass_element);
     453      489688 :                 if (ret != LDB_SUCCESS) {
     454           2 :                         return ret;
     455             :                 }
     456             : 
     457      489686 :                 rdn_name = ldb_dn_get_rdn_name(msg->dn);
     458      489686 :                 if (rdn_name == NULL) {
     459           0 :                         return ldb_operr(ldb);
     460             :                 }
     461      413118 :                 found = false;
     462     1320029 :                 for (i = 0; (!found) && (i < objectclass_element->num_values);
     463      526677 :                      i++) {
     464      409265 :                         const struct dsdb_class *tmp_class =
     465      526677 :                                 dsdb_class_by_lDAPDisplayName_ldb_val(ac->schema,
     466      526677 :                                                                       &objectclass_element->values[i]);
     467             : 
     468      526677 :                         if (tmp_class == NULL) continue;
     469             : 
     470      526677 :                         if (ldb_attr_cmp(rdn_name, tmp_class->rDNAttID) == 0)
     471      489685 :                                 found = true;
     472             :                 }
     473      489686 :                 if (!found) {
     474           1 :                         ldb_asprintf_errstring(ldb,
     475             :                                                "objectclass: Invalid RDN '%s' for objectclass '%s'!",
     476           0 :                                                rdn_name, objectclass->lDAPDisplayName);
     477           1 :                         return LDB_ERR_NAMING_VIOLATION;
     478             :                 }
     479             : 
     480      491204 :                 if (objectclass->systemOnly &&
     481        1549 :                     !ldb_request_get_control(ac->req, LDB_CONTROL_RELAX_OID) &&
     482          42 :                     !check_rodc_ntdsdsa_add(ac, objectclass)) {
     483           1 :                         ldb_asprintf_errstring(ldb,
     484             :                                                "objectclass: object class '%s' is system-only, rejecting creation of '%s'!",
     485           0 :                                                objectclass->lDAPDisplayName,
     486             :                                                ldb_dn_get_linearized(msg->dn));
     487           1 :                         return LDB_ERR_UNWILLING_TO_PERFORM;
     488             :                 }
     489             : 
     490      489684 :                 if (ac->search_res && ac->search_res->message) {
     491      380138 :                         struct ldb_message_element *oc_el
     492      413016 :                                 = ldb_msg_find_element(ac->search_res->message, "objectClass");
     493             : 
     494      489563 :                         bool allowed_class = false;
     495     1475270 :                         for (i=0; allowed_class == false && oc_el && i < oc_el->num_values; i++) {
     496             :                                 const struct dsdb_class *sclass;
     497             : 
     498      985707 :                                 sclass = dsdb_class_by_lDAPDisplayName_ldb_val(ac->schema,
     499      985707 :                                                                                &oc_el->values[i]);
     500      985707 :                                 if (!sclass) {
     501             :                                         /* We don't know this class?  what is going on? */
     502           0 :                                         continue;
     503             :                                 }
     504     9002792 :                                 for (j=0; sclass->systemPossibleInferiors && sclass->systemPossibleInferiors[j]; j++) {
     505     8660214 :                                         if (ldb_attr_cmp(objectclass->lDAPDisplayName, sclass->systemPossibleInferiors[j]) == 0) {
     506      413013 :                                                 allowed_class = true;
     507      413013 :                                                 break;
     508             :                                         }
     509             :                                 }
     510             :                         }
     511             : 
     512      489563 :                         if (!allowed_class) {
     513           3 :                                 ldb_asprintf_errstring(ldb, "structural objectClass %s is not a valid child class for %s",
     514           3 :                                                 objectclass->lDAPDisplayName, ldb_dn_get_linearized(ac->search_res->message->dn));
     515           3 :                                 return LDB_ERR_NAMING_VIOLATION;
     516             :                         }
     517             :                 }
     518             : 
     519      489681 :                 objectcategory = ldb_msg_find_attr_as_dn(ldb, ac, msg,
     520             :                                                          "objectCategory");
     521      489681 :                 if (objectcategory == NULL) {
     522      379946 :                         struct dsdb_extended_dn_store_format *dn_format =
     523      489234 :                                         talloc_get_type(ldb_module_get_private(ac->module),
     524             :                                                         struct dsdb_extended_dn_store_format);
     525      489234 :                         if (dn_format && dn_format->store_extended_dn_in_ldb == false) {
     526             :                                 /* Strip off extended components */
     527           0 :                                 struct ldb_dn *dn = ldb_dn_new(ac, ldb,
     528           0 :                                                                objectclass->defaultObjectCategory);
     529           0 :                                 value = ldb_dn_alloc_linearized(msg, dn);
     530           0 :                                 talloc_free(dn);
     531             :                         } else {
     532      489234 :                                 value = talloc_strdup(msg,
     533      109288 :                                                       objectclass->defaultObjectCategory);
     534             :                         }
     535      489234 :                         if (value == NULL) {
     536           0 :                                 return ldb_module_oom(ac->module);
     537             :                         }
     538             : 
     539      489234 :                         ret = ldb_msg_add_string(msg, "objectCategory", value);
     540      489234 :                         if (ret != LDB_SUCCESS) {
     541           0 :                                 return ret;
     542             :                         }
     543             :                 } else {
     544         283 :                         const struct dsdb_class *ocClass =
     545         447 :                                         dsdb_class_by_cn_ldb_val(ac->schema,
     546             :                                                                  ldb_dn_get_rdn_val(objectcategory));
     547         447 :                         if (ocClass != NULL) {
     548         446 :                                 struct ldb_dn *dn = ldb_dn_new(ac, ldb,
     549         164 :                                                                ocClass->defaultObjectCategory);
     550         446 :                                 if (ldb_dn_compare(objectcategory, dn) != 0) {
     551           0 :                                         ocClass = NULL;
     552             :                                 }
     553             :                         }
     554         447 :                         talloc_free(objectcategory);
     555         447 :                         if (ocClass == NULL) {
     556           1 :                                 ldb_asprintf_errstring(ldb, "objectclass: Cannot add %s, 'objectCategory' attribute invalid!",
     557             :                                                        ldb_dn_get_linearized(msg->dn));
     558           1 :                                 return LDB_ERR_OBJECT_CLASS_VIOLATION;
     559             :                         }
     560             :                 }
     561             : 
     562      489680 :                 if (!ldb_msg_find_element(msg, "showInAdvancedViewOnly") && (objectclass->defaultHidingValue == true)) {
     563      235374 :                         ldb_msg_add_string(msg, "showInAdvancedViewOnly",
     564             :                                                 "TRUE");
     565             :                 }
     566             : 
     567             :                 /* There are very special rules for systemFlags, see MS-ADTS
     568             :                  * MS-ADTS 3.1.1.5.2.4 */
     569             : 
     570      489680 :                 el = ldb_msg_find_element(msg, "systemFlags");
     571      489680 :                 if ((el != NULL) && (el->num_values > 1)) {
     572           1 :                         ldb_asprintf_errstring(ldb, "objectclass: Cannot add %s, 'systemFlags' attribute multivalued!",
     573             :                                                ldb_dn_get_linearized(msg->dn));
     574           1 :                         return LDB_ERR_CONSTRAINT_VIOLATION;
     575             :                 }
     576             : 
     577      489679 :                 systemFlags = ldb_msg_find_attr_as_int(msg, "systemFlags", 0);
     578             : 
     579      489679 :                 ldb_msg_remove_attr(msg, "systemFlags");
     580             : 
     581             :                 /* Only the following flags may be set by a client */
     582      489679 :                 if (ldb_request_get_control(ac->req,
     583             :                                             LDB_CONTROL_RELAX_OID) == NULL) {
     584      232362 :                         systemFlags &= ( SYSTEM_FLAG_CONFIG_ALLOW_RENAME
     585             :                                        | SYSTEM_FLAG_CONFIG_ALLOW_MOVE
     586             :                                        | SYSTEM_FLAG_CONFIG_ALLOW_LIMITED_MOVE
     587             :                                        | SYSTEM_FLAG_ATTR_IS_RDN );
     588             :                 }
     589             : 
     590             :                 /* But the last one ("ATTR_IS_RDN") is only allowed on
     591             :                  * "attributeSchema" objects. So truncate if it does not fit. */
     592      489679 :                 if (ldb_attr_cmp(objectclass->lDAPDisplayName, "attributeSchema") != 0) {
     593      315245 :                         systemFlags &= ~SYSTEM_FLAG_ATTR_IS_RDN;
     594             :                 }
     595             : 
     596      489679 :                 if (ldb_attr_cmp(objectclass->lDAPDisplayName, "server") == 0) {
     597         553 :                         systemFlags |= (int32_t)(SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE | SYSTEM_FLAG_CONFIG_ALLOW_RENAME | SYSTEM_FLAG_CONFIG_ALLOW_LIMITED_MOVE);
     598      489126 :                 } else if (ldb_attr_cmp(objectclass->lDAPDisplayName, "site") == 0
     599      488938 :                                 || ldb_attr_cmp(objectclass->lDAPDisplayName, "serversContainer") == 0
     600      488750 :                                 || ldb_attr_cmp(objectclass->lDAPDisplayName, "nTDSDSA") == 0) {
     601         639 :                         if (ldb_attr_cmp(objectclass->lDAPDisplayName, "site") == 0)
     602         188 :                                 systemFlags |= (int32_t)(SYSTEM_FLAG_CONFIG_ALLOW_RENAME);
     603         639 :                         systemFlags |= (int32_t)(SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE);
     604      488487 :                 } else if (ldb_attr_cmp(objectclass->lDAPDisplayName, "siteLink") == 0
     605      488348 :                                 || ldb_attr_cmp(objectclass->lDAPDisplayName, "subnet") == 0
     606      488141 :                                 || ldb_attr_cmp(objectclass->lDAPDisplayName, "siteLinkBridge") == 0
     607      488141 :                                 || ldb_attr_cmp(objectclass->lDAPDisplayName, "nTDSConnection") == 0) {
     608         404 :                         systemFlags |= (int32_t)(SYSTEM_FLAG_CONFIG_ALLOW_RENAME);
     609             :                 }
     610             :                 /* TODO: If parent object is site or subnet, also add (SYSTEM_FLAG_CONFIG_ALLOW_RENAME) */
     611             : 
     612      489679 :                 linkID = ldb_msg_find_attr_as_int(msg, "linkID", 0);
     613      489679 :                 if (linkID > 0 && linkID % 2 == 1) {
     614        6179 :                         systemFlags |= DS_FLAG_ATTR_NOT_REPLICATED;
     615             :                 }
     616             : 
     617      489679 :                 if (el || systemFlags != 0) {
     618      195282 :                         ret = samdb_msg_add_int(ldb, msg, msg, "systemFlags",
     619             :                                                 systemFlags);
     620      195282 :                         if (ret != LDB_SUCCESS) {
     621           0 :                                 return ret;
     622             :                         }
     623             :                 }
     624             : 
     625             :                 /* make sure that "isCriticalSystemObject" is not specified! */
     626      489679 :                 el = ldb_msg_find_element(msg, "isCriticalSystemObject");
     627      501928 :                 if ((el != NULL) &&
     628       12249 :                     !ldb_request_get_control(ac->req, LDB_CONTROL_RELAX_OID)) {
     629           1 :                         ldb_set_errstring(ldb,
     630             :                                           "objectclass: 'isCriticalSystemObject' must not be specified!");
     631           1 :                         return LDB_ERR_UNWILLING_TO_PERFORM;
     632             :                 }
     633             :         }
     634             : 
     635      793336 :         ret = ldb_build_add_req(&add_req, ldb, ac,
     636             :                                 msg,
     637      413110 :                                 ac->req->controls,
     638      413110 :                                 ac->req, dsdb_next_callback,
     639             :                                 ac->req);
     640      489678 :         LDB_REQ_SET_LOCATION(add_req);
     641      489678 :         if (ret != LDB_SUCCESS) {
     642           0 :                 return ret;
     643             :         }
     644             : 
     645             :         /* perform the add */
     646      489678 :         return ldb_next_request(ac->module, add_req);
     647             : }
     648             : 
     649             : static int oc_modify_callback(struct ldb_request *req,
     650             :                                 struct ldb_reply *ares);
     651             : static int objectclass_do_mod(struct oc_context *ac);
     652             : 
     653      279487 : static int objectclass_modify(struct ldb_module *module, struct ldb_request *req)
     654             : {
     655      279487 :         struct ldb_context *ldb = ldb_module_get_ctx(module);
     656             :         struct ldb_message_element *objectclass_element;
     657             :         struct ldb_message *msg;
     658             :         struct ldb_request *down_req;
     659             :         struct oc_context *ac;
     660      279487 :         bool oc_changes = false;
     661             :         int ret;
     662             : 
     663      279487 :         ldb_debug(ldb, LDB_DEBUG_TRACE, "objectclass_modify\n");
     664             : 
     665             :         /* do not manipulate our control entries */
     666      279487 :         if (ldb_dn_is_special(req->op.mod.message->dn)) {
     667         657 :                 return ldb_next_request(module, req);
     668             :         }
     669             : 
     670             :         /* As with the "real" AD we don't accept empty messages */
     671      278830 :         if (req->op.mod.message->num_elements == 0) {
     672           2 :                 ldb_set_errstring(ldb, "objectclass: modify message must have "
     673             :                                        "elements/attributes!");
     674           2 :                 return LDB_ERR_UNWILLING_TO_PERFORM;
     675             :         }
     676             : 
     677      278828 :         ac = oc_init_context(module, req);
     678      278828 :         if (ac == NULL) {
     679           0 :                 return ldb_operr(ldb);
     680             :         }
     681             : 
     682             :         /* Without schema, there isn't much to do here */
     683      278828 :         if (ac->schema == NULL) {
     684           0 :                 talloc_free(ac);
     685           0 :                 return ldb_next_request(module, req);
     686             :         }
     687             : 
     688      278828 :         msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
     689      278828 :         if (msg == NULL) {
     690           0 :                 return ldb_module_oom(ac->module);
     691             :         }
     692             : 
     693             :         /* For now change everything except the objectclasses */
     694             : 
     695      278828 :         objectclass_element = ldb_msg_find_element(msg, "objectClass");
     696      278828 :         if (objectclass_element != NULL) {
     697         345 :                 ldb_msg_remove_attr(msg, "objectClass");
     698         345 :                 oc_changes = true;
     699             :         }
     700             : 
     701             :         /* MS-ADTS 3.1.1.5.3.5 - on a forest level < 2003 we do allow updates
     702             :          * only on application NCs - not on the default ones */
     703      263101 :         if (oc_changes &&
     704         345 :             (dsdb_forest_functional_level(ldb) < DS_DOMAIN_FUNCTION_2003)) {
     705             :                 struct ldb_dn *nc_root;
     706             : 
     707           0 :                 ret = dsdb_find_nc_root(ldb, ac, req->op.mod.message->dn,
     708             :                                         &nc_root);
     709           0 :                 if (ret != LDB_SUCCESS) {
     710           0 :                         return ret;
     711             :                 }
     712             : 
     713           0 :                 if ((ldb_dn_compare(nc_root, ldb_get_default_basedn(ldb)) == 0) ||
     714           0 :                     (ldb_dn_compare(nc_root, ldb_get_config_basedn(ldb)) == 0) ||
     715           0 :                     (ldb_dn_compare(nc_root, ldb_get_schema_basedn(ldb)) == 0)) {
     716           0 :                         ldb_set_errstring(ldb,
     717             :                                           "objectclass: object class changes on objects under the standard name contexts not allowed!");
     718           0 :                         return LDB_ERR_UNWILLING_TO_PERFORM;
     719             :                 }
     720             : 
     721           0 :                 talloc_free(nc_root);
     722             :         }
     723             : 
     724      278828 :         if (oc_changes) {
     725         345 :                 ret = ldb_build_mod_req(&down_req, ldb, ac,
     726             :                                         msg,
     727             :                                         req->controls, ac,
     728             :                                         oc_modify_callback,
     729             :                                         req);
     730             :         } else {
     731      278483 :                 ret = ldb_build_mod_req(&down_req, ldb, ac,
     732             :                                         msg,
     733             :                                         req->controls, req,
     734             :                                         dsdb_next_callback,
     735             :                                         req);
     736             :         }
     737      278828 :         LDB_REQ_SET_LOCATION(down_req);
     738      278828 :         if (ret != LDB_SUCCESS) {
     739           0 :                 return ret;
     740             :         }
     741             : 
     742      278828 :         return ldb_next_request(module, down_req);
     743             : }
     744             : 
     745         345 : static int oc_modify_callback(struct ldb_request *req, struct ldb_reply *ares)
     746             : {
     747             :         static const char * const attrs[] = { "objectClass", NULL };
     748             :         struct ldb_context *ldb;
     749             :         struct ldb_request *search_req;
     750             :         struct oc_context *ac;
     751             :         int ret;
     752             : 
     753         345 :         ac = talloc_get_type(req->context, struct oc_context);
     754         345 :         ldb = ldb_module_get_ctx(ac->module);
     755             : 
     756         345 :         if (!ares) {
     757           0 :                 return ldb_module_done(ac->req, NULL, NULL,
     758             :                                         LDB_ERR_OPERATIONS_ERROR);
     759             :         }
     760             : 
     761         345 :         if (ares->type == LDB_REPLY_REFERRAL) {
     762           0 :                 return ldb_module_send_referral(ac->req, ares->referral);
     763             :         }
     764             : 
     765         345 :         if (ares->error != LDB_SUCCESS) {
     766          48 :                 return ldb_module_done(ac->req, ares->controls,
     767             :                                         ares->response, ares->error);
     768             :         }
     769             : 
     770         297 :         if (ares->type != LDB_REPLY_DONE) {
     771           0 :                 talloc_free(ares);
     772           0 :                 return ldb_module_done(ac->req, NULL, NULL,
     773             :                                         LDB_ERR_OPERATIONS_ERROR);
     774             :         }
     775             : 
     776         297 :         talloc_free(ares);
     777             : 
     778             :         /* this looks up the real existing object for fetching some important
     779             :          * information (objectclasses) */
     780         594 :         ret = ldb_build_search_req(&search_req, ldb,
     781         297 :                                    ac, ac->req->op.mod.message->dn,
     782             :                                    LDB_SCOPE_BASE,
     783             :                                    "(objectClass=*)",
     784             :                                    attrs, NULL, 
     785             :                                    ac, get_search_callback,
     786             :                                    ac->req);
     787         297 :         LDB_REQ_SET_LOCATION(search_req);
     788         297 :         if (ret != LDB_SUCCESS) {
     789           0 :                 return ldb_module_done(ac->req, NULL, NULL, ret);
     790             :         }
     791             : 
     792         297 :         ret = dsdb_request_add_controls(search_req,
     793             :                                         DSDB_FLAG_AS_SYSTEM |
     794             :                                         DSDB_SEARCH_SHOW_RECYCLED);
     795         297 :         if (ret != LDB_SUCCESS) {
     796           0 :                 return ldb_module_done(ac->req, NULL, NULL, ret);
     797             :         }
     798             : 
     799         297 :         ac->step_fn = objectclass_do_mod;
     800             : 
     801         297 :         ret = ldb_next_request(ac->module, search_req);
     802         297 :         if (ret != LDB_SUCCESS) {
     803           0 :                 return ldb_module_done(ac->req, NULL, NULL, ret);
     804             :         }
     805             : 
     806         291 :         return LDB_SUCCESS;
     807             : }
     808             : 
     809         297 : static int objectclass_do_mod(struct oc_context *ac)
     810             : {
     811             :         struct ldb_context *ldb;
     812             :         struct ldb_request *mod_req;
     813             :         struct ldb_message_element *oc_el_entry, *oc_el_change;
     814             :         struct ldb_val *vals;
     815             :         struct ldb_message *msg;
     816             :         const struct dsdb_class *objectclass;
     817             :         unsigned int i, j, k;
     818             :         bool found;
     819             :         int ret;
     820             : 
     821         297 :         ldb = ldb_module_get_ctx(ac->module);
     822             : 
     823             :         /* we should always have a valid entry when we enter here */
     824         297 :         if (ac->search_res == NULL) {
     825           0 :                 return ldb_operr(ldb);
     826             :         }
     827             : 
     828         297 :         oc_el_entry = ldb_msg_find_element(ac->search_res->message,
     829             :                                            "objectClass");
     830         297 :         if (oc_el_entry == NULL) {
     831             :                 /* existing entry without a valid object class? */
     832           0 :                 return ldb_operr(ldb);
     833             :         }
     834             : 
     835             :         /* use a new message structure */
     836         297 :         msg = ldb_msg_new(ac);
     837         297 :         if (msg == NULL) {
     838           0 :                 return ldb_module_oom(ac->module);
     839             :         }
     840             : 
     841         297 :         msg->dn = ac->req->op.mod.message->dn;
     842             : 
     843             :         /* We've to walk over all "objectClass" message elements */
     844         641 :         for (k = 0; k < ac->req->op.mod.message->num_elements; k++) {
     845         400 :                 if (ldb_attr_cmp(ac->req->op.mod.message->elements[k].name,
     846             :                                  "objectClass") != 0) {
     847         102 :                         continue;
     848             :                 }
     849             : 
     850         298 :                 oc_el_change = &ac->req->op.mod.message->elements[k];
     851             : 
     852         298 :                 switch (oc_el_change->flags & LDB_FLAG_MOD_MASK) {
     853         107 :                 case LDB_FLAG_MOD_ADD:
     854             :                         /* Merge the two message elements */
     855         171 :                         for (i = 0; i < oc_el_change->num_values; i++) {
     856         491 :                                 for (j = 0; j < oc_el_entry->num_values; j++) {
     857         433 :                                         if (ldb_attr_cmp((char *)oc_el_change->values[i].data,
     858             :                                                          (char *)oc_el_entry->values[j].data) == 0) {
     859          49 :                                                 ldb_asprintf_errstring(ldb,
     860             :                                                                        "objectclass: cannot re-add an existing objectclass: '%.*s'!",
     861          49 :                                                                        (int)oc_el_change->values[i].length,
     862          49 :                                                                        (const char *)oc_el_change->values[i].data);
     863          49 :                                                 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
     864             :                                         }
     865             :                                 }
     866             :                                 /* append the new object class value - code was
     867             :                                  * copied from "ldb_msg_add_value" */
     868          64 :                                 vals = talloc_realloc(oc_el_entry, oc_el_entry->values,
     869             :                                                       struct ldb_val,
     870             :                                                       oc_el_entry->num_values + 1);
     871          64 :                                 if (vals == NULL) {
     872           0 :                                         return ldb_module_oom(ac->module);
     873             :                                 }
     874          64 :                                 oc_el_entry->values = vals;
     875          64 :                                 oc_el_entry->values[oc_el_entry->num_values] =
     876          64 :                                                         oc_el_change->values[i];
     877          64 :                                 ++(oc_el_entry->num_values);
     878             :                         }
     879             : 
     880          58 :                         break;
     881             : 
     882         120 :                 case LDB_FLAG_MOD_REPLACE:
     883             :                         /*
     884             :                          * In this case the new "oc_el_entry" is simply
     885             :                          * "oc_el_change"
     886             :                          */
     887         120 :                         oc_el_entry = oc_el_change;
     888             : 
     889         120 :                         break;
     890             : 
     891          65 :                 case LDB_FLAG_MOD_DELETE:
     892             :                         /* Merge the two message elements */
     893         129 :                         for (i = 0; i < oc_el_change->num_values; i++) {
     894          65 :                                 found = false;
     895         141 :                                 for (j = 0; j < oc_el_entry->num_values; j++) {
     896         140 :                                         if (ldb_attr_cmp((char *)oc_el_change->values[i].data,
     897             :                                                          (char *)oc_el_entry->values[j].data) == 0) {
     898          64 :                                                 found = true;
     899             :                                                 /* delete the object class value
     900             :                                                  * - code was copied from
     901             :                                                  * "ldb_msg_remove_element" */
     902          64 :                                                 if (j != oc_el_entry->num_values - 1) {
     903         116 :                                                         memmove(&oc_el_entry->values[j],
     904          58 :                                                                 &oc_el_entry->values[j+1],
     905          58 :                                                                 ((oc_el_entry->num_values-1) - j)*sizeof(struct ldb_val));
     906             :                                                 }
     907          64 :                                                 --(oc_el_entry->num_values);
     908          64 :                                                 break;
     909             :                                         }
     910             :                                 }
     911          65 :                                 if (!found) {
     912             :                                         /* we cannot delete a not existing
     913             :                                          * object class */
     914           1 :                                         ldb_asprintf_errstring(ldb,
     915             :                                                                "objectclass: cannot delete this objectclass: '%.*s'!",
     916           1 :                                                                (int)oc_el_change->values[i].length,
     917           1 :                                                                (const char *)oc_el_change->values[i].data);
     918           1 :                                         return LDB_ERR_NO_SUCH_ATTRIBUTE;
     919             :                                 }
     920             :                         }
     921             : 
     922          64 :                         break;
     923             :                 }
     924             : 
     925             :                 /* Now do the sorting */
     926         248 :                 ret = dsdb_sort_objectClass_attr(ldb, ac->schema, oc_el_entry,
     927             :                                                  msg, oc_el_entry);
     928         248 :                 if (ret != LDB_SUCCESS) {
     929           1 :                         return ret;
     930             :                 }
     931             : 
     932             :                 /*
     933             :                  * Get the new top-most structural object class and check for
     934             :                  * unrelated structural classes
     935             :                  */
     936         247 :                 objectclass = dsdb_get_last_structural_class(ac->schema,
     937             :                                                              oc_el_entry);
     938         247 :                 if (objectclass == NULL) {
     939           2 :                         ldb_set_errstring(ldb,
     940             :                                           "objectclass: cannot delete all structural objectclasses!");
     941           2 :                         return LDB_ERR_OBJECT_CLASS_VIOLATION;
     942             :                 }
     943             : 
     944             :                 /* Check for unrelated objectclasses */
     945         245 :                 ret = check_unrelated_objectclasses(ac->module, ac->schema,
     946             :                                                     objectclass,
     947             :                                                     oc_el_entry);
     948         245 :                 if (ret != LDB_SUCCESS) {
     949           3 :                         return ret;
     950             :                 }
     951             :         }
     952             : 
     953             :         /* Now add the new object class attribute to the change message */
     954         241 :         ret = ldb_msg_add(msg, oc_el_entry, LDB_FLAG_MOD_REPLACE);
     955         241 :         if (ret != LDB_SUCCESS) {
     956           0 :                 ldb_module_oom(ac->module);
     957           0 :                 return ret;
     958             :         }
     959             : 
     960             :         /* Now we have the real and definitive change left to do */
     961             : 
     962         476 :         ret = ldb_build_mod_req(&mod_req, ldb, ac,
     963             :                                 msg,
     964         235 :                                 ac->req->controls,
     965         235 :                                 ac->req, dsdb_next_callback,
     966             :                                 ac->req);
     967         241 :         LDB_REQ_SET_LOCATION(mod_req);
     968         241 :         if (ret != LDB_SUCCESS) {
     969           0 :                 return ret;
     970             :         }
     971             : 
     972         241 :         return ldb_next_request(ac->module, mod_req);
     973             : }
     974             : 
     975             : static int objectclass_do_rename(struct oc_context *ac);
     976             : 
     977         930 : static int objectclass_rename(struct ldb_module *module, struct ldb_request *req)
     978             : {
     979             :         static const char * const attrs[] = { "objectClass", NULL };
     980             :         struct ldb_context *ldb;
     981             :         struct ldb_request *search_req;
     982             :         struct oc_context *ac;
     983             :         struct ldb_dn *parent_dn;
     984             :         int ret;
     985             : 
     986         930 :         ldb = ldb_module_get_ctx(module);
     987             : 
     988         930 :         ldb_debug(ldb, LDB_DEBUG_TRACE, "objectclass_rename\n");
     989             : 
     990             :         /* do not manipulate our control entries */
     991         930 :         if (ldb_dn_is_special(req->op.rename.olddn)) {
     992           0 :                 return ldb_next_request(module, req);
     993             :         }
     994             : 
     995             :         /*
     996             :          * Bypass the constraint checks when we do have the "DBCHECK" control
     997             :          * set, so we can force objects under the deleted objects container.
     998             :          */
     999         930 :         if (ldb_request_get_control(req, DSDB_CONTROL_DBCHECK) != NULL) {
    1000           1 :                 return ldb_next_request(module, req);
    1001             :         }
    1002             : 
    1003         929 :         ac = oc_init_context(module, req);
    1004         929 :         if (ac == NULL) {
    1005           0 :                 return ldb_operr(ldb);
    1006             :         }
    1007             : 
    1008         929 :         parent_dn = ldb_dn_get_parent(ac, req->op.rename.newdn);
    1009         929 :         if (parent_dn == NULL) {
    1010           0 :                 ldb_asprintf_errstring(ldb, "objectclass: Cannot rename %s, the parent DN does not exist!",
    1011             :                                        ldb_dn_get_linearized(req->op.rename.olddn));
    1012           0 :                 return LDB_ERR_NO_SUCH_OBJECT;
    1013             :         }
    1014             : 
    1015             :         /* this looks up the parent object for fetching some important
    1016             :          * information (objectclasses, DN normalisation...) */
    1017         929 :         ret = ldb_build_search_req(&search_req, ldb,
    1018             :                                    ac, parent_dn, LDB_SCOPE_BASE,
    1019             :                                    "(objectClass=*)",
    1020             :                                    attrs, NULL,
    1021             :                                    ac, get_search_callback,
    1022             :                                    req);
    1023         929 :         LDB_REQ_SET_LOCATION(search_req);
    1024         929 :         if (ret != LDB_SUCCESS) {
    1025           0 :                 return ret;
    1026             :         }
    1027             : 
    1028             :         /* we have to add the show recycled control, as otherwise DRS
    1029             :            deletes will be refused as we will think the target parent
    1030             :            does not exist */
    1031         929 :         ret = dsdb_request_add_controls(search_req,
    1032             :                                         DSDB_FLAG_AS_SYSTEM |
    1033             :                                         DSDB_SEARCH_SHOW_RECYCLED);
    1034         929 :         if (ret != LDB_SUCCESS) {
    1035           0 :                 return ret;
    1036             :         }
    1037             : 
    1038         929 :         ac->step_fn = objectclass_do_rename;
    1039             : 
    1040         929 :         return ldb_next_request(ac->module, search_req);
    1041             : }
    1042             : 
    1043             : static int objectclass_do_rename2(struct oc_context *ac);
    1044             : 
    1045         929 : static int objectclass_do_rename(struct oc_context *ac)
    1046             : {
    1047             :         static const char * const attrs[] = { "objectClass", NULL };
    1048             :         struct ldb_context *ldb;
    1049             :         struct ldb_request *search_req;
    1050             :         int ret;
    1051             : 
    1052         929 :         ldb = ldb_module_get_ctx(ac->module);
    1053             : 
    1054             :         /* Check if we have a valid parent - this check is needed since
    1055             :          * we don't get a LDB_ERR_NO_SUCH_OBJECT error. */
    1056         929 :         if (ac->search_res == NULL) {
    1057           3 :                 ldb_asprintf_errstring(ldb, "objectclass: Cannot rename %s, parent does not exist!",
    1058           3 :                                        ldb_dn_get_linearized(ac->req->op.rename.olddn));
    1059           3 :                 return LDB_ERR_OTHER;
    1060             :         }
    1061             : 
    1062             :         /* now assign "search_res2" to the parent entry to have "search_res"
    1063             :          * free for another lookup */
    1064         926 :         ac->search_res2 = ac->search_res;
    1065         926 :         ac->search_res = NULL;
    1066             : 
    1067             :         /* this looks up the real existing object for fetching some important
    1068             :          * information (objectclasses) */
    1069        1636 :         ret = ldb_build_search_req(&search_req, ldb,
    1070         921 :                                    ac, ac->req->op.rename.olddn,
    1071             :                                    LDB_SCOPE_BASE,
    1072             :                                    "(objectClass=*)",
    1073             :                                    attrs, NULL,
    1074             :                                    ac, get_search_callback,
    1075             :                                    ac->req);
    1076         926 :         LDB_REQ_SET_LOCATION(search_req);
    1077         926 :         if (ret != LDB_SUCCESS) {
    1078           0 :                 return ret;
    1079             :         }
    1080             : 
    1081         926 :         ret = dsdb_request_add_controls(search_req,
    1082             :                                         DSDB_FLAG_AS_SYSTEM |
    1083             :                                         DSDB_SEARCH_SHOW_RECYCLED);
    1084         926 :         if (ret != LDB_SUCCESS) {
    1085           0 :                 return ret;
    1086             :         }
    1087             : 
    1088         926 :         ac->step_fn = objectclass_do_rename2;
    1089             : 
    1090         926 :         return ldb_next_request(ac->module, search_req);
    1091             : }
    1092             : 
    1093         925 : static int objectclass_do_rename2(struct oc_context *ac)
    1094             : {
    1095             :         struct ldb_context *ldb;
    1096             :         struct ldb_request *rename_req;
    1097             :         struct ldb_dn *fixed_dn;
    1098             :         int ret;
    1099             : 
    1100         925 :         ldb = ldb_module_get_ctx(ac->module);
    1101             : 
    1102             :         /* Check if we have a valid entry - this check is needed since
    1103             :          * we don't get a LDB_ERR_NO_SUCH_OBJECT error. */
    1104         925 :         if (ac->search_res == NULL) {
    1105           2 :                 ldb_asprintf_errstring(ldb, "objectclass: Cannot rename %s, entry does not exist!",
    1106           2 :                                        ldb_dn_get_linearized(ac->req->op.rename.olddn));
    1107           2 :                 return LDB_ERR_NO_SUCH_OBJECT;
    1108             :         }
    1109             : 
    1110         923 :         if (ac->schema != NULL) {
    1111             :                 struct ldb_message_element *oc_el_entry, *oc_el_parent;
    1112             :                 const struct dsdb_class *objectclass;
    1113             :                 const char *rdn_name;
    1114         923 :                 bool allowed_class = false;
    1115             :                 unsigned int i, j;
    1116             :                 bool found;
    1117             : 
    1118         923 :                 oc_el_entry = ldb_msg_find_element(ac->search_res->message,
    1119             :                                                    "objectClass");
    1120         923 :                 if (oc_el_entry == NULL) {
    1121             :                         /* existing entry without a valid object class? */
    1122           0 :                         return ldb_operr(ldb);
    1123             :                 }
    1124         923 :                 objectclass = dsdb_get_last_structural_class(ac->schema,
    1125             :                                                              oc_el_entry);
    1126         923 :                 if (objectclass == NULL) {
    1127             :                         /* existing entry without a valid object class? */
    1128           0 :                         return ldb_operr(ldb);
    1129             :                 }
    1130             : 
    1131         923 :                 rdn_name = ldb_dn_get_rdn_name(ac->req->op.rename.newdn);
    1132         923 :                 if (rdn_name == NULL) {
    1133           0 :                         return ldb_operr(ldb);
    1134             :                 }
    1135         918 :                 found = false;
    1136        1952 :                 for (i = 0; (!found) && (i < oc_el_entry->num_values); i++) {
    1137         814 :                         const struct dsdb_class *tmp_class =
    1138        1034 :                                 dsdb_class_by_lDAPDisplayName_ldb_val(ac->schema,
    1139        1034 :                                                                       &oc_el_entry->values[i]);
    1140             : 
    1141        1034 :                         if (tmp_class == NULL) continue;
    1142             : 
    1143        1034 :                         if (ldb_attr_cmp(rdn_name, tmp_class->rDNAttID) == 0)
    1144         921 :                                 found = true;
    1145             :                 }
    1146         923 :                 if (!found) {
    1147           2 :                         ldb_asprintf_errstring(ldb,
    1148             :                                                "objectclass: Invalid RDN '%s' for objectclass '%s'!",
    1149           0 :                                                rdn_name, objectclass->lDAPDisplayName);
    1150           2 :                         return LDB_ERR_UNWILLING_TO_PERFORM;
    1151             :                 }
    1152             : 
    1153         921 :                 oc_el_parent = ldb_msg_find_element(ac->search_res2->message,
    1154             :                                                     "objectClass");
    1155         921 :                 if (oc_el_parent == NULL) {
    1156             :                         /* existing entry without a valid object class? */
    1157           0 :                         return ldb_operr(ldb);
    1158             :                 }
    1159             : 
    1160        2804 :                 for (i=0; allowed_class == false && i < oc_el_parent->num_values; i++) {
    1161             :                         const struct dsdb_class *sclass;
    1162             : 
    1163        1888 :                         sclass = dsdb_class_by_lDAPDisplayName_ldb_val(ac->schema,
    1164        1888 :                                                                        &oc_el_parent->values[i]);
    1165        1888 :                         if (!sclass) {
    1166             :                                 /* We don't know this class?  what is going on? */
    1167           0 :                                 continue;
    1168             :                         }
    1169       85461 :                         for (j=0; sclass->systemPossibleInferiors && sclass->systemPossibleInferiors[j]; j++) {
    1170       84502 :                                 if (ldb_attr_cmp(objectclass->lDAPDisplayName, sclass->systemPossibleInferiors[j]) == 0) {
    1171         914 :                                         allowed_class = true;
    1172         914 :                                         break;
    1173             :                                 }
    1174             :                         }
    1175             :                 }
    1176             : 
    1177         921 :                 if (!allowed_class) {
    1178           2 :                         ldb_asprintf_errstring(ldb,
    1179             :                                                "objectclass: structural objectClass %s is not a valid child class for %s",
    1180           2 :                                                objectclass->lDAPDisplayName, ldb_dn_get_linearized(ac->search_res2->message->dn));
    1181           2 :                         return LDB_ERR_NAMING_VIOLATION;
    1182             :                 }
    1183             :         }
    1184             : 
    1185             :         /* Ensure we are not trying to rename it to be a child of itself */
    1186         919 :         if ((ldb_dn_compare_base(ac->req->op.rename.olddn,
    1187         927 :                                  ac->req->op.rename.newdn) == 0)  &&
    1188           8 :             (ldb_dn_compare(ac->req->op.rename.olddn,
    1189           8 :                             ac->req->op.rename.newdn) != 0)) {
    1190           2 :                 ldb_asprintf_errstring(ldb, "objectclass: Cannot rename %s to be a child of itself",
    1191           2 :                                        ldb_dn_get_linearized(ac->req->op.rename.olddn));
    1192           2 :                 return LDB_ERR_UNWILLING_TO_PERFORM;
    1193             :         }
    1194             : 
    1195             :         /* Fix up the DN to be in the standard form, taking
    1196             :          * particular care to match the parent DN */
    1197        1623 :         ret = fix_dn(ldb, ac,
    1198         917 :                      ac->req->op.rename.newdn,
    1199         917 :                      ac->search_res2->message->dn,
    1200             :                      &fixed_dn);
    1201         917 :         if (ret != LDB_SUCCESS) {
    1202           0 :                 ldb_asprintf_errstring(ldb, "objectclass: Could not munge DN %s into normal form",
    1203           0 :                                        ldb_dn_get_linearized(ac->req->op.rename.newdn));
    1204           0 :                 return ret;
    1205             : 
    1206             :         }
    1207             : 
    1208        2319 :         ret = ldb_build_rename_req(&rename_req, ldb, ac,
    1209         912 :                                    ac->req->op.rename.olddn, fixed_dn,
    1210         912 :                                    ac->req->controls,
    1211         912 :                                    ac->req, dsdb_next_callback,
    1212             :                                    ac->req);
    1213         917 :         LDB_REQ_SET_LOCATION(rename_req);
    1214         917 :         if (ret != LDB_SUCCESS) {
    1215           0 :                 return ret;
    1216             :         }
    1217             : 
    1218             :         /* perform the rename */
    1219         917 :         return ldb_next_request(ac->module, rename_req);
    1220             : }
    1221             : 
    1222             : static int objectclass_do_delete(struct oc_context *ac);
    1223             : 
    1224       76702 : static int objectclass_delete(struct ldb_module *module, struct ldb_request *req)
    1225             : {
    1226             :         static const char * const attrs[] = { "nCName", "objectClass",
    1227             :                                               "systemFlags",
    1228             :                                               "isDeleted",
    1229             :                                               "isCriticalSystemObject", NULL };
    1230             :         struct ldb_context *ldb;
    1231             :         struct ldb_request *search_req;
    1232             :         struct oc_context *ac;
    1233             :         int ret;
    1234             : 
    1235       76702 :         ldb = ldb_module_get_ctx(module);
    1236             : 
    1237       76702 :         ldb_debug(ldb, LDB_DEBUG_TRACE, "objectclass_delete\n");
    1238             : 
    1239             :         /* do not manipulate our control entries */
    1240       76702 :         if (ldb_dn_is_special(req->op.del.dn)) {
    1241           1 :                 return ldb_next_request(module, req);
    1242             :         }
    1243             : 
    1244             :         /* Bypass the constraint checks when we do have the "RELAX" control
    1245             :          * set. */
    1246       76701 :         if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID) != NULL) {
    1247          23 :                 return ldb_next_request(module, req);
    1248             :         }
    1249             : 
    1250       76678 :         ac = oc_init_context(module, req);
    1251       76678 :         if (ac == NULL) {
    1252           0 :                 return ldb_operr(ldb);
    1253             :         }
    1254             : 
    1255             :         /* this looks up the entry object for fetching some important
    1256             :          * information (object classes, system flags...) */
    1257       76678 :         ret = ldb_build_search_req(&search_req, ldb,
    1258             :                                    ac, req->op.del.dn, LDB_SCOPE_BASE,
    1259             :                                    "(objectClass=*)",
    1260             :                                    attrs, NULL,
    1261             :                                    ac, get_search_callback,
    1262             :                                    req);
    1263       76678 :         LDB_REQ_SET_LOCATION(search_req);
    1264       76678 :         if (ret != LDB_SUCCESS) {
    1265           0 :                 return ret;
    1266             :         }
    1267             : 
    1268       76678 :         ret = dsdb_request_add_controls(search_req,
    1269             :                                         DSDB_FLAG_AS_SYSTEM |
    1270             :                                         DSDB_SEARCH_SHOW_RECYCLED);
    1271       76678 :         if (ret != LDB_SUCCESS) {
    1272           0 :                 return ret;
    1273             :         }
    1274             : 
    1275       76678 :         ac->step_fn = objectclass_do_delete;
    1276             : 
    1277       76678 :         return ldb_next_request(ac->module, search_req);
    1278             : }
    1279             : 
    1280       76677 : static int objectclass_do_delete(struct oc_context *ac)
    1281             : {
    1282             :         struct ldb_context *ldb;
    1283             :         struct ldb_dn *dn;
    1284             :         int32_t systemFlags;
    1285             :         bool isCriticalSystemObject;
    1286             :         int ret;
    1287             : 
    1288       76677 :         ldb = ldb_module_get_ctx(ac->module);
    1289             : 
    1290             :         /* Check if we have a valid entry - this check is needed since
    1291             :          * we don't get a LDB_ERR_NO_SUCH_OBJECT error. */
    1292       76677 :         if (ac->search_res == NULL) {
    1293       23484 :                 ldb_asprintf_errstring(ldb, "objectclass: Cannot delete %s, entry does not exist!",
    1294       23484 :                                        ldb_dn_get_linearized(ac->req->op.del.dn));
    1295       23484 :                 return LDB_ERR_NO_SUCH_OBJECT;
    1296             :         }
    1297             : 
    1298             :         /* DC's ntDSDSA object */
    1299       53193 :         if (ldb_dn_compare(ac->req->op.del.dn, samdb_ntds_settings_dn(ldb, ac)) == 0) {
    1300           2 :                 ldb_asprintf_errstring(ldb, "objectclass: Cannot delete %s, it's the DC's ntDSDSA object!",
    1301           2 :                                        ldb_dn_get_linearized(ac->req->op.del.dn));
    1302           2 :                 return LDB_ERR_UNWILLING_TO_PERFORM;
    1303             :         }
    1304             : 
    1305             :         /* DC's rIDSet object */
    1306             :         /* Perform this check only when it does exist - this is needed in order
    1307             :          * to don't let existing provisions break, and to delete . */
    1308       53191 :         ret = samdb_rid_set_dn(ldb, ac, &dn);
    1309       53191 :         if ((ret != LDB_SUCCESS) && (ret != LDB_ERR_NO_SUCH_ATTRIBUTE)
    1310           0 :             && (ret != LDB_ERR_NO_SUCH_OBJECT)) {
    1311           0 :                 ldb_asprintf_errstring(ldb, "objectclass: Unable to determine if %s, is this DC's rIDSet object: %s ",
    1312           0 :                                        ldb_dn_get_linearized(ac->req->op.del.dn),
    1313             :                                        ldb_errstring(ldb));
    1314           0 :                 return ret;
    1315             :         }
    1316       53191 :         if (ret == LDB_SUCCESS) {
    1317       53190 :                 if (ldb_dn_compare(ac->req->op.del.dn, dn) == 0) {
    1318           4 :                         talloc_free(dn);
    1319           4 :                         ldb_asprintf_errstring(ldb, "objectclass: Cannot delete %s, it's the DC's rIDSet object!",
    1320           4 :                                                ldb_dn_get_linearized(ac->req->op.del.dn));
    1321           4 :                         return LDB_ERR_UNWILLING_TO_PERFORM;
    1322             :                 }
    1323       53186 :                 talloc_free(dn);
    1324             :         }
    1325             : 
    1326             :         /* Only trusted request from system account are allowed to delete
    1327             :          * deleted objects.
    1328             :          */
    1329       53187 :         if (ldb_msg_check_string_attribute(ac->search_res->message, "isDeleted", "TRUE") &&
    1330           0 :                         (ldb_req_is_untrusted(ac->req) ||
    1331           0 :                                 !dsdb_module_am_system(ac->module))) {
    1332           0 :                 ldb_asprintf_errstring(ldb, "Delete of '%s' failed",
    1333           0 :                                                 ldb_dn_get_linearized(ac->req->op.del.dn));
    1334           0 :                 return LDB_ERR_UNWILLING_TO_PERFORM;
    1335             :         }
    1336             : 
    1337             :         /* crossRef objects regarding config, schema and default domain NCs */
    1338       53187 :         if (samdb_find_attribute(ldb, ac->search_res->message, "objectClass",
    1339             :                                  "crossRef") != NULL) {
    1340          12 :                 dn = ldb_msg_find_attr_as_dn(ldb, ac, ac->search_res->message,
    1341             :                                              "nCName");
    1342          20 :                 if ((ldb_dn_compare(dn, ldb_get_default_basedn(ldb)) == 0) ||
    1343           8 :                     (ldb_dn_compare(dn, ldb_get_config_basedn(ldb)) == 0)) {
    1344           8 :                         talloc_free(dn);
    1345             : 
    1346           8 :                         ldb_asprintf_errstring(ldb, "objectclass: Cannot delete %s, it's a crossRef object to the main or configuration partition!",
    1347           8 :                                                ldb_dn_get_linearized(ac->req->op.del.dn));
    1348           8 :                         return LDB_ERR_NOT_ALLOWED_ON_NON_LEAF;
    1349             :                 }
    1350           4 :                 if (ldb_dn_compare(dn, ldb_get_schema_basedn(ldb)) == 0) {
    1351           4 :                         talloc_free(dn);
    1352             : 
    1353           4 :                         ldb_asprintf_errstring(ldb, "objectclass: Cannot delete %s, it's a crossRef object to the schema partition!",
    1354           4 :                                                ldb_dn_get_linearized(ac->req->op.del.dn));
    1355           4 :                         return LDB_ERR_UNWILLING_TO_PERFORM;
    1356             :                 }
    1357           0 :                 talloc_free(dn);
    1358             :         }
    1359             : 
    1360             :         /* systemFlags */
    1361             : 
    1362       53175 :         systemFlags = ldb_msg_find_attr_as_int(ac->search_res->message,
    1363             :                                                "systemFlags", 0);
    1364       53175 :         if ((systemFlags & SYSTEM_FLAG_DISALLOW_DELETE) != 0) {
    1365           5 :                 ldb_asprintf_errstring(ldb, "objectclass: Cannot delete %s, it isn't permitted!",
    1366           5 :                                        ldb_dn_get_linearized(ac->req->op.del.dn));
    1367           5 :                 return LDB_ERR_UNWILLING_TO_PERFORM;
    1368             :         }
    1369             : 
    1370             :         /* isCriticalSystemObject - but this only applies on tree delete
    1371             :          * operations - MS-ADTS 3.1.1.5.5.7.2 */
    1372       53170 :         if (ldb_request_get_control(ac->req, LDB_CONTROL_TREE_DELETE_OID) != NULL) {
    1373       26364 :                 isCriticalSystemObject = ldb_msg_find_attr_as_bool(ac->search_res->message,
    1374             :                                                                    "isCriticalSystemObject", false);
    1375       26364 :                 if (isCriticalSystemObject) {
    1376             :                         /*
    1377             :                          * Following the explaination from Microsoft
    1378             :                          * https://lists.samba.org/archive/cifs-protocol/2011-August/002046.html
    1379             :                          * "I finished the investigation on this behavior.
    1380             :                          * As per MS-ADTS 3.1.5.5.7.2 , when a tree deletion is performed ,
    1381             :                          * every object in the tree will be checked to see if it has isCriticalSystemObject
    1382             :                          * set to TRUE, including the root node on which the delete operation is performed
    1383             :                          * But there is an exception  if the root object is a SAM specific objects(3.1.1.5.2.3 MS-ADTS)
    1384             :                          * Its deletion is done through SAM manger and isCriticalSystemObject attribute is not checked
    1385             :                          * The root node of the tree delete in your case is CN=ARES,OU=Domain Controllers,DC=w2k8r2,DC=home,DC=matws,DC=net
    1386             :                          * which is a SAM object  with  user class.  Therefore the tree deletion is performed without any error
    1387             :                          */
    1388             : 
    1389         484 :                         if (samdb_find_attribute(ldb, ac->search_res->message, "objectClass", "group") == NULL &&
    1390         484 :                             samdb_find_attribute(ldb, ac->search_res->message, "objectClass", "samDomain") == NULL &&
    1391         484 :                             samdb_find_attribute(ldb, ac->search_res->message, "objectClass", "samServer") == NULL &&
    1392         242 :                             samdb_find_attribute(ldb, ac->search_res->message, "objectClass", "user") == NULL) {
    1393           0 :                                         ldb_asprintf_errstring(ldb,
    1394             :                                                "objectclass: Cannot tree-delete %s, it's a critical system object!",
    1395           0 :                                                ldb_dn_get_linearized(ac->req->op.del.dn));
    1396           0 :                         return LDB_ERR_UNWILLING_TO_PERFORM;
    1397             :                         }
    1398             :                 }
    1399             :         }
    1400             : 
    1401       53170 :         return ldb_next_request(ac->module, ac->req);
    1402             : }
    1403             : 
    1404      132453 : static int objectclass_init(struct ldb_module *module)
    1405             : {
    1406      132453 :         struct ldb_context *ldb = ldb_module_get_ctx(module);
    1407             :         int ret;
    1408             : 
    1409             :         /* Init everything else */
    1410      132453 :         ret = ldb_next_init(module);
    1411      132453 :         if (ret != LDB_SUCCESS) {
    1412           0 :                 return ret;
    1413             :         }
    1414             :         
    1415             :         /* Look for the opaque to indicate we might have to cut down the DN of defaultObjectCategory */
    1416      132453 :         ldb_module_set_private(module, ldb_get_opaque(ldb, DSDB_EXTENDED_DN_STORE_FORMAT_OPAQUE_NAME));
    1417             : 
    1418      132453 :         ret = ldb_mod_register_control(module, LDB_CONTROL_RODC_DCPROMO_OID);
    1419      132453 :         if (ret != LDB_SUCCESS) {
    1420           0 :                 ldb_debug(ldb, LDB_DEBUG_ERROR,
    1421             :                           "objectclass_init: Unable to register control DCPROMO with rootdse\n");
    1422           0 :                 return ldb_operr(ldb);
    1423             :         }
    1424             : 
    1425      127712 :         return ret;
    1426             : }
    1427             : 
    1428             : static const struct ldb_module_ops ldb_objectclass_module_ops = {
    1429             :         .name           = "objectclass",
    1430             :         .add            = objectclass_add,
    1431             :         .modify         = objectclass_modify,
    1432             :         .rename         = objectclass_rename,
    1433             :         .del            = objectclass_delete,
    1434             :         .init_context   = objectclass_init
    1435             : };
    1436             : 
    1437        5536 : int ldb_objectclass_module_init(const char *version)
    1438             : {
    1439        5536 :         LDB_MODULE_CHECK_VERSION(version);
    1440        5536 :         return ldb_register_module(&ldb_objectclass_module_ops);
    1441             : }

Generated by: LCOV version 1.13