LCOV - code coverage report
Current view: top level - source4/dsdb/samdb/ldb_modules - samldb.c (source / functions) Hit Total Coverage
Test: coverage report for abartlet/fix-coverage dd10fb34 Lines: 1637 1947 84.1 %
Date: 2021-09-23 10:06:22 Functions: 64 66 97.0 %

          Line data    Source code
       1             : /*
       2             :    SAM ldb module
       3             : 
       4             :    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005-2014
       5             :    Copyright (C) Simo Sorce  2004-2008
       6             :    Copyright (C) Matthias Dieter Wallnöfer 2009-2011
       7             :    Copyright (C) Matthieu Patou 2012
       8             :    Copyright (C) Catalyst.Net Ltd 2017
       9             : 
      10             :    This program is free software; you can redistribute it and/or modify
      11             :    it under the terms of the GNU General Public License as published by
      12             :    the Free Software Foundation; either version 3 of the License, or
      13             :    (at your option) any later version.
      14             : 
      15             :    This program is distributed in the hope that it will be useful,
      16             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      17             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      18             :    GNU General Public License for more details.
      19             : 
      20             :    You should have received a copy of the GNU General Public License
      21             :    along with this program.  If not, see <http://www.gnu.org/licenses/>.
      22             : */
      23             : 
      24             : /*
      25             :  *  Name: ldb
      26             :  *
      27             :  *  Component: ldb samldb module
      28             :  *
      29             :  *  Description: various internal DSDB triggers - most for SAM specific objects
      30             :  *
      31             :  *  Author: Simo Sorce
      32             :  */
      33             : 
      34             : #include "includes.h"
      35             : #include "libcli/ldap/ldap_ndr.h"
      36             : #include "ldb_module.h"
      37             : #include "auth/auth.h"
      38             : #include "dsdb/samdb/samdb.h"
      39             : #include "dsdb/samdb/ldb_modules/util.h"
      40             : #include "dsdb/samdb/ldb_modules/ridalloc.h"
      41             : #include "libcli/security/security.h"
      42             : #include "librpc/gen_ndr/ndr_security.h"
      43             : #include "ldb_wrap.h"
      44             : #include "param/param.h"
      45             : #include "libds/common/flag_mapping.h"
      46             : #include "system/network.h"
      47             : #include "librpc/gen_ndr/irpc.h"
      48             : #include "lib/util/smb_strtox.h"
      49             : 
      50             : #undef strcasecmp
      51             : 
      52             : struct samldb_ctx;
      53             : enum samldb_add_type {
      54             :         SAMLDB_TYPE_USER,
      55             :         SAMLDB_TYPE_GROUP,
      56             :         SAMLDB_TYPE_CLASS,
      57             :         SAMLDB_TYPE_ATTRIBUTE
      58             : };
      59             : 
      60             : typedef int (*samldb_step_fn_t)(struct samldb_ctx *);
      61             : 
      62             : struct samldb_step {
      63             :         struct samldb_step *next;
      64             :         samldb_step_fn_t fn;
      65             : };
      66             : 
      67             : struct samldb_ctx {
      68             :         struct ldb_module *module;
      69             :         struct ldb_request *req;
      70             : 
      71             :         /* used for add operations */
      72             :         enum samldb_add_type type;
      73             : 
      74             :         /* the resulting message */
      75             :         struct ldb_message *msg;
      76             : 
      77             :         /* used in "samldb_find_for_defaultObjectCategory" */
      78             :         struct ldb_dn *dn, *res_dn;
      79             : 
      80             :         /* all the async steps necessary to complete the operation */
      81             :         struct samldb_step *steps;
      82             :         struct samldb_step *curstep;
      83             : 
      84             :         /* If someone set an ares to forward controls and response back to the caller */
      85             :         struct ldb_reply *ares;
      86             : };
      87             : 
      88      942925 : static struct samldb_ctx *samldb_ctx_init(struct ldb_module *module,
      89             :                                           struct ldb_request *req)
      90             : {
      91             :         struct ldb_context *ldb;
      92             :         struct samldb_ctx *ac;
      93             : 
      94      942925 :         ldb = ldb_module_get_ctx(module);
      95             : 
      96      942925 :         ac = talloc_zero(req, struct samldb_ctx);
      97      942925 :         if (ac == NULL) {
      98           0 :                 ldb_oom(ldb);
      99           0 :                 return NULL;
     100             :         }
     101             : 
     102      942925 :         ac->module = module;
     103      942925 :         ac->req = req;
     104             : 
     105      942925 :         return ac;
     106             : }
     107             : 
     108      319178 : static int samldb_add_step(struct samldb_ctx *ac, samldb_step_fn_t fn)
     109             : {
     110             :         struct samldb_step *step, *stepper;
     111             : 
     112      319178 :         step = talloc_zero(ac, struct samldb_step);
     113      319178 :         if (step == NULL) {
     114           0 :                 return ldb_oom(ldb_module_get_ctx(ac->module));
     115             :         }
     116             : 
     117      319178 :         step->fn = fn;
     118             : 
     119      319178 :         if (ac->steps == NULL) {
     120      237195 :                 ac->steps = step;
     121      237195 :                 ac->curstep = step;
     122             :         } else {
     123       81983 :                 if (ac->curstep == NULL)
     124           0 :                         return ldb_operr(ldb_module_get_ctx(ac->module));
     125      160329 :                 for (stepper = ac->curstep; stepper->next != NULL;
     126       22683 :                         stepper = stepper->next);
     127       81983 :                 stepper->next = step;
     128             :         }
     129             : 
     130      274355 :         return LDB_SUCCESS;
     131             : }
     132             : 
     133      237162 : static int samldb_first_step(struct samldb_ctx *ac)
     134             : {
     135      237162 :         if (ac->steps == NULL) {
     136           0 :                 return ldb_operr(ldb_module_get_ctx(ac->module));
     137             :         }
     138             : 
     139      237162 :         ac->curstep = ac->steps;
     140      237162 :         return ac->curstep->fn(ac);
     141             : }
     142             : 
     143      318942 : static int samldb_next_step(struct samldb_ctx *ac)
     144             : {
     145      318942 :         if (ac->curstep->next) {
     146       81944 :                 ac->curstep = ac->curstep->next;
     147       81944 :                 return ac->curstep->fn(ac);
     148             :         }
     149             : 
     150             :         /* We exit the samldb module here. If someone set an "ares" to forward
     151             :          * controls and response back to the caller, use them. */
     152      236998 :         if (ac->ares) {
     153      236998 :                 return ldb_module_done(ac->req, ac->ares->controls,
     154      198890 :                                        ac->ares->response, LDB_SUCCESS);
     155             :         } else {
     156           0 :                 return ldb_module_done(ac->req, NULL, NULL, LDB_SUCCESS);
     157             :         }
     158             : }
     159             : 
     160      236077 : static int samldb_unique_attr_check(struct samldb_ctx *ac, const char *attr,
     161             :                                     const char *attr_conflict,
     162             :                                     struct ldb_dn *base_dn)
     163             : {
     164      236077 :         struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
     165      236077 :         const char * const no_attrs[] = { NULL };
     166             :         struct ldb_result *res;
     167             :         const char *enc_str;
     168             :         struct ldb_message_element *el;
     169             :         int ret;
     170             : 
     171      236077 :         el = dsdb_get_single_valued_attr(ac->msg, attr,
     172      236077 :                                          ac->req->operation);
     173      236077 :         if (el == NULL) {
     174             :                 /* we are not affected */
     175         476 :                 return LDB_SUCCESS;
     176             :         }
     177             : 
     178      235601 :         if (el->num_values > 1) {
     179           2 :                 ldb_asprintf_errstring(ldb,
     180             :                                        "samldb: %s has %u values, should be single-valued!",
     181             :                                        attr, el->num_values);
     182           2 :                 return LDB_ERR_CONSTRAINT_VIOLATION;
     183      235599 :         } else if (el->num_values == 0) {
     184          18 :                 ldb_asprintf_errstring(ldb,
     185             :                                        "samldb: new value for %s not provided for mandatory, single-valued attribute!",
     186             :                                        attr);
     187          18 :                 return LDB_ERR_OBJECT_CLASS_VIOLATION;
     188             :         }
     189      235581 :         if (el->values[0].length == 0) {
     190           0 :                 ldb_asprintf_errstring(ldb,
     191             :                                        "samldb: %s is of zero length, should have a value!",
     192             :                                        attr);
     193           0 :                 return LDB_ERR_OBJECT_CLASS_VIOLATION;
     194             :         }
     195      235581 :         enc_str = ldb_binary_encode(ac, el->values[0]);
     196             : 
     197      235581 :         if (enc_str == NULL) {
     198           0 :                 return ldb_module_oom(ac->module);
     199             :         }
     200             : 
     201             :         /* Make sure that attr (eg) "sAMAccountName" is only used once */
     202             : 
     203      235581 :         if (attr_conflict != NULL) {
     204        1569 :                 ret = dsdb_module_search(ac->module, ac, &res,
     205             :                                          base_dn,
     206             :                                          LDB_SCOPE_SUBTREE, no_attrs,
     207             :                                          DSDB_FLAG_NEXT_MODULE, ac->req,
     208             :                                          "(|(%s=%s)(%s=%s))",
     209             :                                          attr, enc_str,
     210             :                                          attr_conflict, enc_str);
     211             :         } else {
     212      234012 :                 ret = dsdb_module_search(ac->module, ac, &res,
     213             :                                          base_dn,
     214             :                                          LDB_SCOPE_SUBTREE, no_attrs,
     215             :                                          DSDB_FLAG_NEXT_MODULE, ac->req,
     216             :                                          "(%s=%s)", attr, enc_str);
     217             :         }
     218      235581 :         if (ret != LDB_SUCCESS) {
     219           0 :                 return ret;
     220             :         }
     221      235581 :         if (res->count > 1) {
     222           0 :                 return ldb_operr(ldb);
     223      235581 :         } else if (res->count == 1) {
     224         601 :                 if (ldb_dn_compare(res->msgs[0]->dn, ac->msg->dn) != 0) {
     225          78 :                         ldb_asprintf_errstring(ldb,
     226             :                                                "samldb: %s '%s' already in use!",
     227             :                                                attr, enc_str);
     228          78 :                         return LDB_ERR_ENTRY_ALREADY_EXISTS;
     229             :                 }
     230             :         }
     231      235503 :         talloc_free(res);
     232             : 
     233      235503 :         return LDB_SUCCESS;
     234             : }
     235             : 
     236       28323 : static int samldb_sam_accountname_valid_check(struct samldb_ctx *ac)
     237             : {
     238       28323 :         int ret = samldb_unique_attr_check(ac, "samAccountName", NULL,
     239             :                                            ldb_get_default_basedn(
     240             :                                                    ldb_module_get_ctx(ac->module)));
     241       28323 :         if (ret == LDB_ERR_OBJECT_CLASS_VIOLATION) {
     242           9 :                 ret = LDB_ERR_CONSTRAINT_VIOLATION;
     243             :         }
     244       28323 :         return ret;
     245             : }
     246             : 
     247         971 : static int samldb_schema_attributeid_valid_check(struct samldb_ctx *ac)
     248             : {
     249         971 :         int ret = samldb_unique_attr_check(ac, "attributeID", "governsID",
     250             :                                            ldb_get_schema_basedn(
     251             :                                                    ldb_module_get_ctx(ac->module)));
     252         971 :         if (ret == LDB_ERR_ENTRY_ALREADY_EXISTS) {
     253           9 :                 ret = LDB_ERR_UNWILLING_TO_PERFORM;
     254             :         }
     255         971 :         return ret;
     256             : }
     257             : 
     258         598 : static int samldb_schema_governsid_valid_check(struct samldb_ctx *ac)
     259             : {
     260         598 :         int ret = samldb_unique_attr_check(ac, "governsID", "attributeID",
     261             :                                            ldb_get_schema_basedn(
     262             :                                                    ldb_module_get_ctx(ac->module)));
     263         598 :         if (ret == LDB_ERR_ENTRY_ALREADY_EXISTS) {
     264           9 :                 ret = LDB_ERR_UNWILLING_TO_PERFORM;
     265             :         }
     266         598 :         return ret;
     267             : }
     268             : 
     269      206042 : static int samldb_schema_ldapdisplayname_valid_check(struct samldb_ctx *ac)
     270             : {
     271      206042 :         int ret = samldb_unique_attr_check(ac, "lDAPDisplayName", NULL,
     272             :                                            ldb_get_schema_basedn(
     273             :                                                    ldb_module_get_ctx(ac->module)));
     274      206042 :         if (ret == LDB_ERR_ENTRY_ALREADY_EXISTS) {
     275          36 :                 ret = LDB_ERR_UNWILLING_TO_PERFORM;
     276             :         }
     277      206042 :         return ret;
     278             : }
     279             : 
     280          63 : static int samldb_check_linkid_used(struct samldb_ctx *ac,
     281             :                                     struct dsdb_schema *schema,
     282             :                                     struct ldb_dn *schema_dn,
     283             :                                     struct ldb_context *ldb,
     284             :                                     int32_t linkID,
     285             :                                     bool *found)
     286             : {
     287             :         int ret;
     288             :         struct ldb_result *ldb_res;
     289             : 
     290          63 :         if (dsdb_attribute_by_linkID(schema, linkID)) {
     291          24 :                 *found = true;
     292          24 :                 return LDB_SUCCESS;
     293             :         }
     294             : 
     295          39 :         ret = dsdb_module_search(ac->module, ac,
     296             :                                  &ldb_res,
     297             :                                  schema_dn, LDB_SCOPE_ONELEVEL, NULL,
     298             :                                  DSDB_FLAG_NEXT_MODULE,
     299             :                                  ac->req,
     300             :                                  "(linkID=%d)", linkID);
     301          39 :         if (ret != LDB_SUCCESS) {
     302           0 :                 ldb_debug_set(ldb, LDB_DEBUG_ERROR,
     303             :                               __location__": Searching for linkID=%d failed - %s\n",
     304             :                               linkID,
     305             :                               ldb_errstring(ldb));
     306           0 :                 return ldb_operr(ldb);
     307             :         }
     308             : 
     309          39 :         *found = (ldb_res->count != 0);
     310          39 :         talloc_free(ldb_res);
     311             : 
     312          39 :         return LDB_SUCCESS;
     313             : }
     314             : 
     315             : /* Find the next open forward linkID in the schema. */
     316          27 : static int samldb_generate_next_linkid(struct samldb_ctx *ac,
     317             :                                        struct dsdb_schema *schema,
     318             :                                        int32_t *next_linkID)
     319             : {
     320             :         int ret;
     321             :         struct ldb_context *ldb;
     322             :         struct ldb_dn *schema_dn;
     323          27 :         bool linkID_used = true;
     324             : 
     325             :         /*
     326             :          * Windows starts at about 0xB0000000 in order to stop potential
     327             :          * collisions with future additions to the schema. We pass this
     328             :          * around as a signed int sometimes, but this should be sufficient.
     329             :          */
     330          27 :         *next_linkID = 0x40000000;
     331             : 
     332          27 :         ldb = ldb_module_get_ctx(ac->module);
     333          27 :         schema_dn = ldb_get_schema_basedn(ldb);
     334             : 
     335          39 :         while (linkID_used) {
     336          42 :                 *next_linkID += 2;
     337          42 :                 ret = samldb_check_linkid_used(ac, schema,
     338             :                                                schema_dn, ldb,
     339             :                                                *next_linkID, &linkID_used);
     340          42 :                 if (ret != LDB_SUCCESS) {
     341           0 :                         return ret;
     342             :                 }
     343             :         }
     344             : 
     345          27 :         return LDB_SUCCESS;
     346             : }
     347             : 
     348         962 : static int samldb_schema_add_handle_linkid(struct samldb_ctx *ac)
     349             : {
     350             :         int ret;
     351         962 :         bool ok, found = false;
     352             :         struct ldb_message_element *el;
     353             :         const char *enc_str;
     354             :         const struct dsdb_attribute *attr;
     355             :         struct ldb_context *ldb;
     356             :         struct ldb_dn *schema_dn;
     357             :         struct dsdb_schema *schema;
     358         962 :         int32_t new_linkID = 0;
     359             : 
     360         962 :         ldb = ldb_module_get_ctx(ac->module);
     361         962 :         schema = dsdb_get_schema(ldb, ac);
     362         962 :         schema_dn = ldb_get_schema_basedn(ldb);
     363             : 
     364         962 :         el = dsdb_get_single_valued_attr(ac->msg, "linkID",
     365         962 :                                          ac->req->operation);
     366         962 :         if (el == NULL) {
     367         762 :                 return LDB_SUCCESS;
     368             :         }
     369             : 
     370         200 :         enc_str = ldb_binary_encode(ac, el->values[0]);
     371         200 :         if (enc_str == NULL) {
     372           0 :                 return ldb_module_oom(ac->module);
     373             :         }
     374             : 
     375         200 :         ok = (strcmp(enc_str, "0") == 0);
     376         200 :         if (ok) {
     377           0 :                 return LDB_SUCCESS;
     378             :         }
     379             : 
     380             :         /*
     381             :          * This OID indicates that the caller wants the linkID
     382             :          * to be automatically generated. We therefore assign
     383             :          * it the next open linkID.
     384             :          */
     385         200 :         ok = (strcmp(enc_str, "1.2.840.113556.1.2.50") == 0);
     386         200 :         if (ok) {
     387          27 :                 ret = samldb_generate_next_linkid(ac, schema, &new_linkID);
     388          27 :                 if (ret != LDB_SUCCESS) {
     389           0 :                         return ret;
     390             :                 }
     391             : 
     392          27 :                 ldb_msg_remove_element(ac->msg, el);
     393          27 :                 ret = samdb_msg_add_int(ldb, ac->msg, ac->msg, "linkID",
     394             :                                         new_linkID);
     395          27 :                 return ret;
     396             :         }
     397             : 
     398             :         /*
     399             :          * Using either the attributeID or lDAPDisplayName of
     400             :          * another attribute in the linkID field indicates that
     401             :          * we should make this the backlink of that attribute.
     402             :          */
     403         173 :         attr = dsdb_attribute_by_attributeID_oid(schema, enc_str);
     404         173 :         if (attr == NULL) {
     405         155 :                 attr = dsdb_attribute_by_lDAPDisplayName(schema, enc_str);
     406             :         }
     407             : 
     408         173 :         if (attr != NULL) {
     409             :                 /*
     410             :                  * The attribute we're adding this as a backlink of must
     411             :                  * be a forward link.
     412             :                  */
     413          39 :                 if (attr->linkID % 2 != 0) {
     414          18 :                         return LDB_ERR_UNWILLING_TO_PERFORM;
     415             :                 }
     416             : 
     417          21 :                 new_linkID = attr->linkID + 1;
     418             : 
     419             :                 /* Make sure that this backlink doesn't already exist. */
     420          21 :                 ret = samldb_check_linkid_used(ac, schema,
     421             :                                                schema_dn, ldb,
     422             :                                                new_linkID, &found);
     423          21 :                 if (ret != LDB_SUCCESS) {
     424           0 :                         return ret;
     425             :                 }
     426             : 
     427          21 :                 if (found) {
     428           9 :                         return LDB_ERR_UNWILLING_TO_PERFORM;
     429             :                 }
     430             : 
     431          12 :                 ldb_msg_remove_element(ac->msg, el);
     432          12 :                 ret = samdb_msg_add_int(ldb, ac->msg, ac->msg, "linkID",
     433             :                                         new_linkID);
     434          12 :                 return ret;
     435             :         }
     436             : 
     437         134 :         schema_dn = ldb_get_schema_basedn(ldb_module_get_ctx(ac->module));
     438         134 :         ret = samldb_unique_attr_check(ac, "linkID", NULL, schema_dn);
     439         134 :         if (ret == LDB_ERR_ENTRY_ALREADY_EXISTS) {
     440           9 :                 return LDB_ERR_UNWILLING_TO_PERFORM;
     441             :         } else {
     442         125 :                 return ret;
     443             :         }
     444             : }
     445             : 
     446           9 : static int samldb_check_mapiid_used(struct samldb_ctx *ac,
     447             :                                     struct dsdb_schema *schema,
     448             :                                     struct ldb_dn *schema_dn,
     449             :                                     struct ldb_context *ldb,
     450             :                                     int32_t mapiid,
     451             :                                     bool *found)
     452             : {
     453             :         int ret;
     454             :         struct ldb_result *ldb_res;
     455             : 
     456           9 :         ret = dsdb_module_search(ac->module, ac,
     457             :                                  &ldb_res,
     458             :                                  schema_dn, LDB_SCOPE_ONELEVEL, NULL,
     459             :                                  DSDB_FLAG_NEXT_MODULE,
     460             :                                  ac->req,
     461             :                                  "(mAPIID=%d)", mapiid);
     462           9 :         if (ret != LDB_SUCCESS) {
     463           0 :                 ldb_debug_set(ldb, LDB_DEBUG_ERROR,
     464             :                               __location__": Searching for mAPIID=%d failed - %s\n",
     465             :                               mapiid,
     466             :                               ldb_errstring(ldb));
     467           0 :                 return ldb_operr(ldb);
     468             :         }
     469             : 
     470           9 :         *found = (ldb_res->count != 0);
     471           9 :         talloc_free(ldb_res);
     472             : 
     473           9 :         return LDB_SUCCESS;
     474             : }
     475             : 
     476           9 : static int samldb_generate_next_mapiid(struct samldb_ctx *ac,
     477             :                                        struct dsdb_schema *schema,
     478             :                                        int32_t *next_mapiid)
     479             : {
     480             :         int ret;
     481             :         struct ldb_context *ldb;
     482             :         struct ldb_dn *schema_dn;
     483           9 :         bool mapiid_used = true;
     484             : 
     485             :         /* Windows' generation seems to start about here */
     486           9 :         *next_mapiid = 60000;
     487             : 
     488           9 :         ldb = ldb_module_get_ctx(ac->module);
     489           9 :         schema_dn = ldb_get_schema_basedn(ldb);
     490             : 
     491          13 :         while (mapiid_used) {
     492           9 :                 *next_mapiid += 1;
     493           9 :                 ret = samldb_check_mapiid_used(ac, schema,
     494             :                                                schema_dn, ldb,
     495             :                                                *next_mapiid, &mapiid_used);
     496           9 :                 if (ret != LDB_SUCCESS) {
     497           0 :                         return ret;
     498             :                 }
     499             :         }
     500             : 
     501           9 :         return LDB_SUCCESS;
     502             : }
     503             : 
     504         926 : static int samldb_schema_add_handle_mapiid(struct samldb_ctx *ac)
     505             : {
     506             :         int ret;
     507             :         bool ok;
     508             :         struct ldb_message_element *el;
     509             :         const char *enc_str;
     510             :         struct ldb_context *ldb;
     511             :         struct ldb_dn *schema_dn;
     512             :         struct dsdb_schema *schema;
     513         926 :         int32_t new_mapiid = 0;
     514             : 
     515             :         /*
     516             :          * The mAPIID of a new attribute should be automatically generated
     517             :          * if a specific OID is put as the mAPIID, as according to
     518             :          * [MS-ADTS] 3.1.1.2.3.2.
     519             :          */
     520             : 
     521         926 :         ldb = ldb_module_get_ctx(ac->module);
     522         926 :         schema = dsdb_get_schema(ldb, ac);
     523         926 :         schema_dn = ldb_get_schema_basedn(ldb);
     524             : 
     525         926 :         el = dsdb_get_single_valued_attr(ac->msg, "mAPIID",
     526         926 :                                          ac->req->operation);
     527         926 :         if (el == NULL) {
     528         908 :                 return LDB_SUCCESS;
     529             :         }
     530             : 
     531          18 :         enc_str = ldb_binary_encode(ac, el->values[0]);
     532          18 :         if (enc_str == NULL) {
     533           0 :                 return ldb_module_oom(ac->module);
     534             :         }
     535             : 
     536          18 :         ok = (strcmp(enc_str, "1.2.840.113556.1.2.49") == 0);
     537          18 :         if (ok) {
     538           9 :                 ret = samldb_generate_next_mapiid(ac, schema,
     539             :                                                   &new_mapiid);
     540           9 :                 if (ret != LDB_SUCCESS) {
     541           0 :                         return ret;
     542             :                 }
     543             : 
     544           9 :                 ldb_msg_remove_element(ac->msg, el);
     545           9 :                 ret = samdb_msg_add_int(ldb, ac->msg, ac->msg,
     546             :                                         "mAPIID", new_mapiid);
     547           9 :                 return ret;
     548             :         }
     549             : 
     550           9 :         schema_dn = ldb_get_schema_basedn(ldb_module_get_ctx(ac->module));
     551           9 :         ret = samldb_unique_attr_check(ac, "mAPIID", NULL, schema_dn);
     552           9 :         if (ret == LDB_ERR_ENTRY_ALREADY_EXISTS) {
     553           9 :                 return LDB_ERR_UNWILLING_TO_PERFORM;
     554             :         } else {
     555           0 :                 return ret;
     556             :         }
     557             : }
     558             : 
     559             : /* sAMAccountName handling */
     560        5612 : static int samldb_generate_sAMAccountName(struct ldb_context *ldb,
     561             :                                           struct ldb_message *msg)
     562             : {
     563             :         char *name;
     564             : 
     565             :         /* Format: $000000-000000000000 */
     566             : 
     567        5612 :         name = talloc_asprintf(msg, "$%.6X-%.6X%.6X",
     568        5612 :                                 (unsigned int)generate_random(),
     569        5612 :                                 (unsigned int)generate_random(),
     570        5612 :                                 (unsigned int)generate_random());
     571        5612 :         if (name == NULL) {
     572           0 :                 return ldb_oom(ldb);
     573             :         }
     574        5612 :         return ldb_msg_add_steal_string(msg, "sAMAccountName", name);
     575             : }
     576             : 
     577       27547 : static int samldb_check_sAMAccountName(struct samldb_ctx *ac)
     578             : {
     579       27547 :         struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
     580             :         int ret;
     581             : 
     582       27547 :         if (ldb_msg_find_element(ac->msg, "sAMAccountName") == NULL) {
     583        5612 :                 ret = samldb_generate_sAMAccountName(ldb, ac->msg);
     584        5612 :                 if (ret != LDB_SUCCESS) {
     585           0 :                         return ret;
     586             :                 }
     587             :         }
     588             : 
     589       27547 :         ret = samldb_sam_accountname_valid_check(ac);
     590       27547 :         if (ret != LDB_SUCCESS) {
     591           7 :                 return ret;
     592             :         }
     593             : 
     594       27540 :         return samldb_next_step(ac);
     595             : }
     596             : 
     597             : 
     598       22788 : static bool samldb_msg_add_sid(struct ldb_message *msg,
     599             :                                 const char *name,
     600             :                                 const struct dom_sid *sid)
     601             : {
     602             :         struct ldb_val v;
     603             :         enum ndr_err_code ndr_err;
     604             : 
     605       22788 :         ndr_err = ndr_push_struct_blob(&v, msg, sid,
     606             :                                        (ndr_push_flags_fn_t)ndr_push_dom_sid);
     607       22788 :         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
     608           0 :                 return false;
     609             :         }
     610       22788 :         return (ldb_msg_add_value(msg, name, &v, NULL) == 0);
     611             : }
     612             : 
     613             : 
     614             : /* allocate a SID using our RID Set */
     615       22771 : static int samldb_allocate_sid(struct samldb_ctx *ac)
     616             : {
     617             :         uint32_t rid;
     618             :         struct dom_sid *sid;
     619       22771 :         struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
     620             :         int ret;
     621             : 
     622       22771 :         ret = ridalloc_allocate_rid(ac->module, &rid, ac->req);
     623       22771 :         if (ret != LDB_SUCCESS) {
     624           0 :                 return ret;
     625             :         }
     626             : 
     627       22771 :         sid = dom_sid_add_rid(ac, samdb_domain_sid(ldb), rid);
     628       22771 :         if (sid == NULL) {
     629           0 :                 return ldb_module_oom(ac->module);
     630             :         }
     631             : 
     632       22771 :         if ( ! samldb_msg_add_sid(ac->msg, "objectSid", sid)) {
     633           0 :                 return ldb_operr(ldb);
     634             :         }
     635             : 
     636       22771 :         return samldb_next_step(ac);
     637             : }
     638             : 
     639             : /*
     640             :   see if a krbtgt_number is available
     641             :  */
     642          41 : static bool samldb_krbtgtnumber_available(struct samldb_ctx *ac,
     643             :                                           uint32_t krbtgt_number)
     644             : {
     645          41 :         TALLOC_CTX *tmp_ctx = talloc_new(ac);
     646             :         struct ldb_result *res;
     647          41 :         const char * const no_attrs[] = { NULL };
     648             :         int ret;
     649             : 
     650          41 :         ret = dsdb_module_search(ac->module, tmp_ctx, &res,
     651             :                                  ldb_get_default_basedn(ldb_module_get_ctx(ac->module)),
     652             :                                  LDB_SCOPE_SUBTREE, no_attrs,
     653             :                                  DSDB_FLAG_NEXT_MODULE,
     654             :                                  ac->req,
     655             :                                  "(msDC-SecondaryKrbTgtNumber=%u)",
     656             :                                  krbtgt_number);
     657          41 :         if (ret == LDB_SUCCESS && res->count == 0) {
     658          41 :                 talloc_free(tmp_ctx);
     659          41 :                 return true;
     660             :         }
     661           0 :         talloc_free(tmp_ctx);
     662           0 :         return false;
     663             : }
     664             : 
     665             : /* special handling for add in RODC join */
     666          41 : static int samldb_rodc_add(struct samldb_ctx *ac)
     667             : {
     668          41 :         struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
     669             :         uint32_t krbtgt_number, i_start, i;
     670             :         int ret;
     671             :         struct ldb_val newpass_utf16;
     672             : 
     673             :         /* find a unused msDC-SecondaryKrbTgtNumber */
     674          41 :         i_start = generate_random() & 0xFFFF;
     675          41 :         if (i_start == 0) {
     676           0 :                 i_start = 1;
     677             :         }
     678             : 
     679          41 :         for (i=i_start; i<=0xFFFF; i++) {
     680          41 :                 if (samldb_krbtgtnumber_available(ac, i)) {
     681          41 :                         krbtgt_number = i;
     682          41 :                         goto found;
     683             :                 }
     684             :         }
     685           0 :         for (i=1; i<i_start; i++) {
     686           0 :                 if (samldb_krbtgtnumber_available(ac, i)) {
     687           0 :                         krbtgt_number = i;
     688           0 :                         goto found;
     689             :                 }
     690             :         }
     691             : 
     692           0 :         ldb_asprintf_errstring(ldb,
     693             :                                "%08X: Unable to find available msDS-SecondaryKrbTgtNumber",
     694           0 :                                W_ERROR_V(WERR_NO_SYSTEM_RESOURCES));
     695           0 :         return LDB_ERR_OTHER;
     696             : 
     697          41 : found:
     698          41 :         ret = ldb_msg_add_empty(ac->msg, "msDS-SecondaryKrbTgtNumber",
     699             :                                 LDB_FLAG_INTERNAL_DISABLE_VALIDATION, NULL);
     700          41 :         if (ret != LDB_SUCCESS) {
     701           0 :                 return ldb_operr(ldb);
     702             :         }
     703             : 
     704          41 :         ret = samdb_msg_add_uint(ldb, ac->msg, ac->msg,
     705             :                                  "msDS-SecondaryKrbTgtNumber", krbtgt_number);
     706          41 :         if (ret != LDB_SUCCESS) {
     707           0 :                 return ldb_operr(ldb);
     708             :         }
     709             : 
     710          41 :         ret = ldb_msg_add_fmt(ac->msg, "sAMAccountName", "krbtgt_%u",
     711             :                               krbtgt_number);
     712          41 :         if (ret != LDB_SUCCESS) {
     713           0 :                 return ldb_operr(ldb);
     714             :         }
     715             : 
     716          41 :         newpass_utf16 = data_blob_talloc_zero(ac->module, 256);
     717          41 :         if (newpass_utf16.data == NULL) {
     718           0 :                 return ldb_oom(ldb);
     719             :         }
     720             :         /*
     721             :          * Note that the password_hash module will ignore
     722             :          * this value and use it's own generate_secret_buffer()
     723             :          * that's why we can just use generate_random_buffer()
     724             :          * here.
     725             :          */
     726          41 :         generate_random_buffer(newpass_utf16.data, newpass_utf16.length);
     727          41 :         ret = ldb_msg_add_steal_value(ac->msg, "clearTextPassword", &newpass_utf16);
     728          41 :         if (ret != LDB_SUCCESS) {
     729           0 :                 return ldb_operr(ldb);
     730             :         }
     731             : 
     732          41 :         return samldb_next_step(ac);
     733             : }
     734             : 
     735       31592 : static int samldb_find_for_defaultObjectCategory(struct samldb_ctx *ac)
     736             : {
     737       31592 :         struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
     738             :         struct ldb_result *res;
     739       31592 :         const char * const no_attrs[] = { NULL };
     740             :         int ret;
     741             : 
     742       31592 :         ac->res_dn = NULL;
     743             : 
     744       31592 :         ret = dsdb_module_search(ac->module, ac, &res,
     745             :                                  ac->dn, LDB_SCOPE_BASE, no_attrs,
     746             :                                  DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT
     747             :                                  | DSDB_FLAG_NEXT_MODULE,
     748             :                                  ac->req,
     749             :                                  "(objectClass=classSchema)");
     750       31592 :         if (ret == LDB_ERR_NO_SUCH_OBJECT) {
     751             :                 /* Don't be pricky when the DN doesn't exist if we have the */
     752             :                 /* RELAX control specified */
     753         252 :                 if (ldb_request_get_control(ac->req,
     754             :                                             LDB_CONTROL_RELAX_OID) == NULL) {
     755           0 :                         ldb_set_errstring(ldb,
     756             :                                           "samldb_find_defaultObjectCategory: "
     757             :                                           "Invalid DN for 'defaultObjectCategory'!");
     758           0 :                         return LDB_ERR_CONSTRAINT_VIOLATION;
     759             :                 }
     760             :         }
     761       31592 :         if ((ret != LDB_ERR_NO_SUCH_OBJECT) && (ret != LDB_SUCCESS)) {
     762           0 :                 return ret;
     763             :         }
     764             : 
     765       31592 :         if (ret == LDB_SUCCESS) {
     766             :                 /* ensure the defaultObjectCategory has a full GUID */
     767             :                 struct ldb_message *m;
     768       31340 :                 m = ldb_msg_new(ac->msg);
     769       31340 :                 if (m == NULL) {
     770           0 :                         return ldb_oom(ldb);
     771             :                 }
     772       31340 :                 m->dn = ac->msg->dn;
     773       31340 :                 if (ldb_msg_add_string(m, "defaultObjectCategory",
     774       31340 :                                        ldb_dn_get_extended_linearized(m, res->msgs[0]->dn, 1)) !=
     775             :                     LDB_SUCCESS) {
     776           0 :                         return ldb_oom(ldb);
     777             :                 }
     778       31340 :                 m->elements[0].flags = LDB_FLAG_MOD_REPLACE;
     779             : 
     780       31340 :                 ret = dsdb_module_modify(ac->module, m,
     781             :                                          DSDB_FLAG_NEXT_MODULE,
     782             :                                          ac->req);
     783       31340 :                 if (ret != LDB_SUCCESS) {
     784           0 :                         return ret;
     785             :                 }
     786             :         }
     787             : 
     788             : 
     789       31592 :         ac->res_dn = ac->dn;
     790             : 
     791       31592 :         return samldb_next_step(ac);
     792             : }
     793             : 
     794             : /**
     795             :  * msDS-IntId attributeSchema attribute handling
     796             :  * during LDB_ADD request processing
     797             :  */
     798      174362 : static int samldb_add_handle_msDS_IntId(struct samldb_ctx *ac)
     799             : {
     800             :         int ret;
     801             :         bool id_exists;
     802             :         uint32_t msds_intid;
     803             :         int32_t system_flags;
     804             :         struct ldb_context *ldb;
     805             :         struct ldb_result *ldb_res;
     806             :         struct ldb_dn *schema_dn;
     807             :         struct samldb_msds_intid_persistant *msds_intid_struct;
     808             :         struct dsdb_schema *schema;
     809             : 
     810      174362 :         ldb = ldb_module_get_ctx(ac->module);
     811      174362 :         schema_dn = ldb_get_schema_basedn(ldb);
     812             : 
     813             :         /* replicated update should always go through */
     814      174362 :         if (ldb_request_get_control(ac->req,
     815             :                                     DSDB_CONTROL_REPLICATED_UPDATE_OID)) {
     816           0 :                 return LDB_SUCCESS;
     817             :         }
     818             : 
     819             :         /* msDS-IntId is handled by system and should never be
     820             :          * passed by clients */
     821      174362 :         if (ldb_msg_find_element(ac->msg, "msDS-IntId")) {
     822          18 :                 return LDB_ERR_UNWILLING_TO_PERFORM;
     823             :         }
     824             : 
     825             :         /* do not generate msDS-IntId if Relax control is passed */
     826      174344 :         if (ldb_request_get_control(ac->req, LDB_CONTROL_RELAX_OID)) {
     827      143166 :                 return LDB_SUCCESS;
     828             :         }
     829             : 
     830             :         /* check Functional Level */
     831         245 :         if (dsdb_functional_level(ldb) < DS_DOMAIN_FUNCTION_2003) {
     832          53 :                 return LDB_SUCCESS;
     833             :         }
     834             : 
     835             :         /* check systemFlags for SCHEMA_BASE_OBJECT flag */
     836         192 :         system_flags = ldb_msg_find_attr_as_int(ac->msg, "systemFlags", 0);
     837         192 :         if (system_flags & SYSTEM_FLAG_SCHEMA_BASE_OBJECT) {
     838           0 :                 return LDB_SUCCESS;
     839             :         }
     840         192 :         schema = dsdb_get_schema(ldb, NULL);
     841         192 :         if (!schema) {
     842           0 :                 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
     843             :                               "samldb_schema_info_update: no dsdb_schema loaded");
     844           0 :                 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
     845           0 :                 return ldb_operr(ldb);
     846             :         }
     847             : 
     848         192 :         msds_intid_struct = (struct samldb_msds_intid_persistant*) ldb_get_opaque(ldb, SAMLDB_MSDS_INTID_OPAQUE);
     849         192 :         if (!msds_intid_struct) {
     850         136 :                 msds_intid_struct = talloc(ldb, struct samldb_msds_intid_persistant);
     851             :                 /* Generate new value for msDs-IntId
     852             :                 * Value should be in 0x80000000..0xBFFFFFFF range */
     853         136 :                 msds_intid = generate_random() % 0X3FFFFFFF;
     854         136 :                 msds_intid += 0x80000000;
     855         136 :                 msds_intid_struct->msds_intid = msds_intid;
     856         136 :                 DEBUG(2, ("No samldb_msds_intid_persistant struct, allocating a new one\n"));
     857             :         } else {
     858          56 :                 msds_intid = msds_intid_struct->msds_intid;
     859             :         }
     860             : 
     861             :         /* probe id values until unique one is found */
     862             :         do {
     863         192 :                 msds_intid++;
     864         192 :                 if (msds_intid > 0xBFFFFFFF) {
     865           0 :                         msds_intid = 0x80000001;
     866             :                 }
     867             :                 /*
     868             :                  * We search in the schema if we have already this
     869             :                  * intid (using dsdb_attribute_by_attributeID_id
     870             :                  * because in the range 0x80000000 0xBFFFFFFFF,
     871             :                  * attributeID is a DSDB_ATTID_TYPE_INTID).
     872             :                  *
     873             :                  * If so generate another random value.
     874             :                  *
     875             :                  * We have to check the DB in case someone else has
     876             :                  * modified the database while we are doing our
     877             :                  * changes too (this case should be very bery rare) in
     878             :                  * order to be sure.
     879             :                  */
     880         192 :                 if (dsdb_attribute_by_attributeID_id(schema, msds_intid)) {
     881           0 :                         id_exists = true;
     882           0 :                         msds_intid = generate_random() % 0X3FFFFFFF;
     883           0 :                         msds_intid += 0x80000000;
     884           0 :                         continue;
     885             :                 }
     886             : 
     887             : 
     888         192 :                 ret = dsdb_module_search(ac->module, ac,
     889             :                                          &ldb_res,
     890             :                                          schema_dn, LDB_SCOPE_ONELEVEL, NULL,
     891             :                                          DSDB_FLAG_NEXT_MODULE,
     892             :                                          ac->req,
     893             :                                          "(msDS-IntId=%d)", msds_intid);
     894         192 :                 if (ret != LDB_SUCCESS) {
     895           0 :                         ldb_debug_set(ldb, LDB_DEBUG_ERROR,
     896             :                                       __location__": Searching for msDS-IntId=%d failed - %s\n",
     897             :                                       msds_intid,
     898             :                                       ldb_errstring(ldb));
     899           0 :                         return ldb_operr(ldb);
     900             :                 }
     901         192 :                 id_exists = (ldb_res->count > 0);
     902         192 :                 talloc_free(ldb_res);
     903             : 
     904         192 :         } while(id_exists);
     905         192 :         msds_intid_struct->msds_intid = msds_intid;
     906         192 :         ldb_set_opaque(ldb, SAMLDB_MSDS_INTID_OPAQUE, msds_intid_struct);
     907             : 
     908         192 :         return samdb_msg_add_int(ldb, ac->msg, ac->msg, "msDS-IntId",
     909             :                                  msds_intid);
     910             : }
     911             : 
     912             : 
     913             : /*
     914             :  * samldb_add_entry (async)
     915             :  */
     916             : 
     917      237167 : static int samldb_add_entry_callback(struct ldb_request *req,
     918             :                                         struct ldb_reply *ares)
     919             : {
     920             :         struct ldb_context *ldb;
     921             :         struct samldb_ctx *ac;
     922             :         int ret;
     923             : 
     924      237167 :         ac = talloc_get_type(req->context, struct samldb_ctx);
     925      237167 :         ldb = ldb_module_get_ctx(ac->module);
     926             : 
     927      237167 :         if (!ares) {
     928           0 :                 return ldb_module_done(ac->req, NULL, NULL,
     929             :                                         LDB_ERR_OPERATIONS_ERROR);
     930             :         }
     931             : 
     932      237167 :         if (ares->type == LDB_REPLY_REFERRAL) {
     933           0 :                 return ldb_module_send_referral(ac->req, ares->referral);
     934             :         }
     935             : 
     936      237167 :         if (ares->error != LDB_SUCCESS) {
     937         169 :                 return ldb_module_done(ac->req, ares->controls,
     938             :                                         ares->response, ares->error);
     939             :         }
     940      236998 :         if (ares->type != LDB_REPLY_DONE) {
     941           0 :                 ldb_asprintf_errstring(ldb, "Invalid LDB reply type %d", ares->type);
     942           0 :                 return ldb_module_done(ac->req, NULL, NULL,
     943             :                                         LDB_ERR_OPERATIONS_ERROR);
     944             :         }
     945             : 
     946             :         /* The caller may wish to get controls back from the add */
     947      236998 :         ac->ares = talloc_steal(ac, ares);
     948             : 
     949      236998 :         ret = samldb_next_step(ac);
     950      236998 :         if (ret != LDB_SUCCESS) {
     951           0 :                 return ldb_module_done(ac->req, NULL, NULL, ret);
     952             :         }
     953      198890 :         return ret;
     954             : }
     955             : 
     956      237155 : static int samldb_add_entry(struct samldb_ctx *ac)
     957             : {
     958             :         struct ldb_context *ldb;
     959             :         struct ldb_request *req;
     960             :         int ret;
     961             : 
     962      237155 :         ldb = ldb_module_get_ctx(ac->module);
     963             : 
     964      566049 :         ret = ldb_build_add_req(&req, ldb, ac,
     965      237155 :                                 ac->msg,
     966      199047 :                                 ac->req->controls,
     967             :                                 ac, samldb_add_entry_callback,
     968             :                                 ac->req);
     969      237155 :         LDB_REQ_SET_LOCATION(req);
     970      237155 :         if (ret != LDB_SUCCESS) {
     971           0 :                 return ret;
     972             :         }
     973             : 
     974      237155 :         return ldb_next_request(ac->module, req);
     975             : }
     976             : 
     977             : /*
     978             :  * return true if msg carries an attributeSchema that is intended to be RODC
     979             :  * filtered but is also a system-critical attribute.
     980             :  */
     981      205986 : static bool check_rodc_critical_attribute(struct ldb_message *msg)
     982             : {
     983             :         uint32_t schemaFlagsEx, searchFlags, rodc_filtered_flags;
     984             : 
     985      205986 :         schemaFlagsEx = ldb_msg_find_attr_as_uint(msg, "schemaFlagsEx", 0);
     986      205986 :         searchFlags = ldb_msg_find_attr_as_uint(msg, "searchFlags", 0);
     987      205986 :         rodc_filtered_flags = (SEARCH_FLAG_RODC_ATTRIBUTE
     988             :                               | SEARCH_FLAG_CONFIDENTIAL);
     989             : 
     990      242588 :         if ((schemaFlagsEx & SCHEMA_FLAG_ATTR_IS_CRITICAL) &&
     991       47111 :                 ((searchFlags & rodc_filtered_flags) == rodc_filtered_flags)) {
     992           0 :                 return true;
     993             :         } else {
     994      205986 :                 return false;
     995             :         }
     996             : }
     997             : 
     998             : 
     999      233533 : static int samldb_fill_object(struct samldb_ctx *ac)
    1000             : {
    1001      233533 :         struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
    1002             :         int ret;
    1003             : 
    1004             :         /* Add information for the different account types */
    1005      233533 :         switch(ac->type) {
    1006       19863 :         case SAMLDB_TYPE_USER: {
    1007       19863 :                 struct ldb_control *rodc_control = ldb_request_get_control(ac->req,
    1008             :                                                                            LDB_CONTROL_RODC_DCPROMO_OID);
    1009       19863 :                 if (rodc_control != NULL) {
    1010             :                         /* see [MS-ADTS] 3.1.1.3.4.1.23 LDAP_SERVER_RODC_DCPROMO_OID */
    1011          41 :                         rodc_control->critical = false;
    1012          41 :                         ret = samldb_add_step(ac, samldb_rodc_add);
    1013          41 :                         if (ret != LDB_SUCCESS) return ret;
    1014             :                 }
    1015             : 
    1016             :                 /* check if we have a valid sAMAccountName */
    1017       19863 :                 ret = samldb_add_step(ac, samldb_check_sAMAccountName);
    1018       19863 :                 if (ret != LDB_SUCCESS) return ret;
    1019             : 
    1020       19863 :                 ret = samldb_add_step(ac, samldb_add_entry);
    1021       19863 :                 if (ret != LDB_SUCCESS) return ret;
    1022       19646 :                 break;
    1023             :         }
    1024             : 
    1025        7684 :         case SAMLDB_TYPE_GROUP: {
    1026             :                 /* check if we have a valid sAMAccountName */
    1027        7684 :                 ret = samldb_add_step(ac, samldb_check_sAMAccountName);
    1028        7684 :                 if (ret != LDB_SUCCESS) return ret;
    1029             : 
    1030        7684 :                 ret = samldb_add_step(ac, samldb_add_entry);
    1031        7684 :                 if (ret != LDB_SUCCESS) return ret;
    1032        6900 :                 break;
    1033             :         }
    1034             : 
    1035       31624 :         case SAMLDB_TYPE_CLASS: {
    1036       31624 :                 const char *lDAPDisplayName = NULL;
    1037             :                 const struct ldb_val *rdn_value, *def_obj_cat_val;
    1038       31624 :                 unsigned int v = ldb_msg_find_attr_as_uint(ac->msg, "objectClassCategory", -2);
    1039             : 
    1040             :                 /* As discussed with Microsoft through dochelp in April 2012 this is the behavior of windows*/
    1041       31624 :                 if (!ldb_msg_find_element(ac->msg, "subClassOf")) {
    1042         162 :                         ret = ldb_msg_add_string(ac->msg, "subClassOf", "top");
    1043         162 :                         if (ret != LDB_SUCCESS) return ret;
    1044             :                 }
    1045             : 
    1046       31624 :                 ret = samdb_find_or_add_attribute(ldb, ac->msg,
    1047             :                                                   "rdnAttId", "cn");
    1048       31624 :                 if (ret != LDB_SUCCESS) return ret;
    1049             : 
    1050             :                 /* do not allow one to mark an attributeSchema as RODC filtered if it
    1051             :                  * is system-critical */
    1052       31624 :                 if (check_rodc_critical_attribute(ac->msg)) {
    1053           0 :                         ldb_asprintf_errstring(ldb, "Refusing schema add of %s - cannot combine critical class with RODC filtering",
    1054           0 :                                                ldb_dn_get_linearized(ac->msg->dn));
    1055           0 :                         return LDB_ERR_UNWILLING_TO_PERFORM;
    1056             :                 }
    1057             : 
    1058       31624 :                 rdn_value = ldb_dn_get_rdn_val(ac->msg->dn);
    1059       31624 :                 if (rdn_value == NULL) {
    1060           0 :                         return ldb_operr(ldb);
    1061             :                 }
    1062       31624 :                 if (!ldb_msg_find_element(ac->msg, "lDAPDisplayName")) {
    1063             :                         /* the RDN has prefix "CN" */
    1064         364 :                         ret = ldb_msg_add_string(ac->msg, "lDAPDisplayName",
    1065         364 :                                 samdb_cn_to_lDAPDisplayName(ac->msg,
    1066         364 :                                                             (const char *) rdn_value->data));
    1067         364 :                         if (ret != LDB_SUCCESS) {
    1068           0 :                                 ldb_oom(ldb);
    1069           0 :                                 return ret;
    1070             :                         }
    1071             :                 }
    1072             : 
    1073       31624 :                 lDAPDisplayName = ldb_msg_find_attr_as_string(ac->msg,
    1074             :                                                               "lDAPDisplayName",
    1075             :                                                               NULL);
    1076       31624 :                 ret = ldb_valid_attr_name(lDAPDisplayName);
    1077       56204 :                 if (ret != 1 ||
    1078       56204 :                     lDAPDisplayName[0] == '*' ||
    1079       26080 :                     lDAPDisplayName[0] == '@')
    1080             :                 {
    1081           0 :                         return dsdb_module_werror(ac->module,
    1082             :                                                   LDB_ERR_UNWILLING_TO_PERFORM,
    1083             :                                                   WERR_DS_INVALID_LDAP_DISPLAY_NAME,
    1084             :                                                   "lDAPDisplayName is invalid");
    1085             :                 }
    1086             : 
    1087       31624 :                 if (!ldb_msg_find_element(ac->msg, "schemaIDGUID")) {
    1088             :                         struct GUID guid;
    1089             :                         /* a new GUID */
    1090         428 :                         guid = GUID_random();
    1091         428 :                         ret = dsdb_msg_add_guid(ac->msg, &guid, "schemaIDGUID");
    1092         428 :                         if (ret != LDB_SUCCESS) {
    1093           0 :                                 ldb_oom(ldb);
    1094           0 :                                 return ret;
    1095             :                         }
    1096             :                 }
    1097             : 
    1098       31624 :                 def_obj_cat_val = ldb_msg_find_ldb_val(ac->msg,
    1099             :                                                        "defaultObjectCategory");
    1100       31624 :                 if (def_obj_cat_val != NULL) {
    1101             :                         /* "defaultObjectCategory" has been set by the caller.
    1102             :                          * Do some checks for consistency.
    1103             :                          * NOTE: The real constraint check (that
    1104             :                          * 'defaultObjectCategory' is the DN of the new
    1105             :                          * objectclass or any parent of it) is still incomplete.
    1106             :                          * For now we say that 'defaultObjectCategory' is valid
    1107             :                          * if it exists and it is of objectclass "classSchema".
    1108             :                          */
    1109       31324 :                         ac->dn = ldb_dn_from_ldb_val(ac, ldb, def_obj_cat_val);
    1110       31324 :                         if (ac->dn == NULL) {
    1111           0 :                                 ldb_set_errstring(ldb,
    1112             :                                                   "Invalid DN for 'defaultObjectCategory'!");
    1113           0 :                                 return LDB_ERR_CONSTRAINT_VIOLATION;
    1114             :                         }
    1115             :                 } else {
    1116             :                         /* "defaultObjectCategory" has not been set by the
    1117             :                          * caller. Use the entry DN for it. */
    1118         300 :                         ac->dn = ac->msg->dn;
    1119             : 
    1120         300 :                         ret = ldb_msg_add_string(ac->msg, "defaultObjectCategory",
    1121         300 :                                                  ldb_dn_alloc_linearized(ac->msg, ac->dn));
    1122         300 :                         if (ret != LDB_SUCCESS) {
    1123           0 :                                 ldb_oom(ldb);
    1124           0 :                                 return ret;
    1125             :                         }
    1126             :                 }
    1127             : 
    1128       31624 :                 ret = samldb_add_step(ac, samldb_add_entry);
    1129       31624 :                 if (ret != LDB_SUCCESS) return ret;
    1130             : 
    1131             :                 /* Now perform the checks for the 'defaultObjectCategory'. The
    1132             :                  * lookup DN was already saved in "ac->dn" */
    1133       31624 :                 ret = samldb_add_step(ac, samldb_find_for_defaultObjectCategory);
    1134       31624 :                 if (ret != LDB_SUCCESS) return ret;
    1135             : 
    1136             :                 /* -2 is not a valid objectClassCategory so it means the attribute wasn't present */
    1137       31624 :                 if (v == -2) {
    1138             :                         /* Windows 2003 does this*/
    1139          10 :                         ret = samdb_msg_add_uint(ldb, ac->msg, ac->msg, "objectClassCategory", 0);
    1140          10 :                         if (ret != LDB_SUCCESS) {
    1141           0 :                                 return ret;
    1142             :                         }
    1143             :                 }
    1144       26080 :                 break;
    1145             :         }
    1146             : 
    1147      174362 :         case SAMLDB_TYPE_ATTRIBUTE: {
    1148      174362 :                 const char *lDAPDisplayName = NULL;
    1149             :                 const struct ldb_val *rdn_value;
    1150             :                 struct ldb_message_element *el;
    1151      174362 :                 rdn_value = ldb_dn_get_rdn_val(ac->msg->dn);
    1152      174362 :                 if (rdn_value == NULL) {
    1153           0 :                         return ldb_operr(ldb);
    1154             :                 }
    1155      174362 :                 if (!ldb_msg_find_element(ac->msg, "lDAPDisplayName")) {
    1156             :                         /* the RDN has prefix "CN" */
    1157         106 :                         ret = ldb_msg_add_string(ac->msg, "lDAPDisplayName",
    1158         106 :                                 samdb_cn_to_lDAPDisplayName(ac->msg,
    1159         106 :                                                             (const char *) rdn_value->data));
    1160         106 :                         if (ret != LDB_SUCCESS) {
    1161           0 :                                 ldb_oom(ldb);
    1162           0 :                                 return ret;
    1163             :                         }
    1164             :                 }
    1165             : 
    1166      174362 :                 lDAPDisplayName = ldb_msg_find_attr_as_string(ac->msg,
    1167             :                                                               "lDAPDisplayName",
    1168             :                                                               NULL);
    1169      174362 :                 ret = ldb_valid_attr_name(lDAPDisplayName);
    1170      309807 :                 if (ret != 1 ||
    1171      309807 :                     lDAPDisplayName[0] == '*' ||
    1172      143429 :                     lDAPDisplayName[0] == '@')
    1173             :                 {
    1174           0 :                         return dsdb_module_werror(ac->module,
    1175             :                                                   LDB_ERR_UNWILLING_TO_PERFORM,
    1176             :                                                   WERR_DS_INVALID_LDAP_DISPLAY_NAME,
    1177             :                                                   "lDAPDisplayName is invalid");
    1178             :                 }
    1179             : 
    1180             :                 /* do not allow one to mark an attributeSchema as RODC filtered if it
    1181             :                  * is system-critical */
    1182      174362 :                 if (check_rodc_critical_attribute(ac->msg)) {
    1183           0 :                         ldb_asprintf_errstring(ldb,
    1184             :                                                "samldb: refusing schema add of %s - cannot combine critical attribute with RODC filtering",
    1185           0 :                                                ldb_dn_get_linearized(ac->msg->dn));
    1186           0 :                         return LDB_ERR_UNWILLING_TO_PERFORM;
    1187             :                 }
    1188             : 
    1189      174362 :                 ret = samdb_find_or_add_attribute(ldb, ac->msg,
    1190             :                                                   "isSingleValued", "FALSE");
    1191      174362 :                 if (ret != LDB_SUCCESS) return ret;
    1192             : 
    1193      174362 :                 if (!ldb_msg_find_element(ac->msg, "schemaIDGUID")) {
    1194             :                         struct GUID guid;
    1195             :                         /* a new GUID */
    1196         263 :                         guid = GUID_random();
    1197         263 :                         ret = dsdb_msg_add_guid(ac->msg, &guid, "schemaIDGUID");
    1198         263 :                         if (ret != LDB_SUCCESS) {
    1199           0 :                                 ldb_oom(ldb);
    1200           0 :                                 return ret;
    1201             :                         }
    1202             :                 }
    1203             : 
    1204      174362 :                 el = ldb_msg_find_element(ac->msg, "attributeSyntax");
    1205      174362 :                 if (el) {
    1206             :                         /*
    1207             :                          * No need to scream if there isn't as we have code later on
    1208             :                          * that will take care of it.
    1209             :                          */
    1210      174362 :                         const struct dsdb_syntax *syntax = find_syntax_map_by_ad_oid((const char *)el->values[0].data);
    1211      174362 :                         if (!syntax) {
    1212           0 :                                 DEBUG(9, ("Can't find dsdb_syntax object for attributeSyntax %s\n",
    1213             :                                                 (const char *)el->values[0].data));
    1214             :                         } else {
    1215      174362 :                                 unsigned int v = ldb_msg_find_attr_as_uint(ac->msg, "oMSyntax", 0);
    1216      174362 :                                 const struct ldb_val *val = ldb_msg_find_ldb_val(ac->msg, "oMObjectClass");
    1217             : 
    1218      174362 :                                 if (v == 0) {
    1219           0 :                                         ret = samdb_msg_add_uint(ldb, ac->msg, ac->msg, "oMSyntax", syntax->oMSyntax);
    1220           0 :                                         if (ret != LDB_SUCCESS) {
    1221           0 :                                                 return ret;
    1222             :                                         }
    1223             :                                 }
    1224      174362 :                                 if (!val) {
    1225      150104 :                                         struct ldb_val val2 = ldb_val_dup(ldb, &syntax->oMObjectClass);
    1226      150104 :                                         if (val2.length > 0) {
    1227          57 :                                                 ret = ldb_msg_add_value(ac->msg, "oMObjectClass", &val2, NULL);
    1228          57 :                                                 if (ret != LDB_SUCCESS) {
    1229           0 :                                                         return ret;
    1230             :                                                 }
    1231             :                                         }
    1232             :                                 }
    1233             :                         }
    1234             :                 }
    1235             : 
    1236             :                 /* handle msDS-IntID attribute */
    1237      174362 :                 ret = samldb_add_handle_msDS_IntId(ac);
    1238      174362 :                 if (ret != LDB_SUCCESS) return ret;
    1239             : 
    1240      174344 :                 ret = samldb_add_step(ac, samldb_add_entry);
    1241      174344 :                 if (ret != LDB_SUCCESS) return ret;
    1242      143411 :                 break;
    1243             :         }
    1244             : 
    1245           0 :         default:
    1246           0 :                 ldb_asprintf_errstring(ldb, "Invalid entry type!");
    1247           0 :                 return LDB_ERR_OPERATIONS_ERROR;
    1248             :                 break;
    1249             :         }
    1250             : 
    1251      233515 :         return samldb_first_step(ac);
    1252             : }
    1253             : 
    1254        3649 : static int samldb_fill_foreignSecurityPrincipal_object(struct samldb_ctx *ac)
    1255             : {
    1256        3649 :         struct ldb_context *ldb = NULL;
    1257        3649 :         const struct ldb_val *rdn_value = NULL;
    1258        3649 :         struct ldb_message_element *sid_el = NULL;
    1259        3649 :         struct dom_sid *sid = NULL;
    1260        3649 :         struct ldb_control *as_system = NULL;
    1261        3649 :         struct ldb_control *provision = NULL;
    1262        3649 :         bool allowed = false;
    1263             :         int ret;
    1264             : 
    1265        3649 :         ldb = ldb_module_get_ctx(ac->module);
    1266             : 
    1267        3649 :         as_system = ldb_request_get_control(ac->req, LDB_CONTROL_AS_SYSTEM_OID);
    1268        3649 :         if (as_system != NULL) {
    1269          15 :                 allowed = true;
    1270             :         }
    1271             : 
    1272        3649 :         provision = ldb_request_get_control(ac->req, LDB_CONTROL_PROVISION_OID);
    1273        3649 :         if (provision != NULL) {
    1274        3632 :                 allowed = true;
    1275             :         }
    1276             : 
    1277        3649 :         sid_el = ldb_msg_find_element(ac->msg, "objectSid");
    1278             : 
    1279        3649 :         if (!allowed && sid_el == NULL) {
    1280           1 :                 return dsdb_module_werror(ac->module,
    1281             :                                 LDB_ERR_OBJECT_CLASS_VIOLATION,
    1282             :                                 WERR_DS_MISSING_REQUIRED_ATT,
    1283             :                                 "objectSid missing on foreignSecurityPrincipal");
    1284             :         }
    1285             : 
    1286        3648 :         if (!allowed) {
    1287           1 :                 return dsdb_module_werror(ac->module,
    1288             :                                 LDB_ERR_UNWILLING_TO_PERFORM,
    1289             :                                 WERR_DS_ILLEGAL_MOD_OPERATION,
    1290             :                                 "foreignSecurityPrincipal object not allowed");
    1291             :         }
    1292             : 
    1293        3647 :         if (sid_el != NULL) {
    1294        3630 :                 sid = samdb_result_dom_sid(ac->msg, ac->msg, "objectSid");
    1295        3630 :                 if (sid == NULL) {
    1296           0 :                         ldb_set_errstring(ldb,
    1297             :                                           "samldb: invalid objectSid!");
    1298           0 :                         return LDB_ERR_CONSTRAINT_VIOLATION;
    1299             :                 }
    1300             :         }
    1301             : 
    1302        3017 :         if (sid == NULL) {
    1303          17 :                 rdn_value = ldb_dn_get_rdn_val(ac->msg->dn);
    1304          17 :                 if (rdn_value == NULL) {
    1305           0 :                         return ldb_operr(ldb);
    1306             :                 }
    1307          17 :                 sid = dom_sid_parse_talloc(ac->msg,
    1308          17 :                                            (const char *)rdn_value->data);
    1309          17 :                 if (sid == NULL) {
    1310           0 :                         ldb_set_errstring(ldb,
    1311             :                                           "samldb: No valid SID found in ForeignSecurityPrincipal CN!");
    1312           0 :                         return LDB_ERR_CONSTRAINT_VIOLATION;
    1313             :                 }
    1314          17 :                 if (! samldb_msg_add_sid(ac->msg, "objectSid", sid)) {
    1315           0 :                         return ldb_operr(ldb);
    1316             :                 }
    1317             :         }
    1318             : 
    1319             :         /* finally proceed with adding the entry */
    1320        3647 :         ret = samldb_add_step(ac, samldb_add_entry);
    1321        3647 :         if (ret != LDB_SUCCESS) return ret;
    1322             : 
    1323        3647 :         return samldb_first_step(ac);
    1324             : }
    1325             : 
    1326      205986 : static int samldb_schema_info_update(struct samldb_ctx *ac)
    1327             : {
    1328             :         int ret;
    1329             :         struct ldb_context *ldb;
    1330             :         struct dsdb_schema *schema;
    1331             : 
    1332             :         /* replicated update should always go through */
    1333      205986 :         if (ldb_request_get_control(ac->req,
    1334             :                                     DSDB_CONTROL_REPLICATED_UPDATE_OID)) {
    1335           0 :                 return LDB_SUCCESS;
    1336             :         }
    1337             : 
    1338             :         /* do not update schemaInfo during provisioning */
    1339      205986 :         if (ldb_request_get_control(ac->req, LDB_CONTROL_PROVISION_OID)) {
    1340      168030 :                 return LDB_SUCCESS;
    1341             :         }
    1342             : 
    1343        1479 :         ldb = ldb_module_get_ctx(ac->module);
    1344        1479 :         schema = dsdb_get_schema(ldb, NULL);
    1345        1479 :         if (!schema) {
    1346           0 :                 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
    1347             :                               "samldb_schema_info_update: no dsdb_schema loaded");
    1348           0 :                 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
    1349           0 :                 return ldb_operr(ldb);
    1350             :         }
    1351             : 
    1352        1479 :         ret = dsdb_module_schema_info_update(ac->module, schema,
    1353             :                                              DSDB_FLAG_NEXT_MODULE|
    1354             :                                              DSDB_FLAG_AS_SYSTEM,
    1355             :                                              ac->req);
    1356        1479 :         if (ret != LDB_SUCCESS) {
    1357           0 :                 ldb_asprintf_errstring(ldb,
    1358             :                                        "samldb_schema_info_update: dsdb_module_schema_info_update failed with %s",
    1359             :                                        ldb_errstring(ldb));
    1360           0 :                 return ret;
    1361             :         }
    1362             : 
    1363        1479 :         return LDB_SUCCESS;
    1364             : }
    1365             : 
    1366             : static int samldb_prim_group_tester(struct samldb_ctx *ac, uint32_t rid);
    1367             : static int samldb_check_user_account_control_rules(struct samldb_ctx *ac,
    1368             :                                                    struct dom_sid *sid,
    1369             :                                                    uint32_t req_uac,
    1370             :                                                    uint32_t user_account_control,
    1371             :                                                    uint32_t user_account_control_old);
    1372             : 
    1373             : /*
    1374             :  * "Objectclass" trigger (MS-SAMR 3.1.1.8.1)
    1375             :  *
    1376             :  * Has to be invoked on "add" and "modify" operations on "user", "computer" and
    1377             :  * "group" objects.
    1378             :  * ac->msg contains the "add"/"modify" message
    1379             :  * ac->type contains the object type (main objectclass)
    1380             :  */
    1381       27581 : static int samldb_objectclass_trigger(struct samldb_ctx *ac)
    1382             : {
    1383       27581 :         struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
    1384       27581 :         void *skip_allocate_sids = ldb_get_opaque(ldb,
    1385             :                                                   "skip_allocate_sids");
    1386             :         struct ldb_message_element *el, *el2;
    1387             :         struct dom_sid *sid;
    1388             :         int ret;
    1389             : 
    1390             :         /* make sure that "sAMAccountType" is not specified */
    1391       27581 :         el = ldb_msg_find_element(ac->msg, "sAMAccountType");
    1392       27581 :         if (el != NULL) {
    1393           1 :                 ldb_set_errstring(ldb,
    1394             :                                   "samldb: sAMAccountType must not be specified!");
    1395           1 :                 return LDB_ERR_UNWILLING_TO_PERFORM;
    1396             :         }
    1397             : 
    1398             :         /* Step 1: objectSid assignment */
    1399             : 
    1400             :         /* Don't allow the objectSid to be changed. But beside the RELAX
    1401             :          * control we have also to guarantee that it can always be set with
    1402             :          * SYSTEM permissions. This is needed for the "samba3sam" backend. */
    1403       27580 :         sid = samdb_result_dom_sid(ac, ac->msg, "objectSid");
    1404       32302 :         if ((sid != NULL) && (!dsdb_module_am_system(ac->module)) &&
    1405        4722 :             (ldb_request_get_control(ac->req, LDB_CONTROL_RELAX_OID) == NULL)) {
    1406           0 :                 ldb_set_errstring(ldb,
    1407             :                                   "samldb: objectSid must not be specified!");
    1408           0 :                 return LDB_ERR_UNWILLING_TO_PERFORM;
    1409             :         }
    1410             : 
    1411             :         /* but generate a new SID when we do have an add operations */
    1412       27580 :         if ((sid == NULL) && (ac->req->operation == LDB_ADD) && !skip_allocate_sids) {
    1413       22804 :                 ret = samldb_add_step(ac, samldb_allocate_sid);
    1414       22804 :                 if (ret != LDB_SUCCESS) return ret;
    1415             :         }
    1416             : 
    1417       27580 :         switch(ac->type) {
    1418       19890 :         case SAMLDB_TYPE_USER: {
    1419       19890 :                 bool uac_generated = false, uac_add_flags = false;
    1420             : 
    1421             :                 /* Step 1.2: Default values */
    1422       19890 :                 ret = dsdb_user_obj_set_defaults(ldb, ac->msg, ac->req);
    1423       19890 :                 if (ret != LDB_SUCCESS) return ret;
    1424             : 
    1425             :                 /* On add operations we might need to generate a
    1426             :                  * "userAccountControl" (if it isn't specified). */
    1427       19890 :                 el = ldb_msg_find_element(ac->msg, "userAccountControl");
    1428       19890 :                 if ((el == NULL) && (ac->req->operation == LDB_ADD)) {
    1429       16415 :                         ret = samdb_msg_set_uint(ldb, ac->msg, ac->msg,
    1430             :                                                  "userAccountControl",
    1431             :                                                  UF_NORMAL_ACCOUNT);
    1432       16415 :                         if (ret != LDB_SUCCESS) {
    1433           0 :                                 return ret;
    1434             :                         }
    1435       16362 :                         uac_generated = true;
    1436       16362 :                         uac_add_flags = true;
    1437             :                 }
    1438             : 
    1439       19890 :                 el = ldb_msg_find_element(ac->msg, "userAccountControl");
    1440       19890 :                 if (el != NULL) {
    1441             :                         uint32_t raw_uac;
    1442             :                         uint32_t user_account_control;
    1443             :                         /* Step 1.3: "userAccountControl" -> "sAMAccountType" mapping */
    1444       19890 :                         user_account_control = ldb_msg_find_attr_as_uint(ac->msg,
    1445             :                                                                          "userAccountControl",
    1446             :                                                                          0);
    1447       19890 :                         raw_uac = user_account_control;
    1448             :                         /*
    1449             :                          * "userAccountControl" = 0 or missing one of
    1450             :                          * the types means "UF_NORMAL_ACCOUNT".  See
    1451             :                          * MS-SAMR 3.1.1.8.10 point 8
    1452             :                          */
    1453       19890 :                         if ((user_account_control & UF_ACCOUNT_TYPE_MASK) == 0) {
    1454          30 :                                 user_account_control = UF_NORMAL_ACCOUNT | user_account_control;
    1455          30 :                                 uac_generated = true;
    1456             :                         }
    1457             : 
    1458             :                         /*
    1459             :                          * As per MS-SAMR 3.1.1.8.10 these flags have not to be set
    1460             :                          */
    1461       19890 :                         if ((user_account_control & UF_LOCKOUT) != 0) {
    1462           7 :                                 user_account_control &= ~UF_LOCKOUT;
    1463           7 :                                 uac_generated = true;
    1464             :                         }
    1465       19890 :                         if ((user_account_control & UF_PASSWORD_EXPIRED) != 0) {
    1466           7 :                                 user_account_control &= ~UF_PASSWORD_EXPIRED;
    1467           7 :                                 uac_generated = true;
    1468             :                         }
    1469             : 
    1470       19890 :                         ret = samldb_check_user_account_control_rules(ac, NULL,
    1471             :                                                                       raw_uac,
    1472             :                                                                       user_account_control,
    1473             :                                                                       0);
    1474       19890 :                         if (ret != LDB_SUCCESS) {
    1475          18 :                                 return ret;
    1476             :                         }
    1477             : 
    1478             :                         /* Workstation and (read-only) DC objects do need objectclass "computer" */
    1479       19872 :                         if ((samdb_find_attribute(ldb, ac->msg,
    1480       17945 :                                                   "objectclass", "computer") == NULL) &&
    1481       17945 :                             (user_account_control &
    1482             :                              (UF_SERVER_TRUST_ACCOUNT | UF_WORKSTATION_TRUST_ACCOUNT))) {
    1483           9 :                                 ldb_set_errstring(ldb,
    1484             :                                                   "samldb: Requested account type does need objectclass 'computer'!");
    1485           9 :                                 return LDB_ERR_OBJECT_CLASS_VIOLATION;
    1486             :                         }
    1487             : 
    1488             :                         /* add "sAMAccountType" attribute */
    1489       19863 :                         ret = dsdb_user_obj_set_account_type(ldb, ac->msg, user_account_control, NULL);
    1490       19863 :                         if (ret != LDB_SUCCESS) {
    1491           0 :                                 return ret;
    1492             :                         }
    1493             : 
    1494             :                         /* "isCriticalSystemObject" might be set */
    1495       19863 :                         if (user_account_control &
    1496             :                             (UF_SERVER_TRUST_ACCOUNT | UF_PARTIAL_SECRETS_ACCOUNT)) {
    1497         687 :                                 ret = ldb_msg_add_string(ac->msg, "isCriticalSystemObject",
    1498             :                                                          "TRUE");
    1499         687 :                                 if (ret != LDB_SUCCESS) {
    1500           0 :                                         return ret;
    1501             :                                 }
    1502         687 :                                 el2 = ldb_msg_find_element(ac->msg,
    1503             :                                                            "isCriticalSystemObject");
    1504         687 :                                 el2->flags = LDB_FLAG_MOD_REPLACE;
    1505       19176 :                         } else if (user_account_control & UF_WORKSTATION_TRUST_ACCOUNT) {
    1506        1054 :                                 ret = ldb_msg_add_string(ac->msg, "isCriticalSystemObject",
    1507             :                                                          "FALSE");
    1508        1054 :                                 if (ret != LDB_SUCCESS) {
    1509           0 :                                         return ret;
    1510             :                                 }
    1511        1054 :                                 el2 = ldb_msg_find_element(ac->msg,
    1512             :                                                            "isCriticalSystemObject");
    1513        1054 :                                 el2->flags = LDB_FLAG_MOD_REPLACE;
    1514             :                         }
    1515             : 
    1516             :                         /* Step 1.4: "userAccountControl" -> "primaryGroupID" mapping */
    1517       19863 :                         if (!ldb_msg_find_element(ac->msg, "primaryGroupID")) {
    1518             :                                 uint32_t rid;
    1519             : 
    1520       19741 :                                 ret = dsdb_user_obj_set_primary_group_id(ldb, ac->msg, user_account_control, &rid);
    1521       19741 :                                 if (ret != LDB_SUCCESS) {
    1522           0 :                                         return ret;
    1523             :                                 }
    1524             :                                 /*
    1525             :                                  * Older AD deployments don't know about the
    1526             :                                  * RODC group
    1527             :                                  */
    1528       19741 :                                 if (rid == DOMAIN_RID_READONLY_DCS) {
    1529         100 :                                         ret = samldb_prim_group_tester(ac, rid);
    1530         100 :                                         if (ret != LDB_SUCCESS) {
    1531           0 :                                                 return ret;
    1532             :                                         }
    1533             :                                 }
    1534             :                         }
    1535             : 
    1536             :                         /* Step 1.5: Add additional flags when needed */
    1537             :                         /* Obviously this is done when the "userAccountControl"
    1538             :                          * has been generated here (tested against Windows
    1539             :                          * Server) */
    1540       19863 :                         if (uac_generated) {
    1541       16453 :                                 if (uac_add_flags) {
    1542       16415 :                                         user_account_control |= UF_ACCOUNTDISABLE;
    1543       16415 :                                         user_account_control |= UF_PASSWD_NOTREQD;
    1544             :                                 }
    1545             : 
    1546       16453 :                                 ret = samdb_msg_set_uint(ldb, ac->msg, ac->msg,
    1547             :                                                          "userAccountControl",
    1548             :                                                          user_account_control);
    1549       16453 :                                 if (ret != LDB_SUCCESS) {
    1550           0 :                                         return ret;
    1551             :                                 }
    1552             :                         }
    1553             : 
    1554             :                 }
    1555       19646 :                 break;
    1556             :         }
    1557             : 
    1558        7690 :         case SAMLDB_TYPE_GROUP: {
    1559             :                 const char *tempstr;
    1560             : 
    1561             :                 /* Step 2.2: Default values */
    1562        7690 :                 tempstr = talloc_asprintf(ac->msg, "%d",
    1563             :                                           GTYPE_SECURITY_GLOBAL_GROUP);
    1564        7690 :                 if (tempstr == NULL) return ldb_operr(ldb);
    1565        7690 :                 ret = samdb_find_or_add_attribute(ldb, ac->msg,
    1566             :                         "groupType", tempstr);
    1567        7690 :                 if (ret != LDB_SUCCESS) return ret;
    1568             : 
    1569             :                 /* Step 2.3: "groupType" -> "sAMAccountType" */
    1570        7690 :                 el = ldb_msg_find_element(ac->msg, "groupType");
    1571        7690 :                 if (el != NULL) {
    1572             :                         uint32_t group_type, account_type;
    1573             : 
    1574        7690 :                         group_type = ldb_msg_find_attr_as_uint(ac->msg,
    1575             :                                                                "groupType", 0);
    1576             : 
    1577             :                         /* The creation of builtin groups requires the
    1578             :                          * RELAX control */
    1579        7690 :                         if (group_type == GTYPE_SECURITY_BUILTIN_LOCAL_GROUP) {
    1580        2544 :                                 if (ldb_request_get_control(ac->req,
    1581             :                                                             LDB_CONTROL_RELAX_OID) == NULL) {
    1582           3 :                                         return LDB_ERR_UNWILLING_TO_PERFORM;
    1583             :                                 }
    1584             :                         }
    1585             : 
    1586        7687 :                         account_type = ds_gtype2atype(group_type);
    1587        7687 :                         if (account_type == 0) {
    1588           3 :                                 ldb_set_errstring(ldb, "samldb: Unrecognized account type!");
    1589           3 :                                 return LDB_ERR_UNWILLING_TO_PERFORM;
    1590             :                         }
    1591        7684 :                         ret = samdb_msg_add_uint(ldb, ac->msg, ac->msg,
    1592             :                                                  "sAMAccountType",
    1593             :                                                  account_type);
    1594        7684 :                         if (ret != LDB_SUCCESS) {
    1595           0 :                                 return ret;
    1596             :                         }
    1597        7684 :                         el2 = ldb_msg_find_element(ac->msg, "sAMAccountType");
    1598        7684 :                         el2->flags = LDB_FLAG_MOD_REPLACE;
    1599             :                 }
    1600        6900 :                 break;
    1601             :         }
    1602             : 
    1603           0 :         default:
    1604           0 :                 ldb_asprintf_errstring(ldb,
    1605             :                                 "Invalid entry type!");
    1606           0 :                 return LDB_ERR_OPERATIONS_ERROR;
    1607             :                 break;
    1608             :         }
    1609             : 
    1610       26546 :         return LDB_SUCCESS;
    1611             : }
    1612             : 
    1613             : /*
    1614             :  * "Primary group ID" trigger (MS-SAMR 3.1.1.8.2)
    1615             :  *
    1616             :  * Has to be invoked on "add" and "modify" operations on "user" and "computer"
    1617             :  * objects.
    1618             :  * ac->msg contains the "add"/"modify" message
    1619             :  */
    1620             : 
    1621         231 : static int samldb_prim_group_tester(struct samldb_ctx *ac, uint32_t rid)
    1622             : {
    1623         231 :         struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
    1624             :         struct dom_sid *sid;
    1625             :         struct ldb_result *res;
    1626             :         int ret;
    1627         231 :         const char * const noattrs[] = { NULL };
    1628             : 
    1629         231 :         sid = dom_sid_add_rid(ac, samdb_domain_sid(ldb), rid);
    1630         231 :         if (sid == NULL) {
    1631           0 :                 return ldb_operr(ldb);
    1632             :         }
    1633             : 
    1634         231 :         ret = dsdb_module_search(ac->module, ac, &res,
    1635             :                                  ldb_get_default_basedn(ldb),
    1636             :                                  LDB_SCOPE_SUBTREE,
    1637             :                                  noattrs, DSDB_FLAG_NEXT_MODULE,
    1638             :                                  ac->req,
    1639             :                                  "(objectSid=%s)",
    1640             :                                  ldap_encode_ndr_dom_sid(ac, sid));
    1641         231 :         if (ret != LDB_SUCCESS) {
    1642           0 :                 return ret;
    1643             :         }
    1644         231 :         if (res->count != 1) {
    1645           0 :                 talloc_free(res);
    1646           0 :                 ldb_asprintf_errstring(ldb,
    1647             :                                        "Failed to find primary group with RID %u!",
    1648             :                                        rid);
    1649           0 :                 return LDB_ERR_UNWILLING_TO_PERFORM;
    1650             :         }
    1651         231 :         talloc_free(res);
    1652             : 
    1653         231 :         return LDB_SUCCESS;
    1654             : }
    1655             : 
    1656       19901 : static int samldb_prim_group_set(struct samldb_ctx *ac)
    1657             : {
    1658       19901 :         struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
    1659             :         uint32_t rid;
    1660             : 
    1661       19901 :         rid = ldb_msg_find_attr_as_uint(ac->msg, "primaryGroupID", (uint32_t) -1);
    1662       19901 :         if (rid == (uint32_t) -1) {
    1663             :                 /* we aren't affected of any primary group set */
    1664       19573 :                 return LDB_SUCCESS;
    1665             : 
    1666         132 :         } else if (!ldb_request_get_control(ac->req, LDB_CONTROL_RELAX_OID)) {
    1667          10 :                 ldb_set_errstring(ldb,
    1668             :                                   "The primary group isn't settable on add operations!");
    1669          10 :                 return LDB_ERR_UNWILLING_TO_PERFORM;
    1670             :         }
    1671             : 
    1672         122 :         return samldb_prim_group_tester(ac, rid);
    1673             : }
    1674             : 
    1675         107 : static int samldb_prim_group_change(struct samldb_ctx *ac)
    1676             : {
    1677         107 :         struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
    1678         107 :         const char * const attrs[] = {
    1679             :                 "primaryGroupID",
    1680             :                 "memberOf",
    1681             :                 "userAccountControl",
    1682             :                 NULL };
    1683             :         struct ldb_result *res, *group_res;
    1684             :         struct ldb_message_element *el;
    1685             :         struct ldb_message *msg;
    1686         107 :         uint32_t search_flags =
    1687             :                 DSDB_FLAG_NEXT_MODULE | DSDB_SEARCH_SHOW_EXTENDED_DN;
    1688             :         uint32_t prev_rid, new_rid, uac;
    1689             :         struct dom_sid *prev_sid, *new_sid;
    1690             :         struct ldb_dn *prev_prim_group_dn, *new_prim_group_dn;
    1691         107 :         const char *new_prim_group_dn_ext_str = NULL;
    1692         107 :         struct ldb_dn *user_dn = NULL;
    1693         107 :         const char *user_dn_ext_str = NULL;
    1694             :         int ret;
    1695         107 :         const char * const noattrs[] = { NULL };
    1696             : 
    1697         107 :         el = dsdb_get_single_valued_attr(ac->msg, "primaryGroupID",
    1698         107 :                                          ac->req->operation);
    1699         107 :         if (el == NULL) {
    1700             :                 /* we are not affected */
    1701           3 :                 return LDB_SUCCESS;
    1702             :         }
    1703             : 
    1704             :         /* Fetch information from the existing object */
    1705             : 
    1706         104 :         ret = dsdb_module_search_dn(ac->module, ac, &res, ac->msg->dn, attrs,
    1707             :                                     search_flags, ac->req);
    1708         104 :         if (ret != LDB_SUCCESS) {
    1709           0 :                 return ret;
    1710             :         }
    1711         104 :         user_dn = res->msgs[0]->dn;
    1712         104 :         user_dn_ext_str = ldb_dn_get_extended_linearized(ac, user_dn, 1);
    1713         104 :         if (user_dn_ext_str == NULL) {
    1714           0 :                 return ldb_operr(ldb);
    1715             :         }
    1716             : 
    1717         104 :         uac = ldb_msg_find_attr_as_uint(res->msgs[0], "userAccountControl", 0);
    1718             : 
    1719             :         /* Finds out the DN of the old primary group */
    1720             : 
    1721         104 :         prev_rid = ldb_msg_find_attr_as_uint(res->msgs[0], "primaryGroupID",
    1722             :                                              (uint32_t) -1);
    1723         104 :         if (prev_rid == (uint32_t) -1) {
    1724             :                 /* User objects do always have a mandatory "primaryGroupID"
    1725             :                  * attribute. If this doesn't exist then the object is of the
    1726             :                  * wrong type. This is the exact Windows error code */
    1727           3 :                 return LDB_ERR_OBJECT_CLASS_VIOLATION;
    1728             :         }
    1729             : 
    1730         101 :         prev_sid = dom_sid_add_rid(ac, samdb_domain_sid(ldb), prev_rid);
    1731         101 :         if (prev_sid == NULL) {
    1732           0 :                 return ldb_operr(ldb);
    1733             :         }
    1734             : 
    1735             :         /* Finds out the DN of the new primary group
    1736             :          * Notice: in order to parse the primary group ID correctly we create
    1737             :          * a temporary message here. */
    1738             : 
    1739         101 :         msg = ldb_msg_new(ac->msg);
    1740         101 :         if (msg == NULL) {
    1741           0 :                 return ldb_module_oom(ac->module);
    1742             :         }
    1743         101 :         ret = ldb_msg_add(msg, el, 0);
    1744         101 :         if (ret != LDB_SUCCESS) {
    1745           0 :                 return ret;
    1746             :         }
    1747         101 :         new_rid = ldb_msg_find_attr_as_uint(msg, "primaryGroupID", (uint32_t) -1);
    1748         101 :         talloc_free(msg);
    1749         101 :         if (new_rid == (uint32_t) -1) {
    1750             :                 /* we aren't affected of any primary group change */
    1751           3 :                 return LDB_SUCCESS;
    1752             :         }
    1753             : 
    1754          98 :         if (prev_rid == new_rid) {
    1755          11 :                 return LDB_SUCCESS;
    1756             :         }
    1757             : 
    1758          87 :         if ((uac & UF_SERVER_TRUST_ACCOUNT) && new_rid != DOMAIN_RID_DCS) {
    1759           1 :                 ldb_asprintf_errstring(ldb,
    1760             :                         "%08X: samldb: UF_SERVER_TRUST_ACCOUNT requires "
    1761             :                         "primaryGroupID=%u!",
    1762           1 :                         W_ERROR_V(WERR_DS_CANT_MOD_PRIMARYGROUPID),
    1763             :                         DOMAIN_RID_DCS);
    1764           1 :                 return LDB_ERR_UNWILLING_TO_PERFORM;
    1765             :         }
    1766             : 
    1767          86 :         if ((uac & UF_PARTIAL_SECRETS_ACCOUNT) && new_rid != DOMAIN_RID_READONLY_DCS) {
    1768           1 :                 ldb_asprintf_errstring(ldb,
    1769             :                         "%08X: samldb: UF_PARTIAL_SECRETS_ACCOUNT requires "
    1770             :                         "primaryGroupID=%u!",
    1771           1 :                         W_ERROR_V(WERR_DS_CANT_MOD_PRIMARYGROUPID),
    1772             :                         DOMAIN_RID_READONLY_DCS);
    1773           1 :                 return LDB_ERR_UNWILLING_TO_PERFORM;
    1774             :         }
    1775             : 
    1776          85 :         ret = dsdb_module_search(ac->module, ac, &group_res,
    1777             :                                  ldb_get_default_basedn(ldb),
    1778             :                                  LDB_SCOPE_SUBTREE,
    1779             :                                  noattrs, search_flags,
    1780             :                                  ac->req,
    1781             :                                  "(objectSid=%s)",
    1782             :                                  ldap_encode_ndr_dom_sid(ac, prev_sid));
    1783          85 :         if (ret != LDB_SUCCESS) {
    1784           0 :                 return ret;
    1785             :         }
    1786          85 :         if (group_res->count != 1) {
    1787           0 :                 return ldb_operr(ldb);
    1788             :         }
    1789          85 :         prev_prim_group_dn = group_res->msgs[0]->dn;
    1790             : 
    1791          85 :         new_sid = dom_sid_add_rid(ac, samdb_domain_sid(ldb), new_rid);
    1792          85 :         if (new_sid == NULL) {
    1793           0 :                 return ldb_operr(ldb);
    1794             :         }
    1795             : 
    1796          85 :         ret = dsdb_module_search(ac->module, ac, &group_res,
    1797             :                                  ldb_get_default_basedn(ldb),
    1798             :                                  LDB_SCOPE_SUBTREE,
    1799             :                                  noattrs, search_flags,
    1800             :                                  ac->req,
    1801             :                                  "(objectSid=%s)",
    1802             :                                  ldap_encode_ndr_dom_sid(ac, new_sid));
    1803          85 :         if (ret != LDB_SUCCESS) {
    1804           0 :                 return ret;
    1805             :         }
    1806          85 :         if (group_res->count != 1) {
    1807             :                 /* Here we know if the specified new primary group candidate is
    1808             :                  * valid or not. */
    1809           3 :                 return LDB_ERR_UNWILLING_TO_PERFORM;
    1810             :         }
    1811          82 :         new_prim_group_dn = group_res->msgs[0]->dn;
    1812          82 :         new_prim_group_dn_ext_str = ldb_dn_get_extended_linearized(ac,
    1813             :                                                         new_prim_group_dn, 1);
    1814          82 :         if (new_prim_group_dn_ext_str == NULL) {
    1815           0 :                 return ldb_operr(ldb);
    1816             :         }
    1817             : 
    1818             :         /* We need to be already a normal member of the new primary
    1819             :          * group in order to be successful. */
    1820          82 :         el = samdb_find_attribute(ldb, res->msgs[0], "memberOf",
    1821             :                                   new_prim_group_dn_ext_str);
    1822          82 :         if (el == NULL) {
    1823           4 :                 return LDB_ERR_UNWILLING_TO_PERFORM;
    1824             :         }
    1825             : 
    1826             :         /* Remove the "member" attribute on the new primary group */
    1827          78 :         msg = ldb_msg_new(ac->msg);
    1828          78 :         if (msg == NULL) {
    1829           0 :                 return ldb_module_oom(ac->module);
    1830             :         }
    1831          78 :         msg->dn = new_prim_group_dn;
    1832             : 
    1833          78 :         ret = samdb_msg_add_delval(ldb, msg, msg, "member", user_dn_ext_str);
    1834          78 :         if (ret != LDB_SUCCESS) {
    1835           0 :                 return ret;
    1836             :         }
    1837             : 
    1838          78 :         ret = dsdb_module_modify(ac->module, msg, DSDB_FLAG_NEXT_MODULE, ac->req);
    1839          78 :         if (ret != LDB_SUCCESS) {
    1840           0 :                 return ret;
    1841             :         }
    1842          78 :         talloc_free(msg);
    1843             : 
    1844             :         /* Add a "member" attribute for the previous primary group */
    1845          78 :         msg = ldb_msg_new(ac->msg);
    1846          78 :         if (msg == NULL) {
    1847           0 :                 return ldb_module_oom(ac->module);
    1848             :         }
    1849          78 :         msg->dn = prev_prim_group_dn;
    1850             : 
    1851          78 :         ret = samdb_msg_add_addval(ldb, msg, msg, "member", user_dn_ext_str);
    1852          78 :         if (ret != LDB_SUCCESS) {
    1853           0 :                 return ret;
    1854             :         }
    1855             : 
    1856          78 :         ret = dsdb_module_modify(ac->module, msg, DSDB_FLAG_NEXT_MODULE, ac->req);
    1857          78 :         if (ret != LDB_SUCCESS) {
    1858           0 :                 return ret;
    1859             :         }
    1860          78 :         talloc_free(msg);
    1861             : 
    1862          78 :         return LDB_SUCCESS;
    1863             : }
    1864             : 
    1865       20008 : static int samldb_prim_group_trigger(struct samldb_ctx *ac)
    1866             : {
    1867             :         int ret;
    1868             : 
    1869       20008 :         if (ac->req->operation == LDB_ADD) {
    1870       19901 :                 ret = samldb_prim_group_set(ac);
    1871             :         } else {
    1872         107 :                 ret = samldb_prim_group_change(ac);
    1873             :         }
    1874             : 
    1875       20008 :         return ret;
    1876             : }
    1877             : 
    1878       30706 : static int samldb_check_user_account_control_invariants(struct samldb_ctx *ac,
    1879             :                                                     uint32_t user_account_control)
    1880             : {
    1881             :         size_t i;
    1882       30706 :         int ret = 0;
    1883       30706 :         bool need_check = false;
    1884             :         const struct uac_to_guid {
    1885             :                 uint32_t uac;
    1886             :                 bool never;
    1887             :                 uint32_t needs;
    1888             :                 uint32_t not_with;
    1889             :                 const char *error_string;
    1890       30706 :         } map[] = {
    1891             :                 {
    1892             :                         .uac = UF_TEMP_DUPLICATE_ACCOUNT,
    1893             :                         .never = true,
    1894             :                         .error_string = "Updating the UF_TEMP_DUPLICATE_ACCOUNT flag is never allowed"
    1895             :                 },
    1896             :                 {
    1897             :                         .uac = UF_PARTIAL_SECRETS_ACCOUNT,
    1898             :                         .needs = UF_WORKSTATION_TRUST_ACCOUNT,
    1899             :                         .error_string = "Setting UF_PARTIAL_SECRETS_ACCOUNT only permitted with UF_WORKSTATION_TRUST_ACCOUNT"
    1900             :                 },
    1901             :                 {
    1902             :                         .uac = UF_TRUSTED_FOR_DELEGATION,
    1903             :                         .not_with = UF_PARTIAL_SECRETS_ACCOUNT,
    1904             :                         .error_string = "Setting UF_TRUSTED_FOR_DELEGATION not allowed with UF_PARTIAL_SECRETS_ACCOUNT"
    1905             :                 },
    1906             :                 {
    1907             :                         .uac = UF_NORMAL_ACCOUNT,
    1908             :                         .not_with = UF_ACCOUNT_TYPE_MASK & ~UF_NORMAL_ACCOUNT,
    1909             :                         .error_string = "Setting more than one account type not permitted"
    1910             :                 },
    1911             :                 {
    1912             :                         .uac = UF_WORKSTATION_TRUST_ACCOUNT,
    1913             :                         .not_with = UF_ACCOUNT_TYPE_MASK & ~UF_WORKSTATION_TRUST_ACCOUNT,
    1914             :                         .error_string = "Setting more than one account type not permitted"
    1915             :                 },
    1916             :                 {
    1917             :                         .uac = UF_INTERDOMAIN_TRUST_ACCOUNT,
    1918             :                         .not_with = UF_ACCOUNT_TYPE_MASK & ~UF_INTERDOMAIN_TRUST_ACCOUNT,
    1919             :                         .error_string = "Setting more than one account type not permitted"
    1920             :                 },
    1921             :                 {
    1922             :                         .uac = UF_SERVER_TRUST_ACCOUNT,
    1923             :                         .not_with = UF_ACCOUNT_TYPE_MASK & ~UF_SERVER_TRUST_ACCOUNT,
    1924             :                         .error_string = "Setting more than one account type not permitted"
    1925             :                 },
    1926             :                 {
    1927             :                         .uac = UF_TRUSTED_FOR_DELEGATION,
    1928             :                         .not_with = UF_PARTIAL_SECRETS_ACCOUNT,
    1929             :                         .error_string = "Setting UF_TRUSTED_FOR_DELEGATION not allowed with UF_PARTIAL_SECRETS_ACCOUNT"
    1930             :                 }
    1931             :         };
    1932             : 
    1933      125330 :         for (i = 0; i < ARRAY_SIZE(map); i++) {
    1934      125330 :                 if (user_account_control & map[i].uac) {
    1935       30380 :                         need_check = true;
    1936       30380 :                         break;
    1937             :                 }
    1938             :         }
    1939       30706 :         if (need_check == false) {
    1940           0 :                 return LDB_SUCCESS;
    1941             :         }
    1942             : 
    1943      275873 :         for (i = 0; i < ARRAY_SIZE(map); i++) {
    1944      245513 :                 uint32_t this_uac = user_account_control & map[i].uac;
    1945      245513 :                 if (this_uac != 0) {
    1946       31949 :                         if (map[i].never) {
    1947          16 :                                 ret = LDB_ERR_OTHER;
    1948          16 :                                 break;
    1949       31933 :                         } else if (map[i].needs != 0) {
    1950         204 :                                 if ((map[i].needs & user_account_control) == 0) {
    1951           3 :                                         ret = LDB_ERR_OTHER;
    1952           3 :                                         break;
    1953             :                                 }
    1954       31729 :                         } else if (map[i].not_with != 0) {
    1955       31729 :                                 if ((map[i].not_with & user_account_control) != 0) {
    1956           1 :                                         ret = LDB_ERR_OTHER;
    1957           1 :                                         break;
    1958             :                                 }
    1959             :                         }
    1960             :                 }
    1961             :         }
    1962       30706 :         if (ret != LDB_SUCCESS) {
    1963          20 :                 switch (ac->req->operation) {
    1964           7 :                 case LDB_ADD:
    1965          12 :                         ldb_asprintf_errstring(ldb_module_get_ctx(ac->module),
    1966             :                                                "Failed to add %s: %s",
    1967           7 :                                                ldb_dn_get_linearized(ac->msg->dn),
    1968           2 :                                                map[i].error_string);
    1969           7 :                         break;
    1970          13 :                 case LDB_MODIFY:
    1971          24 :                         ldb_asprintf_errstring(ldb_module_get_ctx(ac->module),
    1972             :                                                "Failed to modify %s: %s",
    1973          13 :                                                ldb_dn_get_linearized(ac->msg->dn),
    1974           2 :                                                map[i].error_string);
    1975          13 :                         break;
    1976           0 :                 default:
    1977           0 :                         return ldb_module_operr(ac->module);
    1978             :                 }
    1979       21404 :         }
    1980       30380 :         return ret;
    1981             : }
    1982             : 
    1983             : /**
    1984             :  * Validate that the restriction in point 5 of MS-SAMR 3.1.1.8.10 userAccountControl is honoured
    1985             :  *
    1986             :  */
    1987       30686 : static int samldb_check_user_account_control_acl(struct samldb_ctx *ac,
    1988             :                                                  struct dom_sid *sid,
    1989             :                                                  uint32_t user_account_control,
    1990             :                                                  uint32_t user_account_control_old)
    1991             : {
    1992             :         size_t i;
    1993       30686 :         int ret = 0;
    1994       30686 :         bool need_acl_check = false;
    1995             :         struct ldb_result *res;
    1996       30686 :         const char * const sd_attrs[] = {"ntSecurityDescriptor", NULL};
    1997             :         struct security_token *user_token;
    1998             :         struct security_descriptor *domain_sd;
    1999       30686 :         struct ldb_dn *domain_dn = ldb_get_default_basedn(ldb_module_get_ctx(ac->module));
    2000       30686 :         struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
    2001             :         const struct uac_to_guid {
    2002             :                 uint32_t uac;
    2003             :                 uint32_t priv_to_change_from;
    2004             :                 const char *oid;
    2005             :                 const char *guid;
    2006             :                 enum sec_privilege privilege;
    2007             :                 bool delete_is_privileged;
    2008             :                 bool admin_required;
    2009             :                 const char *error_string;
    2010       30686 :         } map[] = {
    2011             :                 {
    2012             :                         .uac = UF_PASSWD_NOTREQD,
    2013             :                         .guid = GUID_DRS_UPDATE_PASSWORD_NOT_REQUIRED_BIT,
    2014             :                         .error_string = "Adding the UF_PASSWD_NOTREQD bit in userAccountControl requires the Update-Password-Not-Required-Bit right that was not given on the Domain object"
    2015             :                 },
    2016             :                 {
    2017             :                         .uac = UF_DONT_EXPIRE_PASSWD,
    2018             :                         .guid = GUID_DRS_UNEXPIRE_PASSWORD,
    2019             :                         .error_string = "Adding the UF_DONT_EXPIRE_PASSWD bit in userAccountControl requires the Unexpire-Password right that was not given on the Domain object"
    2020             :                 },
    2021             :                 {
    2022             :                         .uac = UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED,
    2023             :                         .guid = GUID_DRS_ENABLE_PER_USER_REVERSIBLY_ENCRYPTED_PASSWORD,
    2024             :                         .error_string = "Adding the UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED bit in userAccountControl requires the Enable-Per-User-Reversibly-Encrypted-Password right that was not given on the Domain object"
    2025             :                 },
    2026             :                 {
    2027             :                         .uac = UF_SERVER_TRUST_ACCOUNT,
    2028             :                         .guid = GUID_DRS_DS_INSTALL_REPLICA,
    2029             :                         .error_string = "Adding the UF_SERVER_TRUST_ACCOUNT bit in userAccountControl requires the DS-Install-Replica right that was not given on the Domain object"
    2030             :                 },
    2031             :                 {
    2032             :                         .uac = UF_PARTIAL_SECRETS_ACCOUNT,
    2033             :                         .guid = GUID_DRS_DS_INSTALL_REPLICA,
    2034             :                         .error_string = "Adding the UF_PARTIAL_SECRETS_ACCOUNT bit in userAccountControl requires the DS-Install-Replica right that was not given on the Domain object"
    2035             :                 },
    2036             :                 {
    2037             :                         .uac = UF_WORKSTATION_TRUST_ACCOUNT,
    2038             :                         .priv_to_change_from = UF_NORMAL_ACCOUNT,
    2039             :                         .error_string = "Swapping UF_NORMAL_ACCOUNT to UF_WORKSTATION_TRUST_ACCOUNT requires the user to be a member of the domain admins group"
    2040             :                 },
    2041             :                 {
    2042             :                         .uac = UF_NORMAL_ACCOUNT,
    2043             :                         .priv_to_change_from = UF_WORKSTATION_TRUST_ACCOUNT,
    2044             :                         .error_string = "Swapping UF_WORKSTATION_TRUST_ACCOUNT to UF_NORMAL_ACCOUNT requires the user to be a member of the domain admins group"
    2045             :                 },
    2046             :                 {
    2047             :                         .uac = UF_INTERDOMAIN_TRUST_ACCOUNT,
    2048             :                         .oid = DSDB_CONTROL_PERMIT_INTERDOMAIN_TRUST_UAC_OID,
    2049             :                         .error_string = "Updating the UF_INTERDOMAIN_TRUST_ACCOUNT bit in userAccountControl is not permitted over LDAP.  This bit is restricted to the LSA CreateTrustedDomain interface",
    2050             :                         .delete_is_privileged = true
    2051             :                 },
    2052             :                 {
    2053             :                         .uac = UF_TRUSTED_FOR_DELEGATION,
    2054             :                         .privilege = SEC_PRIV_ENABLE_DELEGATION,
    2055             :                         .delete_is_privileged = true,
    2056             :                         .error_string = "Updating the UF_TRUSTED_FOR_DELEGATION bit in userAccountControl is not permitted without the SeEnableDelegationPrivilege"
    2057             :                 },
    2058             :                 {
    2059             :                         .uac = UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION,
    2060             :                         .privilege = SEC_PRIV_ENABLE_DELEGATION,
    2061             :                         .delete_is_privileged = true,
    2062             :                         .error_string = "Updating the UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION bit in userAccountControl is not permitted without the SeEnableDelegationPrivilege"
    2063             :                 }
    2064             : 
    2065             :         };
    2066             : 
    2067       30686 :         if (dsdb_module_am_system(ac->module)) {
    2068        1670 :                 return LDB_SUCCESS;
    2069             :         }
    2070             : 
    2071      187362 :         for (i = 0; i < ARRAY_SIZE(map); i++) {
    2072      187591 :                 if (user_account_control & map[i].uac) {
    2073       28690 :                         need_acl_check = true;
    2074       28690 :                         break;
    2075             :                 }
    2076             :         }
    2077       28919 :         if (need_acl_check == false) {
    2078           0 :                 return LDB_SUCCESS;
    2079             :         }
    2080             : 
    2081       28919 :         user_token = acl_user_token(ac->module);
    2082       28919 :         if (user_token == NULL) {
    2083           0 :                 return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
    2084             :         }
    2085             : 
    2086       28919 :         ret = dsdb_module_search_dn(ac->module, ac, &res,
    2087             :                                     domain_dn,
    2088             :                                     sd_attrs,
    2089             :                                     DSDB_FLAG_NEXT_MODULE | DSDB_SEARCH_SHOW_DELETED,
    2090             :                                     ac->req);
    2091       28919 :         if (ret != LDB_SUCCESS) {
    2092           0 :                 return ret;
    2093             :         }
    2094       28919 :         if (res->count != 1) {
    2095           0 :                 return ldb_module_operr(ac->module);
    2096             :         }
    2097             : 
    2098       28919 :         ret = dsdb_get_sd_from_ldb_message(ldb,
    2099       28919 :                                            ac, res->msgs[0], &domain_sd);
    2100             : 
    2101       28919 :         if (ret != LDB_SUCCESS) {
    2102           0 :                 return ret;
    2103             :         }
    2104             : 
    2105      317751 :         for (i = 0; i < ARRAY_SIZE(map); i++) {
    2106      289097 :                 uint32_t this_uac_new = user_account_control & map[i].uac;
    2107      289097 :                 uint32_t this_uac_old = user_account_control_old & map[i].uac;
    2108      289097 :                 if (this_uac_new != this_uac_old) {
    2109       30481 :                         if (this_uac_old != 0) {
    2110        9368 :                                 if (map[i].delete_is_privileged == false) {
    2111        9354 :                                         continue;
    2112             :                                 }
    2113             :                         }
    2114       21127 :                         if (map[i].oid) {
    2115          79 :                                 struct ldb_control *control = ldb_request_get_control(ac->req, map[i].oid);
    2116          79 :                                 if (control == NULL) {
    2117          16 :                                         ret = LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
    2118             :                                 }
    2119       21048 :                         } else if (map[i].privilege != SEC_PRIV_INVALID) {
    2120         366 :                                 bool have_priv = security_token_has_privilege(user_token,
    2121          58 :                                                                               map[i].privilege);
    2122         366 :                                 if (have_priv == false) {
    2123           8 :                                         ret = LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
    2124             :                                 }
    2125       20682 :                         } else if (map[i].priv_to_change_from & user_account_control_old) {
    2126          92 :                                 bool is_admin = security_token_has_builtin_administrators(user_token);
    2127          92 :                                 if (is_admin == false) {
    2128           5 :                                         ret = LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
    2129             :                                 }
    2130       20590 :                         } else if (map[i].guid) {
    2131        2416 :                                 ret = acl_check_extended_right(ac, domain_sd,
    2132             :                                                                user_token,
    2133         479 :                                                                map[i].guid,
    2134             :                                                                SEC_ADS_CONTROL_ACCESS,
    2135             :                                                                sid);
    2136             :                         } else {
    2137       18074 :                                 ret = LDB_SUCCESS;
    2138             :                         }
    2139       21027 :                         if (ret != LDB_SUCCESS) {
    2140          36 :                                 break;
    2141             :                         }
    2142             :                 }
    2143             :         }
    2144       28919 :         if (ret == LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS) {
    2145          36 :                 switch (ac->req->operation) {
    2146          11 :                 case LDB_ADD:
    2147          20 :                         ldb_asprintf_errstring(ldb_module_get_ctx(ac->module),
    2148             :                                                "Failed to add %s: %s",
    2149          11 :                                                ldb_dn_get_linearized(ac->msg->dn),
    2150           2 :                                                map[i].error_string);
    2151          11 :                         break;
    2152          25 :                 case LDB_MODIFY:
    2153          48 :                         ldb_asprintf_errstring(ldb_module_get_ctx(ac->module),
    2154             :                                                "Failed to modify %s: %s",
    2155          25 :                                                ldb_dn_get_linearized(ac->msg->dn),
    2156           2 :                                                map[i].error_string);
    2157          25 :                         break;
    2158           0 :                 default:
    2159           0 :                         return ldb_module_operr(ac->module);
    2160             :                 }
    2161          36 :                 if (map[i].guid) {
    2162           7 :                         dsdb_acl_debug(domain_sd, acl_user_token(ac->module),
    2163             :                                        domain_dn,
    2164             :                                        true,
    2165             :                                        10);
    2166             :                 }
    2167             :         }
    2168       28690 :         return ret;
    2169             : }
    2170             : 
    2171       30706 : static int samldb_check_user_account_control_rules(struct samldb_ctx *ac,
    2172             :                                                    struct dom_sid *sid,
    2173             :                                                    uint32_t req_uac,
    2174             :                                                    uint32_t user_account_control,
    2175             :                                                    uint32_t user_account_control_old)
    2176             : {
    2177             :         int ret;
    2178       30706 :         struct dsdb_control_password_user_account_control *uac = NULL;
    2179             : 
    2180       30706 :         ret = samldb_check_user_account_control_invariants(ac, user_account_control);
    2181       30706 :         if (ret != LDB_SUCCESS) {
    2182          20 :                 return ret;
    2183             :         }
    2184       30686 :         ret = samldb_check_user_account_control_acl(ac, sid, user_account_control, user_account_control_old);
    2185       30686 :         if (ret != LDB_SUCCESS) {
    2186          36 :                 return ret;
    2187             :         }
    2188             : 
    2189       30650 :         uac = talloc_zero(ac->req,
    2190             :                           struct dsdb_control_password_user_account_control);
    2191       30650 :         if (uac == NULL) {
    2192           0 :                 return ldb_module_oom(ac->module);
    2193             :         }
    2194             : 
    2195       30650 :         uac->req_flags = req_uac;
    2196       30650 :         uac->old_flags = user_account_control_old;
    2197       30650 :         uac->new_flags = user_account_control;
    2198             : 
    2199       30650 :         ret = ldb_request_add_control(ac->req,
    2200             :                                 DSDB_CONTROL_PASSWORD_USER_ACCOUNT_CONTROL_OID,
    2201             :                                 false, uac);
    2202       30324 :         if (ret != LDB_SUCCESS) {
    2203           0 :                 return ret;
    2204             :         }
    2205             : 
    2206       30324 :         return ret;
    2207             : }
    2208             : 
    2209             : 
    2210             : /**
    2211             :  * This function is called on LDB modify operations. It performs some additions/
    2212             :  * replaces on the current LDB message when "userAccountControl" changes.
    2213             :  */
    2214       10822 : static int samldb_user_account_control_change(struct samldb_ctx *ac)
    2215             : {
    2216       10822 :         struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
    2217             :         uint32_t old_uac;
    2218             :         uint32_t new_uac;
    2219             :         uint32_t raw_uac;
    2220             :         uint32_t old_ufa;
    2221             :         uint32_t new_ufa;
    2222             :         uint32_t old_uac_computed;
    2223             :         uint32_t clear_uac;
    2224             :         uint32_t old_atype;
    2225             :         uint32_t new_atype;
    2226             :         uint32_t old_pgrid;
    2227             :         uint32_t new_pgrid;
    2228             :         NTTIME old_lockoutTime;
    2229             :         struct ldb_message_element *el;
    2230             :         struct ldb_val *val;
    2231             :         struct ldb_val computer_val;
    2232             :         struct ldb_message *tmp_msg;
    2233             :         struct dom_sid *sid;
    2234             :         int ret;
    2235             :         struct ldb_result *res;
    2236       10822 :         const char * const attrs[] = {
    2237             :                 "objectClass",
    2238             :                 "isCriticalSystemObject",
    2239             :                 "userAccountControl",
    2240             :                 "msDS-User-Account-Control-Computed",
    2241             :                 "lockoutTime",
    2242             :                 "objectSid",
    2243             :                 NULL
    2244             :         };
    2245       10822 :         bool is_computer = false;
    2246       10822 :         bool old_is_critical = false;
    2247       10822 :         bool new_is_critical = false;
    2248             : 
    2249       10822 :         el = dsdb_get_single_valued_attr(ac->msg, "userAccountControl",
    2250       10822 :                                          ac->req->operation);
    2251       10822 :         if (el == NULL || el->num_values == 0) {
    2252           6 :                 ldb_asprintf_errstring(ldb,
    2253             :                         "%08X: samldb: 'userAccountControl' can't be deleted!",
    2254           6 :                         W_ERROR_V(WERR_DS_ILLEGAL_MOD_OPERATION));
    2255           6 :                 return LDB_ERR_UNWILLING_TO_PERFORM;
    2256             :         }
    2257             : 
    2258             :         /* Create a temporary message for fetching the "userAccountControl" */
    2259       10816 :         tmp_msg = ldb_msg_new(ac->msg);
    2260       10816 :         if (tmp_msg == NULL) {
    2261           0 :                 return ldb_module_oom(ac->module);
    2262             :         }
    2263       10816 :         ret = ldb_msg_add(tmp_msg, el, 0);
    2264       10816 :         if (ret != LDB_SUCCESS) {
    2265           0 :                 return ret;
    2266             :         }
    2267       10816 :         raw_uac = ldb_msg_find_attr_as_uint(tmp_msg,
    2268             :                                             "userAccountControl",
    2269             :                                             0);
    2270       10816 :         talloc_free(tmp_msg);
    2271             :         /*
    2272             :          * UF_LOCKOUT, UF_PASSWD_CANT_CHANGE and UF_PASSWORD_EXPIRED
    2273             :          * are only generated and not stored. We ignore them almost
    2274             :          * completely, along with unknown bits and UF_SCRIPT.
    2275             :          *
    2276             :          * The only exception is ACB_AUTOLOCK, which features in
    2277             :          * clear_acb when the bit is cleared in this modify operation.
    2278             :          *
    2279             :          * MS-SAMR 2.2.1.13 UF_FLAG Codes states that some bits are
    2280             :          * ignored by clients and servers
    2281             :          */
    2282       10816 :         new_uac = raw_uac & UF_SETTABLE_BITS;
    2283             : 
    2284             :         /* Fetch the old "userAccountControl" and "objectClass" */
    2285       10816 :         ret = dsdb_module_search_dn(ac->module, ac, &res, ac->msg->dn, attrs,
    2286             :                                     DSDB_FLAG_NEXT_MODULE, ac->req);
    2287       10816 :         if (ret != LDB_SUCCESS) {
    2288           0 :                 return ret;
    2289             :         }
    2290       10816 :         old_uac = ldb_msg_find_attr_as_uint(res->msgs[0], "userAccountControl", 0);
    2291       10816 :         if (old_uac == 0) {
    2292           0 :                 return ldb_operr(ldb);
    2293             :         }
    2294       10816 :         old_uac_computed = ldb_msg_find_attr_as_uint(res->msgs[0],
    2295             :                                                      "msDS-User-Account-Control-Computed", 0);
    2296       10816 :         old_lockoutTime = ldb_msg_find_attr_as_int64(res->msgs[0],
    2297             :                                                      "lockoutTime", 0);
    2298       10816 :         old_is_critical = ldb_msg_find_attr_as_bool(res->msgs[0],
    2299             :                                                     "isCriticalSystemObject", 0);
    2300             :         /* When we do not have objectclass "computer" we cannot switch to a (read-only) DC */
    2301       10816 :         el = ldb_msg_find_element(res->msgs[0], "objectClass");
    2302       10816 :         if (el == NULL) {
    2303           0 :                 return ldb_operr(ldb);
    2304             :         }
    2305       10816 :         computer_val = data_blob_string_const("computer");
    2306       10816 :         val = ldb_msg_find_val(el, &computer_val);
    2307       10816 :         if (val != NULL) {
    2308        1325 :                 is_computer = true;
    2309             :         }
    2310             : 
    2311       10816 :         old_ufa = old_uac & UF_ACCOUNT_TYPE_MASK;
    2312       10816 :         old_atype = ds_uf2atype(old_ufa);
    2313       10816 :         old_pgrid = ds_uf2prim_group_rid(old_uac);
    2314             : 
    2315       10816 :         new_ufa = new_uac & UF_ACCOUNT_TYPE_MASK;
    2316       10816 :         if (new_ufa == 0) {
    2317             :                 /*
    2318             :                  * "userAccountControl" = 0 or missing one of the
    2319             :                  * types means "UF_NORMAL_ACCOUNT".  See MS-SAMR
    2320             :                  * 3.1.1.8.10 point 8
    2321             :                  */
    2322         220 :                 new_ufa = UF_NORMAL_ACCOUNT;
    2323         220 :                 new_uac |= new_ufa;
    2324             :         }
    2325       10816 :         sid = samdb_result_dom_sid(res, res->msgs[0], "objectSid");
    2326       10816 :         if (sid == NULL) {
    2327           0 :                 return ldb_module_operr(ac->module);
    2328             :         }
    2329             : 
    2330       10816 :         ret = samldb_check_user_account_control_rules(ac, sid,
    2331             :                                                       raw_uac,
    2332             :                                                       new_uac,
    2333             :                                                       old_uac);
    2334       10816 :         if (ret != LDB_SUCCESS) {
    2335          38 :                 return ret;
    2336             :         }
    2337             : 
    2338       10778 :         new_atype = ds_uf2atype(new_ufa);
    2339       10778 :         new_pgrid = ds_uf2prim_group_rid(new_uac);
    2340             : 
    2341       10778 :         clear_uac = (old_uac | old_uac_computed) & ~raw_uac;
    2342             : 
    2343       10778 :         switch (new_ufa) {
    2344        9682 :         case UF_NORMAL_ACCOUNT:
    2345        9682 :                 new_is_critical = old_is_critical;
    2346        9682 :                 break;
    2347             : 
    2348           0 :         case UF_INTERDOMAIN_TRUST_ACCOUNT:
    2349           0 :                 new_is_critical = true;
    2350           0 :                 break;
    2351             : 
    2352         536 :         case UF_WORKSTATION_TRUST_ACCOUNT:
    2353         536 :                 new_is_critical = false;
    2354         536 :                 if (new_uac & UF_PARTIAL_SECRETS_ACCOUNT) {
    2355          94 :                         if (!is_computer) {
    2356           3 :                                 ldb_asprintf_errstring(ldb,
    2357             :                                                        "%08X: samldb: UF_PARTIAL_SECRETS_ACCOUNT "
    2358             :                                                        "requires objectclass 'computer'!",
    2359           3 :                                                        W_ERROR_V(WERR_DS_MACHINE_ACCOUNT_CREATED_PRENT4));
    2360           3 :                                 return LDB_ERR_UNWILLING_TO_PERFORM;
    2361             :                         }
    2362          91 :                         new_is_critical = true;
    2363             :                 }
    2364         497 :                 break;
    2365             : 
    2366         523 :         case UF_SERVER_TRUST_ACCOUNT:
    2367         523 :                 if (!is_computer) {
    2368           3 :                         ldb_asprintf_errstring(ldb,
    2369             :                                 "%08X: samldb: UF_SERVER_TRUST_ACCOUNT "
    2370             :                                 "requires objectclass 'computer'!",
    2371           3 :                                 W_ERROR_V(WERR_DS_MACHINE_ACCOUNT_CREATED_PRENT4));
    2372           3 :                         return LDB_ERR_UNWILLING_TO_PERFORM;
    2373             :                 }
    2374         484 :                 new_is_critical = true;
    2375         484 :                 break;
    2376             : 
    2377           0 :         default:
    2378           0 :                 ldb_asprintf_errstring(ldb,
    2379             :                         "%08X: samldb: invalid userAccountControl[0x%08X]",
    2380           0 :                         W_ERROR_V(WERR_INVALID_PARAMETER), raw_uac);
    2381           0 :                 return LDB_ERR_OTHER;
    2382             :         }
    2383             : 
    2384       10772 :         if (old_atype != new_atype) {
    2385         105 :                 ret = samdb_msg_add_uint(ldb, ac->msg, ac->msg,
    2386             :                                          "sAMAccountType", new_atype);
    2387         105 :                 if (ret != LDB_SUCCESS) {
    2388           0 :                         return ret;
    2389             :                 }
    2390         105 :                 el = ldb_msg_find_element(ac->msg, "sAMAccountType");
    2391         105 :                 el->flags = LDB_FLAG_MOD_REPLACE;
    2392             :         }
    2393             : 
    2394             :         /* As per MS-SAMR 3.1.1.8.10 these flags have not to be set */
    2395       10772 :         if ((clear_uac & UF_LOCKOUT) && (old_lockoutTime != 0)) {
    2396             :                 /* "lockoutTime" reset as per MS-SAMR 3.1.1.8.10 */
    2397          10 :                 ldb_msg_remove_attr(ac->msg, "lockoutTime");
    2398          10 :                 ret = samdb_msg_add_uint64(ldb, ac->msg, ac->msg, "lockoutTime",
    2399             :                                            (NTTIME)0);
    2400          10 :                 if (ret != LDB_SUCCESS) {
    2401           0 :                         return ret;
    2402             :                 }
    2403          10 :                 el = ldb_msg_find_element(ac->msg, "lockoutTime");
    2404          10 :                 el->flags = LDB_FLAG_MOD_REPLACE;
    2405             :         }
    2406             : 
    2407             :         /* "isCriticalSystemObject" might be set/changed */
    2408       10772 :         if (old_is_critical != new_is_critical) {
    2409          44 :                 ret = ldb_msg_add_string(ac->msg, "isCriticalSystemObject",
    2410             :                                          new_is_critical ? "TRUE": "FALSE");
    2411          44 :                 if (ret != LDB_SUCCESS) {
    2412           0 :                         return ret;
    2413             :                 }
    2414          44 :                 el = ldb_msg_find_element(ac->msg,
    2415             :                                            "isCriticalSystemObject");
    2416          44 :                 el->flags = LDB_FLAG_MOD_REPLACE;
    2417             :         }
    2418             : 
    2419       10772 :         if (!ldb_msg_find_element(ac->msg, "primaryGroupID") &&
    2420             :             (old_pgrid != new_pgrid)) {
    2421             :                 /* Older AD deployments don't know about the RODC group */
    2422         137 :                 if (new_pgrid == DOMAIN_RID_READONLY_DCS) {
    2423           9 :                         ret = samldb_prim_group_tester(ac, new_pgrid);
    2424           9 :                         if (ret != LDB_SUCCESS) {
    2425           0 :                                 return ret;
    2426             :                         }
    2427             :                 }
    2428             : 
    2429         137 :                 ret = samdb_msg_add_uint(ldb, ac->msg, ac->msg,
    2430             :                                          "primaryGroupID", new_pgrid);
    2431         137 :                 if (ret != LDB_SUCCESS) {
    2432           0 :                         return ret;
    2433             :                 }
    2434         137 :                 el = ldb_msg_find_element(ac->msg,
    2435             :                                            "primaryGroupID");
    2436         137 :                 el->flags = LDB_FLAG_MOD_REPLACE;
    2437             :         }
    2438             : 
    2439             :         /* Propagate eventual "userAccountControl" attribute changes */
    2440       10772 :         if (old_uac != new_uac) {
    2441       10328 :                 char *tempstr = talloc_asprintf(ac->msg, "%d",
    2442             :                                                 new_uac);
    2443       10328 :                 if (tempstr == NULL) {
    2444           0 :                         return ldb_module_oom(ac->module);
    2445             :                 }
    2446             : 
    2447             :                 /* Overwrite "userAccountControl" correctly */
    2448       10328 :                 el = dsdb_get_single_valued_attr(ac->msg, "userAccountControl",
    2449       10328 :                                                  ac->req->operation);
    2450       10328 :                 el->values[0].data = (uint8_t *) tempstr;
    2451       10328 :                 el->values[0].length = strlen(tempstr);
    2452             :         } else {
    2453         444 :                 ldb_msg_remove_attr(ac->msg, "userAccountControl");
    2454             :         }
    2455             : 
    2456       10663 :         return LDB_SUCCESS;
    2457             : }
    2458             : 
    2459          83 : static int samldb_check_pwd_last_set_acl(struct samldb_ctx *ac,
    2460             :                                          struct dom_sid *sid)
    2461             : {
    2462          83 :         struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
    2463          83 :         int ret = 0;
    2464          83 :         struct ldb_result *res = NULL;
    2465          83 :         const char * const sd_attrs[] = {"ntSecurityDescriptor", NULL};
    2466          83 :         struct security_token *user_token = NULL;
    2467          83 :         struct security_descriptor *domain_sd = NULL;
    2468          83 :         struct ldb_dn *domain_dn = ldb_get_default_basedn(ldb_module_get_ctx(ac->module));
    2469          83 :         const char *operation = "";
    2470             : 
    2471          83 :         if (dsdb_module_am_system(ac->module)) {
    2472           1 :                 return LDB_SUCCESS;
    2473             :         }
    2474             : 
    2475          82 :         switch (ac->req->operation) {
    2476           0 :         case LDB_ADD:
    2477           0 :                 operation = "add";
    2478           0 :                 break;
    2479          82 :         case LDB_MODIFY:
    2480          82 :                 operation = "modify";
    2481          82 :                 break;
    2482           0 :         default:
    2483           0 :                 return ldb_module_operr(ac->module);
    2484             :         }
    2485             : 
    2486          82 :         user_token = acl_user_token(ac->module);
    2487          82 :         if (user_token == NULL) {
    2488           0 :                 return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
    2489             :         }
    2490             : 
    2491          82 :         ret = dsdb_module_search_dn(ac->module, ac, &res,
    2492             :                                     domain_dn,
    2493             :                                     sd_attrs,
    2494             :                                     DSDB_FLAG_NEXT_MODULE | DSDB_SEARCH_SHOW_DELETED,
    2495             :                                     ac->req);
    2496          82 :         if (ret != LDB_SUCCESS) {
    2497           0 :                 return ret;
    2498             :         }
    2499          82 :         if (res->count != 1) {
    2500           0 :                 return ldb_module_operr(ac->module);
    2501             :         }
    2502             : 
    2503          82 :         ret = dsdb_get_sd_from_ldb_message(ldb, ac, res->msgs[0], &domain_sd);
    2504          82 :         if (ret != LDB_SUCCESS) {
    2505           0 :                 return ret;
    2506             :         }
    2507             : 
    2508          82 :         ret = acl_check_extended_right(ac, domain_sd,
    2509             :                                        user_token,
    2510             :                                        GUID_DRS_UNEXPIRE_PASSWORD,
    2511             :                                        SEC_ADS_CONTROL_ACCESS,
    2512             :                                        sid);
    2513          82 :         if (ret != LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS) {
    2514          82 :                 return ret;
    2515             :         }
    2516             : 
    2517           0 :         ldb_debug_set(ldb, LDB_DEBUG_WARNING,
    2518             :                       "Failed to %s %s: "
    2519             :                       "Setting pwdLastSet to -1 requires the "
    2520             :                       "Unexpire-Password right that was not given "
    2521             :                       "on the Domain object",
    2522             :                       operation,
    2523           0 :                       ldb_dn_get_linearized(ac->msg->dn));
    2524           0 :         dsdb_acl_debug(domain_sd, user_token,
    2525             :                        domain_dn, true, 10);
    2526             : 
    2527           0 :         return ret;
    2528             : }
    2529             : 
    2530             : /**
    2531             :  * This function is called on LDB modify operations. It performs some additions/
    2532             :  * replaces on the current LDB message when "pwdLastSet" changes.
    2533             :  */
    2534         356 : static int samldb_pwd_last_set_change(struct samldb_ctx *ac)
    2535             : {
    2536         356 :         struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
    2537         356 :         NTTIME last_set = 0;
    2538         356 :         struct ldb_message_element *el = NULL;
    2539         356 :         struct ldb_message *tmp_msg = NULL;
    2540         356 :         struct dom_sid *self_sid = NULL;
    2541             :         int ret;
    2542         356 :         struct ldb_result *res = NULL;
    2543         356 :         const char * const attrs[] = {
    2544             :                 "objectSid",
    2545             :                 NULL
    2546             :         };
    2547             : 
    2548         356 :         el = dsdb_get_single_valued_attr(ac->msg, "pwdLastSet",
    2549         356 :                                          ac->req->operation);
    2550         356 :         if (el == NULL || el->num_values == 0) {
    2551           6 :                 ldb_asprintf_errstring(ldb,
    2552             :                         "%08X: samldb: 'pwdLastSet' can't be deleted!",
    2553           6 :                         W_ERROR_V(WERR_DS_ILLEGAL_MOD_OPERATION));
    2554           6 :                 return LDB_ERR_UNWILLING_TO_PERFORM;
    2555             :         }
    2556             : 
    2557             :         /* Create a temporary message for fetching the "userAccountControl" */
    2558         350 :         tmp_msg = ldb_msg_new(ac->msg);
    2559         350 :         if (tmp_msg == NULL) {
    2560           0 :                 return ldb_module_oom(ac->module);
    2561             :         }
    2562         350 :         ret = ldb_msg_add(tmp_msg, el, 0);
    2563         350 :         if (ret != LDB_SUCCESS) {
    2564           0 :                 return ret;
    2565             :         }
    2566         350 :         last_set = samdb_result_nttime(tmp_msg, "pwdLastSet", 0);
    2567         350 :         talloc_free(tmp_msg);
    2568             : 
    2569             :         /*
    2570             :          * Setting -1 (0xFFFFFFFFFFFFFFFF) requires the Unexpire-Password right
    2571             :          */
    2572         350 :         if (last_set != UINT64_MAX) {
    2573         256 :                 return LDB_SUCCESS;
    2574             :         }
    2575             : 
    2576             :         /* Fetch the "objectSid" */
    2577          83 :         ret = dsdb_module_search_dn(ac->module, ac, &res, ac->msg->dn, attrs,
    2578             :                                     DSDB_FLAG_NEXT_MODULE, ac->req);
    2579          83 :         if (ret != LDB_SUCCESS) {
    2580           0 :                 return ret;
    2581             :         }
    2582          83 :         self_sid = samdb_result_dom_sid(res, res->msgs[0], "objectSid");
    2583          83 :         if (self_sid == NULL) {
    2584           0 :                 return ldb_module_operr(ac->module);
    2585             :         }
    2586             : 
    2587          83 :         ret = samldb_check_pwd_last_set_acl(ac, self_sid);
    2588          83 :         if (ret != LDB_SUCCESS) {
    2589           0 :                 return ret;
    2590             :         }
    2591             : 
    2592          83 :         return LDB_SUCCESS;
    2593             : }
    2594             : 
    2595         157 : static int samldb_lockout_time(struct samldb_ctx *ac)
    2596             : {
    2597         157 :         struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
    2598             :         NTTIME lockoutTime;
    2599             :         struct ldb_message_element *el;
    2600             :         struct ldb_message *tmp_msg;
    2601             :         int ret;
    2602             : 
    2603         157 :         el = dsdb_get_single_valued_attr(ac->msg, "lockoutTime",
    2604         157 :                                          ac->req->operation);
    2605         157 :         if (el == NULL || el->num_values == 0) {
    2606           0 :                 ldb_asprintf_errstring(ldb,
    2607             :                         "%08X: samldb: 'lockoutTime' can't be deleted!",
    2608           0 :                         W_ERROR_V(WERR_DS_ILLEGAL_MOD_OPERATION));
    2609           0 :                 return LDB_ERR_UNWILLING_TO_PERFORM;
    2610             :         }
    2611             : 
    2612             :         /* Create a temporary message for fetching the "lockoutTime" */
    2613         157 :         tmp_msg = ldb_msg_new(ac->msg);
    2614         157 :         if (tmp_msg == NULL) {
    2615           0 :                 return ldb_module_oom(ac->module);
    2616             :         }
    2617         157 :         ret = ldb_msg_add(tmp_msg, el, 0);
    2618         157 :         if (ret != LDB_SUCCESS) {
    2619           0 :                 return ret;
    2620             :         }
    2621         157 :         lockoutTime = ldb_msg_find_attr_as_int64(tmp_msg,
    2622             :                                                  "lockoutTime",
    2623             :                                                  0);
    2624         157 :         talloc_free(tmp_msg);
    2625             : 
    2626         157 :         if (lockoutTime != 0) {
    2627          55 :                 return LDB_SUCCESS;
    2628             :         }
    2629             : 
    2630             :         /* lockoutTime == 0 resets badPwdCount */
    2631         102 :         ldb_msg_remove_attr(ac->msg, "badPwdCount");
    2632         102 :         ret = samdb_msg_add_int(ldb, ac->msg, ac->msg,
    2633             :                                 "badPwdCount", 0);
    2634         102 :         if (ret != LDB_SUCCESS) {
    2635           0 :                 return ret;
    2636             :         }
    2637         102 :         el = ldb_msg_find_element(ac->msg, "badPwdCount");
    2638         102 :         el->flags = LDB_FLAG_MOD_REPLACE;
    2639             : 
    2640         102 :         return LDB_SUCCESS;
    2641             : }
    2642             : 
    2643         102 : static int samldb_group_type_change(struct samldb_ctx *ac)
    2644             : {
    2645         102 :         struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
    2646             :         uint32_t group_type, old_group_type, account_type;
    2647             :         struct ldb_message_element *el;
    2648             :         struct ldb_message *tmp_msg;
    2649             :         int ret;
    2650             :         struct ldb_result *res;
    2651         102 :         const char * const attrs[] = { "groupType", NULL };
    2652             : 
    2653         102 :         el = dsdb_get_single_valued_attr(ac->msg, "groupType",
    2654         102 :                                          ac->req->operation);
    2655         102 :         if (el == NULL) {
    2656             :                 /* we are not affected */
    2657           3 :                 return LDB_SUCCESS;
    2658             :         }
    2659             : 
    2660             :         /* Create a temporary message for fetching the "groupType" */
    2661          99 :         tmp_msg = ldb_msg_new(ac->msg);
    2662          99 :         if (tmp_msg == NULL) {
    2663           0 :                 return ldb_module_oom(ac->module);
    2664             :         }
    2665          99 :         ret = ldb_msg_add(tmp_msg, el, 0);
    2666          99 :         if (ret != LDB_SUCCESS) {
    2667           0 :                 return ret;
    2668             :         }
    2669          99 :         group_type = ldb_msg_find_attr_as_uint(tmp_msg, "groupType", 0);
    2670          99 :         talloc_free(tmp_msg);
    2671             : 
    2672          99 :         ret = dsdb_module_search_dn(ac->module, ac, &res, ac->msg->dn, attrs,
    2673             :                                     DSDB_FLAG_NEXT_MODULE |
    2674             :                                     DSDB_SEARCH_SHOW_DELETED, ac->req);
    2675          99 :         if (ret != LDB_SUCCESS) {
    2676           0 :                 return ret;
    2677             :         }
    2678          99 :         old_group_type = ldb_msg_find_attr_as_uint(res->msgs[0], "groupType", 0);
    2679          99 :         if (old_group_type == 0) {
    2680           0 :                 return ldb_operr(ldb);
    2681             :         }
    2682             : 
    2683             :         /* Group type switching isn't so easy as it seems: We can only
    2684             :          * change in this directions: global <-> universal <-> local
    2685             :          * On each step also the group type itself
    2686             :          * (security/distribution) is variable. */
    2687             : 
    2688          99 :         if (ldb_request_get_control(ac->req, LDB_CONTROL_PROVISION_OID) == NULL) {
    2689          99 :                 switch (group_type) {
    2690          39 :                 case GTYPE_SECURITY_GLOBAL_GROUP:
    2691             :                 case GTYPE_DISTRIBUTION_GLOBAL_GROUP:
    2692             :                         /* change to "universal" allowed */
    2693          39 :                         if ((old_group_type == GTYPE_SECURITY_DOMAIN_LOCAL_GROUP) ||
    2694           0 :                         (old_group_type == GTYPE_DISTRIBUTION_DOMAIN_LOCAL_GROUP)) {
    2695           9 :                                 ldb_set_errstring(ldb,
    2696             :                                         "samldb: Change from security/distribution local group forbidden!");
    2697           9 :                                 return LDB_ERR_UNWILLING_TO_PERFORM;
    2698             :                         }
    2699          30 :                 break;
    2700             : 
    2701          27 :                 case GTYPE_SECURITY_UNIVERSAL_GROUP:
    2702             :                 case GTYPE_DISTRIBUTION_UNIVERSAL_GROUP:
    2703             :                         /* each change allowed */
    2704          27 :                 break;
    2705          18 :                 case GTYPE_SECURITY_DOMAIN_LOCAL_GROUP:
    2706             :                 case GTYPE_DISTRIBUTION_DOMAIN_LOCAL_GROUP:
    2707             :                         /* change to "universal" allowed */
    2708          18 :                         if ((old_group_type == GTYPE_SECURITY_GLOBAL_GROUP) ||
    2709           0 :                         (old_group_type == GTYPE_DISTRIBUTION_GLOBAL_GROUP)) {
    2710           9 :                                 ldb_set_errstring(ldb,
    2711             :                                         "samldb: Change from security/distribution global group forbidden!");
    2712           9 :                                 return LDB_ERR_UNWILLING_TO_PERFORM;
    2713             :                         }
    2714           9 :                 break;
    2715             : 
    2716          15 :                 case GTYPE_SECURITY_BUILTIN_LOCAL_GROUP:
    2717             :                 default:
    2718             :                         /* we don't allow this "groupType" values */
    2719          15 :                         return LDB_ERR_UNWILLING_TO_PERFORM;
    2720             :                 break;
    2721             :                 }
    2722           0 :         }
    2723             : 
    2724          66 :         account_type =  ds_gtype2atype(group_type);
    2725          66 :         if (account_type == 0) {
    2726           0 :                 ldb_set_errstring(ldb, "samldb: Unrecognized account type!");
    2727           0 :                 return LDB_ERR_UNWILLING_TO_PERFORM;
    2728             :         }
    2729          66 :         ret = samdb_msg_add_uint(ldb, ac->msg, ac->msg, "sAMAccountType",
    2730             :                                  account_type);
    2731          66 :         if (ret != LDB_SUCCESS) {
    2732           0 :                 return ret;
    2733             :         }
    2734          66 :         el = ldb_msg_find_element(ac->msg, "sAMAccountType");
    2735          66 :         el->flags = LDB_FLAG_MOD_REPLACE;
    2736             : 
    2737          66 :         return LDB_SUCCESS;
    2738             : }
    2739             : 
    2740        3127 : static int samldb_member_check(struct samldb_ctx *ac)
    2741             : {
    2742        3127 :         const char * const attrs[] = { "objectSid", NULL };
    2743        3127 :         struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
    2744             :         struct ldb_message_element *el;
    2745             :         struct ldb_dn *member_dn;
    2746             :         struct dom_sid *sid;
    2747             :         struct ldb_result *res;
    2748             :         struct dom_sid *group_sid;
    2749             :         unsigned int i, j;
    2750             :         int ret;
    2751             : 
    2752             :         /* Fetch information from the existing object */
    2753             : 
    2754        3127 :         ret = dsdb_module_search(ac->module, ac, &res, ac->msg->dn, LDB_SCOPE_BASE, attrs,
    2755             :                                  DSDB_FLAG_NEXT_MODULE | DSDB_SEARCH_SHOW_DELETED, ac->req, NULL);
    2756        3127 :         if (ret != LDB_SUCCESS) {
    2757           0 :                 return ret;
    2758             :         }
    2759        3127 :         if (res->count != 1) {
    2760           0 :                 return ldb_operr(ldb);
    2761             :         }
    2762             : 
    2763        3127 :         group_sid = samdb_result_dom_sid(res, res->msgs[0], "objectSid");
    2764        3127 :         if (group_sid == NULL) {
    2765           0 :                 return ldb_operr(ldb);
    2766             :         }
    2767             : 
    2768             :         /* We've to walk over all modification entries and consider the "member"
    2769             :          * ones. */
    2770       10295 :         for (i = 0; i < ac->msg->num_elements; i++) {
    2771        7210 :                 if (ldb_attr_cmp(ac->msg->elements[i].name, "member") != 0) {
    2772           0 :                         continue;
    2773             :                 }
    2774             : 
    2775        7171 :                 el = &ac->msg->elements[i];
    2776       14736 :                 for (j = 0; j < el->num_values; j++) {
    2777             :                         struct ldb_result *group_res;
    2778        7568 :                         const char *group_attrs[] = { "primaryGroupID" , NULL };
    2779             :                         uint32_t prim_group_rid;
    2780             : 
    2781        7568 :                         if (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_DELETE) {
    2782             :                                 /* Deletes will be handled in
    2783             :                                  * repl_meta_data, and deletes not
    2784             :                                  * matching a member will return
    2785             :                                  * LDB_ERR_UNWILLING_TO_PERFORM
    2786             :                                  * there */
    2787         976 :                                 continue;
    2788             :                         }
    2789             : 
    2790        7191 :                         member_dn = ldb_dn_from_ldb_val(ac, ldb,
    2791        7191 :                                                         &el->values[j]);
    2792        7191 :                         if (!ldb_dn_validate(member_dn)) {
    2793           3 :                                 return ldb_operr(ldb);
    2794             :                         }
    2795             : 
    2796             :                         /* Denies to add "member"s to groups which are primary
    2797             :                          * ones for them - in this case return
    2798             :                          * ERR_ENTRY_ALREADY_EXISTS. */
    2799             : 
    2800        7191 :                         ret = dsdb_module_search_dn(ac->module, ac, &group_res,
    2801             :                                                     member_dn, group_attrs,
    2802             :                                                     DSDB_FLAG_NEXT_MODULE, ac->req);
    2803        7191 :                         if (ret == LDB_ERR_NO_SUCH_OBJECT) {
    2804             :                                 /* member DN doesn't exist yet */
    2805           0 :                                 continue;
    2806             :                         }
    2807        7191 :                         if (ret != LDB_SUCCESS) {
    2808           0 :                                 return ret;
    2809             :                         }
    2810        7191 :                         prim_group_rid = ldb_msg_find_attr_as_uint(group_res->msgs[0], "primaryGroupID", (uint32_t)-1);
    2811        7191 :                         if (prim_group_rid == (uint32_t) -1) {
    2812             :                                 /* the member hasn't to be a user account ->
    2813             :                                  * therefore no check needed in this case. */
    2814         266 :                                 continue;
    2815             :                         }
    2816             : 
    2817        6925 :                         sid = dom_sid_add_rid(ac, samdb_domain_sid(ldb),
    2818             :                                               prim_group_rid);
    2819        6925 :                         if (sid == NULL) {
    2820           0 :                                 return ldb_operr(ldb);
    2821             :                         }
    2822             : 
    2823        6925 :                         if (dom_sid_equal(group_sid, sid)) {
    2824           3 :                                 ldb_asprintf_errstring(ldb,
    2825             :                                                        "samldb: member %s already set via primaryGroupID %u",
    2826             :                                                        ldb_dn_get_linearized(member_dn), prim_group_rid);
    2827           3 :                                 return LDB_ERR_ENTRY_ALREADY_EXISTS;
    2828             :                         }
    2829             :                 }
    2830             :         }
    2831             : 
    2832        3124 :         talloc_free(res);
    2833             : 
    2834        3124 :         return LDB_SUCCESS;
    2835             : }
    2836             : 
    2837             : /* SAM objects have special rules regarding the "description" attribute on
    2838             :  * modify operations. */
    2839        1056 : static int samldb_description_check(struct samldb_ctx *ac, bool *modified)
    2840             : {
    2841        1056 :         struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
    2842        1056 :         const char * const attrs[] = { "objectClass", "description", NULL };
    2843             :         struct ldb_result *res;
    2844             :         unsigned int i;
    2845             :         int ret;
    2846             : 
    2847             :         /* Fetch information from the existing object */
    2848        1056 :         ret = dsdb_module_search(ac->module, ac, &res, ac->msg->dn, LDB_SCOPE_BASE, attrs,
    2849             :                                  DSDB_FLAG_NEXT_MODULE | DSDB_SEARCH_SHOW_DELETED, ac->req,
    2850             :                                  "(|(objectclass=user)(objectclass=group)(objectclass=samDomain)(objectclass=samServer))");
    2851        1056 :         if (ret != LDB_SUCCESS) {
    2852             :                 /* don't treat it specially ... let normal error codes
    2853             :                    happen from other places */
    2854           0 :                 ldb_reset_err_string(ldb);
    2855           0 :                 return LDB_SUCCESS;
    2856             :         }
    2857        1056 :         if (res->count == 0) {
    2858             :                 /* we didn't match the filter */
    2859         299 :                 talloc_free(res);
    2860         299 :                 return LDB_SUCCESS;
    2861             :         }
    2862             : 
    2863             :         /* We've to walk over all modification entries and consider the
    2864             :          * "description" ones. */
    2865        2554 :         for (i = 0; i < ac->msg->num_elements; i++) {
    2866        1872 :                 if (ldb_attr_cmp(ac->msg->elements[i].name, "description") == 0) {
    2867         760 :                         ac->msg->elements[i].flags |= LDB_FLAG_INTERNAL_FORCE_SINGLE_VALUE_CHECK;
    2868         760 :                         *modified = true;
    2869             :                 }
    2870             :         }
    2871             : 
    2872         757 :         talloc_free(res);
    2873             : 
    2874         757 :         return LDB_SUCCESS;
    2875             : }
    2876             : 
    2877             : /* This trigger adapts the "servicePrincipalName" attributes if the
    2878             :  * "dNSHostName" and/or "sAMAccountName" attribute change(s) */
    2879        1394 : static int samldb_service_principal_names_change(struct samldb_ctx *ac)
    2880             : {
    2881        1394 :         struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
    2882        1394 :         struct ldb_message_element *el = NULL, *el2 = NULL;
    2883             :         struct ldb_message *msg;
    2884        1394 :         const char * const attrs[] = { "servicePrincipalName", NULL };
    2885             :         struct ldb_result *res;
    2886        1394 :         const char *dns_hostname = NULL, *old_dns_hostname = NULL,
    2887        1394 :                    *sam_accountname = NULL, *old_sam_accountname = NULL;
    2888             :         unsigned int i, j;
    2889             :         int ret;
    2890             : 
    2891        1783 :         el = dsdb_get_single_valued_attr(ac->msg, "dNSHostName",
    2892        1394 :                                          ac->req->operation);
    2893        1394 :         el2 = dsdb_get_single_valued_attr(ac->msg, "sAMAccountName",
    2894        1394 :                                           ac->req->operation);
    2895        1394 :         if ((el == NULL) && (el2 == NULL)) {
    2896             :                 /* we are not affected */
    2897           9 :                 return LDB_SUCCESS;
    2898             :         }
    2899             : 
    2900             :         /* Create a temporary message for fetching the "dNSHostName" */
    2901        1385 :         if (el != NULL) {
    2902         631 :                 const char *dns_attrs[] = { "dNSHostName", NULL };
    2903         631 :                 msg = ldb_msg_new(ac->msg);
    2904         631 :                 if (msg == NULL) {
    2905           0 :                         return ldb_module_oom(ac->module);
    2906             :                 }
    2907         631 :                 ret = ldb_msg_add(msg, el, 0);
    2908         631 :                 if (ret != LDB_SUCCESS) {
    2909           0 :                         return ret;
    2910             :                 }
    2911         631 :                 dns_hostname = talloc_strdup(ac,
    2912             :                                              ldb_msg_find_attr_as_string(msg, "dNSHostName", NULL));
    2913         631 :                 if (dns_hostname == NULL) {
    2914           0 :                         return ldb_module_oom(ac->module);
    2915             :                 }
    2916             : 
    2917         631 :                 talloc_free(msg);
    2918             : 
    2919         631 :                 ret = dsdb_module_search_dn(ac->module, ac, &res, ac->msg->dn,
    2920             :                                             dns_attrs, DSDB_FLAG_NEXT_MODULE, ac->req);
    2921         631 :                 if (ret == LDB_SUCCESS) {
    2922         631 :                         old_dns_hostname = ldb_msg_find_attr_as_string(res->msgs[0], "dNSHostName", NULL);
    2923             :                 }
    2924             :         }
    2925             : 
    2926             :         /* Create a temporary message for fetching the "sAMAccountName" */
    2927        1385 :         if (el2 != NULL) {
    2928         767 :                 char *tempstr, *tempstr2 = NULL;
    2929         767 :                 const char *acct_attrs[] = { "sAMAccountName", NULL };
    2930             : 
    2931         767 :                 msg = ldb_msg_new(ac->msg);
    2932         767 :                 if (msg == NULL) {
    2933           0 :                         return ldb_module_oom(ac->module);
    2934             :                 }
    2935         767 :                 ret = ldb_msg_add(msg, el2, 0);
    2936         767 :                 if (ret != LDB_SUCCESS) {
    2937           0 :                         return ret;
    2938             :                 }
    2939         767 :                 tempstr = talloc_strdup(ac,
    2940             :                                         ldb_msg_find_attr_as_string(msg, "sAMAccountName", NULL));
    2941         767 :                 talloc_free(msg);
    2942             : 
    2943         767 :                 ret = dsdb_module_search_dn(ac->module, ac, &res, ac->msg->dn, acct_attrs,
    2944             :                                             DSDB_FLAG_NEXT_MODULE, ac->req);
    2945         767 :                 if (ret == LDB_SUCCESS) {
    2946         767 :                         tempstr2 = talloc_strdup(ac,
    2947         767 :                                                  ldb_msg_find_attr_as_string(res->msgs[0],
    2948             :                                                                              "sAMAccountName", NULL));
    2949             :                 }
    2950             : 
    2951             : 
    2952             :                 /* The "sAMAccountName" needs some additional trimming: we need
    2953             :                  * to remove the trailing "$"s if they exist. */
    2954        1278 :                 if ((tempstr != NULL) && (tempstr[0] != '\0') &&
    2955         761 :                     (tempstr[strlen(tempstr) - 1] == '$')) {
    2956         175 :                         tempstr[strlen(tempstr) - 1] = '\0';
    2957             :                 }
    2958        1282 :                 if ((tempstr2 != NULL) && (tempstr2[0] != '\0') &&
    2959         767 :                     (tempstr2[strlen(tempstr2) - 1] == '$')) {
    2960         178 :                         tempstr2[strlen(tempstr2) - 1] = '\0';
    2961             :                 }
    2962         767 :                 sam_accountname = tempstr;
    2963         767 :                 old_sam_accountname = tempstr2;
    2964             :         }
    2965             : 
    2966        1385 :         if (old_dns_hostname == NULL) {
    2967             :                 /* we cannot change when the old name is unknown */
    2968        1303 :                 dns_hostname = NULL;
    2969             :         }
    2970        1467 :         if ((old_dns_hostname != NULL) && (dns_hostname != NULL) &&
    2971          82 :             (strcasecmp_m(old_dns_hostname, dns_hostname) == 0)) {
    2972             :                 /* The "dNSHostName" didn't change */
    2973          39 :                 dns_hostname = NULL;
    2974             :         }
    2975             : 
    2976        1385 :         if (old_sam_accountname == NULL) {
    2977             :                 /* we cannot change when the old name is unknown */
    2978         618 :                 sam_accountname = NULL;
    2979             :         }
    2980        2146 :         if ((old_sam_accountname != NULL) && (sam_accountname != NULL) &&
    2981         761 :             (strcasecmp_m(old_sam_accountname, sam_accountname) == 0)) {
    2982             :                 /* The "sAMAccountName" didn't change */
    2983         471 :                 sam_accountname = NULL;
    2984             :         }
    2985             : 
    2986        1385 :         if ((dns_hostname == NULL) && (sam_accountname == NULL)) {
    2987             :                 /* Well, there are information missing (old name(s)) or the
    2988             :                  * names didn't change. We've nothing to do and can exit here */
    2989         982 :                 return LDB_SUCCESS;
    2990             :         }
    2991             : 
    2992             :         /* Potential "servicePrincipalName" changes in the same request have to
    2993             :          * be handled before the update (Windows behaviour). */
    2994         325 :         el = ldb_msg_find_element(ac->msg, "servicePrincipalName");
    2995         325 :         if (el != NULL) {
    2996          23 :                 msg = ldb_msg_new(ac->msg);
    2997          23 :                 if (msg == NULL) {
    2998           0 :                         return ldb_module_oom(ac->module);
    2999             :                 }
    3000          23 :                 msg->dn = ac->msg->dn;
    3001             : 
    3002             :                 do {
    3003          23 :                         ret = ldb_msg_add(msg, el, el->flags);
    3004          23 :                         if (ret != LDB_SUCCESS) {
    3005           0 :                                 return ret;
    3006             :                         }
    3007             : 
    3008          23 :                         ldb_msg_remove_element(ac->msg, el);
    3009             : 
    3010          23 :                         el = ldb_msg_find_element(ac->msg,
    3011             :                                                   "servicePrincipalName");
    3012          23 :                 } while (el != NULL);
    3013             : 
    3014          23 :                 ret = dsdb_module_modify(ac->module, msg,
    3015             :                                          DSDB_FLAG_NEXT_MODULE, ac->req);
    3016          23 :                 if (ret != LDB_SUCCESS) {
    3017           0 :                         return ret;
    3018             :                 }
    3019          23 :                 talloc_free(msg);
    3020             :         }
    3021             : 
    3022             :         /* Fetch the "servicePrincipalName"s if any */
    3023         325 :         ret = dsdb_module_search(ac->module, ac, &res, ac->msg->dn, LDB_SCOPE_BASE, attrs,
    3024             :                                  DSDB_FLAG_NEXT_MODULE, ac->req, NULL);
    3025         325 :         if (ret != LDB_SUCCESS) {
    3026           0 :                 return ret;
    3027             :         }
    3028         325 :         if ((res->count != 1) || (res->msgs[0]->num_elements > 1)) {
    3029           0 :                 return ldb_operr(ldb);
    3030             :         }
    3031             : 
    3032         325 :         if (res->msgs[0]->num_elements == 1) {
    3033             :                 /*
    3034             :                  * Yes, we do have "servicePrincipalName"s. First we update them
    3035             :                  * locally, that means we do always substitute the current
    3036             :                  * "dNSHostName" with the new one and/or "sAMAccountName"
    3037             :                  * without "$" with the new one and then we append the
    3038             :                  * modified "servicePrincipalName"s as a message element
    3039             :                  * replace to the modification request (Windows behaviour). We
    3040             :                  * need also to make sure that the values remain case-
    3041             :                  * insensitively unique.
    3042             :                  */
    3043             : 
    3044          48 :                 ret = ldb_msg_add_empty(ac->msg, "servicePrincipalName",
    3045             :                                         LDB_FLAG_MOD_REPLACE, &el);
    3046          48 :                 if (ret != LDB_SUCCESS) {
    3047           0 :                         return ret;
    3048             :                 }
    3049             : 
    3050         143 :                 for (i = 0; i < res->msgs[0]->elements[0].num_values; i++) {
    3051             :                         char *old_str, *new_str;
    3052          95 :                         char *pos = NULL;
    3053             :                         const char *tok;
    3054             :                         struct ldb_val *vals;
    3055          95 :                         bool found = false;
    3056             : 
    3057          95 :                         old_str = (char *)
    3058          95 :                                 res->msgs[0]->elements[0].values[i].data;
    3059             : 
    3060          95 :                         new_str = talloc_strdup(ac->msg,
    3061          95 :                                                 strtok_r(old_str, "/", &pos));
    3062          95 :                         if (new_str == NULL) {
    3063           0 :                                 return ldb_module_oom(ac->module);
    3064             :                         }
    3065             : 
    3066         258 :                         while ((tok = strtok_r(NULL, "/", &pos)) != NULL) {
    3067         178 :                                 if ((dns_hostname != NULL) &&
    3068          83 :                                     (strcasecmp_m(tok, old_dns_hostname) == 0)) {
    3069          39 :                                         tok = dns_hostname;
    3070             :                                 }
    3071         119 :                                 if ((sam_accountname != NULL) &&
    3072          24 :                                     (strcasecmp_m(tok, old_sam_accountname) == 0)) {
    3073          15 :                                         tok = sam_accountname;
    3074             :                                 }
    3075             : 
    3076          95 :                                 new_str = talloc_asprintf(ac->msg, "%s/%s",
    3077             :                                                           new_str, tok);
    3078          95 :                                 if (new_str == NULL) {
    3079           0 :                                         return ldb_module_oom(ac->module);
    3080             :                                 }
    3081             :                         }
    3082             : 
    3083             :                         /* Uniqueness check */
    3084         161 :                         for (j = 0; (!found) && (j < el->num_values); j++) {
    3085          66 :                                 if (strcasecmp_m((char *)el->values[j].data,
    3086             :                                                new_str) == 0) {
    3087          15 :                                         found = true;
    3088             :                                 }
    3089             :                         }
    3090          95 :                         if (found) {
    3091          15 :                                 continue;
    3092             :                         }
    3093             : 
    3094             :                         /*
    3095             :                          * append the new "servicePrincipalName" -
    3096             :                          * code derived from ldb_msg_add_value().
    3097             :                          *
    3098             :                          * Open coded to make it clear that we must
    3099             :                          * append to the MOD_REPLACE el created above.
    3100             :                          */
    3101          80 :                         vals = talloc_realloc(ac->msg, el->values,
    3102             :                                               struct ldb_val,
    3103             :                                               el->num_values + 1);
    3104          80 :                         if (vals == NULL) {
    3105           0 :                                 return ldb_module_oom(ac->module);
    3106             :                         }
    3107          80 :                         el->values = vals;
    3108          80 :                         el->values[el->num_values] = data_blob_string_const(new_str);
    3109          80 :                         ++(el->num_values);
    3110             :                 }
    3111             :         }
    3112             : 
    3113         325 :         talloc_free(res);
    3114             : 
    3115         325 :         return LDB_SUCCESS;
    3116             : }
    3117             : 
    3118             : /* This checks the "fSMORoleOwner" attributes */
    3119        1244 : static int samldb_fsmo_role_owner_check(struct samldb_ctx *ac)
    3120             : {
    3121        1244 :         struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
    3122        1244 :         const char * const no_attrs[] = { NULL };
    3123             :         struct ldb_message_element *el;
    3124             :         struct ldb_message *tmp_msg;
    3125             :         struct ldb_dn *res_dn;
    3126             :         struct ldb_result *res;
    3127             :         int ret;
    3128             : 
    3129        1244 :         el = dsdb_get_single_valued_attr(ac->msg, "fSMORoleOwner",
    3130        1244 :                                          ac->req->operation);
    3131        1244 :         if (el == NULL) {
    3132             :                 /* we are not affected */
    3133           3 :                 return LDB_SUCCESS;
    3134             :         }
    3135             : 
    3136             :         /* Create a temporary message for fetching the "fSMORoleOwner" */
    3137        1241 :         tmp_msg = ldb_msg_new(ac->msg);
    3138        1241 :         if (tmp_msg == NULL) {
    3139           0 :                 return ldb_module_oom(ac->module);
    3140             :         }
    3141        1241 :         ret = ldb_msg_add(tmp_msg, el, 0);
    3142        1241 :         if (ret != LDB_SUCCESS) {
    3143           0 :                 return ret;
    3144             :         }
    3145        1241 :         res_dn = ldb_msg_find_attr_as_dn(ldb, ac, tmp_msg, "fSMORoleOwner");
    3146        1241 :         talloc_free(tmp_msg);
    3147             : 
    3148        1241 :         if (res_dn == NULL) {
    3149           6 :                 ldb_set_errstring(ldb,
    3150             :                                   "samldb: 'fSMORoleOwner' attributes have to reference 'nTDSDSA' entries!");
    3151           6 :                 if (ac->req->operation == LDB_ADD) {
    3152           3 :                         return LDB_ERR_CONSTRAINT_VIOLATION;
    3153             :                 } else {
    3154           3 :                         return LDB_ERR_UNWILLING_TO_PERFORM;
    3155             :                 }
    3156             :         }
    3157             : 
    3158             :         /* Fetched DN has to reference a "nTDSDSA" entry */
    3159        1235 :         ret = dsdb_module_search(ac->module, ac, &res, res_dn, LDB_SCOPE_BASE,
    3160             :                                  no_attrs,
    3161             :                                  DSDB_FLAG_NEXT_MODULE | DSDB_SEARCH_SHOW_DELETED,
    3162             :                                  ac->req, "(objectClass=nTDSDSA)");
    3163        1235 :         if (ret != LDB_SUCCESS) {
    3164           0 :                 return ret;
    3165             :         }
    3166        1235 :         if (res->count != 1) {
    3167           6 :                 ldb_set_errstring(ldb,
    3168             :                                   "samldb: 'fSMORoleOwner' attributes have to reference 'nTDSDSA' entries!");
    3169           6 :                 return LDB_ERR_UNWILLING_TO_PERFORM;
    3170             :         }
    3171             : 
    3172        1229 :         talloc_free(res);
    3173             : 
    3174        1229 :         return LDB_SUCCESS;
    3175             : }
    3176             : 
    3177             : /*
    3178             :  * Return zero if the number of zero bits in the address (looking from low to
    3179             :  * high) is equal to or greater than the length minus the mask. Otherwise it
    3180             :  * returns -1.
    3181             :  */
    3182         138 : static int check_cidr_zero_bits(uint8_t *address, unsigned int len,
    3183             :                                 unsigned int mask)
    3184             : {
    3185             :         /* <address> is an integer in big-endian form, <len> bits long. All
    3186             :            bits between <mask> and <len> must be zero. */
    3187             :         int i;
    3188             :         unsigned int byte_len;
    3189             :         unsigned int byte_mask;
    3190             :         unsigned int bit_mask;
    3191         138 :         if (len == 32) {
    3192          58 :                 DBG_INFO("Looking at address %02x%02x%02x%02x, mask %u\n",
    3193             :                          address[0], address[1], address[2], address[3],
    3194             :                           mask);
    3195          80 :         } else if (len == 128){
    3196          80 :                 DBG_INFO("Looking at address "
    3197             :                          "%02x%02x-%02x%02x-%02x%02x-%02x%02x-"
    3198             :                          "%02x%02x-%02x%02x-%02x%02x-%02x%02x, mask %u\n",
    3199             :                          address[0], address[1], address[2], address[3],
    3200             :                          address[4], address[5], address[6], address[7],
    3201             :                          address[8], address[9], address[10], address[11],
    3202             :                          address[12], address[13], address[14], address[15],
    3203             :                          mask);
    3204             :         }
    3205             : 
    3206         138 :         if (mask > len){
    3207           5 :                 DBG_INFO("mask %u is too big (> %u)\n", mask, len);
    3208           5 :                 return -1;
    3209             :         }
    3210         133 :         if (mask == len){
    3211             :                 /* single address subnet.
    3212             :                  * In IPv4 all 255s is invalid by the bitmask != address rule
    3213             :                  * in MS-ADTS. IPv6 does not suffer.
    3214             :                  */
    3215          10 :                 if (len == 32){
    3216           6 :                         if (address[0] == 255 &&
    3217           2 :                             address[1] == 255 &&
    3218           2 :                             address[2] == 255 &&
    3219           1 :                             address[3] == 255){
    3220           1 :                                 return -1;
    3221             :                         }
    3222             :                 }
    3223           9 :                 return 0;
    3224             :         }
    3225             : 
    3226         123 :         byte_len = len / 8;
    3227         123 :         byte_mask = mask / 8;
    3228             : 
    3229         714 :         for (i = byte_len - 1; i > byte_mask; i--){
    3230         593 :                 DBG_DEBUG("checking byte %d %02x\n", i, address[i]);
    3231         593 :                 if (address[i] != 0){
    3232           2 :                         return -1;
    3233             :                 }
    3234             :         }
    3235         121 :         bit_mask = (1 << (8 - (mask & 7))) - 1;
    3236         121 :         DBG_DEBUG("checking bitmask %02x & %02x overlap %02x\n", bit_mask, address[byte_mask],
    3237             :                   bit_mask & address[byte_mask]);
    3238         121 :         if (address[byte_mask] & bit_mask){
    3239          15 :                 return -1;
    3240             :         }
    3241             : 
    3242             :         /* According to MS-ADTS, the mask can't exactly equal the bitmask for
    3243             :          * IPv4 (but this is fine for v6). That is 255.255.80.0/17 is bad,
    3244             :          * because the bitmask implied by "/17" is 255.255.80.0.
    3245             :          *
    3246             :          * The bit_mask used in the previous check is the complement of what
    3247             :          * we want here.
    3248             :          */
    3249         106 :         if (len == 32 && address[byte_mask] == (uint8_t)~bit_mask){
    3250          35 :                 bool ok = false;
    3251          39 :                 for (i = 0; i < byte_mask; i++){
    3252          35 :                         if (address[i] != 255){
    3253          31 :                                 ok = true;
    3254          31 :                                 break;
    3255             :                         }
    3256             :                 }
    3257          35 :                 if (ok == false){
    3258           4 :                         return -1;
    3259             :                 }
    3260             :         }
    3261         102 :         return 0;
    3262             : }
    3263             : 
    3264             : 
    3265             : 
    3266         163 : static int check_address_roundtrip(const char *address, int family,
    3267             :                                    const uint8_t *address_bytes,
    3268             :                                    char *buffer, int buffer_len)
    3269             : {
    3270             :         /*
    3271             :          * Check that the address is in the canonical RFC5952 format for IPv6,
    3272             :          * and lacks extra leading zeros for each dotted decimal for IPv4.
    3273             :          * Handily this is what inet_ntop() gives you.
    3274             :          */
    3275         163 :         const char *address_redux = inet_ntop(family, address_bytes,
    3276             :                                               buffer, buffer_len);
    3277         163 :         if (address_redux == NULL){
    3278           0 :                 DBG_INFO("Address round trip %s failed unexpectedly"
    3279             :                          " with errno %d\n", address, errno);
    3280           0 :                 return -1;
    3281             :         }
    3282         163 :         if (strcasecmp(address, address_redux) != 0){
    3283          25 :                 DBG_INFO("Address %s round trips to %s; fail!\n",
    3284             :                          address, address_redux);
    3285             :                 /* If the address family is IPv6, and the address is in a
    3286             :                    certain range
    3287             : 
    3288             :                  */
    3289          25 :                 if (strchr(address_redux, '.') != NULL){
    3290           7 :                         DEBUG(0, ("The IPv6 address '%s' has the misfortune of "
    3291             :                                   "lying in a range that was once used for "
    3292             :                                   "IPv4 embedding (that is, it might also be "
    3293             :                                   "represented as '%s').\n", address,
    3294             :                                   address_redux));
    3295             :                 }
    3296          25 :                 return -1;
    3297             :         }
    3298         138 :         return 0;
    3299             : }
    3300             : 
    3301             : 
    3302             : 
    3303             : /*
    3304             :  * MS-ADTS v20150630 6.1.1.2.2.2.1 Subnet Object, refers to RFC1166 and
    3305             :  * RFC2373. It specifies something seemingly indistinguishable from an RFC4632
    3306             :  * CIDR address range without saying so explicitly. Here we follow the CIDR
    3307             :  * spec.
    3308             :  *
    3309             :  * Return 0 on success, -1 on error.
    3310             :  */
    3311         208 : static int verify_cidr(const char *cidr)
    3312             : {
    3313         208 :         char *address = NULL, *slash = NULL;
    3314             :         bool has_colon, has_dot;
    3315             :         int res, ret;
    3316             :         unsigned long mask;
    3317         208 :         uint8_t *address_bytes = NULL;
    3318         208 :         char *address_redux = NULL;
    3319             :         unsigned int address_len;
    3320         208 :         TALLOC_CTX *frame = NULL;
    3321         208 :         int error = 0;
    3322             : 
    3323         208 :         DBG_DEBUG("CIDR is %s\n", cidr);
    3324         208 :         frame = talloc_stackframe();
    3325         208 :         address = talloc_strdup(frame, cidr);
    3326         208 :         if (address == NULL){
    3327           0 :                 goto error;
    3328             :         }
    3329             : 
    3330             :         /* there must be a '/' */
    3331         208 :         slash = strchr(address, '/');
    3332         208 :         if (slash == NULL){
    3333           2 :                 goto error;
    3334             :         }
    3335             :         /* terminate the address for strchr, inet_pton */
    3336         206 :         *slash = '\0';
    3337             : 
    3338         206 :         mask = smb_strtoul(slash + 1, NULL, 10, &error, SMB_STR_FULL_STR_CONV);
    3339         206 :         if (mask == 0){
    3340           6 :                 DBG_INFO("Windows does not like the zero mask, "
    3341             :                          "so nor do we: %s\n", cidr);
    3342           6 :                 goto error;
    3343             :         }
    3344             : 
    3345         200 :         if (error != 0){
    3346           6 :                 DBG_INFO("CIDR mask is not a proper integer: %s\n", cidr);
    3347           6 :                 goto error;
    3348             :         }
    3349             : 
    3350         194 :         address_bytes = talloc_size(frame, sizeof(struct in6_addr));
    3351         194 :         if (address_bytes == NULL){
    3352           0 :                 goto error;
    3353             :         }
    3354             : 
    3355         194 :         address_redux = talloc_size(frame, INET6_ADDRSTRLEN);
    3356         194 :         if (address_redux == NULL){
    3357           0 :                 goto error;
    3358             :         }
    3359             : 
    3360         194 :         DBG_INFO("found address %s, mask %lu\n", address, mask);
    3361         194 :         has_colon = (strchr(address, ':') == NULL) ? false : true;
    3362         194 :         has_dot = (strchr(address, '.') == NULL) ? false : true;
    3363         194 :         if (has_dot && has_colon){
    3364             :                 /* This seems to be an IPv4 address embedded in IPv6, which is
    3365             :                    icky. We don't support it. */
    3366           2 :                 DBG_INFO("Refusing to consider cidr '%s' with dots and colons\n",
    3367             :                           cidr);
    3368           2 :                 goto error;
    3369         192 :         } else if (has_colon){  /* looks like IPv6 */
    3370         115 :                 res = inet_pton(AF_INET6, address, address_bytes);
    3371         115 :                 if (res != 1) {
    3372          10 :                         DBG_INFO("Address in %s fails to parse as IPv6\n", cidr);
    3373          10 :                         goto error;
    3374             :                 }
    3375         105 :                 address_len = 128;
    3376         105 :                 if (check_address_roundtrip(address, AF_INET6, address_bytes,
    3377             :                                             address_redux, INET6_ADDRSTRLEN)){
    3378          25 :                         goto error;
    3379             :                 }
    3380          77 :         } else if (has_dot) {
    3381             :                 /* looks like IPv4 */
    3382          77 :                 if (strcmp(address, "0.0.0.0") == 0){
    3383           1 :                         DBG_INFO("Windows does not like the zero IPv4 address, "
    3384             :                                  "so nor do we.\n");
    3385           1 :                         goto error;
    3386             :                 }
    3387          76 :                 res = inet_pton(AF_INET, address, address_bytes);
    3388          76 :                 if (res != 1) {
    3389          18 :                         DBG_INFO("Address in %s fails to parse as IPv4\n", cidr);
    3390          18 :                         goto error;
    3391             :                 }
    3392          58 :                 address_len = 32;
    3393             : 
    3394          58 :                 if (check_address_roundtrip(address, AF_INET, address_bytes,
    3395             :                                             address_redux, INET_ADDRSTRLEN)){
    3396           0 :                         goto error;
    3397             :                 }
    3398             :         } else {
    3399             :                 /* This doesn't look like an IP address at all. */
    3400           0 :                 goto error;
    3401             :         }
    3402             : 
    3403         138 :         ret = check_cidr_zero_bits(address_bytes, address_len, mask);
    3404         138 :         talloc_free(frame);
    3405         138 :         return ret;
    3406          70 :   error:
    3407          70 :         talloc_free(frame);
    3408          70 :         return -1;
    3409             : }
    3410             : 
    3411             : 
    3412         208 : static int samldb_verify_subnet(struct samldb_ctx *ac, struct ldb_dn *dn)
    3413             : {
    3414         208 :         struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
    3415         208 :         const char *cidr = NULL;
    3416         208 :         const struct ldb_val *rdn_value = NULL;
    3417             : 
    3418         208 :         rdn_value = ldb_dn_get_rdn_val(dn);
    3419         208 :         if (rdn_value == NULL) {
    3420           0 :                 ldb_set_errstring(ldb, "samldb: ldb_dn_get_rdn_val "
    3421             :                                   "failed");
    3422           0 :                 return LDB_ERR_UNWILLING_TO_PERFORM;
    3423             :         }
    3424             : 
    3425         208 :         cidr = ldb_dn_escape_value(ac, *rdn_value);
    3426         208 :         DBG_INFO("looking at cidr '%s'\n", cidr);
    3427         208 :         if (cidr == NULL) {
    3428           0 :                 ldb_set_errstring(ldb,
    3429             :                                   "samldb: adding an empty subnet cidr seems wrong");
    3430           0 :                 return LDB_ERR_UNWILLING_TO_PERFORM;
    3431             :         }
    3432             : 
    3433         208 :         if (verify_cidr(cidr)){
    3434          97 :                 ldb_set_errstring(ldb,
    3435             :                                   "samldb: subnet value is invalid");
    3436          97 :                 return LDB_ERR_INVALID_DN_SYNTAX;
    3437             :         }
    3438             : 
    3439         111 :         return LDB_SUCCESS;
    3440             : }
    3441             : 
    3442      542853 : static char *refer_if_rodc(struct ldb_context *ldb, struct ldb_request *req,
    3443             :                            struct ldb_dn *dn)
    3444             : {
    3445      542853 :         bool rodc = false;
    3446             :         struct loadparm_context *lp_ctx;
    3447             :         char *referral;
    3448             :         int ret;
    3449             :         WERROR err;
    3450             : 
    3451     1085706 :         if (ldb_request_get_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID) ||
    3452      542853 :             ldb_request_get_control(req, DSDB_CONTROL_DBCHECK_MODIFY_RO_REPLICA)) {
    3453           0 :                 return NULL;
    3454             :         }
    3455             : 
    3456      542853 :         ret = samdb_rodc(ldb, &rodc);
    3457      542853 :         if (ret != LDB_SUCCESS) {
    3458          19 :                 DEBUG(4, (__location__ ": unable to tell if we are an RODC\n"));
    3459           0 :                 return NULL;
    3460             :         }
    3461             : 
    3462      542834 :         if (rodc) {
    3463          23 :                 const char *domain = NULL;
    3464             :                 struct ldb_dn *fsmo_role_dn;
    3465             :                 struct ldb_dn *role_owner_dn;
    3466          23 :                 ldb_set_errstring(ldb, "RODC modify is forbidden!");
    3467          23 :                 lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
    3468             :                                          struct loadparm_context);
    3469             : 
    3470          23 :                 err = dsdb_get_fsmo_role_info(req, ldb, DREPL_PDC_MASTER,
    3471             :                                               &fsmo_role_dn, &role_owner_dn);
    3472          23 :                 if (W_ERROR_IS_OK(err)) {
    3473          23 :                         struct ldb_dn *server_dn = ldb_dn_copy(req, role_owner_dn);
    3474          23 :                         if (server_dn != NULL) {
    3475          23 :                                 ldb_dn_remove_child_components(server_dn, 1);
    3476             : 
    3477          23 :                                 domain = samdb_dn_to_dnshostname(ldb, req,
    3478             :                                                                  server_dn);
    3479             :                         }
    3480             :                 }
    3481          23 :                 if (domain == NULL) {
    3482           0 :                         domain = lpcfg_dnsdomain(lp_ctx);
    3483             :                 }
    3484          23 :                 referral = talloc_asprintf(req,
    3485             :                                            "ldap://%s/%s",
    3486             :                                            domain,
    3487             :                                            ldb_dn_get_linearized(dn));
    3488          23 :                 return referral;
    3489             :         }
    3490             : 
    3491      466095 :         return NULL;
    3492             : }
    3493             : 
    3494             : 
    3495             : /* add */
    3496      490167 : static int samldb_add(struct ldb_module *module, struct ldb_request *req)
    3497             : {
    3498             :         struct ldb_context *ldb;
    3499             :         struct samldb_ctx *ac;
    3500             :         struct ldb_message_element *el;
    3501             :         int ret;
    3502      490167 :         char *referral = NULL;
    3503             : 
    3504      490167 :         ldb = ldb_module_get_ctx(module);
    3505      490167 :         ldb_debug(ldb, LDB_DEBUG_TRACE, "samldb_add\n");
    3506             : 
    3507             :         /* do not manipulate our control entries */
    3508      490167 :         if (ldb_dn_is_special(req->op.add.message->dn)) {
    3509         508 :                 return ldb_next_request(module, req);
    3510             :         }
    3511             : 
    3512      489659 :         referral = refer_if_rodc(ldb, req, req->op.add.message->dn);
    3513      489659 :         if (referral != NULL) {
    3514          22 :                 ret = ldb_module_send_referral(req, referral);
    3515          22 :                 return ret;
    3516             :         }
    3517             : 
    3518      489637 :         el = ldb_msg_find_element(req->op.add.message, "userParameters");
    3519      489637 :         if (el != NULL && ldb_req_is_untrusted(req)) {
    3520           0 :                 const char *reason = "samldb_add: "
    3521             :                         "setting userParameters is not supported over LDAP, "
    3522             :                         "see https://bugzilla.samba.org/show_bug.cgi?id=8077";
    3523           0 :                 ldb_debug(ldb, LDB_DEBUG_WARNING, "%s", reason);
    3524           0 :                 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION, reason);
    3525             :         }
    3526             : 
    3527      489637 :         ac = samldb_ctx_init(module, req);
    3528      489637 :         if (ac == NULL) {
    3529           0 :                 return ldb_operr(ldb);
    3530             :         }
    3531             : 
    3532             :         /* build the new msg */
    3533      489637 :         ac->msg = ldb_msg_copy_shallow(ac, req->op.add.message);
    3534      489637 :         if (ac->msg == NULL) {
    3535           0 :                 talloc_free(ac);
    3536           0 :                 ldb_debug(ldb, LDB_DEBUG_FATAL,
    3537             :                           "samldb_add: ldb_msg_copy_shallow failed!\n");
    3538           0 :                 return ldb_operr(ldb);
    3539             :         }
    3540             : 
    3541      489637 :         el = ldb_msg_find_element(ac->msg, "fSMORoleOwner");
    3542      489637 :         if (el != NULL) {
    3543           9 :                 ret = samldb_fsmo_role_owner_check(ac);
    3544           9 :                 if (ret != LDB_SUCCESS) {
    3545           6 :                         return ret;
    3546             :                 }
    3547             :         }
    3548             : 
    3549      489631 :         if (samdb_find_attribute(ldb, ac->msg,
    3550             :                                  "objectclass", "user") != NULL) {
    3551       19901 :                 ac->type = SAMLDB_TYPE_USER;
    3552             : 
    3553       19901 :                 ret = samldb_prim_group_trigger(ac);
    3554       19901 :                 if (ret != LDB_SUCCESS) {
    3555          10 :                         return ret;
    3556             :                 }
    3557             : 
    3558       19891 :                 ret = samldb_objectclass_trigger(ac);
    3559       19891 :                 if (ret != LDB_SUCCESS) {
    3560          28 :                         return ret;
    3561             :                 }
    3562             : 
    3563       19863 :                 return samldb_fill_object(ac);
    3564             :         }
    3565             : 
    3566      469730 :         if (samdb_find_attribute(ldb, ac->msg,
    3567             :                                  "objectclass", "group") != NULL) {
    3568        7690 :                 ac->type = SAMLDB_TYPE_GROUP;
    3569             : 
    3570        7690 :                 ret = samldb_objectclass_trigger(ac);
    3571        7690 :                 if (ret != LDB_SUCCESS) {
    3572           6 :                         return ret;
    3573             :                 }
    3574             : 
    3575        7684 :                 return samldb_fill_object(ac);
    3576             :         }
    3577             : 
    3578             :         /* perhaps a foreignSecurityPrincipal? */
    3579      462040 :         if (samdb_find_attribute(ldb, ac->msg,
    3580             :                                  "objectclass",
    3581             :                                  "foreignSecurityPrincipal") != NULL) {
    3582        3649 :                 return samldb_fill_foreignSecurityPrincipal_object(ac);
    3583             :         }
    3584             : 
    3585      458391 :         if (samdb_find_attribute(ldb, ac->msg,
    3586             :                                  "objectclass", "classSchema") != NULL) {
    3587       31642 :                 ac->type = SAMLDB_TYPE_CLASS;
    3588             : 
    3589             :                 /* If in provision, these checks are too slow to do */
    3590       31642 :                 if (!ldb_request_get_control(req, DSDB_CONTROL_SKIP_DUPLICATES_CHECK_OID)) {
    3591         598 :                         ret = samldb_schema_governsid_valid_check(ac);
    3592         598 :                         if (ret != LDB_SUCCESS) {
    3593           9 :                                 return ret;
    3594             :                         }
    3595             :                 }
    3596             : 
    3597       31633 :                 ret = samldb_schema_ldapdisplayname_valid_check(ac);
    3598       31633 :                 if (ret != LDB_SUCCESS) {
    3599           9 :                         return ret;
    3600             :                 }
    3601             : 
    3602       31624 :                 ret = samldb_schema_info_update(ac);
    3603       31624 :                 if (ret != LDB_SUCCESS) {
    3604           0 :                         talloc_free(ac);
    3605           0 :                         return ret;
    3606             :                 }
    3607             : 
    3608       31624 :                 return samldb_fill_object(ac);
    3609             :         }
    3610             : 
    3611      426749 :         if (samdb_find_attribute(ldb, ac->msg,
    3612             :                                  "objectclass", "attributeSchema") != NULL) {
    3613      174434 :                 ac->type = SAMLDB_TYPE_ATTRIBUTE;
    3614             : 
    3615             :                 /* If in provision, these checks are too slow to do */
    3616      174434 :                 if (!ldb_request_get_control(req, DSDB_CONTROL_SKIP_DUPLICATES_CHECK_OID)) {
    3617         971 :                         ret = samldb_schema_attributeid_valid_check(ac);
    3618         971 :                         if (ret != LDB_SUCCESS) {
    3619           9 :                                 return ret;
    3620             :                         }
    3621             : 
    3622         962 :                         ret = samldb_schema_add_handle_linkid(ac);
    3623         962 :                         if (ret != LDB_SUCCESS) {
    3624          36 :                                 return ret;
    3625             :                         }
    3626             : 
    3627         926 :                         ret = samldb_schema_add_handle_mapiid(ac);
    3628         926 :                         if (ret != LDB_SUCCESS) {
    3629           9 :                                 return ret;
    3630             :                         }
    3631             :                 }
    3632             : 
    3633      174380 :                 ret = samldb_schema_ldapdisplayname_valid_check(ac);
    3634      174380 :                 if (ret != LDB_SUCCESS) {
    3635          18 :                         return ret;
    3636             :                 }
    3637             : 
    3638      174362 :                 ret = samldb_schema_info_update(ac);
    3639      174362 :                 if (ret != LDB_SUCCESS) {
    3640           0 :                         talloc_free(ac);
    3641           0 :                         return ret;
    3642             :                 }
    3643             : 
    3644      174362 :                 return samldb_fill_object(ac);
    3645             :         }
    3646             : 
    3647      252315 :         if (samdb_find_attribute(ldb, ac->msg,
    3648             :                                  "objectclass", "subnet") != NULL) {
    3649         206 :                 ret = samldb_verify_subnet(ac, ac->msg->dn);
    3650         206 :                 if (ret != LDB_SUCCESS) {
    3651          96 :                         talloc_free(ac);
    3652          96 :                         return ret;
    3653             :                 }
    3654             :                 /* We are just checking the value is valid, and there are no
    3655             :                    values to fill in. */
    3656             :         }
    3657             : 
    3658      252219 :         talloc_free(ac);
    3659             : 
    3660             :         /* nothing matched, go on */
    3661      252219 :         return ldb_next_request(module, req);
    3662             : }
    3663             : 
    3664             : /* modify */
    3665      399687 : static int samldb_modify(struct ldb_module *module, struct ldb_request *req)
    3666             : {
    3667             :         struct ldb_context *ldb;
    3668             :         struct samldb_ctx *ac;
    3669             :         struct ldb_message_element *el, *el2;
    3670             :         struct ldb_control *is_undelete;
    3671      399687 :         bool modified = false;
    3672             :         int ret;
    3673             : 
    3674      399687 :         if (ldb_dn_is_special(req->op.mod.message->dn)) {
    3675             :                 /* do not manipulate our control entries */
    3676         657 :                 return ldb_next_request(module, req);
    3677             :         }
    3678             : 
    3679      399030 :         ldb = ldb_module_get_ctx(module);
    3680             : 
    3681             :         /*
    3682             :          * we are going to need some special handling if in Undelete call.
    3683             :          * Since tombstone_reanimate module will restore certain attributes,
    3684             :          * we need to relax checks for: sAMAccountType, primaryGroupID
    3685             :          */
    3686      399030 :         is_undelete = ldb_request_get_control(req, DSDB_CONTROL_RESTORE_TOMBSTONE_OID);
    3687             : 
    3688             :         /* make sure that "objectSid" is not specified */
    3689      399030 :         el = ldb_msg_find_element(req->op.mod.message, "objectSid");
    3690      399030 :         if (el != NULL) {
    3691          15 :                 if (ldb_request_get_control(req, LDB_CONTROL_PROVISION_OID) == NULL) {
    3692          15 :                         ldb_set_errstring(ldb,
    3693             :                                           "samldb: objectSid must not be specified!");
    3694          15 :                         return LDB_ERR_UNWILLING_TO_PERFORM;
    3695             :                 }
    3696             :         }
    3697      399015 :         if (is_undelete == NULL) {
    3698             :                 /* make sure that "sAMAccountType" is not specified */
    3699      398750 :                 el = ldb_msg_find_element(req->op.mod.message, "sAMAccountType");
    3700      398750 :                 if (el != NULL) {
    3701          15 :                         ldb_set_errstring(ldb,
    3702             :                                           "samldb: sAMAccountType must not be specified!");
    3703          15 :                         return LDB_ERR_UNWILLING_TO_PERFORM;
    3704             :                 }
    3705             :         }
    3706             :         /* make sure that "isCriticalSystemObject" is not specified */
    3707      399000 :         el = ldb_msg_find_element(req->op.mod.message, "isCriticalSystemObject");
    3708      399000 :         if (el != NULL) {
    3709         372 :                 if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID) == NULL) {
    3710           1 :                         ldb_set_errstring(ldb,
    3711             :                                           "samldb: isCriticalSystemObject must not be specified!");
    3712           1 :                         return LDB_ERR_UNWILLING_TO_PERFORM;
    3713             :                 }
    3714             :         }
    3715             : 
    3716             :         /* msDS-IntId is not allowed to be modified
    3717             :          * except when modification comes from replication */
    3718      398999 :         if (ldb_msg_find_element(req->op.mod.message, "msDS-IntId")) {
    3719          36 :                 if (!ldb_request_get_control(req,
    3720             :                                              DSDB_CONTROL_REPLICATED_UPDATE_OID)) {
    3721          36 :                         return LDB_ERR_CONSTRAINT_VIOLATION;
    3722             :                 }
    3723             :         }
    3724             : 
    3725      398963 :         el = ldb_msg_find_element(req->op.mod.message, "userParameters");
    3726      398963 :         if (el != NULL && ldb_req_is_untrusted(req)) {
    3727           0 :                 const char *reason = "samldb: "
    3728             :                         "setting userParameters is not supported over LDAP, "
    3729             :                         "see https://bugzilla.samba.org/show_bug.cgi?id=8077";
    3730           0 :                 ldb_debug(ldb, LDB_DEBUG_WARNING, "%s", reason);
    3731           0 :                 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION, reason);
    3732             :         }
    3733             : 
    3734      398963 :         ac = samldb_ctx_init(module, req);
    3735      398963 :         if (ac == NULL) {
    3736           0 :                 return ldb_operr(ldb);
    3737             :         }
    3738             : 
    3739             :         /* build the new msg */
    3740      398963 :         ac->msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
    3741      398963 :         if (ac->msg == NULL) {
    3742           0 :                 talloc_free(ac);
    3743           0 :                 ldb_debug(ldb, LDB_DEBUG_FATAL,
    3744             :                           "samldb_modify: ldb_msg_copy_shallow failed!\n");
    3745           0 :                 return ldb_operr(ldb);
    3746             :         }
    3747             : 
    3748      398963 :         if (is_undelete == NULL) {
    3749      398698 :                 el = ldb_msg_find_element(ac->msg, "primaryGroupID");
    3750      398698 :                 if (el != NULL) {
    3751         107 :                         ret = samldb_prim_group_trigger(ac);
    3752         107 :                         if (ret != LDB_SUCCESS) {
    3753          12 :                                 return ret;
    3754             :                         }
    3755             :                 }
    3756             :         }
    3757             : 
    3758      398951 :         el = ldb_msg_find_element(ac->msg, "userAccountControl");
    3759      398951 :         if (el != NULL) {
    3760       10822 :                 modified = true;
    3761       10822 :                 ret = samldb_user_account_control_change(ac);
    3762       10822 :                 if (ret != LDB_SUCCESS) {
    3763          50 :                         return ret;
    3764             :                 }
    3765             :         }
    3766             : 
    3767      398901 :         el = ldb_msg_find_element(ac->msg, "pwdLastSet");
    3768      398901 :         if (el != NULL) {
    3769         356 :                 modified = true;
    3770         356 :                 ret = samldb_pwd_last_set_change(ac);
    3771         356 :                 if (ret != LDB_SUCCESS) {
    3772           6 :                         return ret;
    3773             :                 }
    3774             :         }
    3775             : 
    3776      398895 :         el = ldb_msg_find_element(ac->msg, "lockoutTime");
    3777      398895 :         if (el != NULL) {
    3778         157 :                 modified = true;
    3779         157 :                 ret = samldb_lockout_time(ac);
    3780         157 :                 if (ret != LDB_SUCCESS) {
    3781           0 :                         return ret;
    3782             :                 }
    3783             :         }
    3784             : 
    3785      398895 :         el = ldb_msg_find_element(ac->msg, "groupType");
    3786      398895 :         if (el != NULL) {
    3787         102 :                 modified = true;
    3788         102 :                 ret = samldb_group_type_change(ac);
    3789         102 :                 if (ret != LDB_SUCCESS) {
    3790          33 :                         return ret;
    3791             :                 }
    3792             :         }
    3793             : 
    3794      398862 :         el = ldb_msg_find_element(ac->msg, "sAMAccountName");
    3795      398862 :         if (el != NULL) {
    3796         776 :                 ret = samldb_sam_accountname_valid_check(ac);
    3797             :                 /*
    3798             :                  * Other errors are checked for elsewhere, we just
    3799             :                  * want to prevent duplicates
    3800             :                  */
    3801         776 :                 if (ret == LDB_ERR_ENTRY_ALREADY_EXISTS) {
    3802           3 :                         return ret;
    3803             :                 }
    3804             :         }
    3805             : 
    3806      398859 :         el = ldb_msg_find_element(ac->msg, "ldapDisplayName");
    3807      398859 :         if (el != NULL) {
    3808          29 :                 ret = samldb_schema_ldapdisplayname_valid_check(ac);
    3809          29 :                 if (ret != LDB_SUCCESS) {
    3810          18 :                         return ret;
    3811             :                 }
    3812             :         }
    3813             : 
    3814      398841 :         el = ldb_msg_find_element(ac->msg, "attributeID");
    3815      398841 :         if (el != NULL) {
    3816          27 :                 ldb_asprintf_errstring(ldb_module_get_ctx(ac->module),
    3817             :                                        "Once set, attributeID values may not be modified");
    3818          27 :                 return LDB_ERR_CONSTRAINT_VIOLATION;
    3819             :         }
    3820             : 
    3821      398814 :         el = ldb_msg_find_element(ac->msg, "governsID");
    3822      398814 :         if (el != NULL) {
    3823          18 :                 ldb_asprintf_errstring(ldb_module_get_ctx(ac->module),
    3824             :                                        "Once set, governsID values may not be modified");
    3825          18 :                 return LDB_ERR_CONSTRAINT_VIOLATION;
    3826             :         }
    3827             : 
    3828      398796 :         el = ldb_msg_find_element(ac->msg, "member");
    3829      398796 :         if (el != NULL) {
    3830        3129 :                 struct ldb_control *fix_link_sid_ctrl = NULL;
    3831             : 
    3832        3129 :                 fix_link_sid_ctrl = ldb_request_get_control(ac->req,
    3833             :                                         DSDB_CONTROL_DBCHECK_FIX_LINK_DN_SID);
    3834        3129 :                 if (fix_link_sid_ctrl == NULL) {
    3835        3127 :                         ret = samldb_member_check(ac);
    3836        3127 :                         if (ret != LDB_SUCCESS) {
    3837           3 :                                 return ret;
    3838             :                         }
    3839             :                 }
    3840             :         }
    3841             : 
    3842      398793 :         el = ldb_msg_find_element(ac->msg, "description");
    3843      398793 :         if (el != NULL) {
    3844        1056 :                 ret = samldb_description_check(ac, &modified);
    3845        1056 :                 if (ret != LDB_SUCCESS) {
    3846           0 :                         return ret;
    3847             :                 }
    3848             :         }
    3849             : 
    3850      398793 :         el = ldb_msg_find_element(ac->msg, "dNSHostName");
    3851      398793 :         el2 = ldb_msg_find_element(ac->msg, "sAMAccountName");
    3852      398793 :         if ((el != NULL) || (el2 != NULL)) {
    3853        1394 :                 modified = true;
    3854        1394 :                 ret = samldb_service_principal_names_change(ac);
    3855        1394 :                 if (ret != LDB_SUCCESS) {
    3856           0 :                         return ret;
    3857             :                 }
    3858             :         }
    3859             : 
    3860      398793 :         el = ldb_msg_find_element(ac->msg, "fSMORoleOwner");
    3861      398793 :         if (el != NULL) {
    3862        1235 :                 ret = samldb_fsmo_role_owner_check(ac);
    3863        1235 :                 if (ret != LDB_SUCCESS) {
    3864           6 :                         return ret;
    3865             :                 }
    3866             :         }
    3867             : 
    3868      398787 :         if (modified) {
    3869             :                 struct ldb_request *child_req;
    3870             : 
    3871             :                 /* Now perform the real modifications as a child request */
    3872       22194 :                 ret = ldb_build_mod_req(&child_req, ldb, ac,
    3873       13430 :                                         ac->msg,
    3874             :                                         req->controls,
    3875             :                                         req, dsdb_next_callback,
    3876             :                                         req);
    3877       13430 :                 LDB_REQ_SET_LOCATION(child_req);
    3878       13430 :                 if (ret != LDB_SUCCESS) {
    3879           0 :                         return ret;
    3880             :                 }
    3881             : 
    3882       13430 :                 return ldb_next_request(module, child_req);
    3883             :         }
    3884             : 
    3885      385357 :         talloc_free(ac);
    3886             : 
    3887             :         /* no change which interests us, go on */
    3888      385357 :         return ldb_next_request(module, req);
    3889             : }
    3890             : 
    3891             : /* delete */
    3892             : 
    3893       53193 : static int samldb_prim_group_users_check(struct samldb_ctx *ac)
    3894             : {
    3895             :         struct ldb_context *ldb;
    3896             :         struct dom_sid *sid;
    3897             :         uint32_t rid;
    3898             :         NTSTATUS status;
    3899             :         int ret;
    3900       53193 :         struct ldb_result *res = NULL;
    3901       53193 :         struct ldb_result *res_users = NULL;
    3902       53193 :         const char * const attrs[] = { "objectSid", "isDeleted", NULL };
    3903       53193 :         const char * const noattrs[] = { NULL };
    3904             : 
    3905       53193 :         ldb = ldb_module_get_ctx(ac->module);
    3906             : 
    3907             :         /* Finds out the SID/RID of the SAM object */
    3908       53193 :         ret = dsdb_module_search_dn(ac->module, ac, &res, ac->req->op.del.dn,
    3909             :                                         attrs,
    3910             :                                         DSDB_FLAG_NEXT_MODULE | DSDB_SEARCH_SHOW_DELETED,
    3911             :                                         ac->req);
    3912       53193 :         if (ret != LDB_SUCCESS) {
    3913           0 :                 return ret;
    3914             :         }
    3915             : 
    3916       53193 :         if (ldb_msg_check_string_attribute(res->msgs[0], "isDeleted", "TRUE")) {
    3917           7 :                 return LDB_SUCCESS;
    3918             :         }
    3919             : 
    3920       53179 :         sid = samdb_result_dom_sid(ac, res->msgs[0], "objectSid");
    3921       53179 :         if (sid == NULL) {
    3922             :                 /* No SID - it might not be a SAM object - therefore ok */
    3923       32162 :                 return LDB_SUCCESS;
    3924             :         }
    3925       20956 :         status = dom_sid_split_rid(ac, sid, NULL, &rid);
    3926       20956 :         if (!NT_STATUS_IS_OK(status)) {
    3927           0 :                 return ldb_operr(ldb);
    3928             :         }
    3929       20956 :         if (rid == 0) {
    3930             :                 /* Special object (security principal?) */
    3931           0 :                 return LDB_SUCCESS;
    3932             :         }
    3933             :         /* do not allow deletion of well-known sids */
    3934       20971 :         if (rid < DSDB_SAMDB_MINIMUM_ALLOWED_RID &&
    3935          15 :             (ldb_request_get_control(ac->req, LDB_CONTROL_RELAX_OID) == NULL)) {
    3936          15 :                 return LDB_ERR_OTHER;
    3937             :         }
    3938             : 
    3939             :         /* Deny delete requests from groups which are primary ones */
    3940       20941 :         ret = dsdb_module_search(ac->module, ac, &res_users,
    3941             :                                  ldb_get_default_basedn(ldb),
    3942             :                                  LDB_SCOPE_SUBTREE, noattrs,
    3943             :                                  DSDB_FLAG_NEXT_MODULE,
    3944             :                                  ac->req,
    3945             :                                  "(&(primaryGroupID=%u)(objectClass=user))", rid);
    3946       20941 :         if (ret != LDB_SUCCESS) {
    3947           0 :                 return ret;
    3948             :         }
    3949       20941 :         if (res_users->count > 0) {
    3950           5 :                 ldb_asprintf_errstring(ldb_module_get_ctx(ac->module),
    3951             :                                        "Refusing to delete %s, as it "
    3952             :                                        "is still the primaryGroupID "
    3953             :                                        "for %u users",
    3954           3 :                                        ldb_dn_get_linearized(res->msgs[0]->dn),
    3955           3 :                                        res_users->count);
    3956             : 
    3957             :                 /*
    3958             :                  * Yes, this seems very wrong, but we have a test
    3959             :                  * for this exact error code in sam.py
    3960             :                  */
    3961           3 :                 return LDB_ERR_ENTRY_ALREADY_EXISTS;
    3962             :         }
    3963             : 
    3964       20847 :         return LDB_SUCCESS;
    3965             : }
    3966             : 
    3967       53195 : static int samldb_delete(struct ldb_module *module, struct ldb_request *req)
    3968             : {
    3969             :         struct samldb_ctx *ac;
    3970       53195 :         char *referral = NULL;
    3971             :         int ret;
    3972             :         struct ldb_context *ldb;
    3973             : 
    3974       53195 :         if (ldb_dn_is_special(req->op.del.dn)) {
    3975             :                 /* do not manipulate our control entries */
    3976           1 :                 return ldb_next_request(module, req);
    3977             :         }
    3978             : 
    3979       53194 :         ldb = ldb_module_get_ctx(module);
    3980             : 
    3981       53194 :         referral = refer_if_rodc(ldb, req, req->op.del.dn);
    3982       53194 :         if (referral != NULL) {
    3983           1 :                 ret = ldb_module_send_referral(req, referral);
    3984           1 :                 return ret;
    3985             :         }
    3986             : 
    3987       53193 :         ac = samldb_ctx_init(module, req);
    3988       53193 :         if (ac == NULL) {
    3989           0 :                 return ldb_operr(ldb_module_get_ctx(module));
    3990             :         }
    3991             : 
    3992       53193 :         ret = samldb_prim_group_users_check(ac);
    3993       53193 :         if (ret != LDB_SUCCESS) {
    3994          18 :                 return ret;
    3995             :         }
    3996             : 
    3997       53175 :         talloc_free(ac);
    3998             : 
    3999       53175 :         return ldb_next_request(module, req);
    4000             : }
    4001             : 
    4002             : /* rename */
    4003             : 
    4004        1129 : static int check_rename_constraints(struct ldb_message *msg,
    4005             :                                     struct samldb_ctx *ac,
    4006             :                                     struct ldb_dn *olddn, struct ldb_dn *newdn)
    4007             : {
    4008        1129 :         struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
    4009             :         struct ldb_dn *dn1, *dn2, *nc_root;
    4010             :         int32_t systemFlags;
    4011        1129 :         bool move_op = false;
    4012        1129 :         bool rename_op = false;
    4013             :         int ret;
    4014             : 
    4015             :         /* Skip the checks if old and new DN are the same, or if we have the
    4016             :          * relax control specified or if the returned objects is already
    4017             :          * deleted and needs only to be moved for consistency. */
    4018             : 
    4019        1129 :         if (ldb_dn_compare(olddn, newdn) == 0) {
    4020           6 :                 return LDB_SUCCESS;
    4021             :         }
    4022        1123 :         if (ldb_request_get_control(ac->req, LDB_CONTROL_RELAX_OID) != NULL) {
    4023          19 :                 return LDB_SUCCESS;
    4024             :         }
    4025             : 
    4026        1104 :         if (ldb_msg_find_attr_as_bool(msg, "isDeleted", false)) {
    4027             :                 /*
    4028             :                  * check originating request if we are supposed
    4029             :                  * to "see" this record in first place.
    4030             :                  */
    4031           2 :                 if (ldb_request_get_control(ac->req, LDB_CONTROL_SHOW_DELETED_OID) == NULL) {
    4032           1 :                         return LDB_ERR_NO_SUCH_OBJECT;
    4033             :                 }
    4034           1 :                 return LDB_ERR_UNWILLING_TO_PERFORM;
    4035             :         }
    4036             : 
    4037             :         /* Objects under CN=System */
    4038             : 
    4039        1102 :         dn1 = ldb_dn_copy(ac, ldb_get_default_basedn(ldb));
    4040        1102 :         if (dn1 == NULL) return ldb_oom(ldb);
    4041             : 
    4042        1102 :         if ( ! ldb_dn_add_child_fmt(dn1, "CN=System")) {
    4043           0 :                 talloc_free(dn1);
    4044           0 :                 return LDB_ERR_OPERATIONS_ERROR;
    4045             :         }
    4046             : 
    4047        1103 :         if ((ldb_dn_compare_base(dn1, olddn) == 0) &&
    4048           1 :             (ldb_dn_compare_base(dn1, newdn) != 0)) {
    4049           1 :                 talloc_free(dn1);
    4050           1 :                 ldb_asprintf_errstring(ldb,
    4051             :                                        "subtree_rename: Cannot move/rename %s. Objects under CN=System have to stay under it!",
    4052             :                                        ldb_dn_get_linearized(olddn));
    4053           1 :                 return LDB_ERR_OTHER;
    4054             :         }
    4055             : 
    4056        1101 :         talloc_free(dn1);
    4057             : 
    4058             :         /* LSA objects */
    4059             : 
    4060        2202 :         if ((samdb_find_attribute(ldb, msg, "objectClass", "secret") != NULL) ||
    4061        1101 :             (samdb_find_attribute(ldb, msg, "objectClass", "trustedDomain") != NULL)) {
    4062           0 :                 ldb_asprintf_errstring(ldb,
    4063             :                                        "subtree_rename: Cannot move/rename %s. It's an LSA-specific object!",
    4064             :                                        ldb_dn_get_linearized(olddn));
    4065           0 :                 return LDB_ERR_UNWILLING_TO_PERFORM;
    4066             :         }
    4067             : 
    4068             :         /* subnet objects */
    4069        1101 :         if (samdb_find_attribute(ldb, msg, "objectclass", "subnet") != NULL) {
    4070           2 :                 ret = samldb_verify_subnet(ac, newdn);
    4071           2 :                 if (ret != LDB_SUCCESS) {
    4072           1 :                         return ret;
    4073             :                 }
    4074             :         }
    4075             : 
    4076             :         /* systemFlags */
    4077             : 
    4078        1100 :         dn1 = ldb_dn_get_parent(ac, olddn);
    4079        1100 :         if (dn1 == NULL) return ldb_oom(ldb);
    4080        1100 :         dn2 = ldb_dn_get_parent(ac, newdn);
    4081        1100 :         if (dn2 == NULL) return ldb_oom(ldb);
    4082             : 
    4083        1100 :         if (ldb_dn_compare(dn1, dn2) == 0) {
    4084         621 :                 rename_op = true;
    4085             :         } else {
    4086         473 :                 move_op = true;
    4087             :         }
    4088             : 
    4089        1100 :         talloc_free(dn1);
    4090        1100 :         talloc_free(dn2);
    4091             : 
    4092        1100 :         systemFlags = ldb_msg_find_attr_as_int(msg, "systemFlags", 0);
    4093             : 
    4094             :         /* Fetch name context */
    4095             : 
    4096        1100 :         ret = dsdb_find_nc_root(ldb, ac, olddn, &nc_root);
    4097        1100 :         if (ret != LDB_SUCCESS) {
    4098           0 :                 return ret;
    4099             :         }
    4100             : 
    4101        1100 :         if (ldb_dn_compare(nc_root, ldb_get_schema_basedn(ldb)) == 0) {
    4102           8 :                 if (move_op) {
    4103           0 :                         ldb_asprintf_errstring(ldb,
    4104             :                                                "subtree_rename: Cannot move %s within schema partition",
    4105             :                                                ldb_dn_get_linearized(olddn));
    4106           0 :                         return LDB_ERR_UNWILLING_TO_PERFORM;
    4107             :                 }
    4108          16 :                 if (rename_op &&
    4109           8 :                     (systemFlags & SYSTEM_FLAG_SCHEMA_BASE_OBJECT) != 0) {
    4110           1 :                         ldb_asprintf_errstring(ldb,
    4111             :                                                "subtree_rename: Cannot rename %s within schema partition",
    4112             :                                                ldb_dn_get_linearized(olddn));
    4113           1 :                         return LDB_ERR_UNWILLING_TO_PERFORM;
    4114             :                 }
    4115        1092 :         } else if (ldb_dn_compare(nc_root, ldb_get_config_basedn(ldb)) == 0) {
    4116          16 :                 if (move_op &&
    4117           4 :                     (systemFlags & SYSTEM_FLAG_CONFIG_ALLOW_MOVE) == 0) {
    4118             :                         /* Here we have to do more: control the
    4119             :                          * "ALLOW_LIMITED_MOVE" flag. This means that the
    4120             :                          * grand-grand-parents of two objects have to be equal
    4121             :                          * in order to perform the move (this is used for
    4122             :                          * moving "server" objects in the "sites" container). */
    4123           4 :                         bool limited_move =
    4124           4 :                                 systemFlags & SYSTEM_FLAG_CONFIG_ALLOW_LIMITED_MOVE;
    4125             : 
    4126           4 :                         if (limited_move) {
    4127           0 :                                 dn1 = ldb_dn_copy(ac, olddn);
    4128           0 :                                 if (dn1 == NULL) return ldb_oom(ldb);
    4129           0 :                                 dn2 = ldb_dn_copy(ac, newdn);
    4130           0 :                                 if (dn2 == NULL) return ldb_oom(ldb);
    4131             : 
    4132           0 :                                 limited_move &= ldb_dn_remove_child_components(dn1, 3);
    4133           0 :                                 limited_move &= ldb_dn_remove_child_components(dn2, 3);
    4134           0 :                                 limited_move &= ldb_dn_compare(dn1, dn2) == 0;
    4135             : 
    4136           0 :                                 talloc_free(dn1);
    4137           0 :                                 talloc_free(dn2);
    4138             :                         }
    4139             : 
    4140           4 :                         if (!limited_move
    4141           4 :                             && ldb_request_get_control(ac->req, DSDB_CONTROL_RESTORE_TOMBSTONE_OID) == NULL) {
    4142           2 :                                 ldb_asprintf_errstring(ldb,
    4143             :                                                        "subtree_rename: Cannot move %s to %s in config partition",
    4144             :                                                        ldb_dn_get_linearized(olddn), ldb_dn_get_linearized(newdn));
    4145           2 :                                 return LDB_ERR_UNWILLING_TO_PERFORM;
    4146             :                         }
    4147             :                 }
    4148          18 :                 if (rename_op &&
    4149           8 :                     (systemFlags & SYSTEM_FLAG_CONFIG_ALLOW_RENAME) == 0) {
    4150           1 :                         ldb_asprintf_errstring(ldb,
    4151             :                                                "subtree_rename: Cannot rename %s to %s within config partition",
    4152             :                                                ldb_dn_get_linearized(olddn), ldb_dn_get_linearized(newdn));
    4153           1 :                         return LDB_ERR_UNWILLING_TO_PERFORM;
    4154             :                 }
    4155        1080 :         } else if (ldb_dn_compare(nc_root, ldb_get_default_basedn(ldb)) == 0) {
    4156        1494 :                 if (move_op &&
    4157         469 :                     (systemFlags & SYSTEM_FLAG_DOMAIN_DISALLOW_MOVE) != 0) {
    4158           1 :                         ldb_asprintf_errstring(ldb,
    4159             :                                                "subtree_rename: Cannot move %s to %s - DISALLOW_MOVE set",
    4160             :                                                ldb_dn_get_linearized(olddn), ldb_dn_get_linearized(newdn));
    4161           1 :                         return LDB_ERR_UNWILLING_TO_PERFORM;
    4162             :                 }
    4163        1530 :                 if (rename_op &&
    4164         608 :                     (systemFlags & SYSTEM_FLAG_DOMAIN_DISALLOW_RENAME) != 0) {
    4165           1 :                         ldb_asprintf_errstring(ldb,
    4166             :                                                        "subtree_rename: Cannot rename %s to %s - DISALLOW_RENAME set",
    4167             :                                                ldb_dn_get_linearized(olddn), ldb_dn_get_linearized(newdn));
    4168           1 :                         return LDB_ERR_UNWILLING_TO_PERFORM;
    4169             :                 }
    4170             :         }
    4171             : 
    4172        1094 :         talloc_free(nc_root);
    4173             : 
    4174        1094 :         return LDB_SUCCESS;
    4175             : }
    4176             : 
    4177             : 
    4178        2251 : static int samldb_rename_search_base_callback(struct ldb_request *req,
    4179             :                                                struct ldb_reply *ares)
    4180             : {
    4181             :         struct samldb_ctx *ac;
    4182             :         int ret;
    4183             : 
    4184        2251 :         ac = talloc_get_type(req->context, struct samldb_ctx);
    4185             : 
    4186        2251 :         if (!ares) {
    4187           0 :                 return ldb_module_done(ac->req, NULL, NULL,
    4188             :                                         LDB_ERR_OPERATIONS_ERROR);
    4189             :         }
    4190        2251 :         if (ares->error != LDB_SUCCESS) {
    4191           0 :                 return ldb_module_done(ac->req, ares->controls,
    4192             :                                         ares->response, ares->error);
    4193             :         }
    4194             : 
    4195        2251 :         switch (ares->type) {
    4196        1129 :         case LDB_REPLY_ENTRY:
    4197             :                 /*
    4198             :                  * This is the root entry of the originating move
    4199             :                  * respectively rename request. It has been already
    4200             :                  * stored in the list using "subtree_rename_search()".
    4201             :                  * Only this one is subject to constraint checking.
    4202             :                  */
    4203        2045 :                 ret = check_rename_constraints(ares->message, ac,
    4204        1123 :                                                ac->req->op.rename.olddn,
    4205        1129 :                                                ac->req->op.rename.newdn);
    4206        1129 :                 if (ret != LDB_SUCCESS) {
    4207          10 :                         return ldb_module_done(ac->req, NULL, NULL,
    4208             :                                                ret);
    4209             :                 }
    4210        1113 :                 break;
    4211             : 
    4212           0 :         case LDB_REPLY_REFERRAL:
    4213             :                 /* ignore */
    4214           0 :                 break;
    4215             : 
    4216        1122 :         case LDB_REPLY_DONE:
    4217             : 
    4218             :                 /*
    4219             :                  * Great, no problem with the rename, so go ahead as
    4220             :                  * if we never were here
    4221             :                  */
    4222        1122 :                 ret = ldb_next_request(ac->module, ac->req);
    4223        1122 :                 talloc_free(ares);
    4224        1122 :                 return ret;
    4225             :         }
    4226             : 
    4227        1119 :         talloc_free(ares);
    4228        1119 :         return LDB_SUCCESS;
    4229             : }
    4230             : 
    4231             : 
    4232             : /* rename */
    4233        1132 : static int samldb_rename(struct ldb_module *module, struct ldb_request *req)
    4234             : {
    4235             :         struct ldb_context *ldb;
    4236             :         static const char * const attrs[] = { "objectClass", "systemFlags",
    4237             :                                               "isDeleted", NULL };
    4238             :         struct ldb_request *search_req;
    4239             :         struct samldb_ctx *ac;
    4240             :         int ret;
    4241             : 
    4242        1132 :         if (ldb_dn_is_special(req->op.rename.olddn)) { /* do not manipulate our control entries */
    4243           0 :                 return ldb_next_request(module, req);
    4244             :         }
    4245             : 
    4246        1132 :         ldb = ldb_module_get_ctx(module);
    4247             : 
    4248        1132 :         ac = samldb_ctx_init(module, req);
    4249        1132 :         if (!ac) {
    4250           0 :                 return ldb_oom(ldb);
    4251             :         }
    4252             : 
    4253        1132 :         ret = ldb_build_search_req(&search_req, ldb, ac,
    4254             :                                    req->op.rename.olddn,
    4255             :                                    LDB_SCOPE_BASE,
    4256             :                                    "(objectClass=*)",
    4257             :                                    attrs,
    4258             :                                    NULL,
    4259             :                                    ac,
    4260             :                                    samldb_rename_search_base_callback,
    4261             :                                    req);
    4262        1132 :         LDB_REQ_SET_LOCATION(search_req);
    4263        1132 :         if (ret != LDB_SUCCESS) {
    4264           0 :                 return ret;
    4265             :         }
    4266             : 
    4267        1132 :         ret = ldb_request_add_control(search_req, LDB_CONTROL_SHOW_RECYCLED_OID,
    4268             :                                       true, NULL);
    4269        1132 :         if (ret != LDB_SUCCESS) {
    4270           0 :                 return ret;
    4271             :         }
    4272             : 
    4273        1132 :         return ldb_next_request(ac->module, search_req);
    4274             : }
    4275             : 
    4276             : /* extended */
    4277             : 
    4278          34 : static int samldb_extended_allocate_rid_pool(struct ldb_module *module, struct ldb_request *req)
    4279             : {
    4280          34 :         struct ldb_context *ldb = ldb_module_get_ctx(module);
    4281             :         struct dsdb_fsmo_extended_op *exop;
    4282             :         int ret;
    4283             : 
    4284          34 :         exop = talloc_get_type(req->op.extended.data,
    4285             :                                struct dsdb_fsmo_extended_op);
    4286          34 :         if (!exop) {
    4287           0 :                 ldb_set_errstring(ldb,
    4288             :                                   "samldb_extended_allocate_rid_pool: invalid extended data");
    4289           0 :                 return LDB_ERR_PROTOCOL_ERROR;
    4290             :         }
    4291             : 
    4292          34 :         ret = ridalloc_allocate_rid_pool_fsmo(module, exop, req);
    4293          34 :         if (ret != LDB_SUCCESS) {
    4294           0 :                 return ret;
    4295             :         }
    4296             : 
    4297          34 :         return ldb_module_done(req, NULL, NULL, LDB_SUCCESS);
    4298             : }
    4299             : 
    4300         995 : static int samldb_extended_allocate_rid(struct ldb_module *module, struct ldb_request *req)
    4301             : {
    4302         995 :         struct ldb_context *ldb = ldb_module_get_ctx(module);
    4303             :         struct dsdb_extended_allocate_rid *exop;
    4304             :         int ret;
    4305             : 
    4306         995 :         exop = talloc_get_type(req->op.extended.data,
    4307             :                                struct dsdb_extended_allocate_rid);
    4308         995 :         if (!exop) {
    4309           0 :                 ldb_set_errstring(ldb,
    4310             :                                   "samldb_extended_allocate_rid: invalid extended data");
    4311           0 :                 return LDB_ERR_PROTOCOL_ERROR;
    4312             :         }
    4313             : 
    4314         995 :         ret = ridalloc_allocate_rid(module, &exop->rid, req);
    4315         995 :         if (ret != LDB_SUCCESS) {
    4316           2 :                 return ret;
    4317             :         }
    4318             : 
    4319         993 :         return ldb_module_done(req, NULL, NULL, LDB_SUCCESS);
    4320             : }
    4321             : 
    4322          39 : static int samldb_extended_create_own_rid_set(struct ldb_module *module, struct ldb_request *req)
    4323             : {
    4324          39 :         struct ldb_context *ldb = ldb_module_get_ctx(module);
    4325             :         int ret;
    4326             :         struct ldb_dn *dn;
    4327             : 
    4328          39 :         if (req->op.extended.data != NULL) {
    4329           0 :                 ldb_set_errstring(ldb,
    4330             :                                   "samldb_extended_create_own_rid_set: invalid extended data (should be NULL)");
    4331           0 :                 return LDB_ERR_PROTOCOL_ERROR;
    4332             :         }
    4333             : 
    4334          39 :         ret = ridalloc_create_own_rid_set(module, req,
    4335             :                                           &dn, req);
    4336          39 :         if (ret != LDB_SUCCESS) {
    4337           1 :                 return ret;
    4338             :         }
    4339             : 
    4340          38 :         return ldb_module_done(req, NULL, NULL, LDB_SUCCESS);
    4341             : }
    4342             : 
    4343     1111702 : static int samldb_extended(struct ldb_module *module, struct ldb_request *req)
    4344             : {
    4345     1111702 :         if (strcmp(req->op.extended.oid, DSDB_EXTENDED_ALLOCATE_RID_POOL) == 0) {
    4346          34 :                 return samldb_extended_allocate_rid_pool(module, req);
    4347             :         }
    4348             : 
    4349     1111668 :         if (strcmp(req->op.extended.oid, DSDB_EXTENDED_ALLOCATE_RID) == 0) {
    4350         995 :                 return samldb_extended_allocate_rid(module, req);
    4351             :         }
    4352             : 
    4353     1110673 :         if (strcmp(req->op.extended.oid, DSDB_EXTENDED_CREATE_OWN_RID_SET) == 0) {
    4354          39 :                 return samldb_extended_create_own_rid_set(module, req);
    4355             :         }
    4356             : 
    4357     1110634 :         return ldb_next_request(module, req);
    4358             : }
    4359             : 
    4360             : 
    4361             : static const struct ldb_module_ops ldb_samldb_module_ops = {
    4362             :         .name          = "samldb",
    4363             :         .add           = samldb_add,
    4364             :         .modify        = samldb_modify,
    4365             :         .del           = samldb_delete,
    4366             :         .rename        = samldb_rename,
    4367             :         .extended      = samldb_extended
    4368             : };
    4369             : 
    4370             : 
    4371        5536 : int ldb_samldb_module_init(const char *version)
    4372             : {
    4373        5536 :         LDB_MODULE_CHECK_VERSION(version);
    4374        5536 :         return ldb_register_module(&ldb_samldb_module_ops);
    4375             : }

Generated by: LCOV version 1.13