LCOV - code coverage report
Current view: top level - source4/dsdb/samdb/ldb_modules - objectclass_attrs.c (source / functions) Hit Total Coverage
Test: coverage report for fix-15632 9995c5c2 Lines: 302 344 87.8 %
Date: 2024-04-13 12:30:31 Functions: 10 10 100.0 %

          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) Stefan Metzmacher 2009
       7             :    Copyright (C) Matthias Dieter Wallnöfer 2010
       8             : 
       9             :    This program is free software; you can redistribute it and/or modify
      10             :    it under the terms of the GNU General Public License as published by
      11             :    the Free Software Foundation; either version 3 of the License, or
      12             :    (at your option) any later version.
      13             : 
      14             :    This program is distributed in the hope that it will be useful,
      15             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      16             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      17             :    GNU General Public License for more details.
      18             : 
      19             :    You should have received a copy of the GNU Lesser General Public
      20             :    License along with this library; if not, see <http://www.gnu.org/licenses/>.
      21             : */
      22             : 
      23             : /*
      24             :  *  Name: ldb
      25             :  *
      26             :  *  Component: objectclass attribute checking module
      27             :  *
      28             :  *  Description: this checks the attributes on a directory entry (if they're
      29             :  *    allowed, if the syntax is correct, if mandatory ones are missing,
      30             :  *    denies the deletion of mandatory ones...). The module contains portions
      31             :  *    of the "objectclass" and the "validate_update" LDB module.
      32             :  *
      33             :  *  Author: Matthias Dieter Wallnöfer
      34             :  */
      35             : 
      36             : #include "includes.h"
      37             : #include "ldb_module.h"
      38             : #include "dsdb/samdb/samdb.h"
      39             : #include "dsdb/samdb/ldb_modules/util.h"
      40             : 
      41             : #undef strcasecmp
      42             : 
      43             : struct oc_context {
      44             : 
      45             :         struct ldb_module *module;
      46             :         struct ldb_request *req;
      47             :         const struct dsdb_schema *schema;
      48             : 
      49             :         struct ldb_message *msg;
      50             : 
      51             :         struct ldb_reply *search_res;
      52             :         struct ldb_reply *mod_ares;
      53             : };
      54             : 
      55      929799 : static struct oc_context *oc_init_context(struct ldb_module *module,
      56             :                                           struct ldb_request *req)
      57             : {
      58      106787 :         struct ldb_context *ldb;
      59      106787 :         struct oc_context *ac;
      60             : 
      61      929799 :         ldb = ldb_module_get_ctx(module);
      62             : 
      63      929799 :         ac = talloc_zero(req, struct oc_context);
      64      929799 :         if (ac == NULL) {
      65           0 :                 ldb_oom(ldb);
      66           0 :                 return NULL;
      67             :         }
      68             : 
      69      929799 :         ac->module = module;
      70      929799 :         ac->req = req;
      71      929799 :         ac->schema = dsdb_get_schema(ldb, ac);
      72             : 
      73      929799 :         return ac;
      74             : }
      75             : 
      76             : static int oc_op_callback(struct ldb_request *req, struct ldb_reply *ares);
      77             : 
      78             : /*
      79             :  * Checks the correctness of the "dSHeuristics" attribute as described in both
      80             :  * MS-ADTS 7.1.1.2.4.1.2 dSHeuristics and MS-ADTS 3.1.1.5.3.2 Constraints
      81             :  */
      82       13422 : static int oc_validate_dsheuristics(struct ldb_message_element *el)
      83             : {
      84       13422 :         if (el->num_values > 0) {
      85        9479 :                 if ((el->values[0].length >= DS_HR_NINETIETH_CHAR) &&
      86           4 :                     (el->values[0].data[DS_HR_NINETIETH_CHAR-1] != '9')) {
      87           1 :                         return LDB_ERR_CONSTRAINT_VIOLATION;
      88             :                 }
      89        9478 :                 if ((el->values[0].length >= DS_HR_EIGHTIETH_CHAR) &&
      90           6 :                     (el->values[0].data[DS_HR_EIGHTIETH_CHAR-1] != '8')) {
      91           1 :                         return LDB_ERR_CONSTRAINT_VIOLATION;
      92             :                 }
      93        9477 :                 if ((el->values[0].length >= DS_HR_SEVENTIETH_CHAR) &&
      94           8 :                     (el->values[0].data[DS_HR_SEVENTIETH_CHAR-1] != '7')) {
      95           1 :                         return LDB_ERR_CONSTRAINT_VIOLATION;
      96             :                 }
      97        9476 :                 if ((el->values[0].length >= DS_HR_SIXTIETH_CHAR) &&
      98          10 :                     (el->values[0].data[DS_HR_SIXTIETH_CHAR-1] != '6')) {
      99           1 :                         return LDB_ERR_CONSTRAINT_VIOLATION;
     100             :                 }
     101        9475 :                 if ((el->values[0].length >= DS_HR_FIFTIETH_CHAR) &&
     102          12 :                     (el->values[0].data[DS_HR_FIFTIETH_CHAR-1] != '5')) {
     103           1 :                         return LDB_ERR_CONSTRAINT_VIOLATION;
     104             :                 }
     105        9474 :                 if ((el->values[0].length >= DS_HR_FOURTIETH_CHAR) &&
     106          14 :                     (el->values[0].data[DS_HR_FOURTIETH_CHAR-1] != '4')) {
     107           1 :                         return LDB_ERR_CONSTRAINT_VIOLATION;
     108             :                 }
     109        9473 :                 if ((el->values[0].length >= DS_HR_THIRTIETH_CHAR) &&
     110        6406 :                     (el->values[0].data[DS_HR_THIRTIETH_CHAR-1] != '3')) {
     111           1 :                         return LDB_ERR_CONSTRAINT_VIOLATION;
     112             :                 }
     113        9472 :                 if ((el->values[0].length >= DS_HR_TWENTIETH_CHAR) &&
     114        6408 :                     (el->values[0].data[DS_HR_TWENTIETH_CHAR-1] != '2')) {
     115           1 :                         return LDB_ERR_CONSTRAINT_VIOLATION;
     116             :                 }
     117        9471 :                 if ((el->values[0].length >= DS_HR_TENTH_CHAR) &&
     118        6411 :                     (el->values[0].data[DS_HR_TENTH_CHAR-1] != '1')) {
     119           1 :                         return LDB_ERR_CONSTRAINT_VIOLATION;
     120             :                 }
     121             :         }
     122             : 
     123       13413 :         return LDB_SUCCESS;
     124             : }
     125             : 
     126             : /*
     127             :   auto normalise values on input
     128             :  */
     129     2922838 : static int oc_auto_normalise(struct ldb_context *ldb, const struct dsdb_attribute *attr,
     130             :                              struct ldb_message *msg, struct ldb_message_element *el)
     131             : {
     132      407543 :         int i;
     133     2922838 :         bool values_copied = false;
     134             : 
     135     5845097 :         for (i=0; i<el->num_values; i++) {
     136      407543 :                 struct ldb_val v;
     137      407543 :                 int ret;
     138             :                 /*
     139             :                  * We use msg->elements (owned by this module due to
     140             :                  * ldb_msg_copy_shallow()) as a memory context and
     141             :                  * then steal from there to the right spot if we don't
     142             :                  * free it.
     143             :                  */
     144     3329811 :                 ret = attr->ldb_schema_attribute->syntax->canonicalise_fn(ldb,
     145     2922268 :                                                                           msg->elements,
     146     2922268 :                                                                           &el->values[i],
     147             :                                                                           &v);
     148     2922268 :                 if (ret != LDB_SUCCESS) {
     149           9 :                         return ret;
     150             :                 }
     151     2922259 :                 if (data_blob_cmp(&v, &el->values[i]) == 0) {
     152             :                         /* no need to replace it */
     153     2922075 :                         talloc_free(v.data);
     154     2922075 :                         continue;
     155             :                 }
     156             : 
     157             :                 /* we need to copy the values array on the first change */
     158         184 :                 if (!values_copied) {
     159           3 :                         struct ldb_val *v2;
     160         184 :                         v2 = talloc_array(msg->elements, struct ldb_val, el->num_values);
     161         184 :                         if (v2 == NULL) {
     162           0 :                                 return ldb_oom(ldb);
     163             :                         }
     164         184 :                         memcpy(v2, el->values, sizeof(struct ldb_val) * el->num_values);
     165         184 :                         el->values = v2;
     166         184 :                         values_copied = true;
     167             :                 }
     168             : 
     169         184 :                 el->values[i] = v;
     170             : 
     171             :                 /*
     172             :                  * By now el->values is a talloc pointer under
     173             :                  * msg->elements and may now be used
     174             :                  */
     175         184 :                 talloc_steal(el->values, v.data);
     176             :         }
     177     2515286 :         return LDB_SUCCESS;
     178             : }
     179             : 
     180      929799 : static int attr_handler(struct oc_context *ac)
     181             : {
     182      106787 :         struct ldb_context *ldb;
     183      106787 :         struct ldb_message *msg;
     184      106787 :         struct ldb_request *child_req;
     185      106787 :         const struct dsdb_attribute *attr;
     186      106787 :         unsigned int i;
     187      106787 :         int ret;
     188      106787 :         WERROR werr;
     189      106787 :         struct dsdb_syntax_ctx syntax_ctx;
     190             : 
     191      929799 :         ldb = ldb_module_get_ctx(ac->module);
     192             : 
     193      929799 :         if (ac->req->operation == LDB_ADD) {
     194      542080 :                 msg = ldb_msg_copy_shallow(ac, ac->req->op.add.message);
     195             :         } else {
     196      387719 :                 msg = ldb_msg_copy_shallow(ac, ac->req->op.mod.message);
     197             :         }
     198      929799 :         if (msg == NULL) {
     199           0 :                 return ldb_oom(ldb);
     200             :         }
     201      929799 :         ac->msg = msg;
     202             : 
     203             :         /* initialize syntax checking context */
     204      929799 :         dsdb_syntax_ctx_init(&syntax_ctx, ldb, ac->schema);
     205             : 
     206             :         /* Check if attributes exist in the schema, if the values match,
     207             :          * if they're not operational and fix the names to the match the schema
     208             :          * case */
     209     9122607 :         for (i = 0; i < msg->num_elements; i++) {
     210     9262019 :                 attr = dsdb_attribute_by_lDAPDisplayName(ac->schema,
     211     8086125 :                                                          msg->elements[i].name);
     212     8086125 :                 if (attr == NULL) {
     213           2 :                         if (ldb_request_get_control(ac->req, DSDB_CONTROL_DBCHECK) &&
     214           0 :                             ac->req->operation != LDB_ADD) {
     215             :                                 /* we allow this for dbcheck to fix
     216             :                                    broken attributes */
     217           0 :                                 goto no_attribute;
     218             :                         }
     219           2 :                         ldb_asprintf_errstring(ldb, "objectclass_attrs: attribute '%s' on entry '%s' was not found in the schema!",
     220           2 :                                                msg->elements[i].name,
     221             :                                                ldb_dn_get_linearized(msg->dn));
     222           2 :                         return LDB_ERR_NO_SUCH_ATTRIBUTE;
     223             :                 }
     224             : 
     225     8086142 :                 if ((attr->linkID & 1) == 1 &&
     226          25 :                     !ldb_request_get_control(ac->req, LDB_CONTROL_RELAX_OID) &&
     227           6 :                     !ldb_request_get_control(ac->req, DSDB_CONTROL_DBCHECK)) {
     228             :                         /* Odd is for the target.  Illegal to modify */
     229           4 :                         ldb_asprintf_errstring(ldb, 
     230             :                                                "objectclass_attrs: attribute '%s' on entry '%s' must not be modified directly, it is a linked attribute", 
     231           4 :                                                msg->elements[i].name,
     232             :                                                ldb_dn_get_linearized(msg->dn));
     233           4 :                         return LDB_ERR_UNWILLING_TO_PERFORM;
     234             :                 }
     235             : 
     236             :                 /*
     237             :                  * Enforce systemOnly checks from [ADTS] 3.1.1.5.3.2
     238             :                  * Constraints in Modify Operation
     239             :                  */
     240     8086119 :                 if (ac->req->operation == LDB_MODIFY && attr->systemOnly) {
     241             :                         /*
     242             :                          * Allow dbcheck and relax to bypass. objectClass, name
     243             :                          * and distinguishedName are generally handled
     244             :                          * elsewhere.
     245             :                          *
     246             :                          * The remaining cases, undelete, msDS-AdditionalDnsHostName
     247             :                          * and wellKnownObjects are documented in the specification.
     248             :                          */
     249      274155 :                         if (!ldb_request_get_control(ac->req, LDB_CONTROL_RELAX_OID) &&
     250      237183 :                             !ldb_request_get_control(ac->req, DSDB_CONTROL_DBCHECK) &&
     251      101281 :                             !ldb_request_get_control(ac->req, DSDB_CONTROL_RESTORE_TOMBSTONE_OID) &&
     252      100751 :                             ldb_attr_cmp(attr->lDAPDisplayName, "objectClass") != 0 &&
     253      100674 :                             ldb_attr_cmp(attr->lDAPDisplayName, "name") != 0 &&
     254      100673 :                             ldb_attr_cmp(attr->lDAPDisplayName, "distinguishedName") != 0 &&
     255      100670 :                             ldb_attr_cmp(attr->lDAPDisplayName, "msDS-AdditionalDnsHostName") != 0 &&
     256      100666 :                             ldb_attr_cmp(attr->lDAPDisplayName, "wellKnownObjects") != 0) {
     257             :                                 /*
     258             :                                  * Comparison against base schema DN is used as a substitute for
     259             :                                  * fschemaUpgradeInProgress and other specific schema checks.
     260             :                                  */
     261      100666 :                                 if (ldb_dn_compare_base(ldb_get_schema_basedn(ldb), msg->dn) != 0) {
     262       95979 :                                         struct ldb_control *as_system = ldb_request_get_control(ac->req,
     263             :                                                                                                 LDB_CONTROL_AS_SYSTEM_OID);
     264       95979 :                                         if (!dsdb_module_am_system(ac->module) && !as_system) {
     265           5 :                                                 ldb_asprintf_errstring(ldb,
     266             :                                                                        "objectclass_attrs: attribute '%s' on entry '%s' can only be modified as system",
     267           5 :                                                                        msg->elements[i].name,
     268             :                                                                        ldb_dn_get_linearized(msg->dn));
     269           5 :                                                 return LDB_ERR_CONSTRAINT_VIOLATION;
     270             :                                         }
     271             :                                 }
     272             :                         }
     273             :                 }
     274             : 
     275     8086114 :                 if (!(msg->elements[i].flags & LDB_FLAG_INTERNAL_DISABLE_VALIDATION)) {
     276     8086022 :                         werr = attr->syntax->validate_ldb(&syntax_ctx, attr,
     277     6910128 :                                                           &msg->elements[i]);
     278     8086096 :                         if (!W_ERROR_IS_OK(werr) &&
     279          74 :                             !ldb_request_get_control(ac->req, DSDB_CONTROL_DBCHECK)) {
     280          72 :                                 ldb_asprintf_errstring(ldb, "objectclass_attrs: attribute '%s' on entry '%s' contains at least one invalid value!",
     281          72 :                                                        msg->elements[i].name,
     282             :                                                        ldb_dn_get_linearized(msg->dn));
     283          72 :                                 return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
     284             :                         }
     285             :                 }
     286             : 
     287     8086042 :                 if ((attr->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED) != 0) {
     288           3 :                         ldb_asprintf_errstring(ldb, "objectclass_attrs: attribute '%s' on entry '%s' is constructed!",
     289           3 :                                                msg->elements[i].name,
     290             :                                                ldb_dn_get_linearized(msg->dn));
     291           3 :                         if (ac->req->operation == LDB_ADD) {
     292           3 :                                 return LDB_ERR_UNDEFINED_ATTRIBUTE_TYPE;
     293             :                         } else {
     294           0 :                                 return LDB_ERR_CONSTRAINT_VIOLATION;
     295             :                         }
     296             :                 }
     297             : 
     298             :                 /* "dSHeuristics" syntax check */
     299     8086039 :                 if (ldb_attr_cmp(attr->lDAPDisplayName, "dSHeuristics") == 0) {
     300       13422 :                         ret = oc_validate_dsheuristics(&(msg->elements[i]));
     301       13422 :                         if (ret != LDB_SUCCESS) {
     302           9 :                                 return ret;
     303             :                         }
     304             :                 }
     305             : 
     306             :                 /* auto normalise some attribute values */
     307     8086030 :                 if (attr->syntax->auto_normalise) {
     308     2922838 :                         ret = oc_auto_normalise(ldb, attr, msg, &msg->elements[i]);
     309     2922838 :                         if (ret != LDB_SUCCESS) {
     310           9 :                                 return ret;
     311             :                         }
     312             :                 }
     313             : 
     314             :                 /* Substitute the attribute name to match in case */
     315     8086021 :                 msg->elements[i].name = attr->lDAPDisplayName;
     316             :         }
     317             : 
     318      929695 : no_attribute:
     319      929695 :         if (ac->req->operation == LDB_ADD) {
     320      542032 :                 ret = ldb_build_add_req(&child_req, ldb, ac,
     321      458443 :                                         msg, ac->req->controls,
     322             :                                         ac, oc_op_callback, ac->req);
     323      542032 :                 LDB_REQ_SET_LOCATION(child_req);
     324             :         } else {
     325      387663 :                 ret = ldb_build_mod_req(&child_req, ldb, ac,
     326      364465 :                                         msg, ac->req->controls,
     327             :                                         ac, oc_op_callback, ac->req);
     328      387663 :                 LDB_REQ_SET_LOCATION(child_req);
     329             :         }
     330      929695 :         if (ret != LDB_SUCCESS) {
     331           0 :                 return ret;
     332             :         }
     333             : 
     334      929695 :         return ldb_next_request(ac->module, child_req);
     335             : }
     336             : 
     337             : /*
     338             :   these are attributes which are left over from old ways of doing
     339             :   things in ldb, and are harmless
     340             :  */
     341             : static const char *harmless_attrs[] = { "parentGUID", NULL };
     342             : 
     343      928877 : static int attr_handler2(struct oc_context *ac)
     344             : {
     345      106786 :         struct ldb_context *ldb;
     346      106786 :         struct ldb_message_element *oc_element;
     347      106786 :         struct ldb_message *msg;
     348      106786 :         const char **must_contain, **may_contain, **found_must_contain;
     349             :         /* There exists a hardcoded delete-protected attributes list in AD */
     350      928877 :         const char *del_prot_attributes[] = { "nTSecurityDescriptor",
     351             :                 "objectSid", "sAMAccountType", "sAMAccountName", "groupType",
     352             :                 "primaryGroupID", "userAccountControl", "accountExpires",
     353             :                 "badPasswordTime", "badPwdCount", "codePage", "countryCode",
     354             :                 "lastLogoff", "lastLogon", "logonCount", "pwdLastSet", NULL },
     355             :                 **l;
     356      106786 :         const struct dsdb_attribute *attr;
     357      106786 :         unsigned int i;
     358      106786 :         bool found;
     359      928877 :         bool isSchemaAttr = false;
     360             : 
     361      928877 :         ldb = ldb_module_get_ctx(ac->module);
     362             : 
     363      928877 :         if (ac->search_res == NULL) {
     364           0 :                 return ldb_operr(ldb);
     365             :         }
     366             : 
     367             :         /* We rely here on the preceding "objectclass" LDB module which did
     368             :          * already fix up the objectclass list (inheritance, order...). */
     369      928877 :         oc_element = ldb_msg_find_element(ac->search_res->message,
     370             :                                           "objectClass");
     371      928877 :         if (oc_element == NULL) {
     372           0 :                 return ldb_operr(ldb);
     373             :         }
     374             : 
     375             :         /* LSA-specific object classes are not allowed to be created over LDAP,
     376             :          * so we need to tell if this connection is internal (trusted) or not
     377             :          * (untrusted).
     378             :          *
     379             :          * Hongwei Sun from Microsoft explains:
     380             :          * The constraint in 3.1.1.5.2.2 MS-ADTS means that LSA objects cannot
     381             :          * be added or modified through the LDAP interface, instead they can
     382             :          * only be handled through LSA Policy API.  This is also explained in
     383             :          * 7.1.6.9.7 MS-ADTS as follows:
     384             :          * "Despite being replicated normally between peer DCs in a domain,
     385             :          * the process of creating or manipulating TDOs is specifically
     386             :          * restricted to the LSA Policy APIs, as detailed in [MS-LSAD] section
     387             :          * 3.1.1.5. Unlike other objects in the DS, TDOs may not be created or
     388             :          *  manipulated by client machines over the LDAPv3 transport."
     389             :          */
     390     3079771 :         for (i = 0; i < oc_element->num_values; i++) {
     391     2150896 :                 char * attname = (char *)oc_element->values[i].data;
     392     2150896 :                 if (ldb_req_is_untrusted(ac->req)) {
     393      499119 :                         if (strcmp(attname, "secret") == 0 ||
     394      499117 :                             strcmp(attname, "trustedDomain") == 0) {
     395           2 :                                 ldb_asprintf_errstring(ldb, "objectclass_attrs: LSA objectclasses (entry '%s') cannot be created or changed over LDAP!",
     396           2 :                                                        ldb_dn_get_linearized(ac->search_res->message->dn));
     397           2 :                                 return LDB_ERR_UNWILLING_TO_PERFORM;
     398             :                         }
     399             :                 }
     400     2150894 :                 if (strcmp(attname, "attributeSchema") == 0) {
     401      221548 :                         isSchemaAttr = true;
     402             :                 }
     403             :         }
     404             : 
     405      928875 :         must_contain = dsdb_full_attribute_list(ac, ac->schema, oc_element,
     406             :                                                 DSDB_SCHEMA_ALL_MUST);
     407      928875 :         may_contain =  dsdb_full_attribute_list(ac, ac->schema, oc_element,
     408             :                                                 DSDB_SCHEMA_ALL_MAY);
     409      928875 :         found_must_contain = const_str_list(str_list_copy(ac, must_contain));
     410      928875 :         if ((must_contain == NULL) || (may_contain == NULL)
     411      928875 :             || (found_must_contain == NULL)) {
     412           0 :                 return ldb_operr(ldb);
     413             :         }
     414             : 
     415             :         /* Check the delete-protected attributes list */
     416      928875 :         msg = ac->search_res->message;
     417    15790397 :         for (l = del_prot_attributes; *l != NULL; l++) {
     418     1708576 :                 struct ldb_message_element *el;
     419             : 
     420    14861586 :                 el = ldb_msg_find_element(ac->msg, *l);
     421    14861586 :                 if (el == NULL) {
     422             :                         /*
     423             :                          * It was not specified in the add or modify,
     424             :                          * so it doesn't need to be in the stored record
     425             :                          */
     426    13737231 :                         continue;
     427             :                 }
     428             : 
     429     1124355 :                 found = str_list_check_ci(must_contain, *l);
     430     1124355 :                 if (!found) {
     431      463179 :                         found = str_list_check_ci(may_contain, *l);
     432             :                 }
     433     1124355 :                 if (found && (ldb_msg_find_element(msg, *l) == NULL)) {
     434          64 :                         ldb_asprintf_errstring(ldb, "objectclass_attrs: delete protected attribute '%s' on entry '%s' missing!",
     435             :                                                *l,
     436             :                                                ldb_dn_get_linearized(msg->dn));
     437          64 :                         return LDB_ERR_UNWILLING_TO_PERFORM;
     438             :                 }
     439             :         }
     440             : 
     441             :         /* Check if all specified attributes are valid in the given
     442             :          * objectclasses and if they meet additional schema restrictions. */
     443    20642425 :         for (i = 0; i < msg->num_elements; i++) {
     444    21983663 :                 attr = dsdb_attribute_by_lDAPDisplayName(ac->schema,
     445    19713645 :                                                          msg->elements[i].name);
     446    19713645 :                 if (attr == NULL) {
     447           0 :                         if (ldb_request_get_control(ac->req, DSDB_CONTROL_DBCHECK)) {
     448             :                                 /* allow this to make it possible for dbcheck
     449             :                                    to remove bad attributes */
     450           0 :                                 continue;
     451             :                         }
     452           0 :                         return ldb_operr(ldb);
     453             :                 }
     454             : 
     455    19713645 :                 if (attr->linkID & 1) {
     456             :                         /*
     457             :                          * We need to allow backlinks on all objects
     458             :                          * even if the schema doesn't allow it.
     459             :                          */
     460       97131 :                         continue;
     461             :                 }
     462             : 
     463             :                 /* We can use "str_list_check" with "strcmp" here since the
     464             :                  * attribute information from the schema are always equal
     465             :                  * up-down-cased. */
     466    19616514 :                 found = str_list_check(must_contain, attr->lDAPDisplayName);
     467    19616514 :                 if (found) {
     468     6472814 :                         str_list_remove(found_must_contain, attr->lDAPDisplayName);
     469             :                 } else {
     470    13143700 :                         found = str_list_check(may_contain, attr->lDAPDisplayName);
     471             :                 }
     472    19616514 :                 if (!found) {
     473          31 :                         found = str_list_check(harmless_attrs, attr->lDAPDisplayName);
     474             :                 }
     475    19616514 :                 if (!found) {
     476             :                         /* we allow this for dbcheck to fix the rest of this broken entry */
     477          31 :                         if (!ldb_request_get_control(ac->req, DSDB_CONTROL_DBCHECK) || 
     478           0 :                             ac->req->operation == LDB_ADD) {
     479          31 :                                 ldb_asprintf_errstring(ldb, "objectclass_attrs: attribute '%s' on entry '%s' does not exist in the specified objectclasses!",
     480          31 :                                                        msg->elements[i].name,
     481             :                                                        ldb_dn_get_linearized(msg->dn));
     482          31 :                                 return LDB_ERR_OBJECT_CLASS_VIOLATION;
     483             :                         }
     484             :                 }
     485             :         }
     486             : 
     487             :         /*
     488             :          * We skip this check under dbcheck to allow fixing of other
     489             :          * attributes even if an attribute is missing.  This matters
     490             :          * for CN=RID Set as the required attribute rIDNextRid is not
     491             :          * replicated.
     492             :          */
     493      964071 :         if (found_must_contain[0] != NULL &&
     494       35291 :             ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE") == 0) {
     495             : 
     496          62 :                 for (i = 0; found_must_contain[i] != NULL; i++) {
     497          42 :                         const struct dsdb_attribute *broken_attr = dsdb_attribute_by_lDAPDisplayName(ac->schema,
     498          42 :                                                                                                      found_must_contain[i]);
     499             : 
     500          42 :                         bool replicated = (broken_attr->systemFlags &
     501             :                                            (DS_FLAG_ATTR_NOT_REPLICATED | DS_FLAG_ATTR_IS_CONSTRUCTED)) == 0;
     502             : 
     503          42 :                         if (replicated) {
     504           3 :                                 ldb_asprintf_errstring(ldb, "objectclass_attrs: at least one mandatory "
     505             :                                                        "attribute ('%s') on entry '%s' wasn't specified!",
     506           3 :                                                        found_must_contain[i],
     507             :                                                        ldb_dn_get_linearized(msg->dn));
     508           3 :                                 return LDB_ERR_OBJECT_CLASS_VIOLATION;
     509             :                         }
     510             :                 }
     511             :         }
     512             : 
     513      928777 :         if (isSchemaAttr) {
     514             :                 /*
     515             :                  * Before really adding an attribute in the database,
     516             :                  * let's check that we can translate it into a dsdb_attribute and
     517             :                  * that we can find a valid syntax object.
     518             :                  * If not it's better to reject this attribute than not be able
     519             :                  * to start samba next time due to schema being unloadable.
     520             :                  */
     521      221548 :                 struct dsdb_attribute *att = talloc(ac, struct dsdb_attribute);
     522       36920 :                 const struct dsdb_syntax *attrSyntax;
     523       36920 :                 WERROR status;
     524             : 
     525      221548 :                 status = dsdb_attribute_from_ldb(NULL, msg, att);
     526      221548 :                 if (!W_ERROR_IS_OK(status)) {
     527           0 :                         ldb_set_errstring(ldb,
     528             :                                                 "objectclass: failed to translate the schemaAttribute to a dsdb_attribute");
     529           0 :                         return LDB_ERR_UNWILLING_TO_PERFORM;
     530             :                 }
     531             : 
     532      221548 :                 attrSyntax = dsdb_syntax_for_attribute(att);
     533      221548 :                 if (!attrSyntax) {
     534           0 :                         ldb_set_errstring(ldb,
     535             :                                                 "objectclass: unknown attribute syntax");
     536           0 :                         return LDB_ERR_UNWILLING_TO_PERFORM;
     537             :                 }
     538             :         }
     539      928777 :         return ldb_module_done(ac->req, ac->mod_ares->controls,
     540      928777 :                                ac->mod_ares->response, LDB_SUCCESS);
     541             : }
     542             : 
     543     1857754 : static int get_search_callback(struct ldb_request *req, struct ldb_reply *ares)
     544             : {
     545      213572 :         struct ldb_context *ldb;
     546      213572 :         struct oc_context *ac;
     547      213572 :         int ret;
     548             : 
     549     1857754 :         ac = talloc_get_type(req->context, struct oc_context);
     550     1857754 :         ldb = ldb_module_get_ctx(ac->module);
     551             : 
     552     1857754 :         if (!ares) {
     553           0 :                 return ldb_module_done(ac->req, NULL, NULL,
     554             :                                        LDB_ERR_OPERATIONS_ERROR);
     555             :         }
     556     1857754 :         if (ares->error != LDB_SUCCESS) {
     557           0 :                 return ldb_module_done(ac->req, ares->controls,
     558             :                                        ares->response, ares->error);
     559             :         }
     560             : 
     561     1857754 :         ldb_reset_err_string(ldb);
     562             : 
     563     1857754 :         switch (ares->type) {
     564      928877 :         case LDB_REPLY_ENTRY:
     565      928877 :                 if (ac->search_res != NULL) {
     566           0 :                         ldb_set_errstring(ldb, "Too many results");
     567           0 :                         talloc_free(ares);
     568           0 :                         return ldb_module_done(ac->req, NULL, NULL,
     569             :                                                LDB_ERR_OPERATIONS_ERROR);
     570             :                 }
     571             : 
     572      928877 :                 ac->search_res = talloc_steal(ac, ares);
     573      928877 :                 break;
     574             : 
     575           0 :         case LDB_REPLY_REFERRAL:
     576             :                 /* ignore */
     577           0 :                 talloc_free(ares);
     578           0 :                 break;
     579             : 
     580      928877 :         case LDB_REPLY_DONE:
     581      928877 :                 talloc_free(ares);
     582      928877 :                 ret = attr_handler2(ac);
     583      928877 :                 if (ret != LDB_SUCCESS) {
     584         100 :                         return ldb_module_done(ac->req, NULL, NULL, ret);
     585             :                 }
     586      821991 :                 break;
     587             :         }
     588             : 
     589     1644082 :         return LDB_SUCCESS;
     590             : }
     591             : 
     592      929703 : static int oc_op_callback(struct ldb_request *req, struct ldb_reply *ares)
     593             : {
     594      106787 :         struct oc_context *ac;
     595      106787 :         struct ldb_context *ldb;
     596      106787 :         struct ldb_request *search_req;
     597      106787 :         struct ldb_dn *base_dn;
     598      106787 :         int ret;
     599      106787 :         static const char *attrs[] = {"nTSecurityDescriptor", "*", NULL};
     600             : 
     601      929703 :         ac = talloc_get_type(req->context, struct oc_context);
     602      929703 :         ldb = ldb_module_get_ctx(ac->module);
     603             : 
     604      929703 :         if (!ares) {
     605           0 :                 return ldb_module_done(ac->req, NULL, NULL,
     606             :                                        LDB_ERR_OPERATIONS_ERROR);
     607             :         }
     608             : 
     609      929703 :         if (ares->type == LDB_REPLY_REFERRAL) {
     610          10 :                 return ldb_module_send_referral(ac->req, ares->referral);
     611             :         }
     612             : 
     613      929693 :         if (ares->error != LDB_SUCCESS) {
     614         816 :                 return ldb_module_done(ac->req, ares->controls, ares->response,
     615             :                                        ares->error);
     616             :         }
     617             : 
     618      928877 :         if (ares->type != LDB_REPLY_DONE) {
     619           0 :                 talloc_free(ares);
     620           0 :                 return ldb_module_done(ac->req, NULL, NULL,
     621             :                                        LDB_ERR_OPERATIONS_ERROR);
     622             :         }
     623             : 
     624      928877 :         ac->search_res = NULL;
     625      928877 :         ac->mod_ares = talloc_steal(ac, ares);
     626             : 
     627             :         /* This looks up all attributes of our just added/modified entry */
     628     2399513 :         base_dn = ac->req->operation == LDB_ADD ? ac->req->op.add.message->dn
     629      928877 :                 : ac->req->op.mod.message->dn;
     630      928877 :         ret = ldb_build_search_req(&search_req, ldb, ac, base_dn,
     631             :                                    LDB_SCOPE_BASE, "(objectClass=*)",
     632             :                                    attrs, NULL, ac,
     633             :                                    get_search_callback, ac->req);
     634      928877 :         LDB_REQ_SET_LOCATION(search_req);
     635      928877 :         if (ret != LDB_SUCCESS) {
     636           0 :                 return ldb_module_done(ac->req, NULL, NULL, ret);
     637             :         }
     638             : 
     639      928877 :         ret = ldb_request_add_control(search_req, LDB_CONTROL_SHOW_RECYCLED_OID,
     640             :                                       true, NULL);
     641      928877 :         if (ret != LDB_SUCCESS) {
     642           0 :                 return ldb_module_done(ac->req, NULL, NULL, ret);
     643             :         }
     644             : 
     645             :         /*
     646             :          * This ensures we see if there was a DN, that pointed at an
     647             :          * object that is now deleted, that we still consider the
     648             :          * schema check to have passed
     649             :          */
     650      928877 :         ret = ldb_request_add_control(search_req, LDB_CONTROL_REVEAL_INTERNALS,
     651             :                                       false, NULL);
     652      928877 :         if (ret != LDB_SUCCESS) {
     653           0 :                 return ldb_module_done(ac->req, NULL, NULL, ret);
     654             :         }
     655             : 
     656      928877 :         ret = ldb_next_request(ac->module, search_req);
     657      928877 :         if (ret != LDB_SUCCESS) {
     658           0 :                 return ldb_module_done(ac->req, NULL, NULL, ret);
     659             :         }
     660             : 
     661             :         /* "ldb_module_done" isn't called here since we need to do additional
     662             :          * checks. It is called at the end of "attr_handler2". */
     663      822091 :         return LDB_SUCCESS;
     664             : }
     665             : 
     666      542618 : static int objectclass_attrs_add(struct ldb_module *module,
     667             :                                  struct ldb_request *req)
     668             : {
     669       83657 :         struct ldb_context *ldb;
     670       83657 :         struct oc_context *ac;
     671             : 
     672      542618 :         ldb = ldb_module_get_ctx(module);
     673             : 
     674      542618 :         ldb_debug(ldb, LDB_DEBUG_TRACE, "objectclass_attrs_add\n");
     675             : 
     676             :         /* do not manipulate our control entries */
     677      542618 :         if (ldb_dn_is_special(req->op.add.message->dn)) {
     678         538 :                 return ldb_next_request(module, req);
     679             :         }
     680             : 
     681      542080 :         ac = oc_init_context(module, req);
     682      542080 :         if (ac == NULL) {
     683           0 :                 return ldb_operr(ldb);
     684             :         }
     685             : 
     686             :         /* without schema, there isn't much to do here */
     687      542080 :         if (ac->schema == NULL) {
     688           0 :                 talloc_free(ac);
     689           0 :                 return ldb_next_request(module, req);
     690             :         }
     691             : 
     692      542080 :         return attr_handler(ac);
     693             : }
     694             : 
     695      658296 : static int objectclass_attrs_modify(struct ldb_module *module,
     696             :                                     struct ldb_request *req)
     697             : {
     698       27627 :         struct ldb_context *ldb;
     699       27627 :         struct ldb_control *sd_propagation_control;
     700       27627 :         int ret;
     701             : 
     702       27627 :         struct oc_context *ac;
     703             : 
     704      658296 :         ldb = ldb_module_get_ctx(module);
     705             : 
     706      658296 :         ldb_debug(ldb, LDB_DEBUG_TRACE, "objectclass_attrs_modify\n");
     707             : 
     708             :         /* do not manipulate our control entries */
     709      658296 :         if (ldb_dn_is_special(req->op.mod.message->dn)) {
     710         715 :                 return ldb_next_request(module, req);
     711             :         }
     712             : 
     713      657581 :         sd_propagation_control = ldb_request_get_control(req,
     714             :                                         DSDB_CONTROL_SEC_DESC_PROPAGATION_OID);
     715      657581 :         if (sd_propagation_control != NULL) {
     716      269862 :                 if (req->op.mod.message->num_elements != 1) {
     717           0 :                         return ldb_module_operr(module);
     718             :                 }
     719      269862 :                 ret = strcmp(req->op.mod.message->elements[0].name,
     720             :                              "nTSecurityDescriptor");
     721      269862 :                 if (ret != 0) {
     722           0 :                         return ldb_module_operr(module);
     723             :                 }
     724             : 
     725      269862 :                 return ldb_next_request(module, req);
     726             :         }
     727             : 
     728      387719 :         ac = oc_init_context(module, req);
     729      387719 :         if (ac == NULL) {
     730           0 :                 return ldb_operr(ldb);
     731             :         }
     732             : 
     733             :         /* without schema, there isn't much to do here */
     734      387719 :         if (ac->schema == NULL) {
     735           0 :                 talloc_free(ac);
     736           0 :                 return ldb_next_request(module, req);
     737             :         }
     738             : 
     739      387719 :         return attr_handler(ac);
     740             : }
     741             : 
     742             : static const struct ldb_module_ops ldb_objectclass_attrs_module_ops = {
     743             :         .name              = "objectclass_attrs",
     744             :         .add               = objectclass_attrs_add,
     745             :         .modify            = objectclass_attrs_modify
     746             : };
     747             : 
     748        5908 : int ldb_objectclass_attrs_module_init(const char *version)
     749             : {
     750        5908 :         LDB_MODULE_CHECK_VERSION(version);
     751        5908 :         return ldb_register_module(&ldb_objectclass_attrs_module_ops);
     752             : }

Generated by: LCOV version 1.14