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 master 2f515e9b Lines: 301 343 87.8 %
Date: 2024-04-21 15:09:00 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      930133 : static struct oc_context *oc_init_context(struct ldb_module *module,
      56             :                                           struct ldb_request *req)
      57             : {
      58      106809 :         struct ldb_context *ldb;
      59      106809 :         struct oc_context *ac;
      60             : 
      61      930133 :         ldb = ldb_module_get_ctx(module);
      62             : 
      63      930133 :         ac = talloc_zero(req, struct oc_context);
      64      930133 :         if (ac == NULL) {
      65           0 :                 ldb_oom(ldb);
      66           0 :                 return NULL;
      67             :         }
      68             : 
      69      930133 :         ac->module = module;
      70      930133 :         ac->req = req;
      71      930133 :         ac->schema = dsdb_get_schema(ldb, ac);
      72             : 
      73      930133 :         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     2929721 : 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      407697 :         int i;
     133     2929721 :         bool values_copied = false;
     134             : 
     135     5858861 :         for (i=0; i<el->num_values; i++) {
     136      407697 :                 struct ldb_val v;
     137      407697 :                 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     3336846 :                 ret = attr->ldb_schema_attribute->syntax->canonicalise_fn(ldb,
     145     2929149 :                                                                           msg->elements,
     146     2929149 :                                                                           &el->values[i],
     147             :                                                                           &v);
     148     2929149 :                 if (ret != LDB_SUCCESS) {
     149           9 :                         return ret;
     150             :                 }
     151     2929140 :                 if (data_blob_cmp(&v, &el->values[i]) == 0) {
     152             :                         /* no need to replace it */
     153     2928956 :                         talloc_free(v.data);
     154     2928956 :                         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     2522015 :         return LDB_SUCCESS;
     178             : }
     179             : 
     180      930133 : static int attr_handler(struct oc_context *ac)
     181             : {
     182      106809 :         struct ldb_context *ldb;
     183      106809 :         struct ldb_message *msg;
     184      106809 :         struct ldb_request *child_req;
     185      106809 :         const struct dsdb_attribute *attr;
     186      106809 :         unsigned int i;
     187      106809 :         int ret;
     188      106809 :         WERROR werr;
     189      106809 :         struct dsdb_syntax_ctx syntax_ctx;
     190             : 
     191      930133 :         ldb = ldb_module_get_ctx(ac->module);
     192             : 
     193      930133 :         if (ac->req->operation == LDB_ADD) {
     194      542508 :                 msg = ldb_msg_copy_shallow(ac, ac->req->op.add.message);
     195             :         } else {
     196      387625 :                 msg = ldb_msg_copy_shallow(ac, ac->req->op.mod.message);
     197             :         }
     198      930133 :         if (msg == NULL) {
     199           0 :                 return ldb_oom(ldb);
     200             :         }
     201      930133 :         ac->msg = msg;
     202             : 
     203             :         /* initialize syntax checking context */
     204      930133 :         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     9135186 :         for (i = 0; i < msg->num_elements; i++) {
     210     9274594 :                 attr = dsdb_attribute_by_lDAPDisplayName(ac->schema,
     211     8098348 :                                                          msg->elements[i].name);
     212     8098348 :                 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     8098365 :                 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     8098342 :                 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      273691 :                         if (!ldb_request_get_control(ac->req, LDB_CONTROL_RELAX_OID) &&
     250      236719 :                             !ldb_request_get_control(ac->req, DSDB_CONTROL_DBCHECK) &&
     251      101049 :                             !ldb_request_get_control(ac->req, DSDB_CONTROL_RESTORE_TOMBSTONE_OID) &&
     252      100519 :                             ldb_attr_cmp(attr->lDAPDisplayName, "objectClass") != 0 &&
     253      100442 :                             ldb_attr_cmp(attr->lDAPDisplayName, "name") != 0 &&
     254      100441 :                             ldb_attr_cmp(attr->lDAPDisplayName, "distinguishedName") != 0 &&
     255      100438 :                             ldb_attr_cmp(attr->lDAPDisplayName, "msDS-AdditionalDnsHostName") != 0 &&
     256      100434 :                             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      100434 :                                 if (ldb_dn_compare_base(ldb_get_schema_basedn(ldb), msg->dn) != 0) {
     262       96047 :                                         if (!dsdb_have_system_access(
     263             :                                                     ac->module,
     264             :                                                     ac->req,
     265             :                                                     SYSTEM_CONTROL_KEEP_CRITICAL))
     266             :                                         {
     267           5 :                                                 ldb_asprintf_errstring(ldb,
     268             :                                                                        "objectclass_attrs: attribute '%s' on entry '%s' can only be modified as system",
     269           5 :                                                                        msg->elements[i].name,
     270             :                                                                        ldb_dn_get_linearized(msg->dn));
     271           5 :                                                 return LDB_ERR_CONSTRAINT_VIOLATION;
     272             :                                         }
     273             :                                 }
     274             :                         }
     275             :                 }
     276             : 
     277     8098337 :                 if (!(msg->elements[i].flags & LDB_FLAG_INTERNAL_DISABLE_VALIDATION)) {
     278     8098245 :                         werr = attr->syntax->validate_ldb(&syntax_ctx, attr,
     279     6921999 :                                                           &msg->elements[i]);
     280     8098319 :                         if (!W_ERROR_IS_OK(werr) &&
     281          74 :                             !ldb_request_get_control(ac->req, DSDB_CONTROL_DBCHECK)) {
     282          72 :                                 ldb_asprintf_errstring(ldb, "objectclass_attrs: attribute '%s' on entry '%s' contains at least one invalid value!",
     283          72 :                                                        msg->elements[i].name,
     284             :                                                        ldb_dn_get_linearized(msg->dn));
     285          72 :                                 return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
     286             :                         }
     287             :                 }
     288             : 
     289     8098265 :                 if ((attr->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED) != 0) {
     290           3 :                         ldb_asprintf_errstring(ldb, "objectclass_attrs: attribute '%s' on entry '%s' is constructed!",
     291           3 :                                                msg->elements[i].name,
     292             :                                                ldb_dn_get_linearized(msg->dn));
     293           3 :                         if (ac->req->operation == LDB_ADD) {
     294           3 :                                 return LDB_ERR_UNDEFINED_ATTRIBUTE_TYPE;
     295             :                         } else {
     296           0 :                                 return LDB_ERR_CONSTRAINT_VIOLATION;
     297             :                         }
     298             :                 }
     299             : 
     300             :                 /* "dSHeuristics" syntax check */
     301     8098262 :                 if (ldb_attr_cmp(attr->lDAPDisplayName, "dSHeuristics") == 0) {
     302       13422 :                         ret = oc_validate_dsheuristics(&(msg->elements[i]));
     303       13422 :                         if (ret != LDB_SUCCESS) {
     304           9 :                                 return ret;
     305             :                         }
     306             :                 }
     307             : 
     308             :                 /* auto normalise some attribute values */
     309     8098253 :                 if (attr->syntax->auto_normalise) {
     310     2929721 :                         ret = oc_auto_normalise(ldb, attr, msg, &msg->elements[i]);
     311     2929721 :                         if (ret != LDB_SUCCESS) {
     312           9 :                                 return ret;
     313             :                         }
     314             :                 }
     315             : 
     316             :                 /* Substitute the attribute name to match in case */
     317     8098244 :                 msg->elements[i].name = attr->lDAPDisplayName;
     318             :         }
     319             : 
     320      930029 : no_attribute:
     321      930029 :         if (ac->req->operation == LDB_ADD) {
     322      542460 :                 ret = ldb_build_add_req(&child_req, ldb, ac,
     323      458849 :                                         msg, ac->req->controls,
     324             :                                         ac, oc_op_callback, ac->req);
     325      542460 :                 LDB_REQ_SET_LOCATION(child_req);
     326             :         } else {
     327      387569 :                 ret = ldb_build_mod_req(&child_req, ldb, ac,
     328      364371 :                                         msg, ac->req->controls,
     329             :                                         ac, oc_op_callback, ac->req);
     330      387569 :                 LDB_REQ_SET_LOCATION(child_req);
     331             :         }
     332      930029 :         if (ret != LDB_SUCCESS) {
     333           0 :                 return ret;
     334             :         }
     335             : 
     336      930029 :         return ldb_next_request(ac->module, child_req);
     337             : }
     338             : 
     339             : /*
     340             :   these are attributes which are left over from old ways of doing
     341             :   things in ldb, and are harmless
     342             :  */
     343             : static const char *harmless_attrs[] = { "parentGUID", NULL };
     344             : 
     345      929211 : static int attr_handler2(struct oc_context *ac)
     346             : {
     347      106808 :         struct ldb_context *ldb;
     348      106808 :         struct ldb_message_element *oc_element;
     349      106808 :         struct ldb_message *msg;
     350      106808 :         const char **must_contain, **may_contain, **found_must_contain;
     351             :         /* There exists a hardcoded delete-protected attributes list in AD */
     352      929211 :         const char *del_prot_attributes[] = { "nTSecurityDescriptor",
     353             :                 "objectSid", "sAMAccountType", "sAMAccountName", "groupType",
     354             :                 "primaryGroupID", "userAccountControl", "accountExpires",
     355             :                 "badPasswordTime", "badPwdCount", "codePage", "countryCode",
     356             :                 "lastLogoff", "lastLogon", "logonCount", "pwdLastSet", NULL },
     357             :                 **l;
     358      106808 :         const struct dsdb_attribute *attr;
     359      106808 :         unsigned int i;
     360      106808 :         bool found;
     361      929211 :         bool isSchemaAttr = false;
     362             : 
     363      929211 :         ldb = ldb_module_get_ctx(ac->module);
     364             : 
     365      929211 :         if (ac->search_res == NULL) {
     366           0 :                 return ldb_operr(ldb);
     367             :         }
     368             : 
     369             :         /* We rely here on the preceding "objectclass" LDB module which did
     370             :          * already fix up the objectclass list (inheritance, order...). */
     371      929211 :         oc_element = ldb_msg_find_element(ac->search_res->message,
     372             :                                           "objectClass");
     373      929211 :         if (oc_element == NULL) {
     374           0 :                 return ldb_operr(ldb);
     375             :         }
     376             : 
     377             :         /* LSA-specific object classes are not allowed to be created over LDAP,
     378             :          * so we need to tell if this connection is internal (trusted) or not
     379             :          * (untrusted).
     380             :          *
     381             :          * Hongwei Sun from Microsoft explains:
     382             :          * The constraint in 3.1.1.5.2.2 MS-ADTS means that LSA objects cannot
     383             :          * be added or modified through the LDAP interface, instead they can
     384             :          * only be handled through LSA Policy API.  This is also explained in
     385             :          * 7.1.6.9.7 MS-ADTS as follows:
     386             :          * "Despite being replicated normally between peer DCs in a domain,
     387             :          * the process of creating or manipulating TDOs is specifically
     388             :          * restricted to the LSA Policy APIs, as detailed in [MS-LSAD] section
     389             :          * 3.1.1.5. Unlike other objects in the DS, TDOs may not be created or
     390             :          *  manipulated by client machines over the LDAPv3 transport."
     391             :          */
     392     3082903 :         for (i = 0; i < oc_element->num_values; i++) {
     393     2153694 :                 char * attname = (char *)oc_element->values[i].data;
     394     2153694 :                 if (ldb_req_is_untrusted(ac->req)) {
     395      501587 :                         if (strcmp(attname, "secret") == 0 ||
     396      501585 :                             strcmp(attname, "trustedDomain") == 0) {
     397           2 :                                 ldb_asprintf_errstring(ldb, "objectclass_attrs: LSA objectclasses (entry '%s') cannot be created or changed over LDAP!",
     398           2 :                                                        ldb_dn_get_linearized(ac->search_res->message->dn));
     399           2 :                                 return LDB_ERR_UNWILLING_TO_PERFORM;
     400             :                         }
     401             :                 }
     402     2153692 :                 if (strcmp(attname, "attributeSchema") == 0) {
     403      221548 :                         isSchemaAttr = true;
     404             :                 }
     405             :         }
     406             : 
     407      929209 :         must_contain = dsdb_full_attribute_list(ac, ac->schema, oc_element,
     408             :                                                 DSDB_SCHEMA_ALL_MUST);
     409      929209 :         may_contain =  dsdb_full_attribute_list(ac, ac->schema, oc_element,
     410             :                                                 DSDB_SCHEMA_ALL_MAY);
     411      929209 :         found_must_contain = const_str_list(str_list_copy(ac, must_contain));
     412      929209 :         if ((must_contain == NULL) || (may_contain == NULL)
     413      929209 :             || (found_must_contain == NULL)) {
     414           0 :                 return ldb_operr(ldb);
     415             :         }
     416             : 
     417             :         /* Check the delete-protected attributes list */
     418      929209 :         msg = ac->search_res->message;
     419    15796075 :         for (l = del_prot_attributes; *l != NULL; l++) {
     420     1708928 :                 struct ldb_message_element *el;
     421             : 
     422    14866930 :                 el = ldb_msg_find_element(ac->msg, *l);
     423    14866930 :                 if (el == NULL) {
     424             :                         /*
     425             :                          * It was not specified in the add or modify,
     426             :                          * so it doesn't need to be in the stored record
     427             :                          */
     428    13736928 :                         continue;
     429             :                 }
     430             : 
     431     1130002 :                 found = str_list_check_ci(must_contain, *l);
     432     1130002 :                 if (!found) {
     433      467855 :                         found = str_list_check_ci(may_contain, *l);
     434             :                 }
     435     1130002 :                 if (found && (ldb_msg_find_element(msg, *l) == NULL)) {
     436          64 :                         ldb_asprintf_errstring(ldb, "objectclass_attrs: delete protected attribute '%s' on entry '%s' missing!",
     437             :                                                *l,
     438             :                                                ldb_dn_get_linearized(msg->dn));
     439          64 :                         return LDB_ERR_UNWILLING_TO_PERFORM;
     440             :                 }
     441             :         }
     442             : 
     443             :         /* Check if all specified attributes are valid in the given
     444             :          * objectclasses and if they meet additional schema restrictions. */
     445    20656329 :         for (i = 0; i < msg->num_elements; i++) {
     446    21997761 :                 attr = dsdb_attribute_by_lDAPDisplayName(ac->schema,
     447    19727215 :                                                          msg->elements[i].name);
     448    19727215 :                 if (attr == NULL) {
     449           0 :                         if (ldb_request_get_control(ac->req, DSDB_CONTROL_DBCHECK)) {
     450             :                                 /* allow this to make it possible for dbcheck
     451             :                                    to remove bad attributes */
     452           0 :                                 continue;
     453             :                         }
     454           0 :                         return ldb_operr(ldb);
     455             :                 }
     456             : 
     457    19727215 :                 if (attr->linkID & 1) {
     458             :                         /*
     459             :                          * We need to allow backlinks on all objects
     460             :                          * even if the schema doesn't allow it.
     461             :                          */
     462       94779 :                         continue;
     463             :                 }
     464             : 
     465             :                 /* We can use "str_list_check" with "strcmp" here since the
     466             :                  * attribute information from the schema are always equal
     467             :                  * up-down-cased. */
     468    19632436 :                 found = str_list_check(must_contain, attr->lDAPDisplayName);
     469    19632436 :                 if (found) {
     470     6479112 :                         str_list_remove(found_must_contain, attr->lDAPDisplayName);
     471             :                 } else {
     472    13153324 :                         found = str_list_check(may_contain, attr->lDAPDisplayName);
     473             :                 }
     474    19632436 :                 if (!found) {
     475          31 :                         found = str_list_check(harmless_attrs, attr->lDAPDisplayName);
     476             :                 }
     477    19632436 :                 if (!found) {
     478             :                         /* we allow this for dbcheck to fix the rest of this broken entry */
     479          31 :                         if (!ldb_request_get_control(ac->req, DSDB_CONTROL_DBCHECK) ||
     480           0 :                             ac->req->operation == LDB_ADD) {
     481          31 :                                 ldb_asprintf_errstring(ldb, "objectclass_attrs: attribute '%s' on entry '%s' does not exist in the specified objectclasses!",
     482          31 :                                                        msg->elements[i].name,
     483             :                                                        ldb_dn_get_linearized(msg->dn));
     484          31 :                                 return LDB_ERR_OBJECT_CLASS_VIOLATION;
     485             :                         }
     486             :                 }
     487             :         }
     488             : 
     489             :         /*
     490             :          * We skip this check under dbcheck to allow fixing of other
     491             :          * attributes even if an attribute is missing.  This matters
     492             :          * for CN=RID Set as the required attribute rIDNextRid is not
     493             :          * replicated.
     494             :          */
     495      964405 :         if (found_must_contain[0] != NULL &&
     496       35291 :             ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE") == 0) {
     497             : 
     498          62 :                 for (i = 0; found_must_contain[i] != NULL; i++) {
     499          42 :                         const struct dsdb_attribute *broken_attr = dsdb_attribute_by_lDAPDisplayName(ac->schema,
     500          42 :                                                                                                      found_must_contain[i]);
     501             : 
     502          42 :                         bool replicated = (broken_attr->systemFlags &
     503             :                                            (DS_FLAG_ATTR_NOT_REPLICATED | DS_FLAG_ATTR_IS_CONSTRUCTED)) == 0;
     504             : 
     505          42 :                         if (replicated) {
     506           3 :                                 ldb_asprintf_errstring(ldb, "objectclass_attrs: at least one mandatory "
     507             :                                                        "attribute ('%s') on entry '%s' wasn't specified!",
     508           3 :                                                        found_must_contain[i],
     509             :                                                        ldb_dn_get_linearized(msg->dn));
     510           3 :                                 return LDB_ERR_OBJECT_CLASS_VIOLATION;
     511             :                         }
     512             :                 }
     513             :         }
     514             : 
     515      929111 :         if (isSchemaAttr) {
     516             :                 /*
     517             :                  * Before really adding an attribute in the database,
     518             :                  * let's check that we can translate it into a dsdb_attribute and
     519             :                  * that we can find a valid syntax object.
     520             :                  * If not it's better to reject this attribute than not be able
     521             :                  * to start samba next time due to schema being unloadable.
     522             :                  */
     523      221548 :                 struct dsdb_attribute *att = talloc(ac, struct dsdb_attribute);
     524       36920 :                 const struct dsdb_syntax *attrSyntax;
     525       36920 :                 WERROR status;
     526             : 
     527      221548 :                 status = dsdb_attribute_from_ldb(NULL, msg, att);
     528      221548 :                 if (!W_ERROR_IS_OK(status)) {
     529           0 :                         ldb_set_errstring(ldb,
     530             :                                                 "objectclass: failed to translate the schemaAttribute to a dsdb_attribute");
     531           0 :                         return LDB_ERR_UNWILLING_TO_PERFORM;
     532             :                 }
     533             : 
     534      221548 :                 attrSyntax = dsdb_syntax_for_attribute(att);
     535      221548 :                 if (!attrSyntax) {
     536           0 :                         ldb_set_errstring(ldb,
     537             :                                                 "objectclass: unknown attribute syntax");
     538           0 :                         return LDB_ERR_UNWILLING_TO_PERFORM;
     539             :                 }
     540             :         }
     541      929111 :         return ldb_module_done(ac->req, ac->mod_ares->controls,
     542      929111 :                                ac->mod_ares->response, LDB_SUCCESS);
     543             : }
     544             : 
     545     1858422 : static int get_search_callback(struct ldb_request *req, struct ldb_reply *ares)
     546             : {
     547      213616 :         struct ldb_context *ldb;
     548      213616 :         struct oc_context *ac;
     549      213616 :         int ret;
     550             : 
     551     1858422 :         ac = talloc_get_type(req->context, struct oc_context);
     552     1858422 :         ldb = ldb_module_get_ctx(ac->module);
     553             : 
     554     1858422 :         if (!ares) {
     555           0 :                 return ldb_module_done(ac->req, NULL, NULL,
     556             :                                        LDB_ERR_OPERATIONS_ERROR);
     557             :         }
     558     1858422 :         if (ares->error != LDB_SUCCESS) {
     559           0 :                 return ldb_module_done(ac->req, ares->controls,
     560             :                                        ares->response, ares->error);
     561             :         }
     562             : 
     563     1858422 :         ldb_reset_err_string(ldb);
     564             : 
     565     1858422 :         switch (ares->type) {
     566      929211 :         case LDB_REPLY_ENTRY:
     567      929211 :                 if (ac->search_res != NULL) {
     568           0 :                         ldb_set_errstring(ldb, "Too many results");
     569           0 :                         talloc_free(ares);
     570           0 :                         return ldb_module_done(ac->req, NULL, NULL,
     571             :                                                LDB_ERR_OPERATIONS_ERROR);
     572             :                 }
     573             : 
     574      929211 :                 ac->search_res = talloc_steal(ac, ares);
     575      929211 :                 break;
     576             : 
     577           0 :         case LDB_REPLY_REFERRAL:
     578             :                 /* ignore */
     579           0 :                 talloc_free(ares);
     580           0 :                 break;
     581             : 
     582      929211 :         case LDB_REPLY_DONE:
     583      929211 :                 talloc_free(ares);
     584      929211 :                 ret = attr_handler2(ac);
     585      929211 :                 if (ret != LDB_SUCCESS) {
     586         100 :                         return ldb_module_done(ac->req, NULL, NULL, ret);
     587             :                 }
     588      822303 :                 break;
     589             :         }
     590             : 
     591     1644706 :         return LDB_SUCCESS;
     592             : }
     593             : 
     594      930037 : static int oc_op_callback(struct ldb_request *req, struct ldb_reply *ares)
     595             : {
     596      106809 :         struct oc_context *ac;
     597      106809 :         struct ldb_context *ldb;
     598      106809 :         struct ldb_request *search_req;
     599      106809 :         struct ldb_dn *base_dn;
     600      106809 :         int ret;
     601      106809 :         static const char *attrs[] = {"nTSecurityDescriptor", "*", NULL};
     602             : 
     603      930037 :         ac = talloc_get_type(req->context, struct oc_context);
     604      930037 :         ldb = ldb_module_get_ctx(ac->module);
     605             : 
     606      930037 :         if (!ares) {
     607           0 :                 return ldb_module_done(ac->req, NULL, NULL,
     608             :                                        LDB_ERR_OPERATIONS_ERROR);
     609             :         }
     610             : 
     611      930037 :         if (ares->type == LDB_REPLY_REFERRAL) {
     612          10 :                 return ldb_module_send_referral(ac->req, ares->referral);
     613             :         }
     614             : 
     615      930027 :         if (ares->error != LDB_SUCCESS) {
     616         816 :                 return ldb_module_done(ac->req, ares->controls, ares->response,
     617             :                                        ares->error);
     618             :         }
     619             : 
     620      929211 :         if (ares->type != LDB_REPLY_DONE) {
     621           0 :                 talloc_free(ares);
     622           0 :                 return ldb_module_done(ac->req, NULL, NULL,
     623             :                                        LDB_ERR_OPERATIONS_ERROR);
     624             :         }
     625             : 
     626      929211 :         ac->search_res = NULL;
     627      929211 :         ac->mod_ares = talloc_steal(ac, ares);
     628             : 
     629             :         /* This looks up all attributes of our just added/modified entry */
     630     2400609 :         base_dn = ac->req->operation == LDB_ADD ? ac->req->op.add.message->dn
     631      929211 :                 : ac->req->op.mod.message->dn;
     632      929211 :         ret = ldb_build_search_req(&search_req, ldb, ac, base_dn,
     633             :                                    LDB_SCOPE_BASE, "(objectClass=*)",
     634             :                                    attrs, NULL, ac,
     635             :                                    get_search_callback, ac->req);
     636      929211 :         LDB_REQ_SET_LOCATION(search_req);
     637      929211 :         if (ret != LDB_SUCCESS) {
     638           0 :                 return ldb_module_done(ac->req, NULL, NULL, ret);
     639             :         }
     640             : 
     641      929211 :         ret = ldb_request_add_control(search_req, LDB_CONTROL_SHOW_RECYCLED_OID,
     642             :                                       true, NULL);
     643      929211 :         if (ret != LDB_SUCCESS) {
     644           0 :                 return ldb_module_done(ac->req, NULL, NULL, ret);
     645             :         }
     646             : 
     647             :         /*
     648             :          * This ensures we see if there was a DN, that pointed at an
     649             :          * object that is now deleted, that we still consider the
     650             :          * schema check to have passed
     651             :          */
     652      929211 :         ret = ldb_request_add_control(search_req, LDB_CONTROL_REVEAL_INTERNALS,
     653             :                                       false, NULL);
     654      929211 :         if (ret != LDB_SUCCESS) {
     655           0 :                 return ldb_module_done(ac->req, NULL, NULL, ret);
     656             :         }
     657             : 
     658      929211 :         ret = ldb_next_request(ac->module, search_req);
     659      929211 :         if (ret != LDB_SUCCESS) {
     660           0 :                 return ldb_module_done(ac->req, NULL, NULL, ret);
     661             :         }
     662             : 
     663             :         /* "ldb_module_done" isn't called here since we need to do additional
     664             :          * checks. It is called at the end of "attr_handler2". */
     665      822403 :         return LDB_SUCCESS;
     666             : }
     667             : 
     668      543046 : static int objectclass_attrs_add(struct ldb_module *module,
     669             :                                  struct ldb_request *req)
     670             : {
     671       83679 :         struct ldb_context *ldb;
     672       83679 :         struct oc_context *ac;
     673             : 
     674      543046 :         ldb = ldb_module_get_ctx(module);
     675             : 
     676      543046 :         ldb_debug(ldb, LDB_DEBUG_TRACE, "objectclass_attrs_add\n");
     677             : 
     678             :         /* do not manipulate our control entries */
     679      543046 :         if (ldb_dn_is_special(req->op.add.message->dn)) {
     680         538 :                 return ldb_next_request(module, req);
     681             :         }
     682             : 
     683      542508 :         ac = oc_init_context(module, req);
     684      542508 :         if (ac == NULL) {
     685           0 :                 return ldb_operr(ldb);
     686             :         }
     687             : 
     688             :         /* without schema, there isn't much to do here */
     689      542508 :         if (ac->schema == NULL) {
     690           0 :                 talloc_free(ac);
     691           0 :                 return ldb_next_request(module, req);
     692             :         }
     693             : 
     694      542508 :         return attr_handler(ac);
     695             : }
     696             : 
     697      642716 : static int objectclass_attrs_modify(struct ldb_module *module,
     698             :                                     struct ldb_request *req)
     699             : {
     700       27627 :         struct ldb_context *ldb;
     701       27627 :         struct ldb_control *sd_propagation_control;
     702       27627 :         int ret;
     703             : 
     704       27627 :         struct oc_context *ac;
     705             : 
     706      642716 :         ldb = ldb_module_get_ctx(module);
     707             : 
     708      642716 :         ldb_debug(ldb, LDB_DEBUG_TRACE, "objectclass_attrs_modify\n");
     709             : 
     710             :         /* do not manipulate our control entries */
     711      642716 :         if (ldb_dn_is_special(req->op.mod.message->dn)) {
     712         715 :                 return ldb_next_request(module, req);
     713             :         }
     714             : 
     715      642001 :         sd_propagation_control = ldb_request_get_control(req,
     716             :                                         DSDB_CONTROL_SEC_DESC_PROPAGATION_OID);
     717      642001 :         if (sd_propagation_control != NULL) {
     718      254376 :                 if (req->op.mod.message->num_elements != 1) {
     719           0 :                         return ldb_module_operr(module);
     720             :                 }
     721      254376 :                 ret = strcmp(req->op.mod.message->elements[0].name,
     722             :                              "nTSecurityDescriptor");
     723      254376 :                 if (ret != 0) {
     724           0 :                         return ldb_module_operr(module);
     725             :                 }
     726             : 
     727      254376 :                 return ldb_next_request(module, req);
     728             :         }
     729             : 
     730      387625 :         ac = oc_init_context(module, req);
     731      387625 :         if (ac == NULL) {
     732           0 :                 return ldb_operr(ldb);
     733             :         }
     734             : 
     735             :         /* without schema, there isn't much to do here */
     736      387625 :         if (ac->schema == NULL) {
     737           0 :                 talloc_free(ac);
     738           0 :                 return ldb_next_request(module, req);
     739             :         }
     740             : 
     741      387625 :         return attr_handler(ac);
     742             : }
     743             : 
     744             : static const struct ldb_module_ops ldb_objectclass_attrs_module_ops = {
     745             :         .name              = "objectclass_attrs",
     746             :         .add               = objectclass_attrs_add,
     747             :         .modify            = objectclass_attrs_modify
     748             : };
     749             : 
     750        6040 : int ldb_objectclass_attrs_module_init(const char *version)
     751             : {
     752        6040 :         LDB_MODULE_CHECK_VERSION(version);
     753        6040 :         return ldb_register_module(&ldb_objectclass_attrs_module_ops);
     754             : }

Generated by: LCOV version 1.14