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 abartlet/fix-coverage dd10fb34 Lines: 261 303 86.1 %
Date: 2021-09-23 10:06:22 Functions: 11 12 91.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) 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      823050 : static struct oc_context *oc_init_context(struct ldb_module *module,
      56             :                                           struct ldb_request *req)
      57             : {
      58             :         struct ldb_context *ldb;
      59             :         struct oc_context *ac;
      60             : 
      61      823050 :         ldb = ldb_module_get_ctx(module);
      62             : 
      63      823050 :         ac = talloc_zero(req, struct oc_context);
      64      823050 :         if (ac == NULL) {
      65           0 :                 ldb_oom(ldb);
      66           0 :                 return NULL;
      67             :         }
      68             : 
      69      823050 :         ac->module = module;
      70      823050 :         ac->req = req;
      71      823050 :         ac->schema = dsdb_get_schema(ldb, ac);
      72             : 
      73      823050 :         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        5660 : static int oc_validate_dsheuristics(struct ldb_message_element *el)
      83             : {
      84        5660 :         if (el->num_values > 0) {
      85        2895 :                 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        2896 :                 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        2897 :                 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        2898 :                 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        2899 :                 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        2900 :                 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        2901 :                 if ((el->values[0].length >= DS_HR_THIRTIETH_CHAR) &&
     110          16 :                     (el->values[0].data[DS_HR_THIRTIETH_CHAR-1] != '3')) {
     111           1 :                         return LDB_ERR_CONSTRAINT_VIOLATION;
     112             :                 }
     113        2902 :                 if ((el->values[0].length >= DS_HR_TWENTIETH_CHAR) &&
     114          18 :                     (el->values[0].data[DS_HR_TWENTIETH_CHAR-1] != '2')) {
     115           1 :                         return LDB_ERR_CONSTRAINT_VIOLATION;
     116             :                 }
     117        2904 :                 if ((el->values[0].length >= DS_HR_TENTH_CHAR) &&
     118          21 :                     (el->values[0].data[DS_HR_TENTH_CHAR-1] != '1')) {
     119           1 :                         return LDB_ERR_CONSTRAINT_VIOLATION;
     120             :                 }
     121             :         }
     122             : 
     123        5651 :         return LDB_SUCCESS;
     124             : }
     125             : 
     126             : /*
     127             :   auto normalise values on input
     128             :  */
     129     2565422 : 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             :         int i;
     133     2188927 :         bool values_copied = false;
     134             : 
     135     4753762 :         for (i=0; i<el->num_values; i++) {
     136             :                 struct ldb_val v;
     137             :                 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     4554342 :                 ret = attr->ldb_schema_attribute->syntax->canonicalise_fn(ldb,
     145     2564844 :                                                                           msg->elements,
     146     2564844 :                                                                           &el->values[i],
     147             :                                                                           &v);
     148     2564844 :                 if (ret != LDB_SUCCESS) {
     149          15 :                         return ret;
     150             :                 }
     151     2564835 :                 if (data_blob_cmp(&v, &el->values[i]) == 0) {
     152             :                         /* no need to replace it */
     153     2564658 :                         talloc_free(v.data);
     154     2564658 :                         continue;
     155             :                 }
     156             : 
     157             :                 /* we need to copy the values array on the first change */
     158         177 :                 if (!values_copied) {
     159             :                         struct ldb_val *v2;
     160         177 :                         v2 = talloc_array(msg->elements, struct ldb_val, el->num_values);
     161         177 :                         if (v2 == NULL) {
     162           0 :                                 return ldb_oom(ldb);
     163             :                         }
     164         180 :                         memcpy(v2, el->values, sizeof(struct ldb_val) * el->num_values);
     165         177 :                         el->values = v2;
     166         177 :                         values_copied = true;
     167             :                 }
     168             : 
     169         177 :                 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         177 :                 talloc_steal(el->values, v.data);
     176             :         }
     177     2188918 :         return LDB_SUCCESS;
     178             : }
     179             : 
     180      823050 : static int attr_handler(struct oc_context *ac)
     181             : {
     182             :         struct ldb_context *ldb;
     183             :         struct ldb_message *msg;
     184             :         struct ldb_request *child_req;
     185             :         const struct dsdb_attribute *attr;
     186             :         unsigned int i;
     187             :         int ret;
     188             :         WERROR werr;
     189             :         struct dsdb_syntax_ctx syntax_ctx;
     190             : 
     191      823050 :         ldb = ldb_module_get_ctx(ac->module);
     192             : 
     193      823050 :         if (ac->req->operation == LDB_ADD) {
     194      489328 :                 msg = ldb_msg_copy_shallow(ac, ac->req->op.add.message);
     195             :         } else {
     196      333722 :                 msg = ldb_msg_copy_shallow(ac, ac->req->op.mod.message);
     197             :         }
     198      823050 :         if (msg == NULL) {
     199           0 :                 return ldb_oom(ldb);
     200             :         }
     201      823050 :         ac->msg = msg;
     202             : 
     203             :         /* initialize syntax checking context */
     204      823050 :         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     8084135 :         for (i = 0; i < msg->num_elements; i++) {
     210     7261182 :                 attr = dsdb_attribute_by_lDAPDisplayName(ac->schema,
     211     7261182 :                                                          msg->elements[i].name);
     212     7261182 :                 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           4 :                         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     7261199 :                 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           8 :                         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     7261176 :                 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      204846 :                         if (!ldb_request_get_control(ac->req, LDB_CONTROL_RELAX_OID) &&
     250      168041 :                             !ldb_request_get_control(ac->req, DSDB_CONTROL_DBCHECK) &&
     251      120331 :                             !ldb_request_get_control(ac->req, DSDB_CONTROL_RESTORE_TOMBSTONE_OID) &&
     252      119670 :                             ldb_attr_cmp(attr->lDAPDisplayName, "objectClass") != 0 &&
     253      119538 :                             ldb_attr_cmp(attr->lDAPDisplayName, "name") != 0 &&
     254      119534 :                             ldb_attr_cmp(attr->lDAPDisplayName, "distinguishedName") != 0 &&
     255      119529 :                             ldb_attr_cmp(attr->lDAPDisplayName, "msDS-AdditionalDnsHostName") != 0 &&
     256       66048 :                             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       66048 :                                 if (ldb_dn_compare_base(ldb_get_schema_basedn(ldb), msg->dn) != 0) {
     262       61827 :                                         struct ldb_control *as_system = ldb_request_get_control(ac->req,
     263             :                                                                                                 LDB_CONTROL_AS_SYSTEM_OID);
     264       61827 :                                         if (!dsdb_module_am_system(ac->module) && !as_system) {
     265           9 :                                                 ldb_asprintf_errstring(ldb,
     266             :                                                                        "objectclass_attrs: attribute '%s' on entry '%s' must 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     7261171 :                 if (!(msg->elements[i].flags & LDB_FLAG_INTERNAL_DISABLE_VALIDATION)) {
     276    11831194 :                         werr = attr->syntax->validate_ldb(&syntax_ctx, attr,
     277     6168220 :                                                           &msg->elements[i]);
     278     7261197 :                         if (!W_ERROR_IS_OK(werr) &&
     279          67 :                             !ldb_request_get_control(ac->req, DSDB_CONTROL_DBCHECK)) {
     280         130 :                                 ldb_asprintf_errstring(ldb, "objectclass_attrs: attribute '%s' on entry '%s' contains at least one invalid value!",
     281          65 :                                                        msg->elements[i].name,
     282             :                                                        ldb_dn_get_linearized(msg->dn));
     283          65 :                                 return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
     284             :                         }
     285             :                 }
     286             : 
     287     7261106 :                 if ((attr->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED) != 0) {
     288           5 :                         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     7261103 :                 if (ldb_attr_cmp(attr->lDAPDisplayName, "dSHeuristics") == 0) {
     300        5660 :                         ret = oc_validate_dsheuristics(&(msg->elements[i]));
     301        5660 :                         if (ret != LDB_SUCCESS) {
     302           9 :                                 return ret;
     303             :                         }
     304             :                 }
     305             : 
     306             :                 /* auto normalise some attribute values */
     307     7261094 :                 if (attr->syntax->auto_normalise) {
     308     2565422 :                         ret = oc_auto_normalise(ldb, attr, msg, &msg->elements[i]);
     309     2565422 :                         if (ret != LDB_SUCCESS) {
     310           9 :                                 return ret;
     311             :                         }
     312             :                 }
     313             : 
     314             :                 /* Substitute the attribute name to match in case */
     315     7261085 :                 msg->elements[i].name = attr->lDAPDisplayName;
     316             :         }
     317             : 
     318      822953 : no_attribute:
     319      822953 :         if (ac->req->operation == LDB_ADD) {
     320      792631 :                 ret = ldb_build_add_req(&child_req, ldb, ac,
     321      412712 :                                         msg, ac->req->controls,
     322             :                                         ac, oc_op_callback, ac->req);
     323      489280 :                 LDB_REQ_SET_LOCATION(child_req);
     324             :         } else {
     325      596214 :                 ret = ldb_build_mod_req(&child_req, ldb, ac,
     326      311917 :                                         msg, ac->req->controls,
     327             :                                         ac, oc_op_callback, ac->req);
     328      333673 :                 LDB_REQ_SET_LOCATION(child_req);
     329             :         }
     330      822953 :         if (ret != LDB_SUCCESS) {
     331           0 :                 return ret;
     332             :         }
     333             : 
     334      822953 :         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      822474 : static int attr_handler2(struct oc_context *ac)
     344             : {
     345             :         struct ldb_context *ldb;
     346             :         struct ldb_message_element *oc_element;
     347             :         struct ldb_message *msg;
     348             :         const char **must_contain, **may_contain, **found_must_contain;
     349             :         /* There exists a hardcoded delete-protected attributes list in AD */
     350      822474 :         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             :         const struct dsdb_attribute *attr;
     357             :         unsigned int i;
     358             :         bool found;
     359      822474 :         bool isSchemaAttr = false;
     360             : 
     361      822474 :         ldb = ldb_module_get_ctx(ac->module);
     362             : 
     363      822474 :         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      822474 :         oc_element = ldb_msg_find_element(ac->search_res->message,
     370             :                                           "objectClass");
     371      822474 :         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     2588790 :         for (i = 0; i < oc_element->num_values; i++) {
     391     1864642 :                 char * attname = (char *)oc_element->values[i].data;
     392     1864642 :                 if (ldb_req_is_untrusted(ac->req)) {
     393      563743 :                         if (strcmp(attname, "secret") == 0 ||
     394      330597 :                             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     1864640 :                 if (strcmp(attname, "attributeSchema") == 0) {
     401      211110 :                         isSchemaAttr = true;
     402             :                 }
     403             :         }
     404             : 
     405      822472 :         must_contain = dsdb_full_attribute_list(ac, ac->schema, oc_element,
     406             :                                                 DSDB_SCHEMA_ALL_MUST);
     407      822472 :         may_contain =  dsdb_full_attribute_list(ac, ac->schema, oc_element,
     408             :                                                 DSDB_SCHEMA_ALL_MAY);
     409      822472 :         found_must_contain = const_str_list(str_list_copy(ac, must_contain));
     410      822472 :         if ((must_contain == NULL) || (may_contain == NULL)
     411      822472 :             || (found_must_contain == NULL)) {
     412           0 :                 return ldb_operr(ldb);
     413             :         }
     414             : 
     415             :         /* Check the delete-protected attributes list */
     416      822472 :         msg = ac->search_res->message;
     417    13981390 :         for (l = del_prot_attributes; *l != NULL; l++) {
     418             :                 struct ldb_message_element *el;
     419             : 
     420    13158994 :                 el = ldb_msg_find_element(ac->msg, *l);
     421    13158994 :                 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    12255284 :                         continue;
     427             :                 }
     428             : 
     429      903710 :                 found = str_list_check_ci(must_contain, *l);
     430      903710 :                 if (!found) {
     431      323410 :                         found = str_list_check_ci(may_contain, *l);
     432             :                 }
     433      903710 :                 if (found && (ldb_msg_find_element(msg, *l) == NULL)) {
     434          76 :                         ldb_asprintf_errstring(ldb, "objectclass_attrs: delete protected attribute '%s' on entry '%s' missing!",
     435             :                                                *l,
     436             :                                                ldb_dn_get_linearized(msg->dn));
     437          76 :                         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    18161812 :         for (i = 0; i < msg->num_elements; i++) {
     444    17437797 :                 attr = dsdb_attribute_by_lDAPDisplayName(ac->schema,
     445    17437797 :                                                          msg->elements[i].name);
     446    17437797 :                 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             :                 /* We can use "str_list_check" with "strcmp" here since the
     456             :                  * attribute information from the schema are always equal
     457             :                  * up-down-cased. */
     458    17437797 :                 found = str_list_check(must_contain, attr->lDAPDisplayName);
     459    17437797 :                 if (found) {
     460     5767884 :                         str_list_remove(found_must_contain, attr->lDAPDisplayName);
     461             :                 } else {
     462    11669913 :                         found = str_list_check(may_contain, attr->lDAPDisplayName);
     463             :                 }
     464    17437797 :                 if (!found) {
     465          57 :                         found = str_list_check(harmless_attrs, attr->lDAPDisplayName);
     466             :                 }
     467    17437797 :                 if (!found) {
     468             :                         /* we allow this for dbcheck to fix the rest of this broken entry */
     469          57 :                         if (!ldb_request_get_control(ac->req, DSDB_CONTROL_DBCHECK) || 
     470           0 :                             ac->req->operation == LDB_ADD) {
     471         114 :                                 ldb_asprintf_errstring(ldb, "objectclass_attrs: attribute '%s' on entry '%s' does not exist in the specified objectclasses!",
     472          57 :                                                        msg->elements[i].name,
     473             :                                                        ldb_dn_get_linearized(msg->dn));
     474          57 :                                 return LDB_ERR_OBJECT_CLASS_VIOLATION;
     475             :                         }
     476             :                 }
     477             :         }
     478             : 
     479             :         /*
     480             :          * We skip this check under dbcheck to allow fixing of other
     481             :          * attributes even if an attribute is missing.  This matters
     482             :          * for CN=RID Set as the required attribute rIDNextRid is not
     483             :          * replicated.
     484             :          */
     485      857366 :         if (found_must_contain[0] != NULL &&
     486       35027 :             ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE") == 0) {
     487             : 
     488          64 :                 for (i = 0; found_must_contain[i] != NULL; i++) {
     489          44 :                         const struct dsdb_attribute *broken_attr = dsdb_attribute_by_lDAPDisplayName(ac->schema,
     490          44 :                                                                                                      found_must_contain[i]);
     491             : 
     492          44 :                         bool replicated = (broken_attr->systemFlags &
     493             :                                            (DS_FLAG_ATTR_NOT_REPLICATED | DS_FLAG_ATTR_IS_CONSTRUCTED)) == 0;
     494             : 
     495          44 :                         if (replicated) {
     496          10 :                                 ldb_asprintf_errstring(ldb, "objectclass_attrs: at least one mandatory "
     497             :                                                        "attribute ('%s') on entry '%s' wasn't specified!",
     498           5 :                                                        found_must_contain[i],
     499             :                                                        ldb_dn_get_linearized(msg->dn));
     500           5 :                                 return LDB_ERR_OBJECT_CLASS_VIOLATION;
     501             :                         }
     502             :                 }
     503             :         }
     504             : 
     505      822334 :         if (isSchemaAttr) {
     506             :                 /*
     507             :                  * Before really adding an attribute in the database,
     508             :                  * let's check that we can translate it into a dsdb_attribute and
     509             :                  * that we can find a valid syntax object.
     510             :                  * If not it's better to reject this attribute than not be able
     511             :                  * to start samba next time due to schema being unloadable.
     512             :                  */
     513      211086 :                 struct dsdb_attribute *att = talloc(ac, struct dsdb_attribute);
     514             :                 const struct dsdb_syntax *attrSyntax;
     515             :                 WERROR status;
     516             : 
     517      211086 :                 status = dsdb_attribute_from_ldb(NULL, msg, att);
     518      211086 :                 if (!W_ERROR_IS_OK(status)) {
     519           0 :                         ldb_set_errstring(ldb,
     520             :                                                 "objectclass: failed to translate the schemaAttribute to a dsdb_attribute");
     521           0 :                         return LDB_ERR_UNWILLING_TO_PERFORM;
     522             :                 }
     523             : 
     524      211086 :                 attrSyntax = dsdb_syntax_for_attribute(att);
     525      211086 :                 if (!attrSyntax) {
     526           0 :                         ldb_set_errstring(ldb,
     527             :                                                 "objectclass: unknown attribute syntax");
     528           0 :                         return LDB_ERR_UNWILLING_TO_PERFORM;
     529             :                 }
     530             :         }
     531      822334 :         return ldb_module_done(ac->req, ac->mod_ares->controls,
     532      822334 :                                ac->mod_ares->response, LDB_SUCCESS);
     533             : }
     534             : 
     535     1644948 : static int get_search_callback(struct ldb_request *req, struct ldb_reply *ares)
     536             : {
     537             :         struct ldb_context *ldb;
     538             :         struct oc_context *ac;
     539             :         int ret;
     540             : 
     541     1644948 :         ac = talloc_get_type(req->context, struct oc_context);
     542     1644948 :         ldb = ldb_module_get_ctx(ac->module);
     543             : 
     544     1644948 :         if (!ares) {
     545           0 :                 return ldb_module_done(ac->req, NULL, NULL,
     546             :                                        LDB_ERR_OPERATIONS_ERROR);
     547             :         }
     548     1644948 :         if (ares->error != LDB_SUCCESS) {
     549           0 :                 return ldb_module_done(ac->req, ares->controls,
     550             :                                        ares->response, ares->error);
     551             :         }
     552             : 
     553     1644948 :         ldb_reset_err_string(ldb);
     554             : 
     555     1644948 :         switch (ares->type) {
     556      822474 :         case LDB_REPLY_ENTRY:
     557      822474 :                 if (ac->search_res != NULL) {
     558           0 :                         ldb_set_errstring(ldb, "Too many results");
     559           0 :                         talloc_free(ares);
     560           0 :                         return ldb_module_done(ac->req, NULL, NULL,
     561             :                                                LDB_ERR_OPERATIONS_ERROR);
     562             :                 }
     563             : 
     564      822474 :                 ac->search_res = talloc_steal(ac, ares);
     565      822474 :                 break;
     566             : 
     567           0 :         case LDB_REPLY_REFERRAL:
     568             :                 /* ignore */
     569           0 :                 talloc_free(ares);
     570           0 :                 break;
     571             : 
     572      822474 :         case LDB_REPLY_DONE:
     573      822474 :                 talloc_free(ares);
     574      822474 :                 ret = attr_handler2(ac);
     575      822474 :                 if (ret != LDB_SUCCESS) {
     576         140 :                         return ldb_module_done(ac->req, NULL, NULL, ret);
     577             :                 }
     578      724010 :                 break;
     579             :         }
     580             : 
     581     1448160 :         return LDB_SUCCESS;
     582             : }
     583             : 
     584      822961 : static int oc_op_callback(struct ldb_request *req, struct ldb_reply *ares)
     585             : {
     586             :         struct oc_context *ac;
     587             :         struct ldb_context *ldb;
     588             :         struct ldb_request *search_req;
     589             :         struct ldb_dn *base_dn;
     590             :         int ret;
     591             :         static const char *attrs[] = {"nTSecurityDescriptor", "*", NULL};
     592             : 
     593      822961 :         ac = talloc_get_type(req->context, struct oc_context);
     594      822961 :         ldb = ldb_module_get_ctx(ac->module);
     595             : 
     596      822961 :         if (!ares) {
     597           0 :                 return ldb_module_done(ac->req, NULL, NULL,
     598             :                                        LDB_ERR_OPERATIONS_ERROR);
     599             :         }
     600             : 
     601      822961 :         if (ares->type == LDB_REPLY_REFERRAL) {
     602          10 :                 return ldb_module_send_referral(ac->req, ares->referral);
     603             :         }
     604             : 
     605      822951 :         if (ares->error != LDB_SUCCESS) {
     606         477 :                 return ldb_module_done(ac->req, ares->controls, ares->response,
     607             :                                        ares->error);
     608             :         }
     609             : 
     610      822474 :         if (ares->type != LDB_REPLY_DONE) {
     611           0 :                 talloc_free(ares);
     612           0 :                 return ldb_module_done(ac->req, NULL, NULL,
     613             :                                        LDB_ERR_OPERATIONS_ERROR);
     614             :         }
     615             : 
     616      822474 :         ac->search_res = NULL;
     617      822474 :         ac->mod_ares = talloc_steal(ac, ares);
     618             : 
     619             :         /* This looks up all attributes of our just added/modified entry */
     620     2134044 :         base_dn = ac->req->operation == LDB_ADD ? ac->req->op.add.message->dn
     621     1202225 :                 : ac->req->op.mod.message->dn;
     622      822474 :         ret = ldb_build_search_req(&search_req, ldb, ac, base_dn,
     623             :                                    LDB_SCOPE_BASE, "(objectClass=*)",
     624             :                                    attrs, NULL, ac,
     625             :                                    get_search_callback, ac->req);
     626      822474 :         LDB_REQ_SET_LOCATION(search_req);
     627      822474 :         if (ret != LDB_SUCCESS) {
     628           0 :                 return ldb_module_done(ac->req, NULL, NULL, ret);
     629             :         }
     630             : 
     631      822474 :         ret = ldb_request_add_control(search_req, LDB_CONTROL_SHOW_RECYCLED_OID,
     632             :                                       true, NULL);
     633      822474 :         if (ret != LDB_SUCCESS) {
     634           0 :                 return ldb_module_done(ac->req, NULL, NULL, ret);
     635             :         }
     636             : 
     637             :         /*
     638             :          * This ensures we see if there was a DN, that pointed at an
     639             :          * object that is now deleted, that we still consider the
     640             :          * schema check to have passed
     641             :          */
     642      822474 :         ret = ldb_request_add_control(search_req, LDB_CONTROL_REVEAL_INTERNALS,
     643             :                                       false, NULL);
     644      822474 :         if (ret != LDB_SUCCESS) {
     645           0 :                 return ldb_module_done(ac->req, NULL, NULL, ret);
     646             :         }
     647             : 
     648      822474 :         ret = ldb_next_request(ac->module, search_req);
     649      822474 :         if (ret != LDB_SUCCESS) {
     650           0 :                 return ldb_module_done(ac->req, NULL, NULL, ret);
     651             :         }
     652             : 
     653             :         /* "ldb_module_done" isn't called here since we need to do additional
     654             :          * checks. It is called at the end of "attr_handler2". */
     655      724150 :         return LDB_SUCCESS;
     656             : }
     657             : 
     658      489836 : static int objectclass_attrs_add(struct ldb_module *module,
     659             :                                  struct ldb_request *req)
     660             : {
     661             :         struct ldb_context *ldb;
     662             :         struct oc_context *ac;
     663             : 
     664      489836 :         ldb = ldb_module_get_ctx(module);
     665             : 
     666      489836 :         ldb_debug(ldb, LDB_DEBUG_TRACE, "objectclass_attrs_add\n");
     667             : 
     668             :         /* do not manipulate our control entries */
     669      489836 :         if (ldb_dn_is_special(req->op.add.message->dn)) {
     670         508 :                 return ldb_next_request(module, req);
     671             :         }
     672             : 
     673      489328 :         ac = oc_init_context(module, req);
     674      489328 :         if (ac == NULL) {
     675           0 :                 return ldb_operr(ldb);
     676             :         }
     677             : 
     678             :         /* without schema, there isn't much to do here */
     679      489328 :         if (ac->schema == NULL) {
     680           0 :                 talloc_free(ac);
     681           0 :                 return ldb_next_request(module, req);
     682             :         }
     683             : 
     684      489328 :         return attr_handler(ac);
     685             : }
     686             : 
     687      455530 : static int objectclass_attrs_modify(struct ldb_module *module,
     688             :                                     struct ldb_request *req)
     689             : {
     690             :         struct ldb_context *ldb;
     691             :         struct ldb_control *sd_propagation_control;
     692             :         int ret;
     693             : 
     694             :         struct oc_context *ac;
     695             : 
     696      455530 :         ldb = ldb_module_get_ctx(module);
     697             : 
     698      455530 :         ldb_debug(ldb, LDB_DEBUG_TRACE, "objectclass_attrs_modify\n");
     699             : 
     700             :         /* do not manipulate our control entries */
     701      455530 :         if (ldb_dn_is_special(req->op.mod.message->dn)) {
     702         657 :                 return ldb_next_request(module, req);
     703             :         }
     704             : 
     705      454873 :         sd_propagation_control = ldb_request_get_control(req,
     706             :                                         DSDB_CONTROL_SEC_DESC_PROPAGATION_OID);
     707      454873 :         if (sd_propagation_control != NULL) {
     708      121151 :                 if (req->op.mod.message->num_elements != 1) {
     709           0 :                         return ldb_module_operr(module);
     710             :                 }
     711      121151 :                 ret = strcmp(req->op.mod.message->elements[0].name,
     712             :                              "nTSecurityDescriptor");
     713      121151 :                 if (ret != 0) {
     714           0 :                         return ldb_module_operr(module);
     715             :                 }
     716             : 
     717      121151 :                 return ldb_next_request(module, req);
     718             :         }
     719             : 
     720      333722 :         ac = oc_init_context(module, req);
     721      333722 :         if (ac == NULL) {
     722           0 :                 return ldb_operr(ldb);
     723             :         }
     724             : 
     725             :         /* without schema, there isn't much to do here */
     726      333722 :         if (ac->schema == NULL) {
     727           0 :                 talloc_free(ac);
     728           0 :                 return ldb_next_request(module, req);
     729             :         }
     730             : 
     731      333722 :         return attr_handler(ac);
     732             : }
     733             : 
     734             : static const struct ldb_module_ops ldb_objectclass_attrs_module_ops = {
     735             :         .name              = "objectclass_attrs",
     736             :         .add               = objectclass_attrs_add,
     737             :         .modify            = objectclass_attrs_modify
     738             : };
     739             : 
     740        5536 : int ldb_objectclass_attrs_module_init(const char *version)
     741             : {
     742        5536 :         LDB_MODULE_CHECK_VERSION(version);
     743        5536 :         return ldb_register_module(&ldb_objectclass_attrs_module_ops);
     744             : }

Generated by: LCOV version 1.13