LCOV - code coverage report
Current view: top level - source4/dsdb/samdb/ldb_modules - samldb.c (source / functions) Hit Total Coverage
Test: coverage report for v4-17-test 1498b464 Lines: 2094 2517 83.2 %
Date: 2024-06-13 04:01:37 Functions: 77 77 100.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             :         /*
      75             :          * should we apply the need_trailing_dollar restriction to
      76             :          * samAccountName
      77             :          */
      78             : 
      79             :         bool need_trailing_dollar;
      80             : 
      81             :         /* the resulting message */
      82             :         struct ldb_message *msg;
      83             : 
      84             :         /* used in "samldb_find_for_defaultObjectCategory" */
      85             :         struct ldb_dn *dn, *res_dn;
      86             : 
      87             :         /* all the async steps necessary to complete the operation */
      88             :         struct samldb_step *steps;
      89             :         struct samldb_step *curstep;
      90             : 
      91             :         /* If someone set an ares to forward controls and response back to the caller */
      92             :         struct ldb_reply *ares;
      93             : };
      94             : 
      95      732666 : static struct samldb_ctx *samldb_ctx_init(struct ldb_module *module,
      96             :                                           struct ldb_request *req)
      97             : {
      98             :         struct ldb_context *ldb;
      99             :         struct samldb_ctx *ac;
     100             : 
     101      732666 :         ldb = ldb_module_get_ctx(module);
     102             : 
     103      732666 :         ac = talloc_zero(req, struct samldb_ctx);
     104      732666 :         if (ac == NULL) {
     105           0 :                 ldb_oom(ldb);
     106           0 :                 return NULL;
     107             :         }
     108             : 
     109      732666 :         ac->module = module;
     110      732666 :         ac->req = req;
     111             : 
     112      732666 :         return ac;
     113             : }
     114             : 
     115      222924 : static int samldb_add_step(struct samldb_ctx *ac, samldb_step_fn_t fn)
     116             : {
     117             :         struct samldb_step *step, *stepper;
     118             : 
     119      222924 :         step = talloc_zero(ac, struct samldb_step);
     120      222924 :         if (step == NULL) {
     121           0 :                 return ldb_oom(ldb_module_get_ctx(ac->module));
     122             :         }
     123             : 
     124      222924 :         step->fn = fn;
     125             : 
     126      222924 :         if (ac->steps == NULL) {
     127      155540 :                 ac->steps = step;
     128      155540 :                 ac->curstep = step;
     129             :         } else {
     130       67384 :                 if (ac->curstep == NULL)
     131           0 :                         return ldb_operr(ldb_module_get_ctx(ac->module));
     132      142920 :                 for (stepper = ac->curstep; stepper->next != NULL;
     133       22384 :                         stepper = stepper->next);
     134       67384 :                 stepper->next = step;
     135             :         }
     136             : 
     137      222924 :         return LDB_SUCCESS;
     138             : }
     139             : 
     140      155425 : static int samldb_first_step(struct samldb_ctx *ac)
     141             : {
     142      155425 :         if (ac->steps == NULL) {
     143           0 :                 return ldb_operr(ldb_module_get_ctx(ac->module));
     144             :         }
     145             : 
     146      155425 :         ac->curstep = ac->steps;
     147      155425 :         return ac->curstep->fn(ac);
     148             : }
     149             : 
     150      222633 : static int samldb_next_step(struct samldb_ctx *ac)
     151             : {
     152      222633 :         if (ac->curstep->next) {
     153       67344 :                 ac->curstep = ac->curstep->next;
     154       67344 :                 return ac->curstep->fn(ac);
     155             :         }
     156             : 
     157             :         /* We exit the samldb module here. If someone set an "ares" to forward
     158             :          * controls and response back to the caller, use them. */
     159      155289 :         if (ac->ares) {
     160      155289 :                 return ldb_module_done(ac->req, ac->ares->controls,
     161      155289 :                                        ac->ares->response, LDB_SUCCESS);
     162             :         } else {
     163           0 :                 return ldb_module_done(ac->req, NULL, NULL, LDB_SUCCESS);
     164             :         }
     165             : }
     166             : 
     167      208325 : static int samldb_get_single_valued_attr(struct ldb_context *ldb,
     168             :                                          struct samldb_ctx *ac,
     169             :                                          const char *attr,
     170             :                                          const char **value)
     171             : {
     172             :         /*
     173             :          * The steps we end up going through to get and check a single valued
     174             :          * attribute.
     175             :          */
     176      208325 :         struct ldb_message_element *el = NULL;
     177             :         int ret;
     178             : 
     179      208325 :         *value = NULL;
     180             : 
     181      374147 :         ret = dsdb_get_expected_new_values(ac,
     182      208325 :                                            ac->msg,
     183             :                                            attr,
     184             :                                            &el,
     185      208325 :                                            ac->req->operation);
     186             : 
     187      208325 :         if (ret != LDB_SUCCESS) {
     188           0 :                 return ret;
     189             :         }
     190      208325 :         if (el == NULL) {
     191             :                 /* we are not affected */
     192       16751 :                 return LDB_SUCCESS;
     193             :         }
     194             : 
     195      191574 :         if (el->num_values > 1) {
     196           2 :                 ldb_asprintf_errstring(
     197             :                         ldb,
     198             :                         "samldb: %s has %u values, should be single-valued!",
     199           2 :                         attr, el->num_values);
     200           2 :                 return LDB_ERR_CONSTRAINT_VIOLATION;
     201      191572 :         } else if (el->num_values == 0) {
     202           9 :                 ldb_asprintf_errstring(
     203             :                         ldb,
     204             :                         "samldb: new value for %s "
     205             :                         "not provided for mandatory, single-valued attribute!",
     206             :                         attr);
     207           9 :                 return LDB_ERR_OBJECT_CLASS_VIOLATION;
     208             :         }
     209             : 
     210             : 
     211      191563 :         if (el->values[0].length == 0) {
     212           0 :                 ldb_asprintf_errstring(
     213             :                         ldb,
     214             :                         "samldb: %s is of zero length, should have a value!",
     215             :                         attr);
     216           0 :                 return LDB_ERR_OBJECT_CLASS_VIOLATION;
     217             :         }
     218             : 
     219      191563 :         *value = (char *)el->values[0].data;
     220             : 
     221      191563 :         return LDB_SUCCESS;
     222             : }
     223             : 
     224      155655 : static int samldb_unique_attr_check(struct samldb_ctx *ac, const char *attr,
     225             :                                     const char *attr_conflict,
     226             :                                     struct ldb_dn *base_dn)
     227             : {
     228      155655 :         struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
     229      155655 :         const char * const no_attrs[] = { NULL };
     230      155655 :         struct ldb_result *res = NULL;
     231      155655 :         const char *str = NULL;
     232      155655 :         const char *enc_str = NULL;
     233             :         int ret;
     234             : 
     235      155655 :         ret = samldb_get_single_valued_attr(ldb, ac, attr, &str);
     236      155655 :         if (ret != LDB_SUCCESS) {
     237          11 :                 return ret;
     238             :         }
     239      155644 :         if (str == NULL) {
     240             :                 /* the attribute wasn't found */
     241         470 :                 return LDB_SUCCESS;
     242             :         }
     243             : 
     244      155174 :         enc_str = ldb_binary_encode_string(ac, str);
     245      155174 :         if (enc_str == NULL) {
     246           0 :                 return ldb_module_oom(ac->module);
     247             :         }
     248             : 
     249             :         /*
     250             :          * No other object should have the attribute with this value.
     251             :          */
     252      155174 :         if (attr_conflict != NULL) {
     253        1569 :                 ret = dsdb_module_search(ac->module, ac, &res,
     254             :                                          base_dn,
     255             :                                          LDB_SCOPE_SUBTREE, no_attrs,
     256             :                                          DSDB_FLAG_NEXT_MODULE, ac->req,
     257             :                                          "(|(%s=%s)(%s=%s))",
     258             :                                          attr, enc_str,
     259             :                                          attr_conflict, enc_str);
     260             :         } else {
     261      153605 :                 ret = dsdb_module_search(ac->module, ac, &res,
     262             :                                          base_dn,
     263             :                                          LDB_SCOPE_SUBTREE, no_attrs,
     264             :                                          DSDB_FLAG_NEXT_MODULE, ac->req,
     265             :                                          "(%s=%s)", attr, enc_str);
     266             :         }
     267      155174 :         if (ret != LDB_SUCCESS) {
     268           0 :                 return ret;
     269             :         }
     270      155174 :         if (res->count > 1) {
     271           0 :                 return ldb_operr(ldb);
     272      155174 :         } else if (res->count == 1) {
     273         509 :                 if (ldb_dn_compare(res->msgs[0]->dn, ac->msg->dn) != 0) {
     274          84 :                         ldb_asprintf_errstring(ldb,
     275             :                                                "samldb: %s '%s' already in use!",
     276             :                                                attr, enc_str);
     277          84 :                         return LDB_ERR_ENTRY_ALREADY_EXISTS;
     278             :                 }
     279             :         }
     280      155090 :         talloc_free(res);
     281             : 
     282      155090 :         return LDB_SUCCESS;
     283             : }
     284             : 
     285             : 
     286             : 
     287       72637 : static inline int samldb_sam_account_upn_clash_sub_search(
     288             :         struct samldb_ctx *ac,
     289             :         TALLOC_CTX *mem_ctx,
     290             :         struct ldb_dn *base_dn,
     291             :         const char *attr,
     292             :         const char *value,
     293             :         const char *err_msg
     294             :         )
     295             : {
     296             :         /*
     297             :          * A very specific helper function for samldb_sam_account_upn_clash(),
     298             :          * where we end up doing this same thing several times in a row.
     299             :          */
     300       72637 :         const char * const no_attrs[] = { NULL };
     301       72637 :         struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
     302       72637 :         struct ldb_result *res = NULL;
     303             :         int ret;
     304       72637 :         char *enc_value = ldb_binary_encode_string(ac, value);
     305       72637 :         if (enc_value == NULL) {
     306           0 :                 return ldb_module_oom(ac->module);
     307             :         }
     308       72637 :         ret = dsdb_module_search(ac->module, mem_ctx, &res,
     309             :                                  base_dn,
     310             :                                  LDB_SCOPE_SUBTREE, no_attrs,
     311             :                                  DSDB_FLAG_NEXT_MODULE, ac->req,
     312             :                                  "(%s=%s)",
     313             :                                  attr, enc_value);
     314       72637 :         talloc_free(enc_value);
     315             : 
     316       72637 :         if (ret != LDB_SUCCESS) {
     317           0 :                 return ret;
     318       72637 :         } else if (res->count > 1) {
     319           0 :                 return ldb_operr(ldb);
     320       72637 :         } else if (res->count == 1) {
     321         518 :                 if (ldb_dn_compare(res->msgs[0]->dn, ac->msg->dn) != 0){
     322          22 :                         ldb_asprintf_errstring(ldb,
     323             :                                                "samldb: %s '%s' "
     324             :                                                "is already in use %s",
     325             :                                                attr, value, err_msg);
     326             :                         /* different errors for different attrs */
     327          22 :                         if (strcasecmp("userPrincipalName", attr) == 0) {
     328          16 :                                 return LDB_ERR_CONSTRAINT_VIOLATION;
     329             :                         }
     330           6 :                         return LDB_ERR_ENTRY_ALREADY_EXISTS;
     331             :                 }
     332             :         }
     333       72615 :         return LDB_SUCCESS;
     334             : }
     335             : 
     336       26057 : static int samaccountname_bad_chars_check(struct samldb_ctx *ac,
     337             :                                           const char *name)
     338             : {
     339             :         /*
     340             :          * The rules here are based on
     341             :          *
     342             :          * https://social.technet.microsoft.com/wiki/contents/articles/11216.active-directory-requirements-for-creating-objects.aspx
     343             :          *
     344             :          * Windows considers UTF-8 sequences that map to "similar" characters
     345             :          * (e.g. 'a', 'ā') to be the same sAMAccountName, and we don't. Names
     346             :          * that are not valid UTF-8 *are* allowed.
     347             :          *
     348             :          * Additionally, Samba collapses multiple spaces, and Windows doesn't.
     349             :          */
     350       26057 :         struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
     351             :         size_t i;
     352             : 
     353      467732 :         for (i = 0; name[i] != '\0'; i++) {
     354      441681 :                 uint8_t c = name[i];
     355      441681 :                 char *p = NULL;
     356      441681 :                 if (c < 32 || c == 127) {
     357           6 :                         ldb_asprintf_errstring(
     358             :                                 ldb,
     359             :                                 "samldb: sAMAccountName contains invalid "
     360             :                                 "0x%.2x character\n", c);
     361           6 :                         return LDB_ERR_CONSTRAINT_VIOLATION;
     362             :                 }
     363      441675 :                 p = strchr("\"[]:;|=+*?<>/\\,", c);
     364      441675 :                 if (p != NULL) {
     365           0 :                         ldb_asprintf_errstring(
     366             :                                 ldb,
     367             :                                 "samldb: sAMAccountName contains invalid "
     368             :                                 "'%c' character\n", c);
     369           0 :                         return LDB_ERR_CONSTRAINT_VIOLATION;
     370             :                 }
     371             :         }
     372             : 
     373       26051 :         if (i == 0) {
     374           0 :                 ldb_asprintf_errstring(
     375             :                         ldb,
     376             :                         "samldb: sAMAccountName is empty\n");
     377           0 :                 return LDB_ERR_CONSTRAINT_VIOLATION;
     378             :         }
     379             : 
     380       26051 :         if (name[i - 1] == '.') {
     381           0 :                 ldb_asprintf_errstring(
     382             :                         ldb,
     383             :                         "samldb: sAMAccountName ends with '.'");
     384           0 :                 return LDB_ERR_CONSTRAINT_VIOLATION;
     385             :         }
     386       26051 :         return LDB_SUCCESS;
     387             : }
     388             : 
     389       26335 : static int samldb_sam_account_upn_clash(struct samldb_ctx *ac)
     390             : {
     391       26335 :         struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
     392             :         int ret;
     393       26335 :         struct ldb_dn *base_dn = ldb_get_default_basedn(ldb);
     394       26335 :         TALLOC_CTX *tmp_ctx = NULL;
     395       26335 :         const char *real_sam = NULL;
     396       26335 :         const char *real_upn = NULL;
     397       26335 :         char *implied_sam = NULL;
     398       26335 :         char *implied_upn = NULL;
     399       26335 :         const char *realm = NULL;
     400             : 
     401       26335 :         ret = samldb_get_single_valued_attr(ldb, ac,
     402             :                                             "sAMAccountName",
     403             :                                             &real_sam);
     404       26335 :         if (ret != LDB_SUCCESS) {
     405           0 :                 return ret;
     406             :         }
     407       26335 :         ret = samldb_get_single_valued_attr(ldb, ac,
     408             :                                             "userPrincipalName",
     409             :                                             &real_upn);
     410       26335 :         if (ret != LDB_SUCCESS) {
     411           0 :                 return ret;
     412             :         }
     413       26335 :         if (real_upn == NULL && real_sam == NULL) {
     414             :                 /* Not changing these things, so we're done */
     415           0 :                 return LDB_SUCCESS;
     416             :         }
     417             : 
     418       26335 :         tmp_ctx = talloc_new(ac);
     419       26335 :         realm = samdb_dn_to_dns_domain(tmp_ctx, base_dn);
     420       26335 :         if (realm == NULL) {
     421           0 :                 talloc_free(tmp_ctx);
     422           0 :                 return ldb_operr(ldb);
     423             :         }
     424             : 
     425       26335 :         if (real_upn != NULL) {
     426             :                 /*
     427             :                  * note we take the last @ in the upn because the first (i.e.
     428             :                  * sAMAccountName equivalent) part can contain @.
     429             :                  *
     430             :                  * It is also OK (per Windows) for a UPN to have zero @s.
     431             :                  */
     432       10332 :                 char *at = NULL;
     433       10332 :                 char *upn_realm = NULL;
     434       10332 :                 implied_sam = talloc_strdup(tmp_ctx, real_upn);
     435       10332 :                 if (implied_sam == NULL) {
     436           0 :                         talloc_free(tmp_ctx);
     437           0 :                         return ldb_module_oom(ac->module);
     438             :                 }
     439             : 
     440       10332 :                 at = strrchr(implied_sam, '@');
     441       10332 :                 if (at == NULL) {
     442             :                         /*
     443             :                          * there is no @ in this UPN, so we treat the whole
     444             :                          * thing as a sAMAccountName for the purposes of a
     445             :                          * clash.
     446             :                          */
     447          53 :                         DBG_INFO("samldb: userPrincipalName '%s' contains "
     448             :                                  "no '@' character\n", implied_sam);
     449             :                 } else {
     450             :                         /*
     451             :                          * Now, this upn only implies a sAMAccountName if the
     452             :                          * realm is our realm. So we need to compare the tail
     453             :                          * of the upn to the realm.
     454             :                          */
     455       10279 :                         *at = '\0';
     456       10279 :                         upn_realm = at + 1;
     457       10279 :                         if (strcasecmp(upn_realm, realm) != 0) {
     458             :                                 /* implied_sam is not the implied
     459             :                                  * sAMAccountName after all, because it is
     460             :                                  * from a different realm. */
     461         127 :                                 TALLOC_FREE(implied_sam);
     462             :                         }
     463             :                 }
     464             :         }
     465             : 
     466       26335 :         if (real_sam != NULL) {
     467       26057 :                 implied_upn = talloc_asprintf(tmp_ctx, "%s@%s",
     468             :                                               real_sam, realm);
     469       26057 :                 if (implied_upn == NULL) {
     470           0 :                         talloc_free(tmp_ctx);
     471           0 :                         return ldb_module_oom(ac->module);
     472             :                 }
     473             :         }
     474             : 
     475             :         /*
     476             :          * Now we have all of the actual and implied names, in which to search
     477             :          * for conflicts.
     478             :          */
     479       26335 :         if (real_sam != NULL) {
     480       26057 :                 ret = samldb_sam_account_upn_clash_sub_search(
     481             :                         ac, tmp_ctx, base_dn, "sAMAccountName",
     482             :                         real_sam, "");
     483             : 
     484       26057 :                 if (ret != LDB_SUCCESS) {
     485           0 :                         talloc_free(tmp_ctx);
     486           0 :                         return ret;
     487             :                 }
     488       26057 :                 ret = samaccountname_bad_chars_check(ac, real_sam);
     489       26057 :                 if (ret != LDB_SUCCESS) {
     490           6 :                         talloc_free(tmp_ctx);
     491           6 :                         return ret;
     492             :                 }
     493             :         }
     494       26329 :         if (implied_upn != NULL) {
     495       26051 :                 ret = samldb_sam_account_upn_clash_sub_search(
     496             :                         ac, tmp_ctx, base_dn, "userPrincipalName", implied_upn,
     497             :                         "(implied by sAMAccountName)");
     498             : 
     499       26051 :                 if (ret != LDB_SUCCESS) {
     500           6 :                         talloc_free(tmp_ctx);
     501           6 :                         return ret;
     502             :                 }
     503             :         }
     504       26323 :         if (real_upn != NULL) {
     505       10331 :                 ret = samldb_sam_account_upn_clash_sub_search(
     506             :                         ac, tmp_ctx, base_dn, "userPrincipalName",
     507             :                         real_upn, "");
     508             : 
     509       10331 :                 if (ret != LDB_SUCCESS) {
     510          10 :                         talloc_free(tmp_ctx);
     511          10 :                         return ret;
     512             :                 }
     513             :         }
     514       26313 :         if (implied_sam != NULL) {
     515       10198 :                 ret = samldb_sam_account_upn_clash_sub_search(
     516             :                         ac, tmp_ctx, base_dn, "sAMAccountName", implied_sam,
     517             :                         "(implied by userPrincipalName)");
     518       10198 :                 if (ret != LDB_SUCCESS) {
     519           6 :                         talloc_free(tmp_ctx);
     520           6 :                         return ret;
     521             :                 }
     522             :         }
     523             : 
     524       26307 :         talloc_free(tmp_ctx);
     525       26307 :         return LDB_SUCCESS;
     526             : }
     527             : 
     528             : 
     529             : /* This is run during an add or modify */
     530       26081 : static int samldb_sam_accountname_valid_check(struct samldb_ctx *ac)
     531             : {
     532       26081 :         int ret = 0;
     533             :         bool is_admin;
     534       26081 :         struct security_token *user_token = NULL;
     535       26081 :         struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
     536       26081 :         struct ldb_message_element *el = NULL;
     537             : 
     538       46495 :         ret = dsdb_get_expected_new_values(ac,
     539       26081 :                                            ac->msg,
     540             :                                            "samAccountName",
     541             :                                            &el,
     542       26081 :                                            ac->req->operation);
     543       26081 :         if (ret != LDB_SUCCESS) {
     544           0 :                 return ret;
     545             :         }
     546             : 
     547       26081 :         if (el == NULL || el->num_values == 0) {
     548          15 :                 ldb_asprintf_errstring(ldb,
     549             :                         "%08X: samldb: 'samAccountName' can't be deleted/empty!",
     550          15 :                         W_ERROR_V(WERR_DS_ILLEGAL_MOD_OPERATION));
     551          15 :                 if (ac->req->operation == LDB_ADD) {
     552           3 :                         return LDB_ERR_CONSTRAINT_VIOLATION;
     553             :                 } else {
     554          12 :                         return LDB_ERR_UNWILLING_TO_PERFORM;
     555             :                 }
     556             :         }
     557             : 
     558       26066 :         ret = samldb_unique_attr_check(ac, "samAccountName", NULL,
     559             :                                        ldb_get_default_basedn(
     560             :                                                ldb_module_get_ctx(ac->module)));
     561             : 
     562             :         /*
     563             :          * Error code munging to try and match what must be some quite
     564             :          * strange code-paths in Windows
     565             :          */
     566       26066 :         if (ret == LDB_ERR_CONSTRAINT_VIOLATION
     567           2 :             && ac->req->operation == LDB_MODIFY) {
     568           1 :                 ret = LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
     569       26065 :         } else if (ret == LDB_ERR_OBJECT_CLASS_VIOLATION) {
     570           0 :                 ret = LDB_ERR_CONSTRAINT_VIOLATION;
     571             :         }
     572       26066 :         if (ret != LDB_SUCCESS) {
     573          14 :                 return ret;
     574             :         }
     575             : 
     576       26052 :         ret = samldb_sam_account_upn_clash(ac);
     577       26052 :         if (ret != LDB_SUCCESS) {
     578          12 :                 return ret;
     579             :         }
     580             : 
     581       26040 :         if (!ac->need_trailing_dollar) {
     582       23503 :                 return LDB_SUCCESS;
     583             :         }
     584             : 
     585             :         /* This does not permit a single $ */
     586        2537 :         if (el->values[0].length < 2) {
     587           0 :                 ldb_asprintf_errstring(ldb,
     588             :                                        "%08X: samldb: 'samAccountName' "
     589             :                                        "can't just be one character!",
     590           0 :                         W_ERROR_V(WERR_DS_ILLEGAL_MOD_OPERATION));
     591           0 :                 return LDB_ERR_UNWILLING_TO_PERFORM;
     592             :         }
     593             : 
     594        2537 :         user_token = acl_user_token(ac->module);
     595        2537 :         if (user_token == NULL) {
     596           0 :                 return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
     597             :         }
     598             : 
     599             :         is_admin
     600        2537 :                 = security_token_has_builtin_administrators(user_token);
     601             : 
     602        2537 :         if (is_admin) {
     603             :                 /*
     604             :                  * Administrators are allowed to select strange names.
     605             :                  * This is poor practice but not prevented.
     606             :                  */
     607        1904 :                 return false;
     608             :         }
     609             : 
     610         633 :         if (el->values[0].data[el->values[0].length - 1] != '$') {
     611          13 :                 ldb_asprintf_errstring(ldb,
     612             :                                        "%08X: samldb: 'samAccountName' "
     613             :                                        "must have a trailing $!",
     614          13 :                         W_ERROR_V(WERR_DS_ILLEGAL_MOD_OPERATION));
     615          13 :                 return LDB_ERR_UNWILLING_TO_PERFORM;
     616             :         }
     617         620 :         if (el->values[0].data[el->values[0].length - 2] == '$') {
     618           0 :                 ldb_asprintf_errstring(ldb,
     619             :                                        "%08X: samldb: 'samAccountName' "
     620             :                                        "must not have a double trailing $!",
     621           0 :                         W_ERROR_V(WERR_DS_ILLEGAL_MOD_OPERATION));
     622           0 :                 return LDB_ERR_UNWILLING_TO_PERFORM;
     623             :         }
     624             : 
     625         620 :         return ret;
     626             : }
     627             : 
     628         971 : static int samldb_schema_attributeid_valid_check(struct samldb_ctx *ac)
     629             : {
     630         971 :         int ret = samldb_unique_attr_check(ac, "attributeID", "governsID",
     631             :                                            ldb_get_schema_basedn(
     632             :                                                    ldb_module_get_ctx(ac->module)));
     633         971 :         if (ret == LDB_ERR_ENTRY_ALREADY_EXISTS) {
     634           9 :                 ret = LDB_ERR_UNWILLING_TO_PERFORM;
     635             :         }
     636         971 :         return ret;
     637             : }
     638             : 
     639         598 : static int samldb_schema_governsid_valid_check(struct samldb_ctx *ac)
     640             : {
     641         598 :         int ret = samldb_unique_attr_check(ac, "governsID", "attributeID",
     642             :                                            ldb_get_schema_basedn(
     643             :                                                    ldb_module_get_ctx(ac->module)));
     644         598 :         if (ret == LDB_ERR_ENTRY_ALREADY_EXISTS) {
     645           9 :                 ret = LDB_ERR_UNWILLING_TO_PERFORM;
     646             :         }
     647         598 :         return ret;
     648             : }
     649             : 
     650      127877 : static int samldb_schema_ldapdisplayname_valid_check(struct samldb_ctx *ac)
     651             : {
     652      127877 :         int ret = samldb_unique_attr_check(ac, "lDAPDisplayName", NULL,
     653             :                                            ldb_get_schema_basedn(
     654             :                                                    ldb_module_get_ctx(ac->module)));
     655      127877 :         if (ret == LDB_ERR_ENTRY_ALREADY_EXISTS) {
     656          36 :                 ret = LDB_ERR_UNWILLING_TO_PERFORM;
     657             :         }
     658      127877 :         return ret;
     659             : }
     660             : 
     661          63 : static int samldb_check_linkid_used(struct samldb_ctx *ac,
     662             :                                     struct dsdb_schema *schema,
     663             :                                     struct ldb_dn *schema_dn,
     664             :                                     struct ldb_context *ldb,
     665             :                                     int32_t linkID,
     666             :                                     bool *found)
     667             : {
     668             :         int ret;
     669             :         struct ldb_result *ldb_res;
     670             : 
     671          63 :         if (dsdb_attribute_by_linkID(schema, linkID)) {
     672          24 :                 *found = true;
     673          24 :                 return LDB_SUCCESS;
     674             :         }
     675             : 
     676          39 :         ret = dsdb_module_search(ac->module, ac,
     677             :                                  &ldb_res,
     678             :                                  schema_dn, LDB_SCOPE_ONELEVEL, NULL,
     679             :                                  DSDB_FLAG_NEXT_MODULE,
     680             :                                  ac->req,
     681             :                                  "(linkID=%d)", linkID);
     682          39 :         if (ret != LDB_SUCCESS) {
     683           0 :                 ldb_debug_set(ldb, LDB_DEBUG_ERROR,
     684             :                               __location__": Searching for linkID=%d failed - %s\n",
     685             :                               linkID,
     686             :                               ldb_errstring(ldb));
     687           0 :                 return ldb_operr(ldb);
     688             :         }
     689             : 
     690          39 :         *found = (ldb_res->count != 0);
     691          39 :         talloc_free(ldb_res);
     692             : 
     693          39 :         return LDB_SUCCESS;
     694             : }
     695             : 
     696             : /* Find the next open forward linkID in the schema. */
     697          27 : static int samldb_generate_next_linkid(struct samldb_ctx *ac,
     698             :                                        struct dsdb_schema *schema,
     699             :                                        int32_t *next_linkID)
     700             : {
     701             :         int ret;
     702             :         struct ldb_context *ldb;
     703             :         struct ldb_dn *schema_dn;
     704          27 :         bool linkID_used = true;
     705             : 
     706             :         /*
     707             :          * Windows starts at about 0xB0000000 in order to stop potential
     708             :          * collisions with future additions to the schema. We pass this
     709             :          * around as a signed int sometimes, but this should be sufficient.
     710             :          */
     711          27 :         *next_linkID = 0x40000000;
     712             : 
     713          27 :         ldb = ldb_module_get_ctx(ac->module);
     714          27 :         schema_dn = ldb_get_schema_basedn(ldb);
     715             : 
     716          39 :         while (linkID_used) {
     717          42 :                 *next_linkID += 2;
     718          42 :                 ret = samldb_check_linkid_used(ac, schema,
     719             :                                                schema_dn, ldb,
     720             :                                                *next_linkID, &linkID_used);
     721          42 :                 if (ret != LDB_SUCCESS) {
     722           0 :                         return ret;
     723             :                 }
     724             :         }
     725             : 
     726          27 :         return LDB_SUCCESS;
     727             : }
     728             : 
     729         962 : static int samldb_schema_add_handle_linkid(struct samldb_ctx *ac)
     730             : {
     731             :         int ret;
     732         962 :         bool ok, found = false;
     733             :         struct ldb_message_element *el;
     734             :         const char *enc_str;
     735             :         const struct dsdb_attribute *attr;
     736             :         struct ldb_context *ldb;
     737             :         struct ldb_dn *schema_dn;
     738             :         struct dsdb_schema *schema;
     739         962 :         int32_t new_linkID = 0;
     740             : 
     741         962 :         ldb = ldb_module_get_ctx(ac->module);
     742         962 :         schema = dsdb_get_schema(ldb, ac);
     743         962 :         schema_dn = ldb_get_schema_basedn(ldb);
     744             : 
     745        1796 :         ret = dsdb_get_expected_new_values(ac,
     746         962 :                                            ac->msg,
     747             :                                            "linkID",
     748             :                                            &el,
     749         962 :                                            ac->req->operation);
     750         962 :         if (ret != LDB_SUCCESS) {
     751           0 :                 return ret;
     752             :         }
     753             : 
     754         962 :         if (el == NULL || el->num_values == 0) {
     755         762 :                 return LDB_SUCCESS;
     756             :         }
     757             : 
     758         200 :         enc_str = ldb_binary_encode(ac, el->values[0]);
     759         200 :         if (enc_str == NULL) {
     760           0 :                 return ldb_module_oom(ac->module);
     761             :         }
     762             : 
     763         200 :         ok = (strcmp(enc_str, "0") == 0);
     764         200 :         if (ok) {
     765           0 :                 return LDB_SUCCESS;
     766             :         }
     767             : 
     768             :         /*
     769             :          * This OID indicates that the caller wants the linkID
     770             :          * to be automatically generated. We therefore assign
     771             :          * it the next open linkID.
     772             :          */
     773         200 :         ok = (strcmp(enc_str, "1.2.840.113556.1.2.50") == 0);
     774         200 :         if (ok) {
     775          27 :                 ret = samldb_generate_next_linkid(ac, schema, &new_linkID);
     776          27 :                 if (ret != LDB_SUCCESS) {
     777           0 :                         return ret;
     778             :                 }
     779             : 
     780          27 :                 ldb_msg_remove_element(ac->msg, el);
     781          27 :                 ret = samdb_msg_add_int(ldb, ac->msg, ac->msg, "linkID",
     782             :                                         new_linkID);
     783          27 :                 return ret;
     784             :         }
     785             : 
     786             :         /*
     787             :          * Using either the attributeID or lDAPDisplayName of
     788             :          * another attribute in the linkID field indicates that
     789             :          * we should make this the backlink of that attribute.
     790             :          */
     791         173 :         attr = dsdb_attribute_by_attributeID_oid(schema, enc_str);
     792         173 :         if (attr == NULL) {
     793         155 :                 attr = dsdb_attribute_by_lDAPDisplayName(schema, enc_str);
     794             :         }
     795             : 
     796         173 :         if (attr != NULL) {
     797             :                 /*
     798             :                  * The attribute we're adding this as a backlink of must
     799             :                  * be a forward link.
     800             :                  */
     801          39 :                 if (attr->linkID % 2 != 0) {
     802          18 :                         return LDB_ERR_UNWILLING_TO_PERFORM;
     803             :                 }
     804             : 
     805          21 :                 new_linkID = attr->linkID + 1;
     806             : 
     807             :                 /* Make sure that this backlink doesn't already exist. */
     808          21 :                 ret = samldb_check_linkid_used(ac, schema,
     809             :                                                schema_dn, ldb,
     810             :                                                new_linkID, &found);
     811          21 :                 if (ret != LDB_SUCCESS) {
     812           0 :                         return ret;
     813             :                 }
     814             : 
     815          21 :                 if (found) {
     816           9 :                         return LDB_ERR_UNWILLING_TO_PERFORM;
     817             :                 }
     818             : 
     819          12 :                 ldb_msg_remove_element(ac->msg, el);
     820          12 :                 ret = samdb_msg_add_int(ldb, ac->msg, ac->msg, "linkID",
     821             :                                         new_linkID);
     822          12 :                 return ret;
     823             :         }
     824             : 
     825         134 :         schema_dn = ldb_get_schema_basedn(ldb_module_get_ctx(ac->module));
     826         134 :         ret = samldb_unique_attr_check(ac, "linkID", NULL, schema_dn);
     827         134 :         if (ret == LDB_ERR_ENTRY_ALREADY_EXISTS) {
     828           9 :                 return LDB_ERR_UNWILLING_TO_PERFORM;
     829             :         } else {
     830         125 :                 return ret;
     831             :         }
     832             : }
     833             : 
     834           9 : static int samldb_check_mapiid_used(struct samldb_ctx *ac,
     835             :                                     struct dsdb_schema *schema,
     836             :                                     struct ldb_dn *schema_dn,
     837             :                                     struct ldb_context *ldb,
     838             :                                     int32_t mapiid,
     839             :                                     bool *found)
     840             : {
     841             :         int ret;
     842             :         struct ldb_result *ldb_res;
     843             : 
     844           9 :         ret = dsdb_module_search(ac->module, ac,
     845             :                                  &ldb_res,
     846             :                                  schema_dn, LDB_SCOPE_ONELEVEL, NULL,
     847             :                                  DSDB_FLAG_NEXT_MODULE,
     848             :                                  ac->req,
     849             :                                  "(mAPIID=%d)", mapiid);
     850           9 :         if (ret != LDB_SUCCESS) {
     851           0 :                 ldb_debug_set(ldb, LDB_DEBUG_ERROR,
     852             :                               __location__": Searching for mAPIID=%d failed - %s\n",
     853             :                               mapiid,
     854             :                               ldb_errstring(ldb));
     855           0 :                 return ldb_operr(ldb);
     856             :         }
     857             : 
     858           9 :         *found = (ldb_res->count != 0);
     859           9 :         talloc_free(ldb_res);
     860             : 
     861           9 :         return LDB_SUCCESS;
     862             : }
     863             : 
     864           9 : static int samldb_generate_next_mapiid(struct samldb_ctx *ac,
     865             :                                        struct dsdb_schema *schema,
     866             :                                        int32_t *next_mapiid)
     867             : {
     868             :         int ret;
     869             :         struct ldb_context *ldb;
     870             :         struct ldb_dn *schema_dn;
     871           9 :         bool mapiid_used = true;
     872             : 
     873             :         /* Windows' generation seems to start about here */
     874           9 :         *next_mapiid = 60000;
     875             : 
     876           9 :         ldb = ldb_module_get_ctx(ac->module);
     877           9 :         schema_dn = ldb_get_schema_basedn(ldb);
     878             : 
     879          13 :         while (mapiid_used) {
     880           9 :                 *next_mapiid += 1;
     881           9 :                 ret = samldb_check_mapiid_used(ac, schema,
     882             :                                                schema_dn, ldb,
     883             :                                                *next_mapiid, &mapiid_used);
     884           9 :                 if (ret != LDB_SUCCESS) {
     885           0 :                         return ret;
     886             :                 }
     887             :         }
     888             : 
     889           9 :         return LDB_SUCCESS;
     890             : }
     891             : 
     892         926 : static int samldb_schema_add_handle_mapiid(struct samldb_ctx *ac)
     893             : {
     894             :         int ret;
     895             :         bool ok;
     896             :         struct ldb_message_element *el;
     897             :         const char *enc_str;
     898             :         struct ldb_context *ldb;
     899             :         struct ldb_dn *schema_dn;
     900             :         struct dsdb_schema *schema;
     901         926 :         int32_t new_mapiid = 0;
     902             : 
     903             :         /*
     904             :          * The mAPIID of a new attribute should be automatically generated
     905             :          * if a specific OID is put as the mAPIID, as according to
     906             :          * [MS-ADTS] 3.1.1.2.3.2.
     907             :          */
     908             : 
     909         926 :         ldb = ldb_module_get_ctx(ac->module);
     910         926 :         schema = dsdb_get_schema(ldb, ac);
     911         926 :         schema_dn = ldb_get_schema_basedn(ldb);
     912             : 
     913        1740 :         ret = dsdb_get_expected_new_values(ac,
     914         926 :                                            ac->msg,
     915             :                                            "mAPIID",
     916             :                                            &el,
     917         926 :                                            ac->req->operation);
     918         926 :         if (ret != LDB_SUCCESS) {
     919           0 :                 return ret;
     920             :         }
     921             : 
     922         926 :         if (el == NULL || el->num_values == 0) {
     923         908 :                 return LDB_SUCCESS;
     924             :         }
     925             : 
     926          18 :         enc_str = ldb_binary_encode(ac, el->values[0]);
     927          18 :         if (enc_str == NULL) {
     928           0 :                 return ldb_module_oom(ac->module);
     929             :         }
     930             : 
     931          18 :         ok = (strcmp(enc_str, "1.2.840.113556.1.2.49") == 0);
     932          18 :         if (ok) {
     933           9 :                 ret = samldb_generate_next_mapiid(ac, schema,
     934             :                                                   &new_mapiid);
     935           9 :                 if (ret != LDB_SUCCESS) {
     936           0 :                         return ret;
     937             :                 }
     938             : 
     939           9 :                 ldb_msg_remove_element(ac->msg, el);
     940           9 :                 ret = samdb_msg_add_int(ldb, ac->msg, ac->msg,
     941             :                                         "mAPIID", new_mapiid);
     942           9 :                 return ret;
     943             :         }
     944             : 
     945           9 :         schema_dn = ldb_get_schema_basedn(ldb_module_get_ctx(ac->module));
     946           9 :         ret = samldb_unique_attr_check(ac, "mAPIID", NULL, schema_dn);
     947           9 :         if (ret == LDB_ERR_ENTRY_ALREADY_EXISTS) {
     948           9 :                 return LDB_ERR_UNWILLING_TO_PERFORM;
     949             :         } else {
     950           0 :                 return ret;
     951             :         }
     952             : }
     953             : 
     954             : /* sAMAccountName handling */
     955        5183 : static int samldb_generate_sAMAccountName(struct samldb_ctx *ac,
     956             :                                           struct ldb_message *msg)
     957             : {
     958        5183 :         struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
     959             :         char *name;
     960             : 
     961             :         /*
     962             :          * This is currently a Samba-only behaviour, to add a trailing
     963             :          * $ even for the generated accounts.
     964             :          */
     965             : 
     966        5183 :         if (ac->need_trailing_dollar) {
     967             :                 /* Format: $000000-00000000000$ */
     968          92 :                 name = talloc_asprintf(msg, "$%.6X-%.6X%.5X$",
     969          92 :                                        (unsigned int)generate_random(),
     970          92 :                                        (unsigned int)generate_random(),
     971          92 :                                        (unsigned int)generate_random());
     972             :         } else {
     973             :                 /* Format: $000000-000000000000 */
     974             : 
     975        5091 :                 name = talloc_asprintf(msg, "$%.6X-%.6X%.6X",
     976        5091 :                                        (unsigned int)generate_random(),
     977        5091 :                                        (unsigned int)generate_random(),
     978        5091 :                                        (unsigned int)generate_random());
     979             :         }
     980        5183 :         if (name == NULL) {
     981           0 :                 return ldb_oom(ldb);
     982             :         }
     983        5183 :         return ldb_msg_add_steal_string(msg, "sAMAccountName", name);
     984             : }
     985             : 
     986       25325 : static int samldb_check_sAMAccountName(struct samldb_ctx *ac)
     987             : {
     988             :         int ret;
     989             : 
     990       25325 :         if (ldb_msg_find_element(ac->msg, "sAMAccountName") == NULL) {
     991        5183 :                 ret = samldb_generate_sAMAccountName(ac, ac->msg);
     992        5183 :                 if (ret != LDB_SUCCESS) {
     993           0 :                         return ret;
     994             :                 }
     995             :         }
     996             : 
     997       25325 :         ret = samldb_sam_accountname_valid_check(ac);
     998       25325 :         if (ret != LDB_SUCCESS) {
     999           8 :                 return ret;
    1000             :         }
    1001             : 
    1002       25317 :         return samldb_next_step(ac);
    1003             : }
    1004             : 
    1005             : 
    1006       22263 : static bool samldb_msg_add_sid(struct ldb_message *msg,
    1007             :                                 const char *name,
    1008             :                                 const struct dom_sid *sid)
    1009             : {
    1010             :         struct ldb_val v;
    1011             :         enum ndr_err_code ndr_err;
    1012             : 
    1013       22263 :         ndr_err = ndr_push_struct_blob(&v, msg, sid,
    1014             :                                        (ndr_push_flags_fn_t)ndr_push_dom_sid);
    1015       22263 :         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
    1016           0 :                 return false;
    1017             :         }
    1018       22263 :         return (ldb_msg_add_value(msg, name, &v, NULL) == 0);
    1019             : }
    1020             : 
    1021             : 
    1022             : /* allocate a SID using our RID Set */
    1023       22246 : static int samldb_allocate_sid(struct samldb_ctx *ac)
    1024             : {
    1025             :         uint32_t rid;
    1026             :         struct dom_sid *sid;
    1027       22246 :         struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
    1028             :         int ret;
    1029             : 
    1030       22246 :         ret = ridalloc_allocate_rid(ac->module, &rid, ac->req);
    1031       22246 :         if (ret != LDB_SUCCESS) {
    1032           0 :                 return ret;
    1033             :         }
    1034             : 
    1035       22246 :         sid = dom_sid_add_rid(ac, samdb_domain_sid(ldb), rid);
    1036       22246 :         if (sid == NULL) {
    1037           0 :                 return ldb_module_oom(ac->module);
    1038             :         }
    1039             : 
    1040       22246 :         if ( ! samldb_msg_add_sid(ac->msg, "objectSid", sid)) {
    1041           0 :                 return ldb_operr(ldb);
    1042             :         }
    1043             : 
    1044       22246 :         return samldb_next_step(ac);
    1045             : }
    1046             : 
    1047             : /*
    1048             :   see if a krbtgt_number is available
    1049             :  */
    1050          69 : static bool samldb_krbtgtnumber_available(struct samldb_ctx *ac,
    1051             :                                           uint32_t krbtgt_number)
    1052             : {
    1053          69 :         TALLOC_CTX *tmp_ctx = talloc_new(ac);
    1054             :         struct ldb_result *res;
    1055          69 :         const char * const no_attrs[] = { NULL };
    1056             :         int ret;
    1057             : 
    1058          69 :         ret = dsdb_module_search(ac->module, tmp_ctx, &res,
    1059             :                                  ldb_get_default_basedn(ldb_module_get_ctx(ac->module)),
    1060             :                                  LDB_SCOPE_SUBTREE, no_attrs,
    1061             :                                  DSDB_FLAG_NEXT_MODULE,
    1062             :                                  ac->req,
    1063             :                                  "(msDS-SecondaryKrbTgtNumber=%u)",
    1064             :                                  krbtgt_number);
    1065          69 :         if (ret == LDB_SUCCESS && res->count == 0) {
    1066          69 :                 talloc_free(tmp_ctx);
    1067          69 :                 return true;
    1068             :         }
    1069           0 :         talloc_free(tmp_ctx);
    1070           0 :         return false;
    1071             : }
    1072             : 
    1073             : /* special handling for add in RODC join */
    1074          69 : static int samldb_rodc_add(struct samldb_ctx *ac)
    1075             : {
    1076          69 :         struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
    1077             :         uint32_t krbtgt_number, i_start, i;
    1078             :         int ret;
    1079             :         struct ldb_val newpass_utf16;
    1080             : 
    1081             :         /* find a unused msDS-SecondaryKrbTgtNumber */
    1082          69 :         i_start = generate_random() & 0xFFFF;
    1083          69 :         if (i_start == 0) {
    1084           0 :                 i_start = 1;
    1085             :         }
    1086             : 
    1087          69 :         for (i=i_start; i<=0xFFFF; i++) {
    1088          69 :                 if (samldb_krbtgtnumber_available(ac, i)) {
    1089          69 :                         krbtgt_number = i;
    1090          69 :                         goto found;
    1091             :                 }
    1092             :         }
    1093           0 :         for (i=1; i<i_start; i++) {
    1094           0 :                 if (samldb_krbtgtnumber_available(ac, i)) {
    1095           0 :                         krbtgt_number = i;
    1096           0 :                         goto found;
    1097             :                 }
    1098             :         }
    1099             : 
    1100           0 :         ldb_asprintf_errstring(ldb,
    1101             :                                "%08X: Unable to find available msDS-SecondaryKrbTgtNumber",
    1102           0 :                                W_ERROR_V(WERR_NO_SYSTEM_RESOURCES));
    1103           0 :         return LDB_ERR_OTHER;
    1104             : 
    1105          69 : found:
    1106             : 
    1107          69 :         ldb_msg_remove_attr(ac->msg, "msDS-SecondaryKrbTgtNumber");
    1108          69 :         ret = samdb_msg_append_uint(ldb, ac->msg, ac->msg,
    1109             :                                     "msDS-SecondaryKrbTgtNumber", krbtgt_number,
    1110             :                                     LDB_FLAG_INTERNAL_DISABLE_VALIDATION);
    1111          69 :         if (ret != LDB_SUCCESS) {
    1112           0 :                 return ldb_operr(ldb);
    1113             :         }
    1114             : 
    1115          69 :         ret = ldb_msg_add_fmt(ac->msg, "sAMAccountName", "krbtgt_%u",
    1116             :                               krbtgt_number);
    1117          69 :         if (ret != LDB_SUCCESS) {
    1118           0 :                 return ldb_operr(ldb);
    1119             :         }
    1120             : 
    1121          69 :         newpass_utf16 = data_blob_talloc_zero(ac->module, 256);
    1122          69 :         if (newpass_utf16.data == NULL) {
    1123           0 :                 return ldb_oom(ldb);
    1124             :         }
    1125             :         /*
    1126             :          * Note that the password_hash module will ignore
    1127             :          * this value and use it's own generate_secret_buffer()
    1128             :          * that's why we can just use generate_random_buffer()
    1129             :          * here.
    1130             :          */
    1131          69 :         generate_random_buffer(newpass_utf16.data, newpass_utf16.length);
    1132          69 :         ret = ldb_msg_add_steal_value(ac->msg, "clearTextPassword", &newpass_utf16);
    1133          69 :         if (ret != LDB_SUCCESS) {
    1134           0 :                 return ldb_operr(ldb);
    1135             :         }
    1136             : 
    1137          69 :         return samldb_next_step(ac);
    1138             : }
    1139             : 
    1140       19712 : static int samldb_find_for_defaultObjectCategory(struct samldb_ctx *ac)
    1141             : {
    1142       19712 :         struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
    1143             :         struct ldb_result *res;
    1144       19712 :         const char * const no_attrs[] = { NULL };
    1145             :         int ret;
    1146             : 
    1147       19712 :         ac->res_dn = NULL;
    1148             : 
    1149       19712 :         ret = dsdb_module_search(ac->module, ac, &res,
    1150             :                                  ac->dn, LDB_SCOPE_BASE, no_attrs,
    1151             :                                  DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT
    1152             :                                  | DSDB_FLAG_NEXT_MODULE,
    1153             :                                  ac->req,
    1154             :                                  "(objectClass=classSchema)");
    1155       19712 :         if (ret == LDB_ERR_NO_SUCH_OBJECT) {
    1156             :                 /* Don't be pricky when the DN doesn't exist if we have the */
    1157             :                 /* RELAX control specified */
    1158         162 :                 if (ldb_request_get_control(ac->req,
    1159             :                                             LDB_CONTROL_RELAX_OID) == NULL) {
    1160           0 :                         ldb_set_errstring(ldb,
    1161             :                                           "samldb_find_defaultObjectCategory: "
    1162             :                                           "Invalid DN for 'defaultObjectCategory'!");
    1163           0 :                         return LDB_ERR_CONSTRAINT_VIOLATION;
    1164             :                 }
    1165             :         }
    1166       19712 :         if ((ret != LDB_ERR_NO_SUCH_OBJECT) && (ret != LDB_SUCCESS)) {
    1167           0 :                 return ret;
    1168             :         }
    1169             : 
    1170       19712 :         if (ret == LDB_SUCCESS) {
    1171             :                 /* ensure the defaultObjectCategory has a full GUID */
    1172             :                 struct ldb_message *m;
    1173       19550 :                 m = ldb_msg_new(ac->msg);
    1174       19550 :                 if (m == NULL) {
    1175           0 :                         return ldb_oom(ldb);
    1176             :                 }
    1177       19550 :                 m->dn = ac->msg->dn;
    1178       19550 :                 if (ldb_msg_add_string(m, "defaultObjectCategory",
    1179       19550 :                                        ldb_dn_get_extended_linearized(m, res->msgs[0]->dn, 1)) !=
    1180             :                     LDB_SUCCESS) {
    1181           0 :                         return ldb_oom(ldb);
    1182             :                 }
    1183       19550 :                 m->elements[0].flags = LDB_FLAG_MOD_REPLACE;
    1184             : 
    1185       19550 :                 ret = dsdb_module_modify(ac->module, m,
    1186             :                                          DSDB_FLAG_NEXT_MODULE,
    1187             :                                          ac->req);
    1188       19550 :                 if (ret != LDB_SUCCESS) {
    1189           0 :                         return ret;
    1190             :                 }
    1191             :         }
    1192             : 
    1193             : 
    1194       19712 :         ac->res_dn = ac->dn;
    1195             : 
    1196       19712 :         return samldb_next_step(ac);
    1197             : }
    1198             : 
    1199             : /**
    1200             :  * msDS-IntId attributeSchema attribute handling
    1201             :  * during LDB_ADD request processing
    1202             :  */
    1203      108077 : static int samldb_add_handle_msDS_IntId(struct samldb_ctx *ac)
    1204             : {
    1205             :         int ret;
    1206             :         bool id_exists;
    1207             :         uint32_t msds_intid;
    1208             :         int32_t system_flags;
    1209             :         struct ldb_context *ldb;
    1210             :         struct ldb_result *ldb_res;
    1211             :         struct ldb_dn *schema_dn;
    1212             :         struct samldb_msds_intid_persistant *msds_intid_struct;
    1213             :         struct dsdb_schema *schema;
    1214             : 
    1215      108077 :         ldb = ldb_module_get_ctx(ac->module);
    1216      108077 :         schema_dn = ldb_get_schema_basedn(ldb);
    1217             : 
    1218             :         /* replicated update should always go through */
    1219      108077 :         if (ldb_request_get_control(ac->req,
    1220             :                                     DSDB_CONTROL_REPLICATED_UPDATE_OID)) {
    1221           0 :                 return LDB_SUCCESS;
    1222             :         }
    1223             : 
    1224             :         /* msDS-IntId is handled by system and should never be
    1225             :          * passed by clients */
    1226      108077 :         if (ldb_msg_find_element(ac->msg, "msDS-IntId")) {
    1227          18 :                 return LDB_ERR_UNWILLING_TO_PERFORM;
    1228             :         }
    1229             : 
    1230             :         /* do not generate msDS-IntId if Relax control is passed */
    1231      108059 :         if (ldb_request_get_control(ac->req, LDB_CONTROL_RELAX_OID)) {
    1232      107814 :                 return LDB_SUCCESS;
    1233             :         }
    1234             : 
    1235             :         /* check Functional Level */
    1236         245 :         if (dsdb_functional_level(ldb) < DS_DOMAIN_FUNCTION_2003) {
    1237          53 :                 return LDB_SUCCESS;
    1238             :         }
    1239             : 
    1240             :         /* check systemFlags for SCHEMA_BASE_OBJECT flag */
    1241         192 :         system_flags = ldb_msg_find_attr_as_int(ac->msg, "systemFlags", 0);
    1242         192 :         if (system_flags & SYSTEM_FLAG_SCHEMA_BASE_OBJECT) {
    1243           0 :                 return LDB_SUCCESS;
    1244             :         }
    1245         192 :         schema = dsdb_get_schema(ldb, NULL);
    1246         192 :         if (!schema) {
    1247           0 :                 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
    1248             :                               "samldb_schema_info_update: no dsdb_schema loaded");
    1249           0 :                 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
    1250           0 :                 return ldb_operr(ldb);
    1251             :         }
    1252             : 
    1253         192 :         msds_intid_struct = (struct samldb_msds_intid_persistant*) ldb_get_opaque(ldb, SAMLDB_MSDS_INTID_OPAQUE);
    1254         192 :         if (!msds_intid_struct) {
    1255         136 :                 msds_intid_struct = talloc(ldb, struct samldb_msds_intid_persistant);
    1256             :                 /* Generate new value for msDs-IntId
    1257             :                 * Value should be in 0x80000000..0xBFFFFFFF range */
    1258         136 :                 msds_intid = generate_random() % 0X3FFFFFFF;
    1259         136 :                 msds_intid += 0x80000000;
    1260         136 :                 msds_intid_struct->msds_intid = msds_intid;
    1261         136 :                 DEBUG(2, ("No samldb_msds_intid_persistant struct, allocating a new one\n"));
    1262             :         } else {
    1263          56 :                 msds_intid = msds_intid_struct->msds_intid;
    1264             :         }
    1265             : 
    1266             :         /* probe id values until unique one is found */
    1267             :         do {
    1268         192 :                 msds_intid++;
    1269         192 :                 if (msds_intid > 0xBFFFFFFF) {
    1270           0 :                         msds_intid = 0x80000001;
    1271             :                 }
    1272             :                 /*
    1273             :                  * We search in the schema if we have already this
    1274             :                  * intid (using dsdb_attribute_by_attributeID_id
    1275             :                  * because in the range 0x80000000 0xBFFFFFFFF,
    1276             :                  * attributeID is a DSDB_ATTID_TYPE_INTID).
    1277             :                  *
    1278             :                  * If so generate another random value.
    1279             :                  *
    1280             :                  * We have to check the DB in case someone else has
    1281             :                  * modified the database while we are doing our
    1282             :                  * changes too (this case should be very bery rare) in
    1283             :                  * order to be sure.
    1284             :                  */
    1285         192 :                 if (dsdb_attribute_by_attributeID_id(schema, msds_intid)) {
    1286           0 :                         id_exists = true;
    1287           0 :                         msds_intid = generate_random() % 0X3FFFFFFF;
    1288           0 :                         msds_intid += 0x80000000;
    1289           0 :                         continue;
    1290             :                 }
    1291             : 
    1292             : 
    1293         192 :                 ret = dsdb_module_search(ac->module, ac,
    1294             :                                          &ldb_res,
    1295             :                                          schema_dn, LDB_SCOPE_ONELEVEL, NULL,
    1296             :                                          DSDB_FLAG_NEXT_MODULE,
    1297             :                                          ac->req,
    1298             :                                          "(msDS-IntId=%d)", msds_intid);
    1299         192 :                 if (ret != LDB_SUCCESS) {
    1300           0 :                         ldb_debug_set(ldb, LDB_DEBUG_ERROR,
    1301             :                                       __location__": Searching for msDS-IntId=%d failed - %s\n",
    1302             :                                       msds_intid,
    1303             :                                       ldb_errstring(ldb));
    1304           0 :                         return ldb_operr(ldb);
    1305             :                 }
    1306         192 :                 id_exists = (ldb_res->count > 0);
    1307         192 :                 talloc_free(ldb_res);
    1308             : 
    1309         192 :         } while(id_exists);
    1310         192 :         msds_intid_struct->msds_intid = msds_intid;
    1311         192 :         ldb_set_opaque(ldb, SAMLDB_MSDS_INTID_OPAQUE, msds_intid_struct);
    1312             : 
    1313         192 :         return samdb_msg_add_int(ldb, ac->msg, ac->msg, "msDS-IntId",
    1314             :                                  msds_intid);
    1315             : }
    1316             : 
    1317             : 
    1318             : /*
    1319             :  * samldb_add_entry (async)
    1320             :  */
    1321             : 
    1322      155423 : static int samldb_add_entry_callback(struct ldb_request *req,
    1323             :                                         struct ldb_reply *ares)
    1324             : {
    1325             :         struct ldb_context *ldb;
    1326             :         struct samldb_ctx *ac;
    1327             :         int ret;
    1328             : 
    1329      155423 :         ac = talloc_get_type(req->context, struct samldb_ctx);
    1330      155423 :         ldb = ldb_module_get_ctx(ac->module);
    1331             : 
    1332      155423 :         if (!ares) {
    1333           0 :                 return ldb_module_done(ac->req, NULL, NULL,
    1334             :                                         LDB_ERR_OPERATIONS_ERROR);
    1335             :         }
    1336             : 
    1337      155423 :         if (ares->type == LDB_REPLY_REFERRAL) {
    1338           0 :                 return ldb_module_send_referral(ac->req, ares->referral);
    1339             :         }
    1340             : 
    1341      155423 :         if (ares->error != LDB_SUCCESS) {
    1342         134 :                 return ldb_module_done(ac->req, ares->controls,
    1343             :                                         ares->response, ares->error);
    1344             :         }
    1345      155289 :         if (ares->type != LDB_REPLY_DONE) {
    1346           0 :                 ldb_asprintf_errstring(ldb, "Invalid LDB reply type %d", ares->type);
    1347           0 :                 return ldb_module_done(ac->req, NULL, NULL,
    1348             :                                         LDB_ERR_OPERATIONS_ERROR);
    1349             :         }
    1350             : 
    1351             :         /* The caller may wish to get controls back from the add */
    1352      155289 :         ac->ares = talloc_steal(ac, ares);
    1353             : 
    1354      155289 :         ret = samldb_next_step(ac);
    1355      155289 :         if (ret != LDB_SUCCESS) {
    1356           0 :                 return ldb_module_done(ac->req, NULL, NULL, ret);
    1357             :         }
    1358      155289 :         return ret;
    1359             : }
    1360             : 
    1361      155417 : static int samldb_add_entry(struct samldb_ctx *ac)
    1362             : {
    1363             :         struct ldb_context *ldb;
    1364             :         struct ldb_request *req;
    1365             :         int ret;
    1366             : 
    1367      155417 :         ldb = ldb_module_get_ctx(ac->module);
    1368             : 
    1369      404195 :         ret = ldb_build_add_req(&req, ldb, ac,
    1370      155417 :                                 ac->msg,
    1371      155417 :                                 ac->req->controls,
    1372             :                                 ac, samldb_add_entry_callback,
    1373             :                                 ac->req);
    1374      155417 :         LDB_REQ_SET_LOCATION(req);
    1375      155417 :         if (ret != LDB_SUCCESS) {
    1376           0 :                 return ret;
    1377             :         }
    1378             : 
    1379      155417 :         return ldb_next_request(ac->module, req);
    1380             : }
    1381             : 
    1382             : /*
    1383             :  * return true if msg carries an attributeSchema that is intended to be RODC
    1384             :  * filtered but is also a system-critical attribute.
    1385             :  */
    1386      127821 : static bool check_rodc_critical_attribute(struct ldb_message *msg)
    1387             : {
    1388             :         uint32_t schemaFlagsEx, searchFlags, rodc_filtered_flags;
    1389             : 
    1390      127821 :         schemaFlagsEx = ldb_msg_find_attr_as_uint(msg, "schemaFlagsEx", 0);
    1391      127821 :         searchFlags = ldb_msg_find_attr_as_uint(msg, "searchFlags", 0);
    1392      127821 :         rodc_filtered_flags = (SEARCH_FLAG_RODC_ATTRIBUTE
    1393             :                               | SEARCH_FLAG_CONFIDENTIAL);
    1394             : 
    1395      151454 :         if ((schemaFlagsEx & SCHEMA_FLAG_ATTR_IS_CRITICAL) &&
    1396       29426 :                 ((searchFlags & rodc_filtered_flags) == rodc_filtered_flags)) {
    1397           0 :                 return true;
    1398             :         } else {
    1399      127821 :                 return false;
    1400             :         }
    1401             : }
    1402             : 
    1403             : 
    1404      153146 : static int samldb_fill_object(struct samldb_ctx *ac)
    1405             : {
    1406      153146 :         struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
    1407             :         int ret;
    1408             : 
    1409             :         /* Add information for the different account types */
    1410      153146 :         switch(ac->type) {
    1411       19508 :         case SAMLDB_TYPE_USER: {
    1412       19508 :                 struct ldb_control *rodc_control = ldb_request_get_control(ac->req,
    1413             :                                                                            LDB_CONTROL_RODC_DCPROMO_OID);
    1414       19508 :                 if (rodc_control != NULL) {
    1415             :                         /* see [MS-ADTS] 3.1.1.3.4.1.23 LDAP_SERVER_RODC_DCPROMO_OID */
    1416          69 :                         rodc_control->critical = false;
    1417          69 :                         ret = samldb_add_step(ac, samldb_rodc_add);
    1418          69 :                         if (ret != LDB_SUCCESS) return ret;
    1419             :                 }
    1420             : 
    1421             :                 /* check if we have a valid sAMAccountName */
    1422       19508 :                 ret = samldb_add_step(ac, samldb_check_sAMAccountName);
    1423       19508 :                 if (ret != LDB_SUCCESS) return ret;
    1424             : 
    1425       19508 :                 ret = samldb_add_step(ac, samldb_add_entry);
    1426       19508 :                 if (ret != LDB_SUCCESS) return ret;
    1427       19508 :                 break;
    1428             :         }
    1429             : 
    1430        5817 :         case SAMLDB_TYPE_GROUP: {
    1431             :                 /* check if we have a valid sAMAccountName */
    1432        5817 :                 ret = samldb_add_step(ac, samldb_check_sAMAccountName);
    1433        5817 :                 if (ret != LDB_SUCCESS) return ret;
    1434             : 
    1435        5817 :                 ret = samldb_add_step(ac, samldb_add_entry);
    1436        5817 :                 if (ret != LDB_SUCCESS) return ret;
    1437        5817 :                 break;
    1438             :         }
    1439             : 
    1440       19744 :         case SAMLDB_TYPE_CLASS: {
    1441       19744 :                 const char *lDAPDisplayName = NULL;
    1442             :                 const struct ldb_val *rdn_value, *def_obj_cat_val;
    1443       19744 :                 unsigned int v = ldb_msg_find_attr_as_uint(ac->msg, "objectClassCategory", -2);
    1444             : 
    1445             :                 /* As discussed with Microsoft through dochelp in April 2012 this is the behavior of windows*/
    1446       19744 :                 if (!ldb_msg_find_element(ac->msg, "subClassOf")) {
    1447         162 :                         ret = ldb_msg_add_string(ac->msg, "subClassOf", "top");
    1448         162 :                         if (ret != LDB_SUCCESS) return ret;
    1449             :                 }
    1450             : 
    1451       19744 :                 ret = samdb_find_or_add_attribute(ldb, ac->msg,
    1452             :                                                   "rdnAttId", "cn");
    1453       19744 :                 if (ret != LDB_SUCCESS) return ret;
    1454             : 
    1455             :                 /* do not allow one to mark an attributeSchema as RODC filtered if it
    1456             :                  * is system-critical */
    1457       19744 :                 if (check_rodc_critical_attribute(ac->msg)) {
    1458           0 :                         ldb_asprintf_errstring(ldb, "Refusing schema add of %s - cannot combine critical class with RODC filtering",
    1459           0 :                                                ldb_dn_get_linearized(ac->msg->dn));
    1460           0 :                         return LDB_ERR_UNWILLING_TO_PERFORM;
    1461             :                 }
    1462             : 
    1463       19744 :                 rdn_value = ldb_dn_get_rdn_val(ac->msg->dn);
    1464       19744 :                 if (rdn_value == NULL) {
    1465           0 :                         return ldb_operr(ldb);
    1466             :                 }
    1467       19744 :                 if (!ldb_msg_find_element(ac->msg, "lDAPDisplayName")) {
    1468             :                         /* the RDN has prefix "CN" */
    1469         364 :                         ret = ldb_msg_add_string(ac->msg, "lDAPDisplayName",
    1470         364 :                                 samdb_cn_to_lDAPDisplayName(ac->msg,
    1471         364 :                                                             (const char *) rdn_value->data));
    1472         364 :                         if (ret != LDB_SUCCESS) {
    1473           0 :                                 ldb_oom(ldb);
    1474           0 :                                 return ret;
    1475             :                         }
    1476             :                 }
    1477             : 
    1478       19744 :                 lDAPDisplayName = ldb_msg_find_attr_as_string(ac->msg,
    1479             :                                                               "lDAPDisplayName",
    1480             :                                                               NULL);
    1481       19744 :                 ret = ldb_valid_attr_name(lDAPDisplayName);
    1482       35612 :                 if (ret != 1 ||
    1483       35612 :                     lDAPDisplayName[0] == '*' ||
    1484       19744 :                     lDAPDisplayName[0] == '@')
    1485             :                 {
    1486           0 :                         return dsdb_module_werror(ac->module,
    1487             :                                                   LDB_ERR_UNWILLING_TO_PERFORM,
    1488             :                                                   WERR_DS_INVALID_LDAP_DISPLAY_NAME,
    1489             :                                                   "lDAPDisplayName is invalid");
    1490             :                 }
    1491             : 
    1492       19744 :                 if (!ldb_msg_find_element(ac->msg, "schemaIDGUID")) {
    1493             :                         struct GUID guid;
    1494             :                         /* a new GUID */
    1495         428 :                         guid = GUID_random();
    1496         428 :                         ret = dsdb_msg_add_guid(ac->msg, &guid, "schemaIDGUID");
    1497         428 :                         if (ret != LDB_SUCCESS) {
    1498           0 :                                 ldb_oom(ldb);
    1499           0 :                                 return ret;
    1500             :                         }
    1501             :                 }
    1502             : 
    1503       19744 :                 def_obj_cat_val = ldb_msg_find_ldb_val(ac->msg,
    1504             :                                                        "defaultObjectCategory");
    1505       19744 :                 if (def_obj_cat_val != NULL) {
    1506             :                         /* "defaultObjectCategory" has been set by the caller.
    1507             :                          * Do some checks for consistency.
    1508             :                          * NOTE: The real constraint check (that
    1509             :                          * 'defaultObjectCategory' is the DN of the new
    1510             :                          * objectclass or any parent of it) is still incomplete.
    1511             :                          * For now we say that 'defaultObjectCategory' is valid
    1512             :                          * if it exists and it is of objectclass "classSchema".
    1513             :                          */
    1514       19444 :                         ac->dn = ldb_dn_from_ldb_val(ac, ldb, def_obj_cat_val);
    1515       19444 :                         if (ac->dn == NULL) {
    1516           0 :                                 ldb_set_errstring(ldb,
    1517             :                                                   "Invalid DN for 'defaultObjectCategory'!");
    1518           0 :                                 return LDB_ERR_CONSTRAINT_VIOLATION;
    1519             :                         }
    1520             :                 } else {
    1521             :                         /* "defaultObjectCategory" has not been set by the
    1522             :                          * caller. Use the entry DN for it. */
    1523         300 :                         ac->dn = ac->msg->dn;
    1524             : 
    1525         300 :                         ret = ldb_msg_add_string(ac->msg, "defaultObjectCategory",
    1526         300 :                                                  ldb_dn_alloc_linearized(ac->msg, ac->dn));
    1527         300 :                         if (ret != LDB_SUCCESS) {
    1528           0 :                                 ldb_oom(ldb);
    1529           0 :                                 return ret;
    1530             :                         }
    1531             :                 }
    1532             : 
    1533       19744 :                 ret = samldb_add_step(ac, samldb_add_entry);
    1534       19744 :                 if (ret != LDB_SUCCESS) return ret;
    1535             : 
    1536             :                 /* Now perform the checks for the 'defaultObjectCategory'. The
    1537             :                  * lookup DN was already saved in "ac->dn" */
    1538       19744 :                 ret = samldb_add_step(ac, samldb_find_for_defaultObjectCategory);
    1539       19744 :                 if (ret != LDB_SUCCESS) return ret;
    1540             : 
    1541             :                 /* -2 is not a valid objectClassCategory so it means the attribute wasn't present */
    1542       19744 :                 if (v == -2) {
    1543             :                         /* Windows 2003 does this*/
    1544          10 :                         ret = samdb_msg_add_uint(ldb, ac->msg, ac->msg, "objectClassCategory", 0);
    1545          10 :                         if (ret != LDB_SUCCESS) {
    1546           0 :                                 return ret;
    1547             :                         }
    1548             :                 }
    1549       19744 :                 break;
    1550             :         }
    1551             : 
    1552      108077 :         case SAMLDB_TYPE_ATTRIBUTE: {
    1553      108077 :                 const char *lDAPDisplayName = NULL;
    1554             :                 const struct ldb_val *rdn_value;
    1555             :                 struct ldb_message_element *el;
    1556      108077 :                 rdn_value = ldb_dn_get_rdn_val(ac->msg->dn);
    1557      108077 :                 if (rdn_value == NULL) {
    1558           0 :                         return ldb_operr(ldb);
    1559             :                 }
    1560      108077 :                 if (!ldb_msg_find_element(ac->msg, "lDAPDisplayName")) {
    1561             :                         /* the RDN has prefix "CN" */
    1562         106 :                         ret = ldb_msg_add_string(ac->msg, "lDAPDisplayName",
    1563         106 :                                 samdb_cn_to_lDAPDisplayName(ac->msg,
    1564         106 :                                                             (const char *) rdn_value->data));
    1565         106 :                         if (ret != LDB_SUCCESS) {
    1566           0 :                                 ldb_oom(ldb);
    1567           0 :                                 return ret;
    1568             :                         }
    1569             :                 }
    1570             : 
    1571      108077 :                 lDAPDisplayName = ldb_msg_find_attr_as_string(ac->msg,
    1572             :                                                               "lDAPDisplayName",
    1573             :                                                               NULL);
    1574      108077 :                 ret = ldb_valid_attr_name(lDAPDisplayName);
    1575      194913 :                 if (ret != 1 ||
    1576      194913 :                     lDAPDisplayName[0] == '*' ||
    1577      108077 :                     lDAPDisplayName[0] == '@')
    1578             :                 {
    1579           0 :                         return dsdb_module_werror(ac->module,
    1580             :                                                   LDB_ERR_UNWILLING_TO_PERFORM,
    1581             :                                                   WERR_DS_INVALID_LDAP_DISPLAY_NAME,
    1582             :                                                   "lDAPDisplayName is invalid");
    1583             :                 }
    1584             : 
    1585             :                 /* do not allow one to mark an attributeSchema as RODC filtered if it
    1586             :                  * is system-critical */
    1587      108077 :                 if (check_rodc_critical_attribute(ac->msg)) {
    1588           0 :                         ldb_asprintf_errstring(ldb,
    1589             :                                                "samldb: refusing schema add of %s - cannot combine critical attribute with RODC filtering",
    1590           0 :                                                ldb_dn_get_linearized(ac->msg->dn));
    1591           0 :                         return LDB_ERR_UNWILLING_TO_PERFORM;
    1592             :                 }
    1593             : 
    1594      108077 :                 ret = samdb_find_or_add_attribute(ldb, ac->msg,
    1595             :                                                   "isSingleValued", "FALSE");
    1596      108077 :                 if (ret != LDB_SUCCESS) return ret;
    1597             : 
    1598      108077 :                 if (!ldb_msg_find_element(ac->msg, "schemaIDGUID")) {
    1599             :                         struct GUID guid;
    1600             :                         /* a new GUID */
    1601         263 :                         guid = GUID_random();
    1602         263 :                         ret = dsdb_msg_add_guid(ac->msg, &guid, "schemaIDGUID");
    1603         263 :                         if (ret != LDB_SUCCESS) {
    1604           0 :                                 ldb_oom(ldb);
    1605           0 :                                 return ret;
    1606             :                         }
    1607             :                 }
    1608             : 
    1609      108077 :                 el = ldb_msg_find_element(ac->msg, "attributeSyntax");
    1610      108077 :                 if (el) {
    1611             :                         /*
    1612             :                          * No need to scream if there isn't as we have code later on
    1613             :                          * that will take care of it.
    1614             :                          */
    1615      108077 :                         const struct dsdb_syntax *syntax = find_syntax_map_by_ad_oid((const char *)el->values[0].data);
    1616      108077 :                         if (!syntax) {
    1617           0 :                                 DEBUG(9, ("Can't find dsdb_syntax object for attributeSyntax %s\n",
    1618             :                                                 (const char *)el->values[0].data));
    1619             :                         } else {
    1620      108077 :                                 unsigned int v = ldb_msg_find_attr_as_uint(ac->msg, "oMSyntax", 0);
    1621      108077 :                                 const struct ldb_val *val = ldb_msg_find_ldb_val(ac->msg, "oMObjectClass");
    1622             : 
    1623      108077 :                                 if (v == 0) {
    1624           0 :                                         ret = samdb_msg_add_uint(ldb, ac->msg, ac->msg, "oMSyntax", syntax->oMSyntax);
    1625           0 :                                         if (ret != LDB_SUCCESS) {
    1626           0 :                                                 return ret;
    1627             :                                         }
    1628             :                                 }
    1629      108077 :                                 if (!val) {
    1630       93179 :                                         struct ldb_val val2 = ldb_val_dup(ldb, &syntax->oMObjectClass);
    1631       93179 :                                         if (val2.length > 0) {
    1632          57 :                                                 ret = ldb_msg_add_value(ac->msg, "oMObjectClass", &val2, NULL);
    1633          57 :                                                 if (ret != LDB_SUCCESS) {
    1634           0 :                                                         return ret;
    1635             :                                                 }
    1636             :                                         }
    1637             :                                 }
    1638             :                         }
    1639             :                 }
    1640             : 
    1641             :                 /* handle msDS-IntID attribute */
    1642      108077 :                 ret = samldb_add_handle_msDS_IntId(ac);
    1643      108077 :                 if (ret != LDB_SUCCESS) return ret;
    1644             : 
    1645      108059 :                 ret = samldb_add_step(ac, samldb_add_entry);
    1646      108059 :                 if (ret != LDB_SUCCESS) return ret;
    1647      108059 :                 break;
    1648             :         }
    1649             : 
    1650           0 :         default:
    1651           0 :                 ldb_asprintf_errstring(ldb, "Invalid entry type!");
    1652           0 :                 return LDB_ERR_OPERATIONS_ERROR;
    1653             :                 break;
    1654             :         }
    1655             : 
    1656      153128 :         return samldb_first_step(ac);
    1657             : }
    1658             : 
    1659        2299 : static int samldb_fill_foreignSecurityPrincipal_object(struct samldb_ctx *ac)
    1660             : {
    1661        2299 :         struct ldb_context *ldb = NULL;
    1662        2299 :         const struct ldb_val *rdn_value = NULL;
    1663        2299 :         struct ldb_message_element *sid_el = NULL;
    1664        2299 :         struct dom_sid *sid = NULL;
    1665        2299 :         struct ldb_control *as_system = NULL;
    1666        2299 :         struct ldb_control *provision = NULL;
    1667        2299 :         bool allowed = false;
    1668             :         int ret;
    1669             : 
    1670        2299 :         ldb = ldb_module_get_ctx(ac->module);
    1671             : 
    1672        2299 :         as_system = ldb_request_get_control(ac->req, LDB_CONTROL_AS_SYSTEM_OID);
    1673        2299 :         if (as_system != NULL) {
    1674          15 :                 allowed = true;
    1675             :         }
    1676             : 
    1677        2299 :         provision = ldb_request_get_control(ac->req, LDB_CONTROL_PROVISION_OID);
    1678        2299 :         if (provision != NULL) {
    1679        2282 :                 allowed = true;
    1680             :         }
    1681             : 
    1682        2299 :         sid_el = ldb_msg_find_element(ac->msg, "objectSid");
    1683             : 
    1684        2299 :         if (!allowed && sid_el == NULL) {
    1685           1 :                 return dsdb_module_werror(ac->module,
    1686             :                                 LDB_ERR_OBJECT_CLASS_VIOLATION,
    1687             :                                 WERR_DS_MISSING_REQUIRED_ATT,
    1688             :                                 "objectSid missing on foreignSecurityPrincipal");
    1689             :         }
    1690             : 
    1691        2298 :         if (!allowed) {
    1692           1 :                 return dsdb_module_werror(ac->module,
    1693             :                                 LDB_ERR_UNWILLING_TO_PERFORM,
    1694             :                                 WERR_DS_ILLEGAL_MOD_OPERATION,
    1695             :                                 "foreignSecurityPrincipal object not allowed");
    1696             :         }
    1697             : 
    1698        2297 :         if (sid_el != NULL) {
    1699        2280 :                 sid = samdb_result_dom_sid(ac->msg, ac->msg, "objectSid");
    1700        2280 :                 if (sid == NULL) {
    1701           0 :                         ldb_set_errstring(ldb,
    1702             :                                           "samldb: invalid objectSid!");
    1703           0 :                         return LDB_ERR_CONSTRAINT_VIOLATION;
    1704             :                 }
    1705             :         }
    1706             : 
    1707        2297 :         if (sid == NULL) {
    1708          17 :                 rdn_value = ldb_dn_get_rdn_val(ac->msg->dn);
    1709          17 :                 if (rdn_value == NULL) {
    1710           0 :                         return ldb_operr(ldb);
    1711             :                 }
    1712          17 :                 sid = dom_sid_parse_talloc(ac->msg,
    1713          17 :                                            (const char *)rdn_value->data);
    1714          17 :                 if (sid == NULL) {
    1715           0 :                         ldb_set_errstring(ldb,
    1716             :                                           "samldb: No valid SID found in ForeignSecurityPrincipal CN!");
    1717           0 :                         return LDB_ERR_CONSTRAINT_VIOLATION;
    1718             :                 }
    1719          17 :                 if (! samldb_msg_add_sid(ac->msg, "objectSid", sid)) {
    1720           0 :                         return ldb_operr(ldb);
    1721             :                 }
    1722             :         }
    1723             : 
    1724             :         /* finally proceed with adding the entry */
    1725        2297 :         ret = samldb_add_step(ac, samldb_add_entry);
    1726        2297 :         if (ret != LDB_SUCCESS) return ret;
    1727             : 
    1728        2297 :         return samldb_first_step(ac);
    1729             : }
    1730             : 
    1731      127821 : static int samldb_schema_info_update(struct samldb_ctx *ac)
    1732             : {
    1733             :         int ret;
    1734             :         struct ldb_context *ldb;
    1735             :         struct dsdb_schema *schema;
    1736             : 
    1737             :         /* replicated update should always go through */
    1738      127821 :         if (ldb_request_get_control(ac->req,
    1739             :                                     DSDB_CONTROL_REPLICATED_UPDATE_OID)) {
    1740           0 :                 return LDB_SUCCESS;
    1741             :         }
    1742             : 
    1743             :         /* do not update schemaInfo during provisioning */
    1744      127821 :         if (ldb_request_get_control(ac->req, LDB_CONTROL_PROVISION_OID)) {
    1745      126342 :                 return LDB_SUCCESS;
    1746             :         }
    1747             : 
    1748        1479 :         ldb = ldb_module_get_ctx(ac->module);
    1749        1479 :         schema = dsdb_get_schema(ldb, NULL);
    1750        1479 :         if (!schema) {
    1751           0 :                 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
    1752             :                               "samldb_schema_info_update: no dsdb_schema loaded");
    1753           0 :                 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
    1754           0 :                 return ldb_operr(ldb);
    1755             :         }
    1756             : 
    1757        1479 :         ret = dsdb_module_schema_info_update(ac->module, schema,
    1758             :                                              DSDB_FLAG_NEXT_MODULE|
    1759             :                                              DSDB_FLAG_AS_SYSTEM,
    1760             :                                              ac->req);
    1761        1479 :         if (ret != LDB_SUCCESS) {
    1762           0 :                 ldb_asprintf_errstring(ldb,
    1763             :                                        "samldb_schema_info_update: dsdb_module_schema_info_update failed with %s",
    1764             :                                        ldb_errstring(ldb));
    1765           0 :                 return ret;
    1766             :         }
    1767             : 
    1768        1479 :         return LDB_SUCCESS;
    1769             : }
    1770             : 
    1771             : static int samldb_prim_group_tester(struct samldb_ctx *ac, uint32_t rid);
    1772             : static int samldb_check_user_account_control_rules(struct samldb_ctx *ac,
    1773             :                                                    struct dom_sid *sid,
    1774             :                                                    uint32_t req_uac,
    1775             :                                                    uint32_t user_account_control,
    1776             :                                                    uint32_t user_account_control_old,
    1777             :                                                    bool is_computer_objectclass);
    1778             : 
    1779             : /*
    1780             :  * "Objectclass" trigger (MS-SAMR 3.1.1.8.1)
    1781             :  *
    1782             :  * Has to be invoked on "add" operations on "user", "computer" and
    1783             :  * "group" objects.
    1784             :  * ac->msg contains the "add"
    1785             :  * ac->type contains the object type (main objectclass)
    1786             :  */
    1787       25441 : static int samldb_objectclass_trigger(struct samldb_ctx *ac)
    1788             : {
    1789       25441 :         struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
    1790       25441 :         void *skip_allocate_sids = ldb_get_opaque(ldb,
    1791             :                                                   "skip_allocate_sids");
    1792             :         struct ldb_message_element *el;
    1793             :         struct dom_sid *sid;
    1794             :         int ret;
    1795             : 
    1796             :         /* make sure that "sAMAccountType" is not specified */
    1797       25441 :         el = ldb_msg_find_element(ac->msg, "sAMAccountType");
    1798       25441 :         if (el != NULL) {
    1799           1 :                 ldb_set_errstring(ldb,
    1800             :                                   "samldb: sAMAccountType must not be specified!");
    1801           1 :                 return LDB_ERR_UNWILLING_TO_PERFORM;
    1802             :         }
    1803             : 
    1804             :         /* Step 1: objectSid assignment */
    1805             : 
    1806             :         /* Don't allow the objectSid to be changed. But beside the RELAX
    1807             :          * control we have also to guarantee that it can always be set with
    1808             :          * SYSTEM permissions. This is needed for the "samba3sam" backend. */
    1809       25440 :         sid = samdb_result_dom_sid(ac, ac->msg, "objectSid");
    1810       28480 :         if ((sid != NULL) && (!dsdb_module_am_system(ac->module)) &&
    1811        3040 :             (ldb_request_get_control(ac->req, LDB_CONTROL_RELAX_OID) == NULL)) {
    1812           0 :                 ldb_set_errstring(ldb,
    1813             :                                   "samldb: objectSid must not be specified!");
    1814           0 :                 return LDB_ERR_UNWILLING_TO_PERFORM;
    1815             :         }
    1816             : 
    1817             :         /* but generate a new SID when we do have an add operations */
    1818       25440 :         if ((sid == NULL) && (ac->req->operation == LDB_ADD) && !skip_allocate_sids) {
    1819       22361 :                 ret = samldb_add_step(ac, samldb_allocate_sid);
    1820       22361 :                 if (ret != LDB_SUCCESS) return ret;
    1821             :         }
    1822             : 
    1823       25440 :         switch(ac->type) {
    1824       19617 :         case SAMLDB_TYPE_USER: {
    1825             :                 uint32_t raw_uac;
    1826             :                 uint32_t user_account_control;
    1827             :                 bool is_computer_objectclass;
    1828       19617 :                 bool uac_generated = false, uac_add_flags = false;
    1829       19617 :                 uint32_t default_user_account_control = UF_NORMAL_ACCOUNT;
    1830             :                 /* Step 1.2: Default values */
    1831       19617 :                 ret = dsdb_user_obj_set_defaults(ldb, ac->msg, ac->req);
    1832       19617 :                 if (ret != LDB_SUCCESS) return ret;
    1833             : 
    1834             :                 is_computer_objectclass
    1835       24055 :                         = (samdb_find_attribute(ldb,
    1836       19617 :                                                  ac->msg,
    1837             :                                                 "objectclass",
    1838             :                                                 "computer")
    1839             :                            != NULL);
    1840             : 
    1841       19617 :                 if (is_computer_objectclass) {
    1842             :                         default_user_account_control
    1843        2201 :                                 = UF_WORKSTATION_TRUST_ACCOUNT;
    1844             :                 }
    1845             : 
    1846             : 
    1847             :                 /* On add operations we might need to generate a
    1848             :                  * "userAccountControl" (if it isn't specified). */
    1849       19617 :                 el = ldb_msg_find_element(ac->msg, "userAccountControl");
    1850       19617 :                 if (el == NULL) {
    1851       16874 :                         ret = samdb_msg_set_uint(ldb, ac->msg, ac->msg,
    1852             :                                                  "userAccountControl",
    1853             :                                                  default_user_account_control);
    1854       16874 :                         if (ret != LDB_SUCCESS) {
    1855           0 :                                 return ret;
    1856             :                         }
    1857       16874 :                         uac_generated = true;
    1858       16874 :                         uac_add_flags = true;
    1859             :                 }
    1860             : 
    1861       19617 :                 el = ldb_msg_find_element(ac->msg, "userAccountControl");
    1862       19617 :                 SMB_ASSERT(el != NULL);
    1863             : 
    1864             :                 /* Step 1.3: "userAccountControl" -> "sAMAccountType" mapping */
    1865       19617 :                 user_account_control = ldb_msg_find_attr_as_uint(ac->msg,
    1866             :                                                                  "userAccountControl",
    1867             :                                                                  0);
    1868       19617 :                 raw_uac = user_account_control;
    1869             :                 /*
    1870             :                  * "userAccountControl" = 0 or missing one of
    1871             :                  * the types means "UF_NORMAL_ACCOUNT"
    1872             :                  * or "UF_WORKSTATION_TRUST_ACCOUNT" (if a computer).
    1873             :                  * See MS-SAMR 3.1.1.8.10 point 8
    1874             :                  */
    1875       19617 :                 if ((user_account_control & UF_ACCOUNT_TYPE_MASK) == 0) {
    1876             :                         user_account_control
    1877          18 :                                 = default_user_account_control
    1878             :                                 | user_account_control;
    1879          18 :                         uac_generated = true;
    1880             :                 }
    1881             : 
    1882             :                 /*
    1883             :                  * As per MS-SAMR 3.1.1.8.10 these flags have not to be set
    1884             :                  */
    1885       19617 :                 if ((user_account_control & UF_LOCKOUT) != 0) {
    1886           7 :                         user_account_control &= ~UF_LOCKOUT;
    1887           7 :                         uac_generated = true;
    1888             :                 }
    1889       19617 :                 if ((user_account_control & UF_PASSWORD_EXPIRED) != 0) {
    1890           7 :                         user_account_control &= ~UF_PASSWORD_EXPIRED;
    1891           7 :                         uac_generated = true;
    1892             :                 }
    1893             : 
    1894       19617 :                 ret = samldb_check_user_account_control_rules(ac, NULL,
    1895             :                                                               raw_uac,
    1896             :                                                               user_account_control,
    1897             :                                                               0,
    1898             :                                                               is_computer_objectclass);
    1899       19617 :                 if (ret != LDB_SUCCESS) {
    1900         109 :                         return ret;
    1901             :                 }
    1902             : 
    1903             :                 /*
    1904             :                  * Require, for non-admin modifications, a trailing $
    1905             :                  * for either objectclass=computer or a trust account
    1906             :                  * type in userAccountControl
    1907             :                  */
    1908       19508 :                 if ((user_account_control
    1909       19508 :                      & UF_TRUST_ACCOUNT_MASK) != 0) {
    1910        2157 :                         ac->need_trailing_dollar = true;
    1911             :                 }
    1912             : 
    1913       19508 :                 if (is_computer_objectclass) {
    1914        2159 :                         ac->need_trailing_dollar = true;
    1915             :                 }
    1916             : 
    1917             :                 /* add "sAMAccountType" attribute */
    1918       19508 :                 ret = dsdb_user_obj_set_account_type(ldb, ac->msg, user_account_control, NULL);
    1919       19508 :                 if (ret != LDB_SUCCESS) {
    1920           0 :                         return ret;
    1921             :                 }
    1922             : 
    1923             :                 /* "isCriticalSystemObject" might be set */
    1924       19508 :                 if (user_account_control &
    1925             :                     (UF_SERVER_TRUST_ACCOUNT | UF_PARTIAL_SECRETS_ACCOUNT)) {
    1926         679 :                         ret = ldb_msg_add_string_flags(ac->msg, "isCriticalSystemObject",
    1927             :                                                        "TRUE", LDB_FLAG_MOD_REPLACE);
    1928         679 :                         if (ret != LDB_SUCCESS) {
    1929           0 :                                 return ret;
    1930             :                         }
    1931       18829 :                 } else if (user_account_control & UF_WORKSTATION_TRUST_ACCOUNT) {
    1932        1408 :                         ret = ldb_msg_add_string_flags(ac->msg, "isCriticalSystemObject",
    1933             :                                                        "FALSE", LDB_FLAG_MOD_REPLACE);
    1934        1408 :                         if (ret != LDB_SUCCESS) {
    1935           0 :                                 return ret;
    1936             :                         }
    1937             :                 }
    1938             : 
    1939             :                 /* Step 1.4: "userAccountControl" -> "primaryGroupID" mapping */
    1940       19508 :                 if (!ldb_msg_find_element(ac->msg, "primaryGroupID")) {
    1941             :                         uint32_t rid;
    1942             : 
    1943       19431 :                         ret = dsdb_user_obj_set_primary_group_id(ldb, ac->msg, user_account_control, &rid);
    1944       19431 :                         if (ret != LDB_SUCCESS) {
    1945           0 :                                 return ret;
    1946             :                         }
    1947             :                         /*
    1948             :                          * Older AD deployments don't know about the
    1949             :                          * RODC group
    1950             :                          */
    1951       19431 :                         if (rid == DOMAIN_RID_READONLY_DCS) {
    1952         135 :                                 ret = samldb_prim_group_tester(ac, rid);
    1953         135 :                                 if (ret != LDB_SUCCESS) {
    1954           0 :                                         return ret;
    1955             :                                 }
    1956             :                         }
    1957             :                 }
    1958             : 
    1959             :                 /* Step 1.5: Add additional flags when needed */
    1960             :                 /* Obviously this is done when the "userAccountControl"
    1961             :                  * has been generated here (tested against Windows
    1962             :                  * Server) */
    1963       19508 :                 if (uac_generated) {
    1964       16900 :                         if (uac_add_flags) {
    1965       16874 :                                 user_account_control |= UF_ACCOUNTDISABLE;
    1966       16874 :                                 user_account_control |= UF_PASSWD_NOTREQD;
    1967             :                         }
    1968             : 
    1969       16900 :                         ret = samdb_msg_set_uint(ldb, ac->msg, ac->msg,
    1970             :                                                  "userAccountControl",
    1971             :                                                  user_account_control);
    1972       16900 :                         if (ret != LDB_SUCCESS) {
    1973           0 :                                 return ret;
    1974             :                         }
    1975             :                 }
    1976       19508 :                 break;
    1977             :         }
    1978             : 
    1979        5823 :         case SAMLDB_TYPE_GROUP: {
    1980             :                 const char *tempstr;
    1981             : 
    1982             :                 /* Step 2.2: Default values */
    1983        5823 :                 tempstr = talloc_asprintf(ac->msg, "%d",
    1984             :                                           GTYPE_SECURITY_GLOBAL_GROUP);
    1985        5823 :                 if (tempstr == NULL) return ldb_operr(ldb);
    1986        5823 :                 ret = samdb_find_or_add_attribute(ldb, ac->msg,
    1987             :                         "groupType", tempstr);
    1988        5823 :                 if (ret != LDB_SUCCESS) return ret;
    1989             : 
    1990             :                 /* Step 2.3: "groupType" -> "sAMAccountType" */
    1991        5823 :                 el = ldb_msg_find_element(ac->msg, "groupType");
    1992        5823 :                 if (el != NULL) {
    1993             :                         uint32_t group_type, account_type;
    1994             : 
    1995        5823 :                         group_type = ldb_msg_find_attr_as_uint(ac->msg,
    1996             :                                                                "groupType", 0);
    1997             : 
    1998             :                         /* The creation of builtin groups requires the
    1999             :                          * RELAX control */
    2000        5823 :                         if (group_type == GTYPE_SECURITY_BUILTIN_LOCAL_GROUP) {
    2001        1599 :                                 if (ldb_request_get_control(ac->req,
    2002             :                                                             LDB_CONTROL_RELAX_OID) == NULL) {
    2003           3 :                                         return LDB_ERR_UNWILLING_TO_PERFORM;
    2004             :                                 }
    2005             :                         }
    2006             : 
    2007        5820 :                         account_type = ds_gtype2atype(group_type);
    2008        5820 :                         if (account_type == 0) {
    2009           3 :                                 ldb_set_errstring(ldb, "samldb: Unrecognized account type!");
    2010           3 :                                 return LDB_ERR_UNWILLING_TO_PERFORM;
    2011             :                         }
    2012        5817 :                         ret = samdb_msg_add_uint_flags(ldb, ac->msg, ac->msg,
    2013             :                                                        "sAMAccountType",
    2014             :                                                        account_type,
    2015             :                                                        LDB_FLAG_MOD_REPLACE);
    2016        5817 :                         if (ret != LDB_SUCCESS) {
    2017           0 :                                 return ret;
    2018             :                         }
    2019             :                 }
    2020        5817 :                 break;
    2021             :         }
    2022             : 
    2023           0 :         default:
    2024           0 :                 ldb_asprintf_errstring(ldb,
    2025             :                                 "Invalid entry type!");
    2026           0 :                 return LDB_ERR_OPERATIONS_ERROR;
    2027             :                 break;
    2028             :         }
    2029             : 
    2030       25325 :         return LDB_SUCCESS;
    2031             : }
    2032             : 
    2033             : /*
    2034             :  * "Primary group ID" trigger (MS-SAMR 3.1.1.8.2)
    2035             :  *
    2036             :  * Has to be invoked on "add" and "modify" operations on "user" and "computer"
    2037             :  * objects.
    2038             :  * ac->msg contains the "add"/"modify" message
    2039             :  */
    2040             : 
    2041         221 : static int samldb_prim_group_tester(struct samldb_ctx *ac, uint32_t rid)
    2042             : {
    2043         221 :         struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
    2044             :         struct dom_sid *sid;
    2045             :         struct ldb_result *res;
    2046             :         int ret;
    2047         221 :         const char * const noattrs[] = { NULL };
    2048             : 
    2049         221 :         sid = dom_sid_add_rid(ac, samdb_domain_sid(ldb), rid);
    2050         221 :         if (sid == NULL) {
    2051           0 :                 return ldb_operr(ldb);
    2052             :         }
    2053             : 
    2054         221 :         ret = dsdb_module_search(ac->module, ac, &res,
    2055             :                                  ldb_get_default_basedn(ldb),
    2056             :                                  LDB_SCOPE_SUBTREE,
    2057             :                                  noattrs, DSDB_FLAG_NEXT_MODULE,
    2058             :                                  ac->req,
    2059             :                                  "(objectSid=%s)",
    2060             :                                  ldap_encode_ndr_dom_sid(ac, sid));
    2061         221 :         if (ret != LDB_SUCCESS) {
    2062           0 :                 return ret;
    2063             :         }
    2064         221 :         if (res->count != 1) {
    2065           0 :                 talloc_free(res);
    2066           0 :                 ldb_asprintf_errstring(ldb,
    2067             :                                        "Failed to find primary group with RID %u!",
    2068             :                                        rid);
    2069           0 :                 return LDB_ERR_UNWILLING_TO_PERFORM;
    2070             :         }
    2071         221 :         talloc_free(res);
    2072             : 
    2073         221 :         return LDB_SUCCESS;
    2074             : }
    2075             : 
    2076       19644 : static int samldb_prim_group_set(struct samldb_ctx *ac)
    2077             : {
    2078       19644 :         struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
    2079             :         uint32_t rid;
    2080             : 
    2081       19644 :         rid = ldb_msg_find_attr_as_uint(ac->msg, "primaryGroupID", (uint32_t) -1);
    2082       19644 :         if (rid == (uint32_t) -1) {
    2083             :                 /* we aren't affected of any primary group set */
    2084       19541 :                 return LDB_SUCCESS;
    2085             : 
    2086         103 :         } else if (!ldb_request_get_control(ac->req, LDB_CONTROL_RELAX_OID)) {
    2087          26 :                 ldb_set_errstring(ldb,
    2088             :                                   "The primary group isn't settable on add operations!");
    2089          26 :                 return LDB_ERR_UNWILLING_TO_PERFORM;
    2090             :         }
    2091             : 
    2092          77 :         return samldb_prim_group_tester(ac, rid);
    2093             : }
    2094             : 
    2095          89 : static int samldb_prim_group_change(struct samldb_ctx *ac)
    2096             : {
    2097          89 :         struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
    2098          89 :         const char * const attrs[] = {
    2099             :                 "primaryGroupID",
    2100             :                 "memberOf",
    2101             :                 "userAccountControl",
    2102             :                 NULL };
    2103             :         struct ldb_result *res, *group_res;
    2104             :         struct ldb_message_element *el;
    2105             :         struct ldb_message *msg;
    2106          89 :         uint32_t search_flags =
    2107             :                 DSDB_FLAG_NEXT_MODULE | DSDB_SEARCH_SHOW_EXTENDED_DN;
    2108             :         uint32_t prev_rid, new_rid, uac;
    2109             :         struct dom_sid *prev_sid, *new_sid;
    2110             :         struct ldb_dn *prev_prim_group_dn, *new_prim_group_dn;
    2111          89 :         const char *new_prim_group_dn_ext_str = NULL;
    2112          89 :         struct ldb_dn *user_dn = NULL;
    2113          89 :         const char *user_dn_ext_str = NULL;
    2114             :         int ret;
    2115          89 :         const char * const noattrs[] = { NULL };
    2116             : 
    2117         151 :         ret = dsdb_get_expected_new_values(ac,
    2118          89 :                                            ac->msg,
    2119             :                                            "primaryGroupID",
    2120             :                                            &el,
    2121          89 :                                            ac->req->operation);
    2122          89 :         if (ret != LDB_SUCCESS) {
    2123           0 :                 return ret;
    2124             :         }
    2125             : 
    2126          89 :         if (el == NULL) {
    2127             :                 /* we are not affected */
    2128           3 :                 return LDB_SUCCESS;
    2129             :         }
    2130             : 
    2131             :         /* Fetch information from the existing object */
    2132             : 
    2133          86 :         ret = dsdb_module_search_dn(ac->module, ac, &res, ac->msg->dn, attrs,
    2134             :                                     search_flags, ac->req);
    2135          86 :         if (ret != LDB_SUCCESS) {
    2136           0 :                 return ret;
    2137             :         }
    2138          86 :         user_dn = res->msgs[0]->dn;
    2139          86 :         user_dn_ext_str = ldb_dn_get_extended_linearized(ac, user_dn, 1);
    2140          86 :         if (user_dn_ext_str == NULL) {
    2141           0 :                 return ldb_operr(ldb);
    2142             :         }
    2143             : 
    2144          86 :         uac = ldb_msg_find_attr_as_uint(res->msgs[0], "userAccountControl", 0);
    2145             : 
    2146             :         /* Finds out the DN of the old primary group */
    2147             : 
    2148          86 :         prev_rid = ldb_msg_find_attr_as_uint(res->msgs[0], "primaryGroupID",
    2149             :                                              (uint32_t) -1);
    2150          86 :         if (prev_rid == (uint32_t) -1) {
    2151             :                 /* User objects do always have a mandatory "primaryGroupID"
    2152             :                  * attribute. If this doesn't exist then the object is of the
    2153             :                  * wrong type. This is the exact Windows error code */
    2154           3 :                 return LDB_ERR_OBJECT_CLASS_VIOLATION;
    2155             :         }
    2156             : 
    2157          83 :         prev_sid = dom_sid_add_rid(ac, samdb_domain_sid(ldb), prev_rid);
    2158          83 :         if (prev_sid == NULL) {
    2159           0 :                 return ldb_operr(ldb);
    2160             :         }
    2161             : 
    2162             :         /* Finds out the DN of the new primary group
    2163             :          * Notice: in order to parse the primary group ID correctly we create
    2164             :          * a temporary message here. */
    2165             : 
    2166          83 :         msg = ldb_msg_new(ac->msg);
    2167          83 :         if (msg == NULL) {
    2168           0 :                 return ldb_module_oom(ac->module);
    2169             :         }
    2170          83 :         ret = ldb_msg_add(msg, el, 0);
    2171          83 :         if (ret != LDB_SUCCESS) {
    2172           0 :                 return ret;
    2173             :         }
    2174          83 :         new_rid = ldb_msg_find_attr_as_uint(msg, "primaryGroupID", (uint32_t) -1);
    2175          83 :         talloc_free(msg);
    2176          83 :         if (new_rid == (uint32_t) -1) {
    2177             :                 /* we aren't affected of any primary group change */
    2178           3 :                 return LDB_SUCCESS;
    2179             :         }
    2180             : 
    2181          80 :         if (prev_rid == new_rid) {
    2182           9 :                 return LDB_SUCCESS;
    2183             :         }
    2184             : 
    2185          71 :         if ((uac & UF_SERVER_TRUST_ACCOUNT) && new_rid != DOMAIN_RID_DCS) {
    2186           1 :                 ldb_asprintf_errstring(ldb,
    2187             :                         "%08X: samldb: UF_SERVER_TRUST_ACCOUNT requires "
    2188             :                         "primaryGroupID=%u!",
    2189           1 :                         W_ERROR_V(WERR_DS_CANT_MOD_PRIMARYGROUPID),
    2190             :                         DOMAIN_RID_DCS);
    2191           1 :                 return LDB_ERR_UNWILLING_TO_PERFORM;
    2192             :         }
    2193             : 
    2194          70 :         if ((uac & UF_PARTIAL_SECRETS_ACCOUNT) && new_rid != DOMAIN_RID_READONLY_DCS) {
    2195           1 :                 ldb_asprintf_errstring(ldb,
    2196             :                         "%08X: samldb: UF_PARTIAL_SECRETS_ACCOUNT requires "
    2197             :                         "primaryGroupID=%u!",
    2198           1 :                         W_ERROR_V(WERR_DS_CANT_MOD_PRIMARYGROUPID),
    2199             :                         DOMAIN_RID_READONLY_DCS);
    2200           1 :                 return LDB_ERR_UNWILLING_TO_PERFORM;
    2201             :         }
    2202             : 
    2203          69 :         ret = dsdb_module_search(ac->module, ac, &group_res,
    2204             :                                  ldb_get_default_basedn(ldb),
    2205             :                                  LDB_SCOPE_SUBTREE,
    2206             :                                  noattrs, search_flags,
    2207             :                                  ac->req,
    2208             :                                  "(objectSid=%s)",
    2209             :                                  ldap_encode_ndr_dom_sid(ac, prev_sid));
    2210          69 :         if (ret != LDB_SUCCESS) {
    2211           0 :                 return ret;
    2212             :         }
    2213          69 :         if (group_res->count != 1) {
    2214           0 :                 return ldb_operr(ldb);
    2215             :         }
    2216          69 :         prev_prim_group_dn = group_res->msgs[0]->dn;
    2217             : 
    2218          69 :         new_sid = dom_sid_add_rid(ac, samdb_domain_sid(ldb), new_rid);
    2219          69 :         if (new_sid == NULL) {
    2220           0 :                 return ldb_operr(ldb);
    2221             :         }
    2222             : 
    2223          69 :         ret = dsdb_module_search(ac->module, ac, &group_res,
    2224             :                                  ldb_get_default_basedn(ldb),
    2225             :                                  LDB_SCOPE_SUBTREE,
    2226             :                                  noattrs, search_flags,
    2227             :                                  ac->req,
    2228             :                                  "(objectSid=%s)",
    2229             :                                  ldap_encode_ndr_dom_sid(ac, new_sid));
    2230          69 :         if (ret != LDB_SUCCESS) {
    2231           0 :                 return ret;
    2232             :         }
    2233          69 :         if (group_res->count != 1) {
    2234             :                 /* Here we know if the specified new primary group candidate is
    2235             :                  * valid or not. */
    2236           3 :                 return LDB_ERR_UNWILLING_TO_PERFORM;
    2237             :         }
    2238          66 :         new_prim_group_dn = group_res->msgs[0]->dn;
    2239          66 :         new_prim_group_dn_ext_str = ldb_dn_get_extended_linearized(ac,
    2240             :                                                         new_prim_group_dn, 1);
    2241          66 :         if (new_prim_group_dn_ext_str == NULL) {
    2242           0 :                 return ldb_operr(ldb);
    2243             :         }
    2244             : 
    2245             :         /* We need to be already a normal member of the new primary
    2246             :          * group in order to be successful. */
    2247          66 :         el = samdb_find_attribute(ldb, res->msgs[0], "memberOf",
    2248             :                                   new_prim_group_dn_ext_str);
    2249          66 :         if (el == NULL) {
    2250           3 :                 return LDB_ERR_UNWILLING_TO_PERFORM;
    2251             :         }
    2252             : 
    2253             :         /* Remove the "member" attribute on the new primary group */
    2254          63 :         msg = ldb_msg_new(ac->msg);
    2255          63 :         if (msg == NULL) {
    2256           0 :                 return ldb_module_oom(ac->module);
    2257             :         }
    2258          63 :         msg->dn = new_prim_group_dn;
    2259             : 
    2260          63 :         ret = samdb_msg_add_delval(ldb, msg, msg, "member", user_dn_ext_str);
    2261          63 :         if (ret != LDB_SUCCESS) {
    2262           0 :                 return ret;
    2263             :         }
    2264             : 
    2265          63 :         ret = dsdb_module_modify(ac->module, msg, DSDB_FLAG_NEXT_MODULE, ac->req);
    2266          63 :         if (ret != LDB_SUCCESS) {
    2267           0 :                 return ret;
    2268             :         }
    2269          63 :         talloc_free(msg);
    2270             : 
    2271             :         /* Add a "member" attribute for the previous primary group */
    2272          63 :         msg = ldb_msg_new(ac->msg);
    2273          63 :         if (msg == NULL) {
    2274           0 :                 return ldb_module_oom(ac->module);
    2275             :         }
    2276          63 :         msg->dn = prev_prim_group_dn;
    2277             : 
    2278          63 :         ret = samdb_msg_add_addval(ldb, msg, msg, "member", user_dn_ext_str);
    2279          63 :         if (ret != LDB_SUCCESS) {
    2280           0 :                 return ret;
    2281             :         }
    2282             : 
    2283          63 :         ret = dsdb_module_modify(ac->module, msg, DSDB_FLAG_NEXT_MODULE, ac->req);
    2284          63 :         if (ret != LDB_SUCCESS) {
    2285           0 :                 return ret;
    2286             :         }
    2287          63 :         talloc_free(msg);
    2288             : 
    2289          63 :         return LDB_SUCCESS;
    2290             : }
    2291             : 
    2292       19733 : static int samldb_prim_group_trigger(struct samldb_ctx *ac)
    2293             : {
    2294             :         int ret;
    2295             : 
    2296       19733 :         if (ac->req->operation == LDB_ADD) {
    2297       19644 :                 ret = samldb_prim_group_set(ac);
    2298             :         } else {
    2299          89 :                 ret = samldb_prim_group_change(ac);
    2300             :         }
    2301             : 
    2302       19733 :         return ret;
    2303             : }
    2304             : 
    2305       31566 : static int samldb_check_user_account_control_invariants(struct samldb_ctx *ac,
    2306             :                                                     uint32_t user_account_control)
    2307             : {
    2308             :         size_t i;
    2309       31566 :         int ret = 0;
    2310       31566 :         bool need_check = false;
    2311             :         const struct uac_to_guid {
    2312             :                 uint32_t uac;
    2313             :                 bool never;
    2314             :                 uint32_t needs;
    2315             :                 uint32_t not_with;
    2316             :                 const char *error_string;
    2317       31566 :         } map[] = {
    2318             :                 {
    2319             :                         .uac = UF_TEMP_DUPLICATE_ACCOUNT,
    2320             :                         .never = true,
    2321             :                         .error_string = "Updating the UF_TEMP_DUPLICATE_ACCOUNT flag is never allowed"
    2322             :                 },
    2323             :                 {
    2324             :                         .uac = UF_PARTIAL_SECRETS_ACCOUNT,
    2325             :                         .needs = UF_WORKSTATION_TRUST_ACCOUNT,
    2326             :                         .error_string = "Setting UF_PARTIAL_SECRETS_ACCOUNT only permitted with UF_WORKSTATION_TRUST_ACCOUNT"
    2327             :                 },
    2328             :                 {
    2329             :                         .uac = UF_TRUSTED_FOR_DELEGATION,
    2330             :                         .not_with = UF_PARTIAL_SECRETS_ACCOUNT,
    2331             :                         .error_string = "Setting UF_TRUSTED_FOR_DELEGATION not allowed with UF_PARTIAL_SECRETS_ACCOUNT"
    2332             :                 },
    2333             :                 {
    2334             :                         .uac = UF_NORMAL_ACCOUNT,
    2335             :                         .not_with = UF_ACCOUNT_TYPE_MASK & ~UF_NORMAL_ACCOUNT,
    2336             :                         .error_string = "Setting more than one account type not permitted"
    2337             :                 },
    2338             :                 {
    2339             :                         .uac = UF_WORKSTATION_TRUST_ACCOUNT,
    2340             :                         .not_with = UF_ACCOUNT_TYPE_MASK & ~UF_WORKSTATION_TRUST_ACCOUNT,
    2341             :                         .error_string = "Setting more than one account type not permitted"
    2342             :                 },
    2343             :                 {
    2344             :                         .uac = UF_INTERDOMAIN_TRUST_ACCOUNT,
    2345             :                         .not_with = UF_ACCOUNT_TYPE_MASK & ~UF_INTERDOMAIN_TRUST_ACCOUNT,
    2346             :                         .error_string = "Setting more than one account type not permitted"
    2347             :                 },
    2348             :                 {
    2349             :                         .uac = UF_SERVER_TRUST_ACCOUNT,
    2350             :                         .not_with = UF_ACCOUNT_TYPE_MASK & ~UF_SERVER_TRUST_ACCOUNT,
    2351             :                         .error_string = "Setting more than one account type not permitted"
    2352             :                 },
    2353             :                 {
    2354             :                         .uac = UF_TRUSTED_FOR_DELEGATION,
    2355             :                         .not_with = UF_PARTIAL_SECRETS_ACCOUNT,
    2356             :                         .error_string = "Setting UF_TRUSTED_FOR_DELEGATION not allowed with UF_PARTIAL_SECRETS_ACCOUNT"
    2357             :                 }
    2358             :         };
    2359             : 
    2360      128542 :         for (i = 0; i < ARRAY_SIZE(map); i++) {
    2361      128542 :                 if (user_account_control & map[i].uac) {
    2362       31566 :                         need_check = true;
    2363       31566 :                         break;
    2364             :                 }
    2365             :         }
    2366       31566 :         if (need_check == false) {
    2367           0 :                 return LDB_SUCCESS;
    2368             :         }
    2369             : 
    2370      283603 :         for (i = 0; i < ARRAY_SIZE(map); i++) {
    2371      252105 :                 uint32_t this_uac = user_account_control & map[i].uac;
    2372      252105 :                 if (this_uac != 0) {
    2373       33002 :                         if (map[i].never) {
    2374          16 :                                 ret = LDB_ERR_OTHER;
    2375          16 :                                 break;
    2376       32986 :                         } else if (map[i].needs != 0) {
    2377         359 :                                 if ((map[i].needs & user_account_control) == 0) {
    2378          51 :                                         ret = LDB_ERR_OTHER;
    2379          51 :                                         break;
    2380             :                                 }
    2381       32627 :                         } else if (map[i].not_with != 0) {
    2382       32627 :                                 if ((map[i].not_with & user_account_control) != 0) {
    2383           1 :                                         ret = LDB_ERR_OTHER;
    2384           1 :                                         break;
    2385             :                                 }
    2386             :                         }
    2387             :                 }
    2388             :         }
    2389       31566 :         if (ret != LDB_SUCCESS) {
    2390          68 :                 switch (ac->req->operation) {
    2391           7 :                 case LDB_ADD:
    2392          12 :                         ldb_asprintf_errstring(ldb_module_get_ctx(ac->module),
    2393             :                                                "Failed to add %s: %s",
    2394           7 :                                                ldb_dn_get_linearized(ac->msg->dn),
    2395           2 :                                                map[i].error_string);
    2396           7 :                         break;
    2397          61 :                 case LDB_MODIFY:
    2398         120 :                         ldb_asprintf_errstring(ldb_module_get_ctx(ac->module),
    2399             :                                                "Failed to modify %s: %s",
    2400          61 :                                                ldb_dn_get_linearized(ac->msg->dn),
    2401           2 :                                                map[i].error_string);
    2402          61 :                         break;
    2403           0 :                 default:
    2404           0 :                         return ldb_module_operr(ac->module);
    2405             :                 }
    2406       23063 :         }
    2407       31566 :         return ret;
    2408             : }
    2409             : 
    2410             : /*
    2411             :  * It would be best if these rules apply, always, but for now they
    2412             :  * apply only to non-admins
    2413             :  */
    2414       31498 : static int samldb_check_user_account_control_objectclass_invariants(
    2415             :         struct samldb_ctx *ac,
    2416             :         uint32_t user_account_control,
    2417             :         uint32_t user_account_control_old,
    2418             :         bool is_computer_objectclass)
    2419             : {
    2420       31498 :         struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
    2421             : 
    2422       31498 :         uint32_t old_ufa = user_account_control_old & UF_ACCOUNT_TYPE_MASK;
    2423       31498 :         uint32_t new_ufa = user_account_control & UF_ACCOUNT_TYPE_MASK;
    2424             : 
    2425       31498 :         uint32_t old_rodc = user_account_control_old & UF_PARTIAL_SECRETS_ACCOUNT;
    2426       31498 :         uint32_t new_rodc = user_account_control & UF_PARTIAL_SECRETS_ACCOUNT;
    2427             : 
    2428             :         bool is_admin;
    2429       23063 :         struct security_token *user_token
    2430       31498 :                 = acl_user_token(ac->module);
    2431       31498 :         if (user_token == NULL) {
    2432           0 :                 return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
    2433             :         }
    2434             : 
    2435             :         is_admin
    2436       31498 :                 = security_token_has_builtin_administrators(user_token);
    2437             : 
    2438             : 
    2439             :         /*
    2440             :          * We want to allow changes to (eg) disable an account
    2441             :          * that was created wrong, only checking the
    2442             :          * objectclass if the account type changes.
    2443             :          */
    2444       31498 :         if (old_ufa == new_ufa && old_rodc == new_rodc) {
    2445       11591 :                 return LDB_SUCCESS;
    2446             :         }
    2447             : 
    2448       19907 :         switch (new_ufa) {
    2449       17464 :         case UF_NORMAL_ACCOUNT:
    2450       17464 :                 if (is_computer_objectclass && !is_admin) {
    2451          40 :                         ldb_asprintf_errstring(ldb,
    2452             :                                 "%08X: samldb: UF_NORMAL_ACCOUNT "
    2453             :                                 "requires objectclass 'user' not 'computer'!",
    2454          40 :                                 W_ERROR_V(WERR_DS_MACHINE_ACCOUNT_CREATED_PRENT4));
    2455          40 :                         return LDB_ERR_OBJECT_CLASS_VIOLATION;
    2456             :                 }
    2457       17424 :                 break;
    2458             : 
    2459          86 :         case UF_INTERDOMAIN_TRUST_ACCOUNT:
    2460          86 :                 if (is_computer_objectclass) {
    2461           8 :                         ldb_asprintf_errstring(ldb,
    2462             :                                 "%08X: samldb: UF_INTERDOMAIN_TRUST_ACCOUNT "
    2463             :                                 "requires objectclass 'user' not 'computer'!",
    2464           8 :                                 W_ERROR_V(WERR_DS_MACHINE_ACCOUNT_CREATED_PRENT4));
    2465           8 :                         return LDB_ERR_OBJECT_CLASS_VIOLATION;
    2466             :                 }
    2467          78 :                 break;
    2468             : 
    2469        1714 :         case UF_WORKSTATION_TRUST_ACCOUNT:
    2470        1714 :                 if (!is_computer_objectclass) {
    2471             :                         /*
    2472             :                          * Modify of a user account account into a
    2473             :                          * workstation without objectclass computer
    2474             :                          * as an admin is still permitted, but not
    2475             :                          * to make an RODC
    2476             :                          */
    2477          64 :                         if (is_admin
    2478          41 :                             && ac->req->operation == LDB_MODIFY
    2479          19 :                             && new_rodc == 0) {
    2480          16 :                                 break;
    2481             :                         }
    2482          48 :                         ldb_asprintf_errstring(ldb,
    2483             :                                 "%08X: samldb: UF_WORKSTATION_TRUST_ACCOUNT "
    2484             :                                 "requires objectclass 'computer'!",
    2485          48 :                                 W_ERROR_V(WERR_DS_MACHINE_ACCOUNT_CREATED_PRENT4));
    2486          48 :                         return LDB_ERR_OBJECT_CLASS_VIOLATION;
    2487             :                 }
    2488        1650 :                 break;
    2489             : 
    2490         643 :         case UF_SERVER_TRUST_ACCOUNT:
    2491         643 :                 if (!is_computer_objectclass) {
    2492          38 :                         ldb_asprintf_errstring(ldb,
    2493             :                                 "%08X: samldb: UF_SERVER_TRUST_ACCOUNT "
    2494             :                                 "requires objectclass 'computer'!",
    2495          38 :                                 W_ERROR_V(WERR_DS_MACHINE_ACCOUNT_CREATED_PRENT4));
    2496          38 :                         return LDB_ERR_OBJECT_CLASS_VIOLATION;
    2497             :                 }
    2498         605 :                 break;
    2499             : 
    2500           0 :         default:
    2501           0 :                 ldb_asprintf_errstring(ldb,
    2502             :                         "%08X: samldb: invalid userAccountControl[0x%08X]",
    2503           0 :                         W_ERROR_V(WERR_INVALID_PARAMETER),
    2504             :                                        user_account_control);
    2505           0 :                 return LDB_ERR_OTHER;
    2506             :         }
    2507       19773 :         return LDB_SUCCESS;
    2508             : }
    2509             : 
    2510       29988 : static int samldb_get_domain_secdesc_and_oc(struct samldb_ctx *ac,
    2511             :                                             struct security_descriptor **domain_sd,
    2512             :                                             const struct dsdb_class **objectclass)
    2513             : {
    2514       29988 :         const char * const sd_attrs[] = {"ntSecurityDescriptor", "objectClass", NULL};
    2515             :         struct ldb_result *res;
    2516       29988 :         struct ldb_dn *domain_dn = ldb_get_default_basedn(ldb_module_get_ctx(ac->module));
    2517       29988 :         const struct dsdb_schema *schema = NULL;
    2518       29988 :         struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
    2519       29988 :         int ret = dsdb_module_search_dn(ac->module, ac, &res,
    2520             :                                         domain_dn,
    2521             :                                         sd_attrs,
    2522             :                                         DSDB_FLAG_NEXT_MODULE | DSDB_SEARCH_SHOW_DELETED,
    2523             :                                         ac->req);
    2524       29988 :         if (ret != LDB_SUCCESS) {
    2525           0 :                 return ret;
    2526             :         }
    2527       29988 :         if (res->count != 1) {
    2528           0 :                 return ldb_module_operr(ac->module);
    2529             :         }
    2530             : 
    2531       29988 :         schema = dsdb_get_schema(ldb, ac->req);
    2532       29988 :         if (!schema) {
    2533           0 :                 return ldb_module_operr(ac->module);;
    2534             :         }
    2535       29988 :         *objectclass = dsdb_get_structural_oc_from_msg(schema, res->msgs[0]);
    2536       29988 :         return dsdb_get_sd_from_ldb_message(ldb_module_get_ctx(ac->module),
    2537       29988 :                                             ac, res->msgs[0], domain_sd);
    2538             : 
    2539             : }
    2540             : 
    2541             : /**
    2542             :  * Validate that the restriction in point 5 of MS-SAMR 3.1.1.8.10 userAccountControl is honoured
    2543             :  *
    2544             :  */
    2545       31364 : static int samldb_check_user_account_control_acl(struct samldb_ctx *ac,
    2546             :                                                  struct dom_sid *sid,
    2547             :                                                  uint32_t user_account_control,
    2548             :                                                  uint32_t user_account_control_old)
    2549             : {
    2550             :         size_t i;
    2551       31364 :         int ret = 0;
    2552       31364 :         bool need_acl_check = false;
    2553             :         struct security_token *user_token;
    2554             :         struct security_descriptor *domain_sd;
    2555       31364 :         const struct dsdb_class *objectclass = NULL;
    2556             :         const struct uac_to_guid {
    2557             :                 uint32_t uac;
    2558             :                 uint32_t priv_to_change_from;
    2559             :                 const char *oid;
    2560             :                 const char *guid;
    2561             :                 enum sec_privilege privilege;
    2562             :                 bool delete_is_privileged;
    2563             :                 bool admin_required;
    2564             :                 const char *error_string;
    2565       31364 :         } map[] = {
    2566             :                 {
    2567             :                         .uac = UF_PASSWD_NOTREQD,
    2568             :                         .guid = GUID_DRS_UPDATE_PASSWORD_NOT_REQUIRED_BIT,
    2569             :                         .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"
    2570             :                 },
    2571             :                 {
    2572             :                         .uac = UF_DONT_EXPIRE_PASSWD,
    2573             :                         .guid = GUID_DRS_UNEXPIRE_PASSWORD,
    2574             :                         .error_string = "Adding the UF_DONT_EXPIRE_PASSWD bit in userAccountControl requires the Unexpire-Password right that was not given on the Domain object"
    2575             :                 },
    2576             :                 {
    2577             :                         .uac = UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED,
    2578             :                         .guid = GUID_DRS_ENABLE_PER_USER_REVERSIBLY_ENCRYPTED_PASSWORD,
    2579             :                         .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"
    2580             :                 },
    2581             :                 {
    2582             :                         .uac = UF_SERVER_TRUST_ACCOUNT,
    2583             :                         .guid = GUID_DRS_DS_INSTALL_REPLICA,
    2584             :                         .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"
    2585             :                 },
    2586             :                 {
    2587             :                         .uac = UF_PARTIAL_SECRETS_ACCOUNT,
    2588             :                         .guid = GUID_DRS_DS_INSTALL_REPLICA,
    2589             :                         .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"
    2590             :                 },
    2591             :                 {
    2592             :                         .uac = UF_WORKSTATION_TRUST_ACCOUNT,
    2593             :                         .priv_to_change_from = UF_NORMAL_ACCOUNT,
    2594             :                         .error_string = "Swapping UF_NORMAL_ACCOUNT to UF_WORKSTATION_TRUST_ACCOUNT requires the user to be a member of the domain admins group"
    2595             :                 },
    2596             :                 {
    2597             :                         .uac = UF_NORMAL_ACCOUNT,
    2598             :                         .priv_to_change_from = UF_WORKSTATION_TRUST_ACCOUNT,
    2599             :                         .error_string = "Swapping UF_WORKSTATION_TRUST_ACCOUNT to UF_NORMAL_ACCOUNT requires the user to be a member of the domain admins group"
    2600             :                 },
    2601             :                 {
    2602             :                         .uac = UF_INTERDOMAIN_TRUST_ACCOUNT,
    2603             :                         .oid = DSDB_CONTROL_PERMIT_INTERDOMAIN_TRUST_UAC_OID,
    2604             :                         .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",
    2605             :                         .delete_is_privileged = true
    2606             :                 },
    2607             :                 {
    2608             :                         .uac = UF_TRUSTED_FOR_DELEGATION,
    2609             :                         .privilege = SEC_PRIV_ENABLE_DELEGATION,
    2610             :                         .delete_is_privileged = true,
    2611             :                         .error_string = "Updating the UF_TRUSTED_FOR_DELEGATION bit in userAccountControl is not permitted without the SeEnableDelegationPrivilege"
    2612             :                 },
    2613             :                 {
    2614             :                         .uac = UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION,
    2615             :                         .privilege = SEC_PRIV_ENABLE_DELEGATION,
    2616             :                         .delete_is_privileged = true,
    2617             :                         .error_string = "Updating the UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION bit in userAccountControl is not permitted without the SeEnableDelegationPrivilege"
    2618             :                 }
    2619             : 
    2620             :         };
    2621             : 
    2622       31364 :         if (dsdb_module_am_system(ac->module)) {
    2623        1446 :                 return LDB_SUCCESS;
    2624             :         }
    2625             : 
    2626      195438 :         for (i = 0; i < ARRAY_SIZE(map); i++) {
    2627      195438 :                 if (user_account_control & map[i].uac) {
    2628       29918 :                         need_acl_check = true;
    2629       29918 :                         break;
    2630             :                 }
    2631             :         }
    2632       29918 :         if (need_acl_check == false) {
    2633           0 :                 return LDB_SUCCESS;
    2634             :         }
    2635             : 
    2636       29918 :         user_token = acl_user_token(ac->module);
    2637       29918 :         if (user_token == NULL) {
    2638           0 :                 return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
    2639             :         }
    2640             : 
    2641       29918 :         ret = samldb_get_domain_secdesc_and_oc(ac, &domain_sd, &objectclass);
    2642       29918 :         if (ret != LDB_SUCCESS) {
    2643           0 :                 return ret;
    2644             :         }
    2645             : 
    2646      328737 :         for (i = 0; i < ARRAY_SIZE(map); i++) {
    2647      298904 :                 uint32_t this_uac_new = user_account_control & map[i].uac;
    2648      298904 :                 uint32_t this_uac_old = user_account_control_old & map[i].uac;
    2649      298904 :                 if (this_uac_new != this_uac_old) {
    2650       31094 :                         if (this_uac_old != 0) {
    2651       10396 :                                 if (map[i].delete_is_privileged == false) {
    2652       10382 :                                         continue;
    2653             :                                 }
    2654             :                         }
    2655       20712 :                         if (map[i].oid) {
    2656          70 :                                 struct ldb_control *control = ldb_request_get_control(ac->req, map[i].oid);
    2657          70 :                                 if (control == NULL) {
    2658           8 :                                         ret = LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
    2659             :                                 }
    2660       20642 :                         } else if (map[i].privilege != SEC_PRIV_INVALID) {
    2661         439 :                                 bool have_priv = security_token_has_privilege(user_token,
    2662          75 :                                                                               map[i].privilege);
    2663         439 :                                 if (have_priv == false) {
    2664          32 :                                         ret = LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
    2665             :                                 }
    2666       20203 :                         } else if (map[i].priv_to_change_from & user_account_control_old) {
    2667         114 :                                 bool is_admin = security_token_has_builtin_administrators(user_token);
    2668         114 :                                 if (is_admin == false) {
    2669           7 :                                         ret = LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
    2670             :                                 }
    2671       20089 :                         } else if (map[i].guid) {
    2672        2027 :                                 ret = acl_check_extended_right(ac,
    2673             :                                                                ac->module,
    2674             :                                                                ac->req,
    2675             :                                                                objectclass,
    2676             :                                                                domain_sd,
    2677             :                                                                user_token,
    2678         171 :                                                                map[i].guid,
    2679             :                                                                SEC_ADS_CONTROL_ACCESS,
    2680             :                                                                sid);
    2681             :                         } else {
    2682       18062 :                                 ret = LDB_SUCCESS;
    2683             :                         }
    2684       20712 :                         if (ret != LDB_SUCCESS) {
    2685          85 :                                 break;
    2686             :                         }
    2687             :                 }
    2688             :         }
    2689       29918 :         if (ret == LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS) {
    2690          85 :                 switch (ac->req->operation) {
    2691          33 :                 case LDB_ADD:
    2692          65 :                         ldb_asprintf_errstring(ldb_module_get_ctx(ac->module),
    2693             :                                                "Failed to add %s: %s",
    2694          33 :                                                ldb_dn_get_linearized(ac->msg->dn),
    2695           1 :                                                map[i].error_string);
    2696          33 :                         break;
    2697          52 :                 case LDB_MODIFY:
    2698         103 :                         ldb_asprintf_errstring(ldb_module_get_ctx(ac->module),
    2699             :                                                "Failed to modify %s: %s",
    2700          52 :                                                ldb_dn_get_linearized(ac->msg->dn),
    2701           1 :                                                map[i].error_string);
    2702          52 :                         break;
    2703           0 :                 default:
    2704           0 :                         return ldb_module_operr(ac->module);
    2705             :                 }
    2706          85 :                 if (map[i].guid) {
    2707          38 :                         struct ldb_dn *domain_dn
    2708          38 :                                 = ldb_get_default_basedn(ldb_module_get_ctx(ac->module));
    2709          38 :                         dsdb_acl_debug(domain_sd, acl_user_token(ac->module),
    2710             :                                        domain_dn,
    2711             :                                        true,
    2712             :                                        10);
    2713             :                 }
    2714             :         }
    2715       29918 :         return ret;
    2716             : }
    2717             : 
    2718       31566 : static int samldb_check_user_account_control_rules(struct samldb_ctx *ac,
    2719             :                                                    struct dom_sid *sid,
    2720             :                                                    uint32_t req_uac,
    2721             :                                                    uint32_t user_account_control,
    2722             :                                                    uint32_t user_account_control_old,
    2723             :                                                    bool is_computer_objectclass)
    2724             : {
    2725             :         int ret;
    2726       31566 :         struct dsdb_control_password_user_account_control *uac = NULL;
    2727             : 
    2728       31566 :         ret = samldb_check_user_account_control_invariants(ac, user_account_control);
    2729       31566 :         if (ret != LDB_SUCCESS) {
    2730          68 :                 return ret;
    2731             :         }
    2732       31498 :         ret = samldb_check_user_account_control_objectclass_invariants(ac,
    2733             :                                                                        user_account_control,
    2734             :                                                                        user_account_control_old,
    2735             :                                                                        is_computer_objectclass);
    2736       31498 :         if (ret != LDB_SUCCESS) {
    2737         134 :                 return ret;
    2738             :         }
    2739             : 
    2740       31364 :         ret = samldb_check_user_account_control_acl(ac, sid, user_account_control, user_account_control_old);
    2741       31364 :         if (ret != LDB_SUCCESS) {
    2742          85 :                 return ret;
    2743             :         }
    2744             : 
    2745       31279 :         uac = talloc_zero(ac->req,
    2746             :                           struct dsdb_control_password_user_account_control);
    2747       31279 :         if (uac == NULL) {
    2748           0 :                 return ldb_module_oom(ac->module);
    2749             :         }
    2750             : 
    2751       31279 :         uac->req_flags = req_uac;
    2752       31279 :         uac->old_flags = user_account_control_old;
    2753       31279 :         uac->new_flags = user_account_control;
    2754             : 
    2755       31279 :         ret = ldb_request_add_control(ac->req,
    2756             :                                 DSDB_CONTROL_PASSWORD_USER_ACCOUNT_CONTROL_OID,
    2757             :                                 false, uac);
    2758       31279 :         if (ret != LDB_SUCCESS) {
    2759           0 :                 return ret;
    2760             :         }
    2761             : 
    2762       31279 :         return ret;
    2763             : }
    2764             : 
    2765             : 
    2766             : /**
    2767             :  * This function is called on LDB modify operations. It performs some additions/
    2768             :  * replaces on the current LDB message when "userAccountControl" changes.
    2769             :  */
    2770       11955 : static int samldb_user_account_control_change(struct samldb_ctx *ac)
    2771             : {
    2772       11955 :         struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
    2773             :         uint32_t old_uac;
    2774             :         uint32_t new_uac;
    2775             :         uint32_t raw_uac;
    2776             :         uint32_t old_ufa;
    2777             :         uint32_t new_ufa;
    2778             :         uint32_t old_uac_computed;
    2779             :         uint32_t clear_uac;
    2780             :         uint32_t old_atype;
    2781             :         uint32_t new_atype;
    2782             :         uint32_t old_pgrid;
    2783             :         uint32_t new_pgrid;
    2784             :         NTTIME old_lockoutTime;
    2785             :         struct ldb_message_element *el;
    2786             :         struct ldb_val *val;
    2787             :         struct ldb_val computer_val;
    2788             :         struct ldb_message *tmp_msg;
    2789             :         struct dom_sid *sid;
    2790             :         int ret;
    2791             :         struct ldb_result *res;
    2792       11955 :         const char * const attrs[] = {
    2793             :                 "objectClass",
    2794             :                 "isCriticalSystemObject",
    2795             :                 "userAccountControl",
    2796             :                 "msDS-User-Account-Control-Computed",
    2797             :                 "lockoutTime",
    2798             :                 "objectSid",
    2799             :                 NULL
    2800             :         };
    2801       11955 :         bool is_computer_objectclass = false;
    2802       11955 :         bool old_is_critical = false;
    2803       11955 :         bool new_is_critical = false;
    2804             : 
    2805       19907 :         ret = dsdb_get_expected_new_values(ac,
    2806       11955 :                                            ac->msg,
    2807             :                                            "userAccountControl",
    2808             :                                            &el,
    2809       11955 :                                            ac->req->operation);
    2810       11955 :         if (ret != LDB_SUCCESS) {
    2811           0 :                 return ret;
    2812             :         }
    2813             : 
    2814       11955 :         if (el == NULL || el->num_values == 0) {
    2815           6 :                 ldb_asprintf_errstring(ldb,
    2816             :                         "%08X: samldb: 'userAccountControl' can't be deleted!",
    2817           6 :                         W_ERROR_V(WERR_DS_ILLEGAL_MOD_OPERATION));
    2818           6 :                 return LDB_ERR_UNWILLING_TO_PERFORM;
    2819             :         }
    2820             : 
    2821             :         /* Create a temporary message for fetching the "userAccountControl" */
    2822       11949 :         tmp_msg = ldb_msg_new(ac->msg);
    2823       11949 :         if (tmp_msg == NULL) {
    2824           0 :                 return ldb_module_oom(ac->module);
    2825             :         }
    2826       11949 :         ret = ldb_msg_add(tmp_msg, el, 0);
    2827       11949 :         if (ret != LDB_SUCCESS) {
    2828           0 :                 return ret;
    2829             :         }
    2830       11949 :         raw_uac = ldb_msg_find_attr_as_uint(tmp_msg,
    2831             :                                             "userAccountControl",
    2832             :                                             0);
    2833       11949 :         talloc_free(tmp_msg);
    2834             :         /*
    2835             :          * UF_LOCKOUT, UF_PASSWD_CANT_CHANGE and UF_PASSWORD_EXPIRED
    2836             :          * are only generated and not stored. We ignore them almost
    2837             :          * completely, along with unknown bits and UF_SCRIPT.
    2838             :          *
    2839             :          * The only exception is ACB_AUTOLOCK, which features in
    2840             :          * clear_acb when the bit is cleared in this modify operation.
    2841             :          *
    2842             :          * MS-SAMR 2.2.1.13 UF_FLAG Codes states that some bits are
    2843             :          * ignored by clients and servers
    2844             :          */
    2845       11949 :         new_uac = raw_uac & UF_SETTABLE_BITS;
    2846             : 
    2847             :         /* Fetch the old "userAccountControl" and "objectClass" */
    2848       11949 :         ret = dsdb_module_search_dn(ac->module, ac, &res, ac->msg->dn, attrs,
    2849             :                                     DSDB_FLAG_NEXT_MODULE, ac->req);
    2850       11949 :         if (ret != LDB_SUCCESS) {
    2851           0 :                 return ret;
    2852             :         }
    2853       11949 :         old_uac = ldb_msg_find_attr_as_uint(res->msgs[0], "userAccountControl", 0);
    2854       11949 :         if (old_uac == 0) {
    2855           0 :                 return ldb_operr(ldb);
    2856             :         }
    2857       11949 :         old_uac_computed = ldb_msg_find_attr_as_uint(res->msgs[0],
    2858             :                                                      "msDS-User-Account-Control-Computed", 0);
    2859       11949 :         old_lockoutTime = ldb_msg_find_attr_as_int64(res->msgs[0],
    2860             :                                                      "lockoutTime", 0);
    2861       11949 :         old_is_critical = ldb_msg_find_attr_as_bool(res->msgs[0],
    2862             :                                                     "isCriticalSystemObject", 0);
    2863             :         /*
    2864             :          * When we do not have objectclass "computer" we cannot
    2865             :          * switch to a workstation or (RO)DC
    2866             :          */
    2867       11949 :         el = ldb_msg_find_element(res->msgs[0], "objectClass");
    2868       11949 :         if (el == NULL) {
    2869           0 :                 return ldb_operr(ldb);
    2870             :         }
    2871       11949 :         computer_val = data_blob_string_const("computer");
    2872       11949 :         val = ldb_msg_find_val(el, &computer_val);
    2873       11949 :         if (val != NULL) {
    2874        1179 :                 is_computer_objectclass = true;
    2875             :         }
    2876             : 
    2877       11949 :         old_ufa = old_uac & UF_ACCOUNT_TYPE_MASK;
    2878       11949 :         old_atype = ds_uf2atype(old_ufa);
    2879       11949 :         old_pgrid = ds_uf2prim_group_rid(old_uac);
    2880             : 
    2881       11949 :         new_ufa = new_uac & UF_ACCOUNT_TYPE_MASK;
    2882       11949 :         if (new_ufa == 0) {
    2883             :                 /*
    2884             :                  * "userAccountControl" = 0 or missing one of the
    2885             :                  * types means "UF_NORMAL_ACCOUNT".  See MS-SAMR
    2886             :                  * 3.1.1.8.10 point 8
    2887             :                  */
    2888         267 :                 new_ufa = UF_NORMAL_ACCOUNT;
    2889         267 :                 new_uac |= new_ufa;
    2890             :         }
    2891       11949 :         sid = samdb_result_dom_sid(res, res->msgs[0], "objectSid");
    2892       11949 :         if (sid == NULL) {
    2893           0 :                 return ldb_module_operr(ac->module);
    2894             :         }
    2895             : 
    2896       11949 :         ret = samldb_check_user_account_control_rules(ac, sid,
    2897             :                                                       raw_uac,
    2898             :                                                       new_uac,
    2899             :                                                       old_uac,
    2900             :                                                       is_computer_objectclass);
    2901       11949 :         if (ret != LDB_SUCCESS) {
    2902         178 :                 return ret;
    2903             :         }
    2904             : 
    2905       11771 :         new_atype = ds_uf2atype(new_ufa);
    2906       11771 :         new_pgrid = ds_uf2prim_group_rid(new_uac);
    2907             : 
    2908       11771 :         clear_uac = (old_uac | old_uac_computed) & ~raw_uac;
    2909             : 
    2910       11771 :         switch (new_ufa) {
    2911       10826 :         case UF_NORMAL_ACCOUNT:
    2912       10826 :                 new_is_critical = old_is_critical;
    2913       10826 :                 break;
    2914             : 
    2915           0 :         case UF_INTERDOMAIN_TRUST_ACCOUNT:
    2916           0 :                 new_is_critical = true;
    2917           0 :                 break;
    2918             : 
    2919         485 :         case UF_WORKSTATION_TRUST_ACCOUNT:
    2920         485 :                 new_is_critical = false;
    2921         485 :                 if (new_uac & UF_PARTIAL_SECRETS_ACCOUNT) {
    2922         147 :                         new_is_critical = true;
    2923             :                 }
    2924         485 :                 break;
    2925             : 
    2926         460 :         case UF_SERVER_TRUST_ACCOUNT:
    2927         460 :                 new_is_critical = true;
    2928         460 :                 break;
    2929             : 
    2930           0 :         default:
    2931           0 :                 ldb_asprintf_errstring(ldb,
    2932             :                         "%08X: samldb: invalid userAccountControl[0x%08X]",
    2933           0 :                         W_ERROR_V(WERR_INVALID_PARAMETER), raw_uac);
    2934           0 :                 return LDB_ERR_OTHER;
    2935             :         }
    2936             : 
    2937       11771 :         if (old_atype != new_atype) {
    2938         136 :                 ret = samdb_msg_append_uint(ldb, ac->msg, ac->msg,
    2939             :                                             "sAMAccountType", new_atype,
    2940             :                                             LDB_FLAG_MOD_REPLACE);
    2941         136 :                 if (ret != LDB_SUCCESS) {
    2942           0 :                         return ret;
    2943             :                 }
    2944             :         }
    2945             : 
    2946             :         /* As per MS-SAMR 3.1.1.8.10 these flags have not to be set */
    2947       11771 :         if ((clear_uac & UF_LOCKOUT) && (old_lockoutTime != 0)) {
    2948             :                 /* "lockoutTime" reset as per MS-SAMR 3.1.1.8.10 */
    2949          10 :                 ldb_msg_remove_attr(ac->msg, "lockoutTime");
    2950          10 :                 ret = samdb_msg_append_uint64(ldb, ac->msg, ac->msg, "lockoutTime",
    2951             :                                               (NTTIME)0, LDB_FLAG_MOD_REPLACE);
    2952          10 :                 if (ret != LDB_SUCCESS) {
    2953           0 :                         return ret;
    2954             :                 }
    2955             :         }
    2956             : 
    2957             :         /*
    2958             :          * "isCriticalSystemObject" might be set/changed
    2959             :          *
    2960             :          * Even a change from UF_NORMAL_ACCOUNT (implicitly FALSE) to
    2961             :          * UF_WORKSTATION_TRUST_ACCOUNT (actually FALSE) triggers
    2962             :          * creating the attribute.
    2963             :          */
    2964       11771 :         if (old_is_critical != new_is_critical || old_atype != new_atype) {
    2965         191 :                 ret = ldb_msg_append_string(ac->msg, "isCriticalSystemObject",
    2966             :                                             new_is_critical ? "TRUE": "FALSE",
    2967             :                                             LDB_FLAG_MOD_REPLACE);
    2968         191 :                 if (ret != LDB_SUCCESS) {
    2969           0 :                         return ret;
    2970             :                 }
    2971             :         }
    2972             : 
    2973       11771 :         if (!ldb_msg_find_element(ac->msg, "primaryGroupID") &&
    2974             :             (old_pgrid != new_pgrid)) {
    2975             :                 /* Older AD deployments don't know about the RODC group */
    2976         194 :                 if (new_pgrid == DOMAIN_RID_READONLY_DCS) {
    2977           9 :                         ret = samldb_prim_group_tester(ac, new_pgrid);
    2978           9 :                         if (ret != LDB_SUCCESS) {
    2979           0 :                                 return ret;
    2980             :                         }
    2981             :                 }
    2982             : 
    2983         194 :                 ret = samdb_msg_append_uint(ldb, ac->msg, ac->msg,
    2984             :                                             "primaryGroupID", new_pgrid,
    2985             :                                             LDB_FLAG_MOD_REPLACE);
    2986         194 :                 if (ret != LDB_SUCCESS) {
    2987           0 :                         return ret;
    2988             :                 }
    2989             :         }
    2990             : 
    2991             :         /* Propagate eventual "userAccountControl" attribute changes */
    2992       11771 :         if (old_uac != new_uac) {
    2993       11263 :                 char *tempstr = talloc_asprintf(ac->msg, "%d",
    2994             :                                                 new_uac);
    2995       11263 :                 if (tempstr == NULL) {
    2996           0 :                         return ldb_module_oom(ac->module);
    2997             :                 }
    2998             : 
    2999       11263 :                 ret = ldb_msg_add_empty(ac->msg,
    3000             :                                         "userAccountControl",
    3001             :                                         LDB_FLAG_MOD_REPLACE,
    3002             :                                         &el);
    3003       11263 :                 el->values = talloc(ac->msg, struct ldb_val);
    3004       11263 :                 el->num_values = 1;
    3005       11263 :                 el->values[0].data = (uint8_t *) tempstr;
    3006       11263 :                 el->values[0].length = strlen(tempstr);
    3007             :         } else {
    3008         508 :                 ldb_msg_remove_attr(ac->msg, "userAccountControl");
    3009             :         }
    3010             : 
    3011       11771 :         return LDB_SUCCESS;
    3012             : }
    3013             : 
    3014          55 : static int samldb_check_pwd_last_set_acl(struct samldb_ctx *ac,
    3015             :                                          struct dom_sid *sid)
    3016             : {
    3017          55 :         struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
    3018          55 :         int ret = 0;
    3019          55 :         struct security_token *user_token = NULL;
    3020          55 :         struct security_descriptor *domain_sd = NULL;
    3021          55 :         struct ldb_dn *domain_dn = ldb_get_default_basedn(ldb_module_get_ctx(ac->module));
    3022          55 :         const char *operation = "";
    3023          55 :         const struct dsdb_class *objectclass = NULL;
    3024             : 
    3025          55 :         if (dsdb_module_am_system(ac->module)) {
    3026           1 :                 return LDB_SUCCESS;
    3027             :         }
    3028             : 
    3029          54 :         switch (ac->req->operation) {
    3030           0 :         case LDB_ADD:
    3031           0 :                 operation = "add";
    3032           0 :                 break;
    3033          54 :         case LDB_MODIFY:
    3034          54 :                 operation = "modify";
    3035          54 :                 break;
    3036           0 :         default:
    3037           0 :                 return ldb_module_operr(ac->module);
    3038             :         }
    3039             : 
    3040          54 :         user_token = acl_user_token(ac->module);
    3041          54 :         if (user_token == NULL) {
    3042           0 :                 return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
    3043             :         }
    3044             : 
    3045          54 :         ret = samldb_get_domain_secdesc_and_oc(ac, &domain_sd, &objectclass);
    3046          54 :         if (ret != LDB_SUCCESS) {
    3047           0 :                 return ret;
    3048             :         }
    3049          54 :         ret = acl_check_extended_right(ac,
    3050             :                                        ac->module,
    3051             :                                        ac->req,
    3052             :                                        objectclass,
    3053             :                                        domain_sd,
    3054             :                                        user_token,
    3055             :                                        GUID_DRS_UNEXPIRE_PASSWORD,
    3056             :                                        SEC_ADS_CONTROL_ACCESS,
    3057             :                                        sid);
    3058          54 :         if (ret != LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS) {
    3059          54 :                 return ret;
    3060             :         }
    3061             : 
    3062           0 :         ldb_debug_set(ldb, LDB_DEBUG_WARNING,
    3063             :                       "Failed to %s %s: "
    3064             :                       "Setting pwdLastSet to -1 requires the "
    3065             :                       "Unexpire-Password right that was not given "
    3066             :                       "on the Domain object",
    3067             :                       operation,
    3068           0 :                       ldb_dn_get_linearized(ac->msg->dn));
    3069           0 :         dsdb_acl_debug(domain_sd, user_token,
    3070             :                        domain_dn, true, 10);
    3071             : 
    3072           0 :         return ret;
    3073             : }
    3074             : 
    3075             : /**
    3076             :  * This function is called on LDB modify operations. It performs some additions/
    3077             :  * replaces on the current LDB message when "pwdLastSet" changes.
    3078             :  */
    3079         249 : static int samldb_pwd_last_set_change(struct samldb_ctx *ac)
    3080             : {
    3081         249 :         struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
    3082         249 :         NTTIME last_set = 0;
    3083         249 :         struct ldb_message_element *el = NULL;
    3084         249 :         struct ldb_message *tmp_msg = NULL;
    3085         249 :         struct dom_sid *self_sid = NULL;
    3086             :         int ret;
    3087         249 :         struct ldb_result *res = NULL;
    3088         249 :         const char * const attrs[] = {
    3089             :                 "objectSid",
    3090             :                 NULL
    3091             :         };
    3092             : 
    3093         461 :         ret = dsdb_get_expected_new_values(ac,
    3094         249 :                                            ac->msg,
    3095             :                                            "pwdLastSet",
    3096             :                                            &el,
    3097         249 :                                            ac->req->operation);
    3098         249 :         if (ret != LDB_SUCCESS) {
    3099           0 :                 return ret;
    3100             :         }
    3101             : 
    3102         249 :         if (el == NULL || el->num_values == 0) {
    3103           6 :                 ldb_asprintf_errstring(ldb,
    3104             :                         "%08X: samldb: 'pwdLastSet' can't be deleted!",
    3105           6 :                         W_ERROR_V(WERR_DS_ILLEGAL_MOD_OPERATION));
    3106           6 :                 return LDB_ERR_UNWILLING_TO_PERFORM;
    3107             :         }
    3108             : 
    3109             :         /* Create a temporary message for fetching the "userAccountControl" */
    3110         243 :         tmp_msg = ldb_msg_new(ac->msg);
    3111         243 :         if (tmp_msg == NULL) {
    3112           0 :                 return ldb_module_oom(ac->module);
    3113             :         }
    3114         243 :         ret = ldb_msg_add(tmp_msg, el, 0);
    3115         243 :         if (ret != LDB_SUCCESS) {
    3116           0 :                 return ret;
    3117             :         }
    3118         243 :         last_set = samdb_result_nttime(tmp_msg, "pwdLastSet", 0);
    3119         243 :         talloc_free(tmp_msg);
    3120             : 
    3121             :         /*
    3122             :          * Setting -1 (0xFFFFFFFFFFFFFFFF) requires the Unexpire-Password right
    3123             :          */
    3124         243 :         if (last_set != UINT64_MAX) {
    3125         188 :                 return LDB_SUCCESS;
    3126             :         }
    3127             : 
    3128             :         /* Fetch the "objectSid" */
    3129          55 :         ret = dsdb_module_search_dn(ac->module, ac, &res, ac->msg->dn, attrs,
    3130             :                                     DSDB_FLAG_NEXT_MODULE, ac->req);
    3131          55 :         if (ret != LDB_SUCCESS) {
    3132           0 :                 return ret;
    3133             :         }
    3134          55 :         self_sid = samdb_result_dom_sid(res, res->msgs[0], "objectSid");
    3135          55 :         if (self_sid == NULL) {
    3136           0 :                 return ldb_module_operr(ac->module);
    3137             :         }
    3138             : 
    3139          55 :         ret = samldb_check_pwd_last_set_acl(ac, self_sid);
    3140          55 :         if (ret != LDB_SUCCESS) {
    3141           0 :                 return ret;
    3142             :         }
    3143             : 
    3144          55 :         return LDB_SUCCESS;
    3145             : }
    3146             : 
    3147         133 : static int samldb_lockout_time(struct samldb_ctx *ac)
    3148             : {
    3149         133 :         struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
    3150             :         NTTIME lockoutTime;
    3151             :         struct ldb_message_element *el;
    3152             :         struct ldb_message *tmp_msg;
    3153             :         int ret;
    3154             : 
    3155         264 :         ret = dsdb_get_expected_new_values(ac,
    3156         133 :                                            ac->msg,
    3157             :                                            "lockoutTime",
    3158             :                                            &el,
    3159         133 :                                            ac->req->operation);
    3160         133 :         if (ret != LDB_SUCCESS) {
    3161           0 :                 return ret;
    3162             :         }
    3163             : 
    3164         133 :         if (el == NULL || el->num_values == 0) {
    3165           0 :                 ldb_asprintf_errstring(ldb,
    3166             :                         "%08X: samldb: 'lockoutTime' can't be deleted!",
    3167           0 :                         W_ERROR_V(WERR_DS_ILLEGAL_MOD_OPERATION));
    3168           0 :                 return LDB_ERR_UNWILLING_TO_PERFORM;
    3169             :         }
    3170             : 
    3171             :         /* Create a temporary message for fetching the "lockoutTime" */
    3172         133 :         tmp_msg = ldb_msg_new(ac->msg);
    3173         133 :         if (tmp_msg == NULL) {
    3174           0 :                 return ldb_module_oom(ac->module);
    3175             :         }
    3176         133 :         ret = ldb_msg_add(tmp_msg, el, 0);
    3177         133 :         if (ret != LDB_SUCCESS) {
    3178           0 :                 return ret;
    3179             :         }
    3180         133 :         lockoutTime = ldb_msg_find_attr_as_int64(tmp_msg,
    3181             :                                                  "lockoutTime",
    3182             :                                                  0);
    3183         133 :         talloc_free(tmp_msg);
    3184             : 
    3185         133 :         if (lockoutTime != 0) {
    3186          55 :                 return LDB_SUCCESS;
    3187             :         }
    3188             : 
    3189             :         /* lockoutTime == 0 resets badPwdCount */
    3190          78 :         ldb_msg_remove_attr(ac->msg, "badPwdCount");
    3191          78 :         ret = samdb_msg_append_int(ldb, ac->msg, ac->msg,
    3192             :                                    "badPwdCount", 0,
    3193             :                                    LDB_FLAG_MOD_REPLACE);
    3194          78 :         if (ret != LDB_SUCCESS) {
    3195           0 :                 return ret;
    3196             :         }
    3197             : 
    3198          78 :         return LDB_SUCCESS;
    3199             : }
    3200             : 
    3201         102 : static int samldb_group_type_change(struct samldb_ctx *ac)
    3202             : {
    3203         102 :         struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
    3204             :         uint32_t group_type, old_group_type, account_type;
    3205             :         struct ldb_message_element *el;
    3206             :         struct ldb_message *tmp_msg;
    3207             :         int ret;
    3208             :         struct ldb_result *res;
    3209         102 :         const char * const attrs[] = { "groupType", NULL };
    3210             : 
    3211         170 :         ret = dsdb_get_expected_new_values(ac,
    3212         102 :                                            ac->msg,
    3213             :                                            "groupType",
    3214             :                                            &el,
    3215         102 :                                            ac->req->operation);
    3216         102 :         if (ret != LDB_SUCCESS) {
    3217           0 :                 return ret;
    3218             :         }
    3219             : 
    3220         102 :         if (el == NULL) {
    3221             :                 /* we are not affected */
    3222           3 :                 return LDB_SUCCESS;
    3223             :         }
    3224             : 
    3225             :         /* Create a temporary message for fetching the "groupType" */
    3226          99 :         tmp_msg = ldb_msg_new(ac->msg);
    3227          99 :         if (tmp_msg == NULL) {
    3228           0 :                 return ldb_module_oom(ac->module);
    3229             :         }
    3230          99 :         ret = ldb_msg_add(tmp_msg, el, 0);
    3231          99 :         if (ret != LDB_SUCCESS) {
    3232           0 :                 return ret;
    3233             :         }
    3234          99 :         group_type = ldb_msg_find_attr_as_uint(tmp_msg, "groupType", 0);
    3235          99 :         talloc_free(tmp_msg);
    3236             : 
    3237          99 :         ret = dsdb_module_search_dn(ac->module, ac, &res, ac->msg->dn, attrs,
    3238             :                                     DSDB_FLAG_NEXT_MODULE |
    3239             :                                     DSDB_SEARCH_SHOW_DELETED, ac->req);
    3240          99 :         if (ret != LDB_SUCCESS) {
    3241           0 :                 return ret;
    3242             :         }
    3243          99 :         old_group_type = ldb_msg_find_attr_as_uint(res->msgs[0], "groupType", 0);
    3244          99 :         if (old_group_type == 0) {
    3245           0 :                 return ldb_operr(ldb);
    3246             :         }
    3247             : 
    3248             :         /* Group type switching isn't so easy as it seems: We can only
    3249             :          * change in this directions: global <-> universal <-> local
    3250             :          * On each step also the group type itself
    3251             :          * (security/distribution) is variable. */
    3252             : 
    3253          99 :         if (ldb_request_get_control(ac->req, LDB_CONTROL_PROVISION_OID) == NULL) {
    3254          99 :                 switch (group_type) {
    3255          39 :                 case GTYPE_SECURITY_GLOBAL_GROUP:
    3256             :                 case GTYPE_DISTRIBUTION_GLOBAL_GROUP:
    3257             :                         /* change to "universal" allowed */
    3258          39 :                         if ((old_group_type == GTYPE_SECURITY_DOMAIN_LOCAL_GROUP) ||
    3259             :                         (old_group_type == GTYPE_DISTRIBUTION_DOMAIN_LOCAL_GROUP)) {
    3260           9 :                                 ldb_set_errstring(ldb,
    3261             :                                         "samldb: Change from security/distribution local group forbidden!");
    3262           9 :                                 return LDB_ERR_UNWILLING_TO_PERFORM;
    3263             :                         }
    3264          30 :                 break;
    3265             : 
    3266          27 :                 case GTYPE_SECURITY_UNIVERSAL_GROUP:
    3267             :                 case GTYPE_DISTRIBUTION_UNIVERSAL_GROUP:
    3268             :                         /* each change allowed */
    3269          27 :                 break;
    3270          18 :                 case GTYPE_SECURITY_DOMAIN_LOCAL_GROUP:
    3271             :                 case GTYPE_DISTRIBUTION_DOMAIN_LOCAL_GROUP:
    3272             :                         /* change to "universal" allowed */
    3273          18 :                         if ((old_group_type == GTYPE_SECURITY_GLOBAL_GROUP) ||
    3274             :                         (old_group_type == GTYPE_DISTRIBUTION_GLOBAL_GROUP)) {
    3275           9 :                                 ldb_set_errstring(ldb,
    3276             :                                         "samldb: Change from security/distribution global group forbidden!");
    3277           9 :                                 return LDB_ERR_UNWILLING_TO_PERFORM;
    3278             :                         }
    3279           9 :                 break;
    3280             : 
    3281          15 :                 case GTYPE_SECURITY_BUILTIN_LOCAL_GROUP:
    3282             :                 default:
    3283             :                         /* we don't allow this "groupType" values */
    3284          15 :                         return LDB_ERR_UNWILLING_TO_PERFORM;
    3285             :                 break;
    3286             :                 }
    3287           0 :         }
    3288             : 
    3289          66 :         account_type =  ds_gtype2atype(group_type);
    3290          66 :         if (account_type == 0) {
    3291           0 :                 ldb_set_errstring(ldb, "samldb: Unrecognized account type!");
    3292           0 :                 return LDB_ERR_UNWILLING_TO_PERFORM;
    3293             :         }
    3294          66 :         ret = samdb_msg_append_uint(ldb, ac->msg, ac->msg, "sAMAccountType",
    3295             :                                     account_type, LDB_FLAG_MOD_REPLACE);
    3296          66 :         if (ret != LDB_SUCCESS) {
    3297           0 :                 return ret;
    3298             :         }
    3299             : 
    3300          66 :         return LDB_SUCCESS;
    3301             : }
    3302             : 
    3303        3102 : static int samldb_member_check(struct samldb_ctx *ac)
    3304             : {
    3305        3102 :         const char * const attrs[] = { "objectSid", NULL };
    3306        3102 :         struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
    3307             :         struct ldb_message_element *el;
    3308             :         struct ldb_dn *member_dn;
    3309             :         struct dom_sid *sid;
    3310             :         struct ldb_result *res;
    3311             :         struct dom_sid *group_sid;
    3312             :         unsigned int i, j;
    3313             :         int ret;
    3314             : 
    3315             :         /* Fetch information from the existing object */
    3316             : 
    3317        3102 :         ret = dsdb_module_search(ac->module, ac, &res, ac->msg->dn, LDB_SCOPE_BASE, attrs,
    3318             :                                  DSDB_FLAG_NEXT_MODULE | DSDB_SEARCH_SHOW_DELETED, ac->req, NULL);
    3319        3102 :         if (ret != LDB_SUCCESS) {
    3320           0 :                 return ret;
    3321             :         }
    3322        3102 :         if (res->count != 1) {
    3323           0 :                 return ldb_operr(ldb);
    3324             :         }
    3325             : 
    3326        3102 :         group_sid = samdb_result_dom_sid(res, res->msgs[0], "objectSid");
    3327        3102 :         if (group_sid == NULL) {
    3328           0 :                 return ldb_operr(ldb);
    3329             :         }
    3330             : 
    3331             :         /* We've to walk over all modification entries and consider the "member"
    3332             :          * ones. */
    3333       10281 :         for (i = 0; i < ac->msg->num_elements; i++) {
    3334        7182 :                 if (ldb_attr_cmp(ac->msg->elements[i].name, "member") != 0) {
    3335           0 :                         continue;
    3336             :                 }
    3337             : 
    3338        7182 :                 el = &ac->msg->elements[i];
    3339       14717 :                 for (j = 0; j < el->num_values; j++) {
    3340             :                         struct ldb_result *group_res;
    3341        7538 :                         const char *group_attrs[] = { "primaryGroupID" , NULL };
    3342             :                         uint32_t prim_group_rid;
    3343             : 
    3344        7538 :                         if (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_DELETE) {
    3345             :                                 /* Deletes will be handled in
    3346             :                                  * repl_meta_data, and deletes not
    3347             :                                  * matching a member will return
    3348             :                                  * LDB_ERR_UNWILLING_TO_PERFORM
    3349             :                                  * there */
    3350         898 :                                 continue;
    3351             :                         }
    3352             : 
    3353        7202 :                         member_dn = ldb_dn_from_ldb_val(ac, ldb,
    3354        7202 :                                                         &el->values[j]);
    3355        7202 :                         if (!ldb_dn_validate(member_dn)) {
    3356           3 :                                 return ldb_operr(ldb);
    3357             :                         }
    3358             : 
    3359             :                         /* Denies to add "member"s to groups which are primary
    3360             :                          * ones for them - in this case return
    3361             :                          * ERR_ENTRY_ALREADY_EXISTS. */
    3362             : 
    3363        7202 :                         ret = dsdb_module_search_dn(ac->module, ac, &group_res,
    3364             :                                                     member_dn, group_attrs,
    3365             :                                                     DSDB_FLAG_NEXT_MODULE, ac->req);
    3366        7202 :                         if (ret == LDB_ERR_NO_SUCH_OBJECT) {
    3367             :                                 /* member DN doesn't exist yet */
    3368           0 :                                 continue;
    3369             :                         }
    3370        7202 :                         if (ret != LDB_SUCCESS) {
    3371           0 :                                 return ret;
    3372             :                         }
    3373        7202 :                         prim_group_rid = ldb_msg_find_attr_as_uint(group_res->msgs[0], "primaryGroupID", (uint32_t)-1);
    3374        7202 :                         if (prim_group_rid == (uint32_t) -1) {
    3375             :                                 /* the member hasn't to be a user account ->
    3376             :                                  * therefore no check needed in this case. */
    3377         267 :                                 continue;
    3378             :                         }
    3379             : 
    3380        6935 :                         sid = dom_sid_add_rid(ac, samdb_domain_sid(ldb),
    3381             :                                               prim_group_rid);
    3382        6935 :                         if (sid == NULL) {
    3383           0 :                                 return ldb_operr(ldb);
    3384             :                         }
    3385             : 
    3386        6935 :                         if (dom_sid_equal(group_sid, sid)) {
    3387           3 :                                 ldb_asprintf_errstring(ldb,
    3388             :                                                        "samldb: member %s already set via primaryGroupID %u",
    3389             :                                                        ldb_dn_get_linearized(member_dn), prim_group_rid);
    3390           3 :                                 return LDB_ERR_ENTRY_ALREADY_EXISTS;
    3391             :                         }
    3392             :                 }
    3393             :         }
    3394             : 
    3395        3099 :         talloc_free(res);
    3396             : 
    3397        3099 :         return LDB_SUCCESS;
    3398             : }
    3399             : 
    3400             : /* SAM objects have special rules regarding the "description" attribute on
    3401             :  * modify operations. */
    3402         671 : static int samldb_description_check(struct samldb_ctx *ac, bool *modified)
    3403             : {
    3404         671 :         struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
    3405         671 :         const char * const attrs[] = { "objectClass", "description", NULL };
    3406             :         struct ldb_result *res;
    3407             :         unsigned int i;
    3408             :         int ret;
    3409             : 
    3410             :         /* Fetch information from the existing object */
    3411         671 :         ret = dsdb_module_search(ac->module, ac, &res, ac->msg->dn, LDB_SCOPE_BASE, attrs,
    3412             :                                  DSDB_FLAG_NEXT_MODULE | DSDB_SEARCH_SHOW_DELETED, ac->req,
    3413             :                                  "(|(objectclass=user)(objectclass=group)(objectclass=samDomain)(objectclass=samServer))");
    3414         671 :         if (ret != LDB_SUCCESS) {
    3415             :                 /* don't treat it specially ... let normal error codes
    3416             :                    happen from other places */
    3417           0 :                 ldb_reset_err_string(ldb);
    3418           0 :                 return LDB_SUCCESS;
    3419             :         }
    3420         671 :         if (res->count == 0) {
    3421             :                 /* we didn't match the filter */
    3422         205 :                 talloc_free(res);
    3423         205 :                 return LDB_SUCCESS;
    3424             :         }
    3425             : 
    3426             :         /* We've to walk over all modification entries and consider the
    3427             :          * "description" ones. */
    3428        1490 :         for (i = 0; i < ac->msg->num_elements; i++) {
    3429        1024 :                 if (ldb_attr_cmp(ac->msg->elements[i].name, "description") == 0) {
    3430         469 :                         ac->msg->elements[i].flags |= LDB_FLAG_INTERNAL_FORCE_SINGLE_VALUE_CHECK;
    3431         469 :                         *modified = true;
    3432             :                 }
    3433             :         }
    3434             : 
    3435         466 :         talloc_free(res);
    3436             : 
    3437         466 :         return LDB_SUCCESS;
    3438             : }
    3439             : 
    3440             : #define SPN_ALIAS_NONE 0
    3441             : #define SPN_ALIAS_LINK 1
    3442             : #define SPN_ALIAS_TARGET 2
    3443             : 
    3444        3207 : static int find_spn_aliases(struct ldb_context *ldb,
    3445             :                             TALLOC_CTX *mem_ctx,
    3446             :                             const char *service_class,
    3447             :                             char ***aliases,
    3448             :                             size_t *n_aliases,
    3449             :                             int *direction)
    3450             : {
    3451             :         /*
    3452             :          * If you change the way this works, you should also look at changing
    3453             :          * LDB_lookup_spn_alias() in source4/dsdb/samdb/cracknames.c, which
    3454             :          * does some of the same work.
    3455             :          *
    3456             :          * In particular, note that sPNMappings are resolved on a first come,
    3457             :          * first served basis. For example, if we have
    3458             :          *
    3459             :          *  host=ldap,cifs
    3460             :          *  foo=ldap
    3461             :          *  cifs=host,alerter
    3462             :          *
    3463             :          * then 'ldap', 'cifs', and 'host' will resolve to 'host', and
    3464             :          * 'alerter' will resolve to 'cifs'.
    3465             :          *
    3466             :          * If this resolution method is made more complicated, then the
    3467             :          * cracknames function should also be changed.
    3468             :          */
    3469             :         size_t i, j;
    3470             :         int ret;
    3471             :         bool ok;
    3472        3207 :         struct ldb_result *res = NULL;
    3473        3207 :         struct ldb_message_element *spnmappings = NULL;
    3474        3207 :         TALLOC_CTX *tmp_ctx = NULL;
    3475        3207 :         struct ldb_dn *service_dn = NULL;
    3476             : 
    3477        3207 :         const char *attrs[] = {
    3478             :                 "sPNMappings",
    3479             :                 NULL
    3480             :         };
    3481             : 
    3482        3207 :         *direction = SPN_ALIAS_NONE;
    3483             : 
    3484        3207 :         tmp_ctx = talloc_new(mem_ctx);
    3485        3207 :         if (tmp_ctx == NULL) {
    3486           0 :                 return ldb_oom(ldb);
    3487             :         }
    3488             : 
    3489        3207 :         service_dn = ldb_dn_new(
    3490             :                 tmp_ctx, ldb,
    3491             :                 "CN=Directory Service,CN=Windows NT,CN=Services");
    3492        3207 :         if (service_dn == NULL) {
    3493           0 :                 talloc_free(tmp_ctx);
    3494           0 :                 return ldb_oom(ldb);
    3495             :         }
    3496             : 
    3497        3207 :         ok = ldb_dn_add_base(service_dn, ldb_get_config_basedn(ldb));
    3498        3207 :         if (! ok) {
    3499           0 :                 talloc_free(tmp_ctx);
    3500           0 :                 return LDB_ERR_OPERATIONS_ERROR;
    3501             :         }
    3502             : 
    3503        3207 :         ret = ldb_search(ldb, tmp_ctx, &res, service_dn, LDB_SCOPE_BASE,
    3504             :                          attrs, "(objectClass=nTDSService)");
    3505             : 
    3506        3207 :         if (ret != LDB_SUCCESS || res->count != 1) {
    3507           0 :                 DBG_WARNING("sPNMappings not found.\n");
    3508           0 :                 talloc_free(tmp_ctx);
    3509           0 :                 return ret;
    3510             :         }
    3511             : 
    3512        3207 :         spnmappings = ldb_msg_find_element(res->msgs[0], "sPNMappings");
    3513        3207 :         if (spnmappings == NULL || spnmappings->num_values == 0) {
    3514           0 :                 DBG_WARNING("no sPNMappings attribute\n");
    3515           0 :                 talloc_free(tmp_ctx);
    3516           0 :                 return LDB_ERR_NO_SUCH_OBJECT;
    3517             :         }
    3518        3207 :         *n_aliases = 0;
    3519             : 
    3520        4388 :         for (i = 0; i < spnmappings->num_values; i++) {
    3521        3207 :                 char *p = NULL;
    3522        5603 :                 char *mapping = talloc_strndup(
    3523             :                         tmp_ctx,
    3524        3207 :                         (char *)spnmappings->values[i].data,
    3525        3207 :                         spnmappings->values[i].length);
    3526        3207 :                 if (mapping == NULL) {
    3527           0 :                         talloc_free(tmp_ctx);
    3528           0 :                         return ldb_oom(ldb);
    3529             :                 }
    3530             : 
    3531        3207 :                 p = strchr(mapping, '=');
    3532        3207 :                 if (p == NULL) {
    3533           0 :                         talloc_free(tmp_ctx);
    3534           0 :                         return LDB_ERR_ALIAS_PROBLEM;
    3535             :                 }
    3536        3207 :                 p[0] = '\0';
    3537        3207 :                 p++;
    3538             : 
    3539        3207 :                 if (strcasecmp(mapping, service_class) == 0) {
    3540             :                         /*
    3541             :                          * We need to return the reverse aliases for this one.
    3542             :                          *
    3543             :                          * typically, this means the service_class is "host"
    3544             :                          * and the mapping is "host=alerter,appmgmt,cisvc,..",
    3545             :                          * so we get "alerter", "appmgmt", etc in the list of
    3546             :                          * aliases.
    3547             :                          */
    3548             : 
    3549             :                         /* There is one more field than there are commas */
    3550        1947 :                         size_t n = 1;
    3551             : 
    3552      771012 :                         for (j = 0; p[j] != '\0'; j++) {
    3553      769065 :                                 if (p[j] == ',') {
    3554      101244 :                                         n++;
    3555      101244 :                                         p[j] = '\0';
    3556             :                                 }
    3557             :                         }
    3558        1947 :                         *aliases = talloc_array(mem_ctx, char*, n);
    3559        1947 :                         if (*aliases == NULL) {
    3560           0 :                                 talloc_free(tmp_ctx);
    3561           0 :                                 return ldb_oom(ldb);
    3562             :                         }
    3563        1947 :                         *n_aliases = n;
    3564        1947 :                         talloc_steal(mem_ctx, mapping);
    3565      105138 :                         for (j = 0; j < n; j++) {
    3566      103191 :                                 (*aliases)[j] = p;
    3567      103191 :                                 p += strlen(p) + 1;
    3568             :                         }
    3569        1947 :                         talloc_free(tmp_ctx);
    3570        1947 :                         *direction = SPN_ALIAS_LINK;
    3571        1947 :                         return LDB_SUCCESS;
    3572             :                 }
    3573             :                 /*
    3574             :                  * We need to look along the list to see if service_class is
    3575             :                  * there; if so, we return a list of one item (probably "host").
    3576             :                  */
    3577             :                 do {
    3578       65333 :                         char *str = p;
    3579       65333 :                         p = strchr(p, ',');
    3580       65333 :                         if (p != NULL) {
    3581       64152 :                                 p[0] = '\0';
    3582       64152 :                                 p++;
    3583             :                         }
    3584       65333 :                         if (strcasecmp(str, service_class) == 0) {
    3585          79 :                                 *aliases = talloc_array(mem_ctx, char*, 1);
    3586          79 :                                 if (*aliases == NULL) {
    3587           0 :                                         talloc_free(tmp_ctx);
    3588           0 :                                         return ldb_oom(ldb);
    3589             :                                 }
    3590          79 :                                 *n_aliases = 1;
    3591          79 :                                 (*aliases)[0] = mapping;
    3592          79 :                                 talloc_steal(mem_ctx, mapping);
    3593          79 :                                 talloc_free(tmp_ctx);
    3594          79 :                                 *direction = SPN_ALIAS_TARGET;
    3595          79 :                                 return LDB_SUCCESS;
    3596             :                         }
    3597       65254 :                 } while (p != NULL);
    3598             :         }
    3599        1181 :         DBG_INFO("no sPNMappings alias for '%s'\n", service_class);
    3600        1181 :         talloc_free(tmp_ctx);
    3601        1181 :         *aliases = NULL;
    3602        1181 :         *n_aliases = 0;
    3603        1181 :         return LDB_SUCCESS;
    3604             : }
    3605             : 
    3606             : 
    3607      106675 : static int get_spn_dn(struct ldb_context *ldb,
    3608             :                       TALLOC_CTX *tmp_ctx,
    3609             :                       const char *candidate,
    3610             :                       struct ldb_dn **dn)
    3611             : {
    3612             :         int ret;
    3613      106675 :         const char *empty_attrs[] = { NULL };
    3614      106675 :         struct ldb_message *msg = NULL;
    3615      106675 :         struct ldb_dn *base_dn = ldb_get_default_basedn(ldb);
    3616             : 
    3617      106675 :         const char *enc_candidate = NULL;
    3618             : 
    3619      106675 :         *dn = NULL;
    3620             : 
    3621      106675 :         enc_candidate = ldb_binary_encode_string(tmp_ctx, candidate);
    3622      106675 :         if (enc_candidate == NULL) {
    3623           0 :                 return ldb_operr(ldb);
    3624             :         }
    3625             : 
    3626      106675 :         ret = dsdb_search_one(ldb,
    3627             :                               tmp_ctx,
    3628             :                               &msg,
    3629             :                               base_dn,
    3630             :                               LDB_SCOPE_SUBTREE,
    3631             :                               empty_attrs,
    3632             :                               0,
    3633             :                               "(servicePrincipalName=%s)",
    3634             :                               enc_candidate);
    3635      106675 :         if (ret != LDB_SUCCESS) {
    3636      106471 :                 return ret;
    3637             :         }
    3638         204 :         *dn = msg->dn;
    3639         204 :         return LDB_SUCCESS;
    3640             : }
    3641             : 
    3642             : 
    3643           6 : static int check_spn_write_rights(struct ldb_context *ldb,
    3644             :                                   TALLOC_CTX *mem_ctx,
    3645             :                                   const char *spn,
    3646             :                                   struct ldb_dn *dn)
    3647             : {
    3648             :         int ret;
    3649           6 :         struct ldb_message *msg = NULL;
    3650           6 :         struct ldb_message_element *del_el = NULL;
    3651           6 :         struct ldb_message_element *add_el = NULL;
    3652          12 :         struct ldb_val val = {
    3653             :                 .data = discard_const_p(uint8_t, spn),
    3654           6 :                 .length = strlen(spn)
    3655             :         };
    3656             : 
    3657           6 :         msg = ldb_msg_new(mem_ctx);
    3658           6 :         if (msg == NULL) {
    3659           0 :                 return ldb_oom(ldb);
    3660             :         }
    3661           6 :         msg->dn = dn;
    3662             : 
    3663           6 :         ret = ldb_msg_add_empty(msg,
    3664             :                                 "servicePrincipalName",
    3665             :                                 LDB_FLAG_MOD_DELETE,
    3666             :                                 &del_el);
    3667           6 :         if (ret != LDB_SUCCESS) {
    3668           0 :                 talloc_free(msg);
    3669           0 :                 return ret;
    3670             :         }
    3671             : 
    3672           6 :         del_el->values = talloc_array(msg->elements, struct ldb_val, 1);
    3673           6 :         if (del_el->values == NULL) {
    3674           0 :                 talloc_free(msg);
    3675           0 :                 return ret;
    3676             :         }
    3677             : 
    3678           6 :         del_el->values[0] = val;
    3679           6 :         del_el->num_values = 1;
    3680             : 
    3681           6 :         ret = ldb_msg_add_empty(msg,
    3682             :                                 "servicePrincipalName",
    3683             :                                 LDB_FLAG_MOD_ADD,
    3684             :                                 &add_el);
    3685           6 :         if (ret != LDB_SUCCESS) {
    3686           0 :                 talloc_free(msg);
    3687           0 :                 return ret;
    3688             :         }
    3689             : 
    3690           6 :         add_el->values = talloc_array(msg->elements, struct ldb_val, 1);
    3691           6 :         if (add_el->values == NULL) {
    3692           0 :                 talloc_free(msg);
    3693           0 :                 return ret;
    3694             :         }
    3695             : 
    3696           6 :         add_el->values[0] = val;
    3697           6 :         add_el->num_values = 1;
    3698             : 
    3699           6 :         ret = ldb_modify(ldb, msg);
    3700           6 :         if (ret == LDB_ERR_NO_SUCH_ATTRIBUTE) {
    3701           0 :                 DBG_ERR("hmm I think we're OK, but not sure\n");
    3702           6 :         } else if (ret != LDB_SUCCESS) {
    3703           0 :                 DBG_ERR("SPN write rights check failed with %d\n", ret);
    3704           0 :                 talloc_free(msg);
    3705           0 :                 return ret;
    3706             :         }
    3707           6 :         talloc_free(msg);
    3708           6 :         return LDB_SUCCESS;
    3709             : }
    3710             : 
    3711             : 
    3712        3207 : static int check_spn_alias_collision(struct ldb_context *ldb,
    3713             :                                      TALLOC_CTX *mem_ctx,
    3714             :                                      const char *spn,
    3715             :                                      struct ldb_dn *target_dn)
    3716             : {
    3717             :         int ret;
    3718        3207 :         char *service_class = NULL;
    3719        3207 :         char *spn_tail = NULL;
    3720        3207 :         char *p = NULL;
    3721        3207 :         char **aliases = NULL;
    3722        3207 :         size_t n_aliases = 0;
    3723             :         size_t i, len;
    3724        3207 :         TALLOC_CTX *tmp_ctx = NULL;
    3725        3207 :         const char *target_dnstr = ldb_dn_get_linearized(target_dn);
    3726             :         int link_direction;
    3727             : 
    3728        3207 :         tmp_ctx = talloc_new(mem_ctx);
    3729        3207 :         if (tmp_ctx == NULL) {
    3730           0 :                 return ldb_oom(ldb);
    3731             :         }
    3732             : 
    3733             :         /*
    3734             :          * "dns/example.com/xxx"  gives
    3735             :          *    service_class = "dns"
    3736             :          *    spn_tail      = "example.com/xxx"
    3737             :          */
    3738        3207 :         p = strchr(spn, '/');
    3739        3207 :         if (p == NULL) {
    3740             :                 /* bad SPN */
    3741           0 :                 talloc_free(tmp_ctx);
    3742           0 :                 return ldb_error(ldb,
    3743             :                                  LDB_ERR_OPERATIONS_ERROR,
    3744             :                                  "malformed servicePrincipalName");
    3745             :         }
    3746        3207 :         len = p - spn;
    3747             : 
    3748        3207 :         service_class = talloc_strndup(tmp_ctx, spn, len);
    3749        3207 :         if (service_class == NULL) {
    3750           0 :                 talloc_free(tmp_ctx);
    3751           0 :                 return ldb_oom(ldb);
    3752             :         }
    3753        3207 :         spn_tail = p + 1;
    3754             : 
    3755        3207 :         ret = find_spn_aliases(ldb,
    3756             :                                tmp_ctx,
    3757             :                                service_class,
    3758             :                                &aliases,
    3759             :                                &n_aliases,
    3760             :                                &link_direction);
    3761        3207 :         if (ret != LDB_SUCCESS) {
    3762           0 :                 talloc_free(tmp_ctx);
    3763           0 :                 return ret;
    3764             :         }
    3765             : 
    3766             :         /*
    3767             :          * we have the list of aliases, and now we need to combined them with
    3768             :          * spn_tail and see if we can find the SPN.
    3769             :          */
    3770      106477 :         for (i = 0; i < n_aliases; i++) {
    3771      103270 :                 struct ldb_dn *colliding_dn = NULL;
    3772      103270 :                 const char *colliding_dnstr = NULL;
    3773             : 
    3774      103270 :                 char *candidate = talloc_asprintf(tmp_ctx,
    3775             :                                                   "%s/%s",
    3776      103270 :                                                   aliases[i],
    3777             :                                                   spn_tail);
    3778      103270 :                 if (candidate == NULL) {
    3779           0 :                         talloc_free(tmp_ctx);
    3780           0 :                         return ldb_oom(ldb);
    3781             :                 }
    3782             : 
    3783      103270 :                 ret = get_spn_dn(ldb, tmp_ctx, candidate, &colliding_dn);
    3784      103270 :                 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
    3785      103264 :                         DBG_DEBUG("SPN alias '%s' not found (good)\n",
    3786             :                                   candidate);
    3787      103264 :                         talloc_free(candidate);
    3788      103264 :                         continue;
    3789             :                 }
    3790           6 :                 if (ret != LDB_SUCCESS) {
    3791           0 :                         DBG_ERR("SPN '%s' search error %d\n", candidate, ret);
    3792           0 :                         talloc_free(tmp_ctx);
    3793           0 :                         return ret;
    3794             :                 }
    3795             : 
    3796           6 :                 target_dnstr = ldb_dn_get_linearized(target_dn);
    3797             :                 /*
    3798             :                  * We have found an existing SPN that matches the alias. That
    3799             :                  * is OK only if it is on the object we are trying to add to,
    3800             :                  * or if the SPN on the other side is a more generic alias for
    3801             :                  * this one and we also have rights to modify it.
    3802             :                  *
    3803             :                  * That is, we can put "host/X" and "cifs/X" on the same
    3804             :                  * object, but not on different objects, unless we put the
    3805             :                  * host/X on first, and could also change that object when we
    3806             :                  * add cifs/X. It is forbidden to add the objects in the other
    3807             :                  * order.
    3808             :                  *
    3809             :                  * The rationale for this is that adding "cifs/X" effectively
    3810             :                  * changes "host/X" by diverting traffic. If "host/X" can be
    3811             :                  * added after "cifs/X", a sneaky person could get "cifs/X" in
    3812             :                  * first, making "host/X" have less effect than intended.
    3813             :                  *
    3814             :                  * Note: we also can't have "host/X" and "Host/X" on the same
    3815             :                  * object, but that is not relevant here.
    3816             :                  */
    3817             : 
    3818           6 :                 ret = ldb_dn_compare(colliding_dn, target_dn);
    3819           6 :                 if (ret != 0) {
    3820           6 :                         colliding_dnstr = ldb_dn_get_linearized(colliding_dn);
    3821           6 :                         DBG_ERR("trying to add SPN '%s' on '%s' when '%s' is "
    3822             :                                 "on '%s'\n",
    3823             :                                 spn,
    3824             :                                 target_dnstr,
    3825             :                                 candidate,
    3826             :                                 colliding_dnstr);
    3827             : 
    3828           6 :                         if (link_direction == SPN_ALIAS_LINK) {
    3829             :                                 /* we don't allow host/X if there is a
    3830             :                                  * cifs/X */
    3831           0 :                                 talloc_free(tmp_ctx);
    3832           0 :                                 return LDB_ERR_CONSTRAINT_VIOLATION;
    3833             :                         }
    3834           6 :                         ret = check_spn_write_rights(ldb,
    3835             :                                                      tmp_ctx,
    3836             :                                                      candidate,
    3837             :                                                      colliding_dn);
    3838           6 :                         if (ret != LDB_SUCCESS) {
    3839           0 :                                 DBG_ERR("SPN '%s' is on '%s' so '%s' can't be "
    3840             :                                         "added to '%s'\n",
    3841             :                                         candidate,
    3842             :                                         colliding_dnstr,
    3843             :                                         spn,
    3844             :                                         target_dnstr);
    3845           0 :                                 talloc_free(tmp_ctx);
    3846           0 :                                 ldb_asprintf_errstring(ldb,
    3847             :                                                        "samldb: spn[%s] would cause a conflict",
    3848             :                                                        spn);
    3849           0 :                                 return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
    3850             :                         }
    3851             :                 } else {
    3852           0 :                         DBG_INFO("SPNs '%s' and '%s' alias both on '%s'\n",
    3853             :                                  candidate, spn, target_dnstr);
    3854             :                 }
    3855           6 :                 talloc_free(candidate);
    3856             :         }
    3857             : 
    3858        3207 :         talloc_free(tmp_ctx);
    3859        3207 :         return LDB_SUCCESS;
    3860             : }
    3861             : 
    3862        3405 : static int check_spn_direct_collision(struct ldb_context *ldb,
    3863             :                                       TALLOC_CTX *mem_ctx,
    3864             :                                       const char *spn,
    3865             :                                       struct ldb_dn *target_dn)
    3866             : {
    3867             :         int ret;
    3868        3405 :         TALLOC_CTX *tmp_ctx = NULL;
    3869        3405 :         struct ldb_dn *colliding_dn = NULL;
    3870        3405 :         const char *target_dnstr = NULL;
    3871        3405 :         const char *colliding_dnstr = NULL;
    3872             : 
    3873        3405 :         tmp_ctx = talloc_new(mem_ctx);
    3874        3405 :         if (tmp_ctx == NULL) {
    3875           0 :                 return ldb_oom(ldb);
    3876             :         }
    3877             : 
    3878        3405 :         ret = get_spn_dn(ldb, tmp_ctx, spn, &colliding_dn);
    3879        3405 :         if (ret == LDB_ERR_NO_SUCH_OBJECT) {
    3880        3207 :                 DBG_DEBUG("SPN '%s' not found (good)\n", spn);
    3881        3207 :                 talloc_free(tmp_ctx);
    3882        3207 :                 return LDB_SUCCESS;
    3883             :         }
    3884         198 :         if (ret != LDB_SUCCESS) {
    3885           0 :                 DBG_ERR("SPN '%s' search error %d\n", spn, ret);
    3886           0 :                 talloc_free(tmp_ctx);
    3887           0 :                 if (ret == LDB_ERR_COMPARE_TRUE) {
    3888             :                         /*
    3889             :                          * COMPARE_TRUE has special meaning here and we don't
    3890             :                          * want to return it by mistake.
    3891             :                          */
    3892           0 :                         ret = LDB_ERR_OPERATIONS_ERROR;
    3893             :                 }
    3894           0 :                 return ret;
    3895             :         }
    3896             :         /*
    3897             :          * We have found this exact SPN. This is mostly harmless (depend on
    3898             :          * ADD vs REPLACE) when the spn is being put on the object that
    3899             :          * already has, so we let it through to succeed or fail as some other
    3900             :          * module sees fit.
    3901             :          */
    3902         198 :         target_dnstr = ldb_dn_get_linearized(target_dn);
    3903         198 :         ret = ldb_dn_compare(colliding_dn, target_dn);
    3904         198 :         if (ret != 0) {
    3905           3 :                 colliding_dnstr = ldb_dn_get_linearized(colliding_dn);
    3906           3 :                 DBG_ERR("SPN '%s' is on '%s' so it can't be "
    3907             :                         "added to '%s'\n",
    3908             :                         spn,
    3909             :                         colliding_dnstr,
    3910             :                         target_dnstr);
    3911           3 :                 ldb_asprintf_errstring(ldb,
    3912             :                                        "samldb: spn[%s] would cause a conflict",
    3913             :                                        spn);
    3914           3 :                 talloc_free(tmp_ctx);
    3915           3 :                 return LDB_ERR_CONSTRAINT_VIOLATION;
    3916             :         }
    3917             : 
    3918         195 :         DBG_INFO("SPN '%s' is already on '%s'\n",
    3919             :                  spn, target_dnstr);
    3920         195 :         talloc_free(tmp_ctx);
    3921         195 :         return LDB_ERR_COMPARE_TRUE;
    3922             : }
    3923             : 
    3924             : 
    3925        3405 : static int count_spn_components(struct ldb_val val)
    3926             : {
    3927             :         /*
    3928             :          * a 3 part servicePrincipalName has two slashes, like
    3929             :          * ldap/example.com/DomainDNSZones.example.com.
    3930             :          *
    3931             :          * In krb5_parse_name_flags() we don't count "\/" as a slash (i.e.
    3932             :          * escaped by a backslash), but this is not the behaviour of Windows
    3933             :          * on setting a servicePrincipalName -- slashes are counted regardless
    3934             :          * of backslashes.
    3935             :          *
    3936             :          * Accordingly, here we ignore backslashes. This will reject
    3937             :          * multi-slash SPNs that krb5_parse_name_flags() would accept, and
    3938             :          * allow ones in the form "a\/b" that it won't parse.
    3939             :          */
    3940             :         size_t i;
    3941        3405 :         int slashes = 0;
    3942      125640 :         for (i = 0; i < val.length; i++) {
    3943      122235 :                 char c = val.data[i];
    3944      122235 :                 if (c == '/') {
    3945        4450 :                         slashes++;
    3946        4450 :                         if (slashes == 3) {
    3947             :                                 /* at this point we don't care */
    3948           0 :                                 return 4;
    3949             :                         }
    3950             :                 }
    3951             :         }
    3952        3405 :         return slashes + 1;
    3953             : }
    3954             : 
    3955             : 
    3956             : /* Check that "servicePrincipalName" changes do not introduce a collision
    3957             :  * globally. */
    3958        1802 : static int samldb_spn_uniqueness_check(struct samldb_ctx *ac,
    3959             :                                        struct ldb_message_element *spn_el)
    3960             : {
    3961        1802 :         struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
    3962             :         int ret;
    3963        1802 :         const char *spn = NULL;
    3964             :         size_t i;
    3965        1802 :         TALLOC_CTX *tmp_ctx = talloc_new(ac->msg);
    3966        1802 :         if (tmp_ctx == NULL) {
    3967           0 :                 return ldb_oom(ldb);
    3968             :         }
    3969             : 
    3970        5204 :         for (i = 0; i < spn_el->num_values; i++) {
    3971             :                 int n_components;
    3972        3405 :                 spn = (char *)spn_el->values[i].data;
    3973             : 
    3974        3405 :                 n_components = count_spn_components(spn_el->values[i]);
    3975        3405 :                 if (n_components > 3 || n_components < 2) {
    3976           0 :                         ldb_asprintf_errstring(ldb,
    3977             :                                                "samldb: spn[%s] invalid with %u components",
    3978             :                                                spn, n_components);
    3979           0 :                         talloc_free(tmp_ctx);
    3980           0 :                         return LDB_ERR_CONSTRAINT_VIOLATION;
    3981             :                 }
    3982             : 
    3983        3405 :                 ret = check_spn_direct_collision(ldb,
    3984             :                                                  tmp_ctx,
    3985             :                                                  spn,
    3986        3405 :                                                  ac->msg->dn);
    3987        3405 :                 if (ret == LDB_ERR_COMPARE_TRUE) {
    3988         195 :                         DBG_INFO("SPN %s re-added to the same object\n", spn);
    3989         195 :                         continue;
    3990             :                 }
    3991        3210 :                 if (ret != LDB_SUCCESS) {
    3992           3 :                         DBG_ERR("SPN %s failed direct uniqueness check\n", spn);
    3993           3 :                         talloc_free(tmp_ctx);
    3994           3 :                         return ret;
    3995             :                 }
    3996             : 
    3997        3207 :                 ret = check_spn_alias_collision(ldb,
    3998             :                                                 tmp_ctx,
    3999             :                                                 spn,
    4000        3207 :                                                 ac->msg->dn);
    4001             : 
    4002        3207 :                 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
    4003             :                         /* we have no sPNMappings, hence no aliases */
    4004           0 :                         break;
    4005             :                 }
    4006        3207 :                 if (ret != LDB_SUCCESS) {
    4007           0 :                         DBG_ERR("SPN %s failed alias uniqueness check\n", spn);
    4008           0 :                         talloc_free(tmp_ctx);
    4009           0 :                         return ret;
    4010             :                 }
    4011        3207 :                 DBG_INFO("SPN %s seems to be unique\n", spn);
    4012             :         }
    4013             : 
    4014        1799 :         talloc_free(tmp_ctx);
    4015        1799 :         return LDB_SUCCESS;
    4016             : }
    4017             : 
    4018             : 
    4019             : 
    4020             : /* This trigger adapts the "servicePrincipalName" attributes if the
    4021             :  * "dNSHostName" and/or "sAMAccountName" attribute change(s) */
    4022        1155 : static int samldb_service_principal_names_change(struct samldb_ctx *ac)
    4023             : {
    4024        1155 :         struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
    4025        1155 :         struct ldb_message_element *el = NULL, *el2 = NULL;
    4026             :         struct ldb_message *msg;
    4027        1155 :         const char * const attrs[] = { "servicePrincipalName", NULL };
    4028             :         struct ldb_result *res;
    4029        1155 :         const char *dns_hostname = NULL, *old_dns_hostname = NULL,
    4030        1155 :                    *sam_accountname = NULL, *old_sam_accountname = NULL;
    4031             :         unsigned int i, j;
    4032             :         int ret;
    4033             : 
    4034        2029 :         ret = dsdb_get_expected_new_values(ac,
    4035        1155 :                                            ac->msg,
    4036             :                                            "dNSHostName",
    4037             :                                            &el,
    4038        1155 :                                            ac->req->operation);
    4039        1155 :         if (ret != LDB_SUCCESS) {
    4040           0 :                 return ret;
    4041             :         }
    4042        2029 :         ret = dsdb_get_expected_new_values(ac,
    4043        1155 :                                            ac->msg,
    4044             :                                            "sAMAccountName",
    4045             :                                            &el2,
    4046        1155 :                                            ac->req->operation);
    4047        1155 :         if (ret != LDB_SUCCESS) {
    4048           0 :                 return ret;
    4049             :         }
    4050        1155 :         if ((el == NULL) && (el2 == NULL)) {
    4051             :                 /* we are not affected */
    4052           3 :                 return LDB_SUCCESS;
    4053             :         }
    4054             : 
    4055             :         /* Create a temporary message for fetching the "dNSHostName" */
    4056        1152 :         if (el != NULL) {
    4057         462 :                 const char *dns_attrs[] = { "dNSHostName", NULL };
    4058         462 :                 msg = ldb_msg_new(ac->msg);
    4059         462 :                 if (msg == NULL) {
    4060           0 :                         return ldb_module_oom(ac->module);
    4061             :                 }
    4062         462 :                 ret = ldb_msg_add(msg, el, 0);
    4063         462 :                 if (ret != LDB_SUCCESS) {
    4064           0 :                         return ret;
    4065             :                 }
    4066         462 :                 dns_hostname = talloc_strdup(ac,
    4067             :                                              ldb_msg_find_attr_as_string(msg, "dNSHostName", NULL));
    4068         462 :                 if (dns_hostname == NULL) {
    4069           0 :                         return ldb_module_oom(ac->module);
    4070             :                 }
    4071             : 
    4072         462 :                 talloc_free(msg);
    4073             : 
    4074         462 :                 ret = dsdb_module_search_dn(ac->module, ac, &res, ac->msg->dn,
    4075             :                                             dns_attrs, DSDB_FLAG_NEXT_MODULE, ac->req);
    4076         462 :                 if (ret == LDB_SUCCESS) {
    4077         462 :                         old_dns_hostname = ldb_msg_find_attr_as_string(res->msgs[0], "dNSHostName", NULL);
    4078             :                 }
    4079             :         }
    4080             : 
    4081             :         /* Create a temporary message for fetching the "sAMAccountName" */
    4082        1152 :         if (el2 != NULL) {
    4083         710 :                 char *tempstr, *tempstr2 = NULL;
    4084         710 :                 const char *acct_attrs[] = { "sAMAccountName", NULL };
    4085             : 
    4086         710 :                 msg = ldb_msg_new(ac->msg);
    4087         710 :                 if (msg == NULL) {
    4088           0 :                         return ldb_module_oom(ac->module);
    4089             :                 }
    4090         710 :                 ret = ldb_msg_add(msg, el2, 0);
    4091         710 :                 if (ret != LDB_SUCCESS) {
    4092           0 :                         return ret;
    4093             :                 }
    4094         710 :                 tempstr = talloc_strdup(ac,
    4095             :                                         ldb_msg_find_attr_as_string(msg, "sAMAccountName", NULL));
    4096         710 :                 talloc_free(msg);
    4097             : 
    4098         710 :                 ret = dsdb_module_search_dn(ac->module, ac, &res, ac->msg->dn, acct_attrs,
    4099             :                                             DSDB_FLAG_NEXT_MODULE, ac->req);
    4100         710 :                 if (ret == LDB_SUCCESS) {
    4101         710 :                         tempstr2 = talloc_strdup(ac,
    4102         710 :                                                  ldb_msg_find_attr_as_string(res->msgs[0],
    4103             :                                                                              "sAMAccountName", NULL));
    4104             :                 }
    4105             : 
    4106             : 
    4107             :                 /* The "sAMAccountName" needs some additional trimming: we need
    4108             :                  * to remove the trailing "$"s if they exist. */
    4109        1224 :                 if ((tempstr != NULL) && (tempstr[0] != '\0') &&
    4110         710 :                     (tempstr[strlen(tempstr) - 1] == '$')) {
    4111         182 :                         tempstr[strlen(tempstr) - 1] = '\0';
    4112             :                 }
    4113        1224 :                 if ((tempstr2 != NULL) && (tempstr2[0] != '\0') &&
    4114         710 :                     (tempstr2[strlen(tempstr2) - 1] == '$')) {
    4115         221 :                         tempstr2[strlen(tempstr2) - 1] = '\0';
    4116             :                 }
    4117         710 :                 sam_accountname = tempstr;
    4118         710 :                 old_sam_accountname = tempstr2;
    4119             :         }
    4120             : 
    4121        1152 :         if (old_dns_hostname == NULL) {
    4122             :                 /* we cannot change when the old name is unknown */
    4123        1094 :                 dns_hostname = NULL;
    4124             :         }
    4125        1210 :         if ((old_dns_hostname != NULL) && (dns_hostname != NULL) &&
    4126          58 :             (strcasecmp_m(old_dns_hostname, dns_hostname) == 0)) {
    4127             :                 /* The "dNSHostName" didn't change */
    4128          21 :                 dns_hostname = NULL;
    4129             :         }
    4130             : 
    4131        1152 :         if (old_sam_accountname == NULL) {
    4132             :                 /* we cannot change when the old name is unknown */
    4133         442 :                 sam_accountname = NULL;
    4134             :         }
    4135        1862 :         if ((old_sam_accountname != NULL) && (sam_accountname != NULL) &&
    4136         710 :             (strcasecmp_m(old_sam_accountname, sam_accountname) == 0)) {
    4137             :                 /* The "sAMAccountName" didn't change */
    4138         433 :                 sam_accountname = NULL;
    4139             :         }
    4140             : 
    4141        1152 :         if ((dns_hostname == NULL) && (sam_accountname == NULL)) {
    4142             :                 /* Well, there are information missing (old name(s)) or the
    4143             :                  * names didn't change. We've nothing to do and can exit here */
    4144         844 :                 return LDB_SUCCESS;
    4145             :         }
    4146             : 
    4147             :         /*
    4148             :          * Potential "servicePrincipalName" changes in the same request have
    4149             :          * to be handled before the update (Windows behaviour).
    4150             :          *
    4151             :          * We extract the SPN changes into a new message and run it through
    4152             :          * the stack from this module, so that it subjects them to the SPN
    4153             :          * checks we have here.
    4154             :          */
    4155         308 :         el = ldb_msg_find_element(ac->msg, "servicePrincipalName");
    4156         308 :         if (el != NULL) {
    4157          30 :                 msg = ldb_msg_new(ac->msg);
    4158          30 :                 if (msg == NULL) {
    4159           0 :                         return ldb_module_oom(ac->module);
    4160             :                 }
    4161          30 :                 msg->dn = ac->msg->dn;
    4162             : 
    4163             :                 do {
    4164          30 :                         ret = ldb_msg_add(msg, el, el->flags);
    4165          30 :                         if (ret != LDB_SUCCESS) {
    4166           0 :                                 return ret;
    4167             :                         }
    4168             : 
    4169          30 :                         ldb_msg_remove_element(ac->msg, el);
    4170             : 
    4171          30 :                         el = ldb_msg_find_element(ac->msg,
    4172             :                                                   "servicePrincipalName");
    4173          30 :                 } while (el != NULL);
    4174             : 
    4175          30 :                 ret = dsdb_module_modify(ac->module, msg,
    4176             :                                          DSDB_FLAG_OWN_MODULE, ac->req);
    4177          30 :                 if (ret != LDB_SUCCESS) {
    4178           0 :                         return ret;
    4179             :                 }
    4180          30 :                 talloc_free(msg);
    4181             :         }
    4182             : 
    4183             :         /* Fetch the "servicePrincipalName"s if any */
    4184         308 :         ret = dsdb_module_search(ac->module, ac, &res, ac->msg->dn, LDB_SCOPE_BASE, attrs,
    4185             :                                  DSDB_FLAG_NEXT_MODULE, ac->req, NULL);
    4186         308 :         if (ret != LDB_SUCCESS) {
    4187           0 :                 return ret;
    4188             :         }
    4189         308 :         if ((res->count != 1) || (res->msgs[0]->num_elements > 1)) {
    4190           0 :                 return ldb_operr(ldb);
    4191             :         }
    4192             : 
    4193         308 :         if (res->msgs[0]->num_elements == 1) {
    4194             :                 /*
    4195             :                  * Yes, we do have "servicePrincipalName"s. First we update them
    4196             :                  * locally, that means we do always substitute the current
    4197             :                  * "dNSHostName" with the new one and/or "sAMAccountName"
    4198             :                  * without "$" with the new one and then we append the
    4199             :                  * modified "servicePrincipalName"s as a message element
    4200             :                  * replace to the modification request (Windows behaviour). We
    4201             :                  * need also to make sure that the values remain case-
    4202             :                  * insensitively unique.
    4203             :                  */
    4204             : 
    4205          55 :                 ret = ldb_msg_add_empty(ac->msg, "servicePrincipalName",
    4206             :                                         LDB_FLAG_MOD_REPLACE, &el);
    4207          55 :                 if (ret != LDB_SUCCESS) {
    4208           0 :                         return ret;
    4209             :                 }
    4210             : 
    4211         147 :                 for (i = 0; i < res->msgs[0]->elements[0].num_values; i++) {
    4212             :                         char *old_str, *new_str;
    4213          92 :                         char *pos = NULL;
    4214             :                         const char *tok;
    4215             :                         struct ldb_val *vals;
    4216          92 :                         bool found = false;
    4217             : 
    4218          92 :                         old_str = (char *)
    4219          92 :                                 res->msgs[0]->elements[0].values[i].data;
    4220             : 
    4221          92 :                         new_str = talloc_strdup(ac->msg,
    4222          92 :                                                 strtok_r(old_str, "/", &pos));
    4223          92 :                         if (new_str == NULL) {
    4224           0 :                                 return ldb_module_oom(ac->module);
    4225             :                         }
    4226             : 
    4227         251 :                         while ((tok = strtok_r(NULL, "/", &pos)) != NULL) {
    4228         163 :                                 if ((dns_hostname != NULL) &&
    4229          71 :                                     (strcasecmp_m(tok, old_dns_hostname) == 0)) {
    4230          35 :                                         tok = dns_hostname;
    4231             :                                 }
    4232         125 :                                 if ((sam_accountname != NULL) &&
    4233          33 :                                     (strcasecmp_m(tok, old_sam_accountname) == 0)) {
    4234          15 :                                         tok = sam_accountname;
    4235             :                                 }
    4236             : 
    4237          92 :                                 new_str = talloc_asprintf(ac->msg, "%s/%s",
    4238             :                                                           new_str, tok);
    4239          92 :                                 if (new_str == NULL) {
    4240           0 :                                         return ldb_module_oom(ac->module);
    4241             :                                 }
    4242             :                         }
    4243             : 
    4244             :                         /* Uniqueness check */
    4245         138 :                         for (j = 0; (!found) && (j < el->num_values); j++) {
    4246          46 :                                 if (strcasecmp_m((char *)el->values[j].data,
    4247             :                                                new_str) == 0) {
    4248          15 :                                         found = true;
    4249             :                                 }
    4250             :                         }
    4251          92 :                         if (found) {
    4252          15 :                                 continue;
    4253             :                         }
    4254             : 
    4255             :                         /*
    4256             :                          * append the new "servicePrincipalName" -
    4257             :                          * code derived from ldb_msg_add_value().
    4258             :                          *
    4259             :                          * Open coded to make it clear that we must
    4260             :                          * append to the MOD_REPLACE el created above.
    4261             :                          */
    4262          77 :                         vals = talloc_realloc(ac->msg, el->values,
    4263             :                                               struct ldb_val,
    4264             :                                               el->num_values + 1);
    4265          77 :                         if (vals == NULL) {
    4266           0 :                                 return ldb_module_oom(ac->module);
    4267             :                         }
    4268          77 :                         el->values = vals;
    4269          77 :                         el->values[el->num_values] = data_blob_string_const(new_str);
    4270          77 :                         ++(el->num_values);
    4271             :                 }
    4272             :         }
    4273             : 
    4274         308 :         talloc_free(res);
    4275             : 
    4276         308 :         return LDB_SUCCESS;
    4277             : }
    4278             : 
    4279             : /* This checks the "fSMORoleOwner" attributes */
    4280         862 : static int samldb_fsmo_role_owner_check(struct samldb_ctx *ac)
    4281             : {
    4282         862 :         struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
    4283         862 :         const char * const no_attrs[] = { NULL };
    4284             :         struct ldb_message_element *el;
    4285             :         struct ldb_message *tmp_msg;
    4286             :         struct ldb_dn *res_dn;
    4287             :         struct ldb_result *res;
    4288             :         int ret;
    4289        1620 :         ret = dsdb_get_expected_new_values(ac,
    4290         862 :                                            ac->msg,
    4291             :                                            "fSMORoleOwner",
    4292             :                                            &el,
    4293         862 :                                            ac->req->operation);
    4294         862 :         if (ret != LDB_SUCCESS) {
    4295           0 :                 return ret;
    4296             :         }
    4297             : 
    4298         862 :         if (el == NULL) {
    4299             :                 /* we are not affected */
    4300           3 :                 return LDB_SUCCESS;
    4301             :         }
    4302         859 :         if (el->num_values != 1) {
    4303           6 :                 goto choose_error_code;
    4304             :         }
    4305             : 
    4306             :         /* Create a temporary message for fetching the "fSMORoleOwner" */
    4307         853 :         tmp_msg = ldb_msg_new(ac->msg);
    4308         853 :         if (tmp_msg == NULL) {
    4309           0 :                 return ldb_module_oom(ac->module);
    4310             :         }
    4311         853 :         ret = ldb_msg_add(tmp_msg, el, 0);
    4312         853 :         if (ret != LDB_SUCCESS) {
    4313           0 :                 return ret;
    4314             :         }
    4315         853 :         res_dn = ldb_msg_find_attr_as_dn(ldb, ac, tmp_msg, "fSMORoleOwner");
    4316         853 :         talloc_free(tmp_msg);
    4317             : 
    4318         853 :         if (res_dn == NULL) {
    4319           0 :                 ldb_set_errstring(ldb,
    4320             :                                   "samldb: 'fSMORoleOwner' attributes have to reference 'nTDSDSA' entries!");
    4321           0 :                 goto choose_error_code;
    4322             :         }
    4323             : 
    4324             :         /* Fetched DN has to reference a "nTDSDSA" entry */
    4325         853 :         ret = dsdb_module_search(ac->module, ac, &res, res_dn, LDB_SCOPE_BASE,
    4326             :                                  no_attrs,
    4327             :                                  DSDB_FLAG_NEXT_MODULE | DSDB_SEARCH_SHOW_DELETED,
    4328             :                                  ac->req, "(objectClass=nTDSDSA)");
    4329         853 :         if (ret != LDB_SUCCESS) {
    4330           0 :                 return ret;
    4331             :         }
    4332         853 :         if (res->count != 1) {
    4333           6 :                 ldb_set_errstring(ldb,
    4334             :                                   "samldb: 'fSMORoleOwner' attributes have to reference 'nTDSDSA' entries!");
    4335           6 :                 return LDB_ERR_UNWILLING_TO_PERFORM;
    4336             :         }
    4337             : 
    4338         847 :         talloc_free(res);
    4339             : 
    4340         847 :         return LDB_SUCCESS;
    4341             : 
    4342           6 : choose_error_code:
    4343             :         /* this is just how it is */
    4344           6 :         if (ac->req->operation == LDB_ADD) {
    4345           3 :                 return LDB_ERR_CONSTRAINT_VIOLATION;
    4346             :         } else {
    4347           3 :                 return LDB_ERR_UNWILLING_TO_PERFORM;
    4348             :         }
    4349             : }
    4350             : 
    4351             : /*
    4352             :  * Return zero if the number of zero bits in the address (looking from low to
    4353             :  * high) is equal to or greater than the length minus the mask. Otherwise it
    4354             :  * returns -1.
    4355             :  */
    4356         138 : static int check_cidr_zero_bits(uint8_t *address, unsigned int len,
    4357             :                                 unsigned int mask)
    4358             : {
    4359             :         /* <address> is an integer in big-endian form, <len> bits long. All
    4360             :            bits between <mask> and <len> must be zero. */
    4361             :         int i;
    4362             :         unsigned int byte_len;
    4363             :         unsigned int byte_mask;
    4364             :         unsigned int bit_mask;
    4365         138 :         if (len == 32) {
    4366          58 :                 DBG_INFO("Looking at address %02x%02x%02x%02x, mask %u\n",
    4367             :                          address[0], address[1], address[2], address[3],
    4368             :                           mask);
    4369          80 :         } else if (len == 128){
    4370          80 :                 DBG_INFO("Looking at address "
    4371             :                          "%02x%02x-%02x%02x-%02x%02x-%02x%02x-"
    4372             :                          "%02x%02x-%02x%02x-%02x%02x-%02x%02x, mask %u\n",
    4373             :                          address[0], address[1], address[2], address[3],
    4374             :                          address[4], address[5], address[6], address[7],
    4375             :                          address[8], address[9], address[10], address[11],
    4376             :                          address[12], address[13], address[14], address[15],
    4377             :                          mask);
    4378             :         }
    4379             : 
    4380         138 :         if (mask > len){
    4381           5 :                 DBG_INFO("mask %u is too big (> %u)\n", mask, len);
    4382           5 :                 return -1;
    4383             :         }
    4384         133 :         if (mask == len){
    4385             :                 /* single address subnet.
    4386             :                  * In IPv4 all 255s is invalid by the bitmask != address rule
    4387             :                  * in MS-ADTS. IPv6 does not suffer.
    4388             :                  */
    4389          10 :                 if (len == 32){
    4390           6 :                         if (address[0] == 255 &&
    4391           2 :                             address[1] == 255 &&
    4392           2 :                             address[2] == 255 &&
    4393           1 :                             address[3] == 255){
    4394           1 :                                 return -1;
    4395             :                         }
    4396             :                 }
    4397           9 :                 return 0;
    4398             :         }
    4399             : 
    4400         123 :         byte_len = len / 8;
    4401         123 :         byte_mask = mask / 8;
    4402             : 
    4403         714 :         for (i = byte_len - 1; i > byte_mask; i--){
    4404         593 :                 DBG_DEBUG("checking byte %d %02x\n", i, address[i]);
    4405         593 :                 if (address[i] != 0){
    4406           2 :                         return -1;
    4407             :                 }
    4408             :         }
    4409         121 :         bit_mask = (1 << (8 - (mask & 7))) - 1;
    4410         121 :         DBG_DEBUG("checking bitmask %02x & %02x overlap %02x\n", bit_mask, address[byte_mask],
    4411             :                   bit_mask & address[byte_mask]);
    4412         121 :         if (address[byte_mask] & bit_mask){
    4413          15 :                 return -1;
    4414             :         }
    4415             : 
    4416             :         /* According to MS-ADTS, the mask can't exactly equal the bitmask for
    4417             :          * IPv4 (but this is fine for v6). That is 255.255.80.0/17 is bad,
    4418             :          * because the bitmask implied by "/17" is 255.255.80.0.
    4419             :          *
    4420             :          * The bit_mask used in the previous check is the complement of what
    4421             :          * we want here.
    4422             :          */
    4423         106 :         if (len == 32 && address[byte_mask] == (uint8_t)~bit_mask){
    4424          35 :                 bool ok = false;
    4425          39 :                 for (i = 0; i < byte_mask; i++){
    4426          35 :                         if (address[i] != 255){
    4427          31 :                                 ok = true;
    4428          31 :                                 break;
    4429             :                         }
    4430             :                 }
    4431          35 :                 if (ok == false){
    4432           4 :                         return -1;
    4433             :                 }
    4434             :         }
    4435         102 :         return 0;
    4436             : }
    4437             : 
    4438             : 
    4439             : 
    4440         163 : static int check_address_roundtrip(const char *address, int family,
    4441             :                                    const uint8_t *address_bytes,
    4442             :                                    char *buffer, int buffer_len)
    4443             : {
    4444             :         /*
    4445             :          * Check that the address is in the canonical RFC5952 format for IPv6,
    4446             :          * and lacks extra leading zeros for each dotted decimal for IPv4.
    4447             :          * Handily this is what inet_ntop() gives you.
    4448             :          */
    4449         163 :         const char *address_redux = inet_ntop(family, address_bytes,
    4450             :                                               buffer, buffer_len);
    4451         163 :         if (address_redux == NULL){
    4452           0 :                 DBG_INFO("Address round trip %s failed unexpectedly"
    4453             :                          " with errno %d\n", address, errno);
    4454           0 :                 return -1;
    4455             :         }
    4456         163 :         if (strcasecmp(address, address_redux) != 0){
    4457          25 :                 DBG_INFO("Address %s round trips to %s; fail!\n",
    4458             :                          address, address_redux);
    4459             :                 /* If the address family is IPv6, and the address is in a
    4460             :                    certain range
    4461             : 
    4462             :                  */
    4463          25 :                 if (strchr(address_redux, '.') != NULL){
    4464           7 :                         DEBUG(0, ("The IPv6 address '%s' has the misfortune of "
    4465             :                                   "lying in a range that was once used for "
    4466             :                                   "IPv4 embedding (that is, it might also be "
    4467             :                                   "represented as '%s').\n", address,
    4468             :                                   address_redux));
    4469             :                 }
    4470          25 :                 return -1;
    4471             :         }
    4472         138 :         return 0;
    4473             : }
    4474             : 
    4475             : 
    4476             : 
    4477             : /*
    4478             :  * MS-ADTS v20150630 6.1.1.2.2.2.1 Subnet Object, refers to RFC1166 and
    4479             :  * RFC2373. It specifies something seemingly indistinguishable from an RFC4632
    4480             :  * CIDR address range without saying so explicitly. Here we follow the CIDR
    4481             :  * spec.
    4482             :  *
    4483             :  * Return 0 on success, -1 on error.
    4484             :  */
    4485         208 : static int verify_cidr(const char *cidr)
    4486             : {
    4487         208 :         char *address = NULL, *slash = NULL;
    4488             :         bool has_colon, has_dot;
    4489             :         int res, ret;
    4490             :         unsigned long mask;
    4491         208 :         uint8_t *address_bytes = NULL;
    4492         208 :         char *address_redux = NULL;
    4493             :         unsigned int address_len;
    4494         208 :         TALLOC_CTX *frame = NULL;
    4495         208 :         int error = 0;
    4496             : 
    4497         208 :         DBG_DEBUG("CIDR is %s\n", cidr);
    4498         208 :         frame = talloc_stackframe();
    4499         208 :         address = talloc_strdup(frame, cidr);
    4500         208 :         if (address == NULL){
    4501           0 :                 goto error;
    4502             :         }
    4503             : 
    4504             :         /* there must be a '/' */
    4505         208 :         slash = strchr(address, '/');
    4506         208 :         if (slash == NULL){
    4507           2 :                 goto error;
    4508             :         }
    4509             :         /* terminate the address for strchr, inet_pton */
    4510         206 :         *slash = '\0';
    4511             : 
    4512         206 :         mask = smb_strtoul(slash + 1, NULL, 10, &error, SMB_STR_FULL_STR_CONV);
    4513         206 :         if (mask == 0){
    4514           6 :                 DBG_INFO("Windows does not like the zero mask, "
    4515             :                          "so nor do we: %s\n", cidr);
    4516           6 :                 goto error;
    4517             :         }
    4518             : 
    4519         200 :         if (error != 0){
    4520           6 :                 DBG_INFO("CIDR mask is not a proper integer: %s\n", cidr);
    4521           6 :                 goto error;
    4522             :         }
    4523             : 
    4524         194 :         address_bytes = talloc_size(frame, sizeof(struct in6_addr));
    4525         194 :         if (address_bytes == NULL){
    4526           0 :                 goto error;
    4527             :         }
    4528             : 
    4529         194 :         address_redux = talloc_size(frame, INET6_ADDRSTRLEN);
    4530         194 :         if (address_redux == NULL){
    4531           0 :                 goto error;
    4532             :         }
    4533             : 
    4534         194 :         DBG_INFO("found address %s, mask %lu\n", address, mask);
    4535         194 :         has_colon = (strchr(address, ':') == NULL) ? false : true;
    4536         194 :         has_dot = (strchr(address, '.') == NULL) ? false : true;
    4537         194 :         if (has_dot && has_colon){
    4538             :                 /* This seems to be an IPv4 address embedded in IPv6, which is
    4539             :                    icky. We don't support it. */
    4540           2 :                 DBG_INFO("Refusing to consider cidr '%s' with dots and colons\n",
    4541             :                           cidr);
    4542           2 :                 goto error;
    4543         192 :         } else if (has_colon){  /* looks like IPv6 */
    4544         115 :                 res = inet_pton(AF_INET6, address, address_bytes);
    4545         115 :                 if (res != 1) {
    4546          10 :                         DBG_INFO("Address in %s fails to parse as IPv6\n", cidr);
    4547          10 :                         goto error;
    4548             :                 }
    4549         105 :                 address_len = 128;
    4550         105 :                 if (check_address_roundtrip(address, AF_INET6, address_bytes,
    4551             :                                             address_redux, INET6_ADDRSTRLEN)){
    4552          25 :                         goto error;
    4553             :                 }
    4554          77 :         } else if (has_dot) {
    4555             :                 /* looks like IPv4 */
    4556          77 :                 if (strcmp(address, "0.0.0.0") == 0){
    4557           1 :                         DBG_INFO("Windows does not like the zero IPv4 address, "
    4558             :                                  "so nor do we.\n");
    4559           1 :                         goto error;
    4560             :                 }
    4561          76 :                 res = inet_pton(AF_INET, address, address_bytes);
    4562          76 :                 if (res != 1) {
    4563          18 :                         DBG_INFO("Address in %s fails to parse as IPv4\n", cidr);
    4564          18 :                         goto error;
    4565             :                 }
    4566          58 :                 address_len = 32;
    4567             : 
    4568          58 :                 if (check_address_roundtrip(address, AF_INET, address_bytes,
    4569             :                                             address_redux, INET_ADDRSTRLEN)){
    4570           0 :                         goto error;
    4571             :                 }
    4572             :         } else {
    4573             :                 /* This doesn't look like an IP address at all. */
    4574           0 :                 goto error;
    4575             :         }
    4576             : 
    4577         138 :         ret = check_cidr_zero_bits(address_bytes, address_len, mask);
    4578         138 :         talloc_free(frame);
    4579         138 :         return ret;
    4580          70 :   error:
    4581          70 :         talloc_free(frame);
    4582          70 :         return -1;
    4583             : }
    4584             : 
    4585             : 
    4586         208 : static int samldb_verify_subnet(struct samldb_ctx *ac, struct ldb_dn *dn)
    4587             : {
    4588         208 :         struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
    4589         208 :         const char *cidr = NULL;
    4590         208 :         const struct ldb_val *rdn_value = NULL;
    4591             : 
    4592         208 :         rdn_value = ldb_dn_get_rdn_val(dn);
    4593         208 :         if (rdn_value == NULL) {
    4594           0 :                 ldb_set_errstring(ldb, "samldb: ldb_dn_get_rdn_val "
    4595             :                                   "failed");
    4596           0 :                 return LDB_ERR_UNWILLING_TO_PERFORM;
    4597             :         }
    4598             : 
    4599         208 :         cidr = ldb_dn_escape_value(ac, *rdn_value);
    4600         208 :         DBG_INFO("looking at cidr '%s'\n", cidr);
    4601         208 :         if (cidr == NULL) {
    4602           0 :                 ldb_set_errstring(ldb,
    4603             :                                   "samldb: adding an empty subnet cidr seems wrong");
    4604           0 :                 return LDB_ERR_UNWILLING_TO_PERFORM;
    4605             :         }
    4606             : 
    4607         208 :         if (verify_cidr(cidr)){
    4608          97 :                 ldb_set_errstring(ldb,
    4609             :                                   "samldb: subnet value is invalid");
    4610          97 :                 return LDB_ERR_INVALID_DN_SYNTAX;
    4611             :         }
    4612             : 
    4613         111 :         return LDB_SUCCESS;
    4614             : }
    4615             : 
    4616      367623 : static char *refer_if_rodc(struct ldb_context *ldb, struct ldb_request *req,
    4617             :                            struct ldb_dn *dn)
    4618             : {
    4619      367623 :         bool rodc = false;
    4620             :         struct loadparm_context *lp_ctx;
    4621             :         char *referral;
    4622             :         int ret;
    4623             :         WERROR err;
    4624             : 
    4625      735246 :         if (ldb_request_get_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID) ||
    4626      367623 :             ldb_request_get_control(req, DSDB_CONTROL_DBCHECK_MODIFY_RO_REPLICA)) {
    4627           0 :                 return NULL;
    4628             :         }
    4629             : 
    4630      367623 :         ret = samdb_rodc(ldb, &rodc);
    4631      367623 :         if (ret != LDB_SUCCESS) {
    4632           0 :                 DEBUG(4, (__location__ ": unable to tell if we are an RODC\n"));
    4633           0 :                 return NULL;
    4634             :         }
    4635             : 
    4636      367623 :         if (rodc) {
    4637          23 :                 const char *domain = NULL;
    4638             :                 struct ldb_dn *fsmo_role_dn;
    4639             :                 struct ldb_dn *role_owner_dn;
    4640          23 :                 ldb_set_errstring(ldb, "RODC modify is forbidden!");
    4641          23 :                 lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
    4642             :                                          struct loadparm_context);
    4643             : 
    4644          23 :                 err = dsdb_get_fsmo_role_info(req, ldb, DREPL_PDC_MASTER,
    4645             :                                               &fsmo_role_dn, &role_owner_dn);
    4646          23 :                 if (W_ERROR_IS_OK(err)) {
    4647          23 :                         struct ldb_dn *server_dn = ldb_dn_copy(req, role_owner_dn);
    4648          23 :                         if (server_dn != NULL) {
    4649          23 :                                 ldb_dn_remove_child_components(server_dn, 1);
    4650             : 
    4651          23 :                                 domain = samdb_dn_to_dnshostname(ldb, req,
    4652             :                                                                  server_dn);
    4653             :                         }
    4654             :                 }
    4655          23 :                 if (domain == NULL) {
    4656           0 :                         domain = lpcfg_dnsdomain(lp_ctx);
    4657             :                 }
    4658          23 :                 referral = talloc_asprintf(req,
    4659             :                                            "ldap://%s/%s",
    4660             :                                            domain,
    4661             :                                            ldb_dn_get_linearized(dn));
    4662          23 :                 return referral;
    4663             :         }
    4664             : 
    4665      367600 :         return NULL;
    4666             : }
    4667             : 
    4668             : /*
    4669             :  * Restrict all access to sensitive attributes.
    4670             :  *
    4671             :  * We don't want to even inspect the values, so we can use the same
    4672             :  * routine for ADD and MODIFY.
    4673             :  *
    4674             :  */
    4675             : 
    4676      684055 : static int samldb_check_sensitive_attributes(struct samldb_ctx *ac)
    4677             : {
    4678      684055 :         struct ldb_message_element *el = NULL;
    4679      684055 :         struct security_token *user_token = NULL;
    4680             :         int ret;
    4681             : 
    4682      684055 :         if (dsdb_module_am_system(ac->module)) {
    4683      180517 :                 return LDB_SUCCESS;
    4684             :         }
    4685             : 
    4686      503538 :         user_token = acl_user_token(ac->module);
    4687      503538 :         if (user_token == NULL) {
    4688           0 :                 return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
    4689             :         }
    4690             : 
    4691      503538 :         el = ldb_msg_find_element(ac->msg, "sidHistory");
    4692      503538 :         if (el) {
    4693             :                /*
    4694             :                 * sidHistory is restricted to the (not implemented
    4695             :                 * yet in Samba) DsAddSidHistory call (direct LDB access is
    4696             :                 * as SYSTEM so will bypass this).
    4697             :                 *
    4698             :                 * If you want to modify this, say to merge domains,
    4699             :                 * directly modify the sam.ldb as root.
    4700             :                 */
    4701          16 :                 ldb_asprintf_errstring(ldb_module_get_ctx(ac->module),
    4702             :                                        "sidHistory "
    4703             :                                        "(entry %s) cannot be created "
    4704             :                                        "or changed over LDAP!",
    4705          16 :                                        ldb_dn_get_linearized(ac->msg->dn));
    4706          16 :                 return LDB_ERR_UNWILLING_TO_PERFORM;
    4707             :         }
    4708             : 
    4709      503522 :         el = ldb_msg_find_element(ac->msg, "msDS-SecondaryKrbTgtNumber");
    4710      503522 :         if (el) {
    4711             :                 struct security_descriptor *domain_sd;
    4712          16 :                 const struct dsdb_class *objectclass = NULL;
    4713             :                 /*
    4714             :                  * msDS-SecondaryKrbTgtNumber allows the creator to
    4715             :                  * become an RODC, this is trusted as an RODC
    4716             :                  * account
    4717             :                  */
    4718          16 :                 ret = samldb_get_domain_secdesc_and_oc(ac, &domain_sd, &objectclass);
    4719          16 :                 if (ret != LDB_SUCCESS) {
    4720           8 :                         return ret;
    4721             :                 }
    4722          16 :                 ret = acl_check_extended_right(ac,
    4723             :                                                ac->module,
    4724             :                                                ac->req,
    4725             :                                                objectclass,
    4726             :                                                domain_sd,
    4727             :                                                user_token,
    4728             :                                                GUID_DRS_DS_INSTALL_REPLICA,
    4729             :                                                SEC_ADS_CONTROL_ACCESS,
    4730             :                                                NULL);
    4731          16 :                 if (ret != LDB_SUCCESS) {
    4732           8 :                         ldb_asprintf_errstring(ldb_module_get_ctx(ac->module),
    4733             :                                                "msDS-SecondaryKrbTgtNumber "
    4734             :                                                "(entry %s) cannot be created "
    4735             :                                                "or changed without "
    4736             :                                                "DS-Install-Replica extended right!",
    4737           8 :                                                ldb_dn_get_linearized(ac->msg->dn));
    4738           8 :                         return ret;
    4739             :                 }
    4740             :         }
    4741             : 
    4742      503514 :         el = ldb_msg_find_element(ac->msg, "msDS-AllowedToDelegateTo");
    4743      503514 :         if (el) {
    4744             :                 /*
    4745             :                  * msDS-AllowedToDelegateTo is incredibly powerful,
    4746             :                  * given that it allows a server to become ANY USER on
    4747             :                  * the target server only listed by SPN so needs to be
    4748             :                  * protected just as the userAccountControl
    4749             :                  * UF_TRUSTED_FOR_DELEGATION is.
    4750             :                  */
    4751             : 
    4752          40 :                 bool have_priv = security_token_has_privilege(user_token,
    4753             :                                                               SEC_PRIV_ENABLE_DELEGATION);
    4754          40 :                 if (have_priv == false) {
    4755           8 :                         ldb_asprintf_errstring(ldb_module_get_ctx(ac->module),
    4756             :                                                "msDS-AllowedToDelegateTo "
    4757             :                                                "(entry %s) cannot be created "
    4758             :                                                "or changed without SePrivEnableDelegation!",
    4759           8 :                                                ldb_dn_get_linearized(ac->msg->dn));
    4760           8 :                         return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
    4761             :                 }
    4762             :         }
    4763      503506 :         return LDB_SUCCESS;
    4764             : }
    4765             : /* add */
    4766      320252 : static int samldb_add(struct ldb_module *module, struct ldb_request *req)
    4767             : {
    4768             :         struct ldb_context *ldb;
    4769             :         struct samldb_ctx *ac;
    4770             :         struct ldb_message_element *el;
    4771             :         int ret;
    4772      320252 :         char *referral = NULL;
    4773             : 
    4774      320252 :         ldb = ldb_module_get_ctx(module);
    4775      320252 :         ldb_debug(ldb, LDB_DEBUG_TRACE, "samldb_add\n");
    4776             : 
    4777             :         /* do not manipulate our control entries */
    4778      320252 :         if (ldb_dn_is_special(req->op.add.message->dn)) {
    4779         371 :                 return ldb_next_request(module, req);
    4780             :         }
    4781             : 
    4782      319881 :         referral = refer_if_rodc(ldb, req, req->op.add.message->dn);
    4783      319881 :         if (referral != NULL) {
    4784          22 :                 ret = ldb_module_send_referral(req, referral);
    4785          22 :                 return ret;
    4786             :         }
    4787             : 
    4788      319859 :         el = ldb_msg_find_element(req->op.add.message, "userParameters");
    4789      319859 :         if (el != NULL && ldb_req_is_untrusted(req)) {
    4790           0 :                 const char *reason = "samldb_add: "
    4791             :                         "setting userParameters is not supported over LDAP, "
    4792             :                         "see https://bugzilla.samba.org/show_bug.cgi?id=8077";
    4793           0 :                 ldb_debug(ldb, LDB_DEBUG_WARNING, "%s", reason);
    4794           0 :                 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION, reason);
    4795             :         }
    4796             : 
    4797      319859 :         ac = samldb_ctx_init(module, req);
    4798      319859 :         if (ac == NULL) {
    4799           0 :                 return ldb_operr(ldb);
    4800             :         }
    4801             : 
    4802             :         /* build the new msg */
    4803      319859 :         ac->msg = ldb_msg_copy_shallow(ac, req->op.add.message);
    4804      319859 :         if (ac->msg == NULL) {
    4805           0 :                 talloc_free(ac);
    4806           0 :                 ldb_debug(ldb, LDB_DEBUG_FATAL,
    4807             :                           "samldb_add: ldb_msg_copy_shallow failed!\n");
    4808           0 :                 return ldb_operr(ldb);
    4809             :         }
    4810             : 
    4811      319859 :         ret = samldb_check_sensitive_attributes(ac);
    4812      319859 :         if (ret != LDB_SUCCESS) {
    4813          32 :                 talloc_free(ac);
    4814          32 :                 return ret;
    4815             :         }
    4816             : 
    4817      319827 :         el = ldb_msg_find_element(ac->msg, "fSMORoleOwner");
    4818      319827 :         if (el != NULL) {
    4819           9 :                 ret = samldb_fsmo_role_owner_check(ac);
    4820           9 :                 if (ret != LDB_SUCCESS) {
    4821           6 :                         return ret;
    4822             :                 }
    4823             :         }
    4824             : 
    4825      319821 :         el = ldb_msg_find_element(ac->msg, "servicePrincipalName");
    4826      319821 :         if ((el != NULL)) {
    4827             :                 /*
    4828             :                  * We need to check whether the SPN collides with an existing
    4829             :                  * one (anywhere) including via aliases.
    4830             :                  */
    4831         484 :                 ret = samldb_spn_uniqueness_check(ac, el);
    4832         484 :                 if (ret != LDB_SUCCESS) {
    4833           3 :                         return ret;
    4834             :                 }
    4835             :         }
    4836             : 
    4837      319818 :         if (samdb_find_attribute(ldb, ac->msg,
    4838             :                                  "objectclass", "user") != NULL) {
    4839       19644 :                 ac->type = SAMLDB_TYPE_USER;
    4840             : 
    4841       19644 :                 ret = samldb_prim_group_trigger(ac);
    4842       19644 :                 if (ret != LDB_SUCCESS) {
    4843          26 :                         return ret;
    4844             :                 }
    4845             : 
    4846       19618 :                 ret = samldb_objectclass_trigger(ac);
    4847       19618 :                 if (ret != LDB_SUCCESS) {
    4848         110 :                         return ret;
    4849             :                 }
    4850             : 
    4851       19508 :                 return samldb_fill_object(ac);
    4852             :         }
    4853             : 
    4854      300174 :         if (samdb_find_attribute(ldb, ac->msg,
    4855             :                                  "objectclass", "group") != NULL) {
    4856        5823 :                 ac->type = SAMLDB_TYPE_GROUP;
    4857             : 
    4858        5823 :                 ret = samldb_objectclass_trigger(ac);
    4859        5823 :                 if (ret != LDB_SUCCESS) {
    4860           6 :                         return ret;
    4861             :                 }
    4862             : 
    4863        5817 :                 return samldb_fill_object(ac);
    4864             :         }
    4865             : 
    4866             :         /* perhaps a foreignSecurityPrincipal? */
    4867      294351 :         if (samdb_find_attribute(ldb, ac->msg,
    4868             :                                  "objectclass",
    4869             :                                  "foreignSecurityPrincipal") != NULL) {
    4870        2299 :                 return samldb_fill_foreignSecurityPrincipal_object(ac);
    4871             :         }
    4872             : 
    4873      292052 :         if (samdb_find_attribute(ldb, ac->msg,
    4874             :                                  "objectclass", "classSchema") != NULL) {
    4875       19762 :                 ac->type = SAMLDB_TYPE_CLASS;
    4876             : 
    4877             :                 /* If in provision, these checks are too slow to do */
    4878       19762 :                 if (!ldb_request_get_control(req, DSDB_CONTROL_SKIP_DUPLICATES_CHECK_OID)) {
    4879         598 :                         ret = samldb_schema_governsid_valid_check(ac);
    4880         598 :                         if (ret != LDB_SUCCESS) {
    4881           9 :                                 return ret;
    4882             :                         }
    4883             :                 }
    4884             : 
    4885       19753 :                 ret = samldb_schema_ldapdisplayname_valid_check(ac);
    4886       19753 :                 if (ret != LDB_SUCCESS) {
    4887           9 :                         return ret;
    4888             :                 }
    4889             : 
    4890       19744 :                 ret = samldb_schema_info_update(ac);
    4891       19744 :                 if (ret != LDB_SUCCESS) {
    4892           0 :                         talloc_free(ac);
    4893           0 :                         return ret;
    4894             :                 }
    4895             : 
    4896       19744 :                 return samldb_fill_object(ac);
    4897             :         }
    4898             : 
    4899      272290 :         if (samdb_find_attribute(ldb, ac->msg,
    4900             :                                  "objectclass", "attributeSchema") != NULL) {
    4901      108149 :                 ac->type = SAMLDB_TYPE_ATTRIBUTE;
    4902             : 
    4903             :                 /* If in provision, these checks are too slow to do */
    4904      108149 :                 if (!ldb_request_get_control(req, DSDB_CONTROL_SKIP_DUPLICATES_CHECK_OID)) {
    4905         971 :                         ret = samldb_schema_attributeid_valid_check(ac);
    4906         971 :                         if (ret != LDB_SUCCESS) {
    4907           9 :                                 return ret;
    4908             :                         }
    4909             : 
    4910         962 :                         ret = samldb_schema_add_handle_linkid(ac);
    4911         962 :                         if (ret != LDB_SUCCESS) {
    4912          36 :                                 return ret;
    4913             :                         }
    4914             : 
    4915         926 :                         ret = samldb_schema_add_handle_mapiid(ac);
    4916         926 :                         if (ret != LDB_SUCCESS) {
    4917           9 :                                 return ret;
    4918             :                         }
    4919             :                 }
    4920             : 
    4921      108095 :                 ret = samldb_schema_ldapdisplayname_valid_check(ac);
    4922      108095 :                 if (ret != LDB_SUCCESS) {
    4923          18 :                         return ret;
    4924             :                 }
    4925             : 
    4926      108077 :                 ret = samldb_schema_info_update(ac);
    4927      108077 :                 if (ret != LDB_SUCCESS) {
    4928           0 :                         talloc_free(ac);
    4929           0 :                         return ret;
    4930             :                 }
    4931             : 
    4932      108077 :                 return samldb_fill_object(ac);
    4933             :         }
    4934             : 
    4935      164141 :         if (samdb_find_attribute(ldb, ac->msg,
    4936             :                                  "objectclass", "subnet") != NULL) {
    4937         206 :                 ret = samldb_verify_subnet(ac, ac->msg->dn);
    4938         206 :                 if (ret != LDB_SUCCESS) {
    4939          96 :                         talloc_free(ac);
    4940          96 :                         return ret;
    4941             :                 }
    4942             :                 /* We are just checking the value is valid, and there are no
    4943             :                    values to fill in. */
    4944             :         }
    4945             : 
    4946      164045 :         talloc_free(ac);
    4947             : 
    4948             :         /* nothing matched, go on */
    4949      164045 :         return ldb_next_request(module, req);
    4950             : }
    4951             : 
    4952             : /* modify */
    4953      364821 : static int samldb_modify(struct ldb_module *module, struct ldb_request *req)
    4954             : {
    4955             :         struct ldb_context *ldb;
    4956             :         struct samldb_ctx *ac;
    4957             :         struct ldb_message_element *el, *el2;
    4958             :         struct ldb_control *is_undelete;
    4959      364821 :         bool modified = false;
    4960             :         int ret;
    4961             : 
    4962      364821 :         if (ldb_dn_is_special(req->op.mod.message->dn)) {
    4963             :                 /* do not manipulate our control entries */
    4964         558 :                 return ldb_next_request(module, req);
    4965             :         }
    4966             : 
    4967      364263 :         ldb = ldb_module_get_ctx(module);
    4968             : 
    4969             :         /*
    4970             :          * we are going to need some special handling if in Undelete call.
    4971             :          * Since tombstone_reanimate module will restore certain attributes,
    4972             :          * we need to relax checks for: sAMAccountType, primaryGroupID
    4973             :          */
    4974      364263 :         is_undelete = ldb_request_get_control(req, DSDB_CONTROL_RESTORE_TOMBSTONE_OID);
    4975             : 
    4976             :         /* make sure that "objectSid" is not specified */
    4977      364263 :         el = ldb_msg_find_element(req->op.mod.message, "objectSid");
    4978      364263 :         if (el != NULL) {
    4979          15 :                 if (ldb_request_get_control(req, LDB_CONTROL_PROVISION_OID) == NULL) {
    4980          15 :                         ldb_set_errstring(ldb,
    4981             :                                           "samldb: objectSid must not be specified!");
    4982          15 :                         return LDB_ERR_UNWILLING_TO_PERFORM;
    4983             :                 }
    4984             :         }
    4985      364248 :         if (is_undelete == NULL) {
    4986             :                 /* make sure that "sAMAccountType" is not specified */
    4987      363983 :                 el = ldb_msg_find_element(req->op.mod.message, "sAMAccountType");
    4988      363983 :                 if (el != NULL) {
    4989          15 :                         ldb_set_errstring(ldb,
    4990             :                                           "samldb: sAMAccountType must not be specified!");
    4991          15 :                         return LDB_ERR_UNWILLING_TO_PERFORM;
    4992             :                 }
    4993             :         }
    4994             :         /* make sure that "isCriticalSystemObject" is not specified */
    4995      364233 :         el = ldb_msg_find_element(req->op.mod.message, "isCriticalSystemObject");
    4996      364233 :         if (el != NULL) {
    4997         237 :                 if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID) == NULL) {
    4998           1 :                         ldb_set_errstring(ldb,
    4999             :                                           "samldb: isCriticalSystemObject must not be specified!");
    5000           1 :                         return LDB_ERR_UNWILLING_TO_PERFORM;
    5001             :                 }
    5002             :         }
    5003             : 
    5004             :         /* msDS-IntId is not allowed to be modified
    5005             :          * except when modification comes from replication */
    5006      364232 :         if (ldb_msg_find_element(req->op.mod.message, "msDS-IntId")) {
    5007          36 :                 if (!ldb_request_get_control(req,
    5008             :                                              DSDB_CONTROL_REPLICATED_UPDATE_OID)) {
    5009          36 :                         return LDB_ERR_CONSTRAINT_VIOLATION;
    5010             :                 }
    5011             :         }
    5012             : 
    5013      364196 :         el = ldb_msg_find_element(req->op.mod.message, "userParameters");
    5014      364196 :         if (el != NULL && ldb_req_is_untrusted(req)) {
    5015           0 :                 const char *reason = "samldb: "
    5016             :                         "setting userParameters is not supported over LDAP, "
    5017             :                         "see https://bugzilla.samba.org/show_bug.cgi?id=8077";
    5018           0 :                 ldb_debug(ldb, LDB_DEBUG_WARNING, "%s", reason);
    5019           0 :                 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION, reason);
    5020             :         }
    5021             : 
    5022      364196 :         ac = samldb_ctx_init(module, req);
    5023      364196 :         if (ac == NULL) {
    5024           0 :                 return ldb_operr(ldb);
    5025             :         }
    5026             : 
    5027             :         /* build the new msg */
    5028      364196 :         ac->msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
    5029      364196 :         if (ac->msg == NULL) {
    5030           0 :                 talloc_free(ac);
    5031           0 :                 ldb_debug(ldb, LDB_DEBUG_FATAL,
    5032             :                           "samldb_modify: ldb_msg_copy_shallow failed!\n");
    5033           0 :                 return ldb_operr(ldb);
    5034             :         }
    5035             : 
    5036      364196 :         ret = samldb_check_sensitive_attributes(ac);
    5037      364196 :         if (ret != LDB_SUCCESS) {
    5038           0 :                 talloc_free(ac);
    5039           0 :                 return ret;
    5040             :         }
    5041             : 
    5042      364196 :         if (is_undelete == NULL) {
    5043      363931 :                 el = ldb_msg_find_element(ac->msg, "primaryGroupID");
    5044      363931 :                 if (el != NULL) {
    5045          89 :                         ret = samldb_prim_group_trigger(ac);
    5046          89 :                         if (ret != LDB_SUCCESS) {
    5047          11 :                                 return ret;
    5048             :                         }
    5049             :                 }
    5050             :         }
    5051             : 
    5052      364185 :         el = ldb_msg_find_element(ac->msg, "userAccountControl");
    5053      364185 :         if (el != NULL) {
    5054       11955 :                 modified = true;
    5055       11955 :                 ret = samldb_user_account_control_change(ac);
    5056       11955 :                 if (ret != LDB_SUCCESS) {
    5057         184 :                         return ret;
    5058             :                 }
    5059             :         }
    5060             : 
    5061      364001 :         el = ldb_msg_find_element(ac->msg, "pwdLastSet");
    5062      364001 :         if (el != NULL) {
    5063         249 :                 modified = true;
    5064         249 :                 ret = samldb_pwd_last_set_change(ac);
    5065         249 :                 if (ret != LDB_SUCCESS) {
    5066           6 :                         return ret;
    5067             :                 }
    5068             :         }
    5069             : 
    5070      363995 :         el = ldb_msg_find_element(ac->msg, "lockoutTime");
    5071      363995 :         if (el != NULL) {
    5072         133 :                 modified = true;
    5073         133 :                 ret = samldb_lockout_time(ac);
    5074         133 :                 if (ret != LDB_SUCCESS) {
    5075           0 :                         return ret;
    5076             :                 }
    5077             :         }
    5078             : 
    5079      363995 :         el = ldb_msg_find_element(ac->msg, "groupType");
    5080      363995 :         if (el != NULL) {
    5081         102 :                 modified = true;
    5082         102 :                 ret = samldb_group_type_change(ac);
    5083         102 :                 if (ret != LDB_SUCCESS) {
    5084          33 :                         return ret;
    5085             :                 }
    5086             :         }
    5087             : 
    5088      363962 :         el = ldb_msg_find_element(ac->msg, "sAMAccountName");
    5089      363962 :         if (el != NULL) {
    5090             :                 uint32_t user_account_control;
    5091         756 :                 struct ldb_result *res = NULL;
    5092         756 :                 const char * const attrs[] = { "userAccountControl",
    5093             :                                                "objectclass",
    5094             :                                                NULL };
    5095        1311 :                 ret = dsdb_module_search_dn(ac->module,
    5096             :                                             ac,
    5097             :                                             &res,
    5098         756 :                                             ac->msg->dn,
    5099             :                                             attrs,
    5100             :                                             DSDB_FLAG_NEXT_MODULE | DSDB_SEARCH_SHOW_DELETED,
    5101             :                                             ac->req);
    5102         756 :                 if (ret != LDB_SUCCESS) {
    5103          46 :                         return ret;
    5104             :                 }
    5105             :                 user_account_control
    5106         756 :                         = ldb_msg_find_attr_as_uint(res->msgs[0],
    5107             :                                                     "userAccountControl",
    5108             :                                                     0);
    5109             : 
    5110         756 :                 if ((user_account_control
    5111         756 :                      & UF_TRUST_ACCOUNT_MASK) != 0) {
    5112         296 :                         ac->need_trailing_dollar = true;
    5113             : 
    5114         460 :                 } else if (samdb_find_attribute(ldb,
    5115         460 :                                                 res->msgs[0],
    5116             :                                                 "objectclass",
    5117             :                                                 "computer")
    5118             :                            != NULL) {
    5119          12 :                         ac->need_trailing_dollar = true;
    5120             :                 }
    5121             : 
    5122         756 :                 ret = samldb_sam_accountname_valid_check(ac);
    5123         756 :                 if (ret != LDB_SUCCESS) {
    5124          46 :                         return ret;
    5125             :                 }
    5126             :         }
    5127             : 
    5128      363916 :         el = ldb_msg_find_element(ac->msg, "userPrincipalName");
    5129      363916 :         if (el != NULL) {
    5130         283 :                 ret = samldb_sam_account_upn_clash(ac);
    5131         283 :                 if (ret != LDB_SUCCESS) {
    5132          16 :                         talloc_free(ac);
    5133          16 :                         return ret;
    5134             :                 }
    5135             :         }
    5136             : 
    5137      363900 :         el = ldb_msg_find_element(ac->msg, "ldapDisplayName");
    5138      363900 :         if (el != NULL) {
    5139          29 :                 ret = samldb_schema_ldapdisplayname_valid_check(ac);
    5140          29 :                 if (ret != LDB_SUCCESS) {
    5141          18 :                         return ret;
    5142             :                 }
    5143             :         }
    5144             : 
    5145      363882 :         el = ldb_msg_find_element(ac->msg, "attributeID");
    5146      363882 :         if (el != NULL) {
    5147          27 :                 ldb_asprintf_errstring(ldb_module_get_ctx(ac->module),
    5148             :                                        "Once set, attributeID values may not be modified");
    5149          27 :                 return LDB_ERR_CONSTRAINT_VIOLATION;
    5150             :         }
    5151             : 
    5152      363855 :         el = ldb_msg_find_element(ac->msg, "governsID");
    5153      363855 :         if (el != NULL) {
    5154          18 :                 ldb_asprintf_errstring(ldb_module_get_ctx(ac->module),
    5155             :                                        "Once set, governsID values may not be modified");
    5156          18 :                 return LDB_ERR_CONSTRAINT_VIOLATION;
    5157             :         }
    5158             : 
    5159      363837 :         el = ldb_msg_find_element(ac->msg, "member");
    5160      363837 :         if (el != NULL) {
    5161        3104 :                 struct ldb_control *fix_link_sid_ctrl = NULL;
    5162             : 
    5163        3104 :                 fix_link_sid_ctrl = ldb_request_get_control(ac->req,
    5164             :                                         DSDB_CONTROL_DBCHECK_FIX_LINK_DN_SID);
    5165        3104 :                 if (fix_link_sid_ctrl == NULL) {
    5166        3102 :                         ret = samldb_member_check(ac);
    5167        3102 :                         if (ret != LDB_SUCCESS) {
    5168           3 :                                 return ret;
    5169             :                         }
    5170             :                 }
    5171             :         }
    5172             : 
    5173      363834 :         el = ldb_msg_find_element(ac->msg, "description");
    5174      363834 :         if (el != NULL) {
    5175         671 :                 ret = samldb_description_check(ac, &modified);
    5176         671 :                 if (ret != LDB_SUCCESS) {
    5177           0 :                         return ret;
    5178             :                 }
    5179             :         }
    5180             : 
    5181      363834 :         el = ldb_msg_find_element(ac->msg, "dNSHostName");
    5182      363834 :         el2 = ldb_msg_find_element(ac->msg, "sAMAccountName");
    5183      363834 :         if ((el != NULL) || (el2 != NULL)) {
    5184        1155 :                 modified = true;
    5185             :                 /*
    5186             :                  * samldb_service_principal_names_change() might add SPN
    5187             :                  * changes to the request, so this must come before the SPN
    5188             :                  * uniqueness check below.
    5189             :                  *
    5190             :                  * Note we ALSO have to do the SPN uniqueness check inside
    5191             :                  * samldb_service_principal_names_change(), because it does a
    5192             :                  * subrequest to do requested SPN modifications *before* its
    5193             :                  * automatic ones are added.
    5194             :                  */
    5195        1155 :                 ret = samldb_service_principal_names_change(ac);
    5196        1155 :                 if (ret != LDB_SUCCESS) {
    5197           0 :                         return ret;
    5198             :                 }
    5199             :         }
    5200             : 
    5201      363834 :         el = ldb_msg_find_element(ac->msg, "servicePrincipalName");
    5202      363834 :         if ((el != NULL)) {
    5203             :                 /*
    5204             :                  * We need to check whether the SPN collides with an existing
    5205             :                  * one (anywhere) including via aliases.
    5206             :                  */
    5207        1318 :                 modified = true;
    5208        1318 :                 ret = samldb_spn_uniqueness_check(ac, el);
    5209        1318 :                 if (ret != LDB_SUCCESS) {
    5210           0 :                         return ret;
    5211             :                 }
    5212             :         }
    5213             : 
    5214      363834 :         el = ldb_msg_find_element(ac->msg, "fSMORoleOwner");
    5215      363834 :         if (el != NULL) {
    5216         853 :                 ret = samldb_fsmo_role_owner_check(ac);
    5217         853 :                 if (ret != LDB_SUCCESS) {
    5218           6 :                         return ret;
    5219             :                 }
    5220             :         }
    5221             : 
    5222      363828 :         if (modified) {
    5223             :                 struct ldb_request *child_req;
    5224             : 
    5225             :                 /* Now perform the real modifications as a child request */
    5226       24660 :                 ret = ldb_build_mod_req(&child_req, ldb, ac,
    5227       14706 :                                         ac->msg,
    5228             :                                         req->controls,
    5229             :                                         req, dsdb_next_callback,
    5230             :                                         req);
    5231       14706 :                 LDB_REQ_SET_LOCATION(child_req);
    5232       14706 :                 if (ret != LDB_SUCCESS) {
    5233           0 :                         return ret;
    5234             :                 }
    5235             : 
    5236       14706 :                 return ldb_next_request(module, child_req);
    5237             :         }
    5238             : 
    5239      349122 :         talloc_free(ac);
    5240             : 
    5241             :         /* no change which interests us, go on */
    5242      349122 :         return ldb_next_request(module, req);
    5243             : }
    5244             : 
    5245             : /* delete */
    5246             : 
    5247       47741 : static int samldb_prim_group_users_check(struct samldb_ctx *ac)
    5248             : {
    5249             :         struct ldb_context *ldb;
    5250             :         struct dom_sid *sid;
    5251             :         uint32_t rid;
    5252             :         NTSTATUS status;
    5253             :         int ret;
    5254       47741 :         struct ldb_result *res = NULL;
    5255       47741 :         struct ldb_result *res_users = NULL;
    5256       47741 :         const char * const attrs[] = { "objectSid", "isDeleted", NULL };
    5257       47741 :         const char * const noattrs[] = { NULL };
    5258             : 
    5259       47741 :         ldb = ldb_module_get_ctx(ac->module);
    5260             : 
    5261             :         /* Finds out the SID/RID of the SAM object */
    5262       47741 :         ret = dsdb_module_search_dn(ac->module, ac, &res, ac->req->op.del.dn,
    5263             :                                         attrs,
    5264             :                                         DSDB_FLAG_NEXT_MODULE | DSDB_SEARCH_SHOW_DELETED,
    5265             :                                         ac->req);
    5266       47741 :         if (ret != LDB_SUCCESS) {
    5267           0 :                 return ret;
    5268             :         }
    5269             : 
    5270       47741 :         if (ldb_msg_check_string_attribute(res->msgs[0], "isDeleted", "TRUE")) {
    5271           7 :                 return LDB_SUCCESS;
    5272             :         }
    5273             : 
    5274       47734 :         sid = samdb_result_dom_sid(ac, res->msgs[0], "objectSid");
    5275       47734 :         if (sid == NULL) {
    5276             :                 /* No SID - it might not be a SAM object - therefore ok */
    5277       26179 :                 return LDB_SUCCESS;
    5278             :         }
    5279       21555 :         status = dom_sid_split_rid(ac, sid, NULL, &rid);
    5280       21555 :         if (!NT_STATUS_IS_OK(status)) {
    5281           0 :                 return ldb_operr(ldb);
    5282             :         }
    5283       21555 :         if (rid == 0) {
    5284             :                 /* Special object (security principal?) */
    5285           0 :                 return LDB_SUCCESS;
    5286             :         }
    5287             :         /* do not allow deletion of well-known sids */
    5288       21573 :         if (rid < DSDB_SAMDB_MINIMUM_ALLOWED_RID &&
    5289          18 :             (ldb_request_get_control(ac->req, LDB_CONTROL_RELAX_OID) == NULL)) {
    5290          18 :                 return LDB_ERR_OTHER;
    5291             :         }
    5292             : 
    5293             :         /* Deny delete requests from groups which are primary ones */
    5294       21537 :         ret = dsdb_module_search(ac->module, ac, &res_users,
    5295             :                                  ldb_get_default_basedn(ldb),
    5296             :                                  LDB_SCOPE_SUBTREE, noattrs,
    5297             :                                  DSDB_FLAG_NEXT_MODULE,
    5298             :                                  ac->req,
    5299             :                                  "(&(primaryGroupID=%u)(objectClass=user))", rid);
    5300       21537 :         if (ret != LDB_SUCCESS) {
    5301           0 :                 return ret;
    5302             :         }
    5303       21537 :         if (res_users->count > 0) {
    5304           5 :                 ldb_asprintf_errstring(ldb_module_get_ctx(ac->module),
    5305             :                                        "Refusing to delete %s, as it "
    5306             :                                        "is still the primaryGroupID "
    5307             :                                        "for %u users",
    5308           3 :                                        ldb_dn_get_linearized(res->msgs[0]->dn),
    5309           3 :                                        res_users->count);
    5310             : 
    5311             :                 /*
    5312             :                  * Yes, this seems very wrong, but we have a test
    5313             :                  * for this exact error code in sam.py
    5314             :                  */
    5315           3 :                 return LDB_ERR_ENTRY_ALREADY_EXISTS;
    5316             :         }
    5317             : 
    5318       21534 :         return LDB_SUCCESS;
    5319             : }
    5320             : 
    5321       47743 : static int samldb_delete(struct ldb_module *module, struct ldb_request *req)
    5322             : {
    5323             :         struct samldb_ctx *ac;
    5324       47743 :         char *referral = NULL;
    5325             :         int ret;
    5326             :         struct ldb_context *ldb;
    5327             : 
    5328       47743 :         if (ldb_dn_is_special(req->op.del.dn)) {
    5329             :                 /* do not manipulate our control entries */
    5330           1 :                 return ldb_next_request(module, req);
    5331             :         }
    5332             : 
    5333       47742 :         ldb = ldb_module_get_ctx(module);
    5334             : 
    5335       47742 :         referral = refer_if_rodc(ldb, req, req->op.del.dn);
    5336       47742 :         if (referral != NULL) {
    5337           1 :                 ret = ldb_module_send_referral(req, referral);
    5338           1 :                 return ret;
    5339             :         }
    5340             : 
    5341       47741 :         ac = samldb_ctx_init(module, req);
    5342       47741 :         if (ac == NULL) {
    5343           0 :                 return ldb_operr(ldb_module_get_ctx(module));
    5344             :         }
    5345             : 
    5346       47741 :         ret = samldb_prim_group_users_check(ac);
    5347       47741 :         if (ret != LDB_SUCCESS) {
    5348          21 :                 return ret;
    5349             :         }
    5350             : 
    5351       47720 :         talloc_free(ac);
    5352             : 
    5353       47720 :         return ldb_next_request(module, req);
    5354             : }
    5355             : 
    5356             : /* rename */
    5357             : 
    5358         870 : static int check_rename_constraints(struct ldb_message *msg,
    5359             :                                     struct samldb_ctx *ac,
    5360             :                                     struct ldb_dn *olddn, struct ldb_dn *newdn)
    5361             : {
    5362         870 :         struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
    5363             :         struct ldb_dn *dn1, *dn2, *nc_root;
    5364             :         int32_t systemFlags;
    5365         870 :         bool move_op = false;
    5366         870 :         bool rename_op = false;
    5367             :         int ret;
    5368             : 
    5369             :         /* Skip the checks if old and new DN are the same, or if we have the
    5370             :          * relax control specified or if the returned objects is already
    5371             :          * deleted and needs only to be moved for consistency. */
    5372             : 
    5373         870 :         if (ldb_dn_compare(olddn, newdn) == 0) {
    5374           6 :                 return LDB_SUCCESS;
    5375             :         }
    5376         864 :         if (ldb_request_get_control(ac->req, LDB_CONTROL_RELAX_OID) != NULL) {
    5377          19 :                 return LDB_SUCCESS;
    5378             :         }
    5379             : 
    5380         845 :         if (ldb_msg_find_attr_as_bool(msg, "isDeleted", false)) {
    5381             :                 /*
    5382             :                  * check originating request if we are supposed
    5383             :                  * to "see" this record in first place.
    5384             :                  */
    5385           2 :                 if (ldb_request_get_control(ac->req, LDB_CONTROL_SHOW_DELETED_OID) == NULL) {
    5386           1 :                         return LDB_ERR_NO_SUCH_OBJECT;
    5387             :                 }
    5388           1 :                 return LDB_ERR_UNWILLING_TO_PERFORM;
    5389             :         }
    5390             : 
    5391             :         /* Objects under CN=System */
    5392             : 
    5393         843 :         dn1 = samdb_system_container_dn(ldb, ac);
    5394         843 :         if (dn1 == NULL) return ldb_oom(ldb);
    5395             : 
    5396         844 :         if ((ldb_dn_compare_base(dn1, olddn) == 0) &&
    5397           1 :             (ldb_dn_compare_base(dn1, newdn) != 0)) {
    5398           1 :                 talloc_free(dn1);
    5399           1 :                 ldb_asprintf_errstring(ldb,
    5400             :                                        "subtree_rename: Cannot move/rename %s. Objects under CN=System have to stay under it!",
    5401             :                                        ldb_dn_get_linearized(olddn));
    5402           1 :                 return LDB_ERR_OTHER;
    5403             :         }
    5404             : 
    5405         842 :         talloc_free(dn1);
    5406             : 
    5407             :         /* LSA objects */
    5408             : 
    5409        1684 :         if ((samdb_find_attribute(ldb, msg, "objectClass", "secret") != NULL) ||
    5410         842 :             (samdb_find_attribute(ldb, msg, "objectClass", "trustedDomain") != NULL)) {
    5411           0 :                 ldb_asprintf_errstring(ldb,
    5412             :                                        "subtree_rename: Cannot move/rename %s. It's an LSA-specific object!",
    5413             :                                        ldb_dn_get_linearized(olddn));
    5414           0 :                 return LDB_ERR_UNWILLING_TO_PERFORM;
    5415             :         }
    5416             : 
    5417             :         /* subnet objects */
    5418         842 :         if (samdb_find_attribute(ldb, msg, "objectclass", "subnet") != NULL) {
    5419           2 :                 ret = samldb_verify_subnet(ac, newdn);
    5420           2 :                 if (ret != LDB_SUCCESS) {
    5421           1 :                         return ret;
    5422             :                 }
    5423             :         }
    5424             : 
    5425             :         /* systemFlags */
    5426             : 
    5427         841 :         dn1 = ldb_dn_get_parent(ac, olddn);
    5428         841 :         if (dn1 == NULL) return ldb_oom(ldb);
    5429         841 :         dn2 = ldb_dn_get_parent(ac, newdn);
    5430         841 :         if (dn2 == NULL) return ldb_oom(ldb);
    5431             : 
    5432         841 :         if (ldb_dn_compare(dn1, dn2) == 0) {
    5433         412 :                 rename_op = true;
    5434             :         } else {
    5435         429 :                 move_op = true;
    5436             :         }
    5437             : 
    5438         841 :         talloc_free(dn1);
    5439         841 :         talloc_free(dn2);
    5440             : 
    5441         841 :         systemFlags = ldb_msg_find_attr_as_int(msg, "systemFlags", 0);
    5442             : 
    5443             :         /* Fetch name context */
    5444             : 
    5445         841 :         ret = dsdb_find_nc_root(ldb, ac, olddn, &nc_root);
    5446         841 :         if (ret != LDB_SUCCESS) {
    5447           0 :                 return ret;
    5448             :         }
    5449             : 
    5450         841 :         if (ldb_dn_compare(nc_root, ldb_get_schema_basedn(ldb)) == 0) {
    5451           8 :                 if (move_op) {
    5452           0 :                         ldb_asprintf_errstring(ldb,
    5453             :                                                "subtree_rename: Cannot move %s within schema partition",
    5454             :                                                ldb_dn_get_linearized(olddn));
    5455           0 :                         return LDB_ERR_UNWILLING_TO_PERFORM;
    5456             :                 }
    5457          16 :                 if (rename_op &&
    5458           8 :                     (systemFlags & SYSTEM_FLAG_SCHEMA_BASE_OBJECT) != 0) {
    5459           1 :                         ldb_asprintf_errstring(ldb,
    5460             :                                                "subtree_rename: Cannot rename %s within schema partition",
    5461             :                                                ldb_dn_get_linearized(olddn));
    5462           1 :                         return LDB_ERR_UNWILLING_TO_PERFORM;
    5463             :                 }
    5464         833 :         } else if (ldb_dn_compare(nc_root, ldb_get_config_basedn(ldb)) == 0) {
    5465          14 :                 if (move_op &&
    5466           4 :                     (systemFlags & SYSTEM_FLAG_CONFIG_ALLOW_MOVE) == 0) {
    5467             :                         /* Here we have to do more: control the
    5468             :                          * "ALLOW_LIMITED_MOVE" flag. This means that the
    5469             :                          * grand-grand-parents of two objects have to be equal
    5470             :                          * in order to perform the move (this is used for
    5471             :                          * moving "server" objects in the "sites" container). */
    5472           4 :                         bool limited_move =
    5473           4 :                                 systemFlags & SYSTEM_FLAG_CONFIG_ALLOW_LIMITED_MOVE;
    5474             : 
    5475           4 :                         if (limited_move) {
    5476           0 :                                 dn1 = ldb_dn_copy(ac, olddn);
    5477           0 :                                 if (dn1 == NULL) return ldb_oom(ldb);
    5478           0 :                                 dn2 = ldb_dn_copy(ac, newdn);
    5479           0 :                                 if (dn2 == NULL) return ldb_oom(ldb);
    5480             : 
    5481           0 :                                 limited_move &= ldb_dn_remove_child_components(dn1, 3);
    5482           0 :                                 limited_move &= ldb_dn_remove_child_components(dn2, 3);
    5483           0 :                                 limited_move &= ldb_dn_compare(dn1, dn2) == 0;
    5484             : 
    5485           0 :                                 talloc_free(dn1);
    5486           0 :                                 talloc_free(dn2);
    5487             :                         }
    5488             : 
    5489           4 :                         if (!limited_move
    5490           4 :                             && ldb_request_get_control(ac->req, DSDB_CONTROL_RESTORE_TOMBSTONE_OID) == NULL) {
    5491           2 :                                 ldb_asprintf_errstring(ldb,
    5492             :                                                        "subtree_rename: Cannot move %s to %s in config partition",
    5493             :                                                        ldb_dn_get_linearized(olddn), ldb_dn_get_linearized(newdn));
    5494           2 :                                 return LDB_ERR_UNWILLING_TO_PERFORM;
    5495             :                         }
    5496             :                 }
    5497          14 :                 if (rename_op &&
    5498           6 :                     (systemFlags & SYSTEM_FLAG_CONFIG_ALLOW_RENAME) == 0) {
    5499           1 :                         ldb_asprintf_errstring(ldb,
    5500             :                                                "subtree_rename: Cannot rename %s to %s within config partition",
    5501             :                                                ldb_dn_get_linearized(olddn), ldb_dn_get_linearized(newdn));
    5502           1 :                         return LDB_ERR_UNWILLING_TO_PERFORM;
    5503             :                 }
    5504         823 :         } else if (ldb_dn_compare(nc_root, ldb_get_default_basedn(ldb)) == 0) {
    5505        1219 :                 if (move_op &&
    5506         425 :                     (systemFlags & SYSTEM_FLAG_DOMAIN_DISALLOW_MOVE) != 0) {
    5507           1 :                         ldb_asprintf_errstring(ldb,
    5508             :                                                "subtree_rename: Cannot move %s to %s - DISALLOW_MOVE set",
    5509             :                                                ldb_dn_get_linearized(olddn), ldb_dn_get_linearized(newdn));
    5510           1 :                         return LDB_ERR_UNWILLING_TO_PERFORM;
    5511             :                 }
    5512        1170 :                 if (rename_op &&
    5513         397 :                     (systemFlags & SYSTEM_FLAG_DOMAIN_DISALLOW_RENAME) != 0) {
    5514           1 :                         ldb_asprintf_errstring(ldb,
    5515             :                                                        "subtree_rename: Cannot rename %s to %s - DISALLOW_RENAME set",
    5516             :                                                ldb_dn_get_linearized(olddn), ldb_dn_get_linearized(newdn));
    5517           1 :                         return LDB_ERR_UNWILLING_TO_PERFORM;
    5518             :                 }
    5519             :         }
    5520             : 
    5521         835 :         talloc_free(nc_root);
    5522             : 
    5523         835 :         return LDB_SUCCESS;
    5524             : }
    5525             : 
    5526             : 
    5527        1730 : static int samldb_rename_search_base_callback(struct ldb_request *req,
    5528             :                                                struct ldb_reply *ares)
    5529             : {
    5530             :         struct samldb_ctx *ac;
    5531             :         int ret;
    5532             : 
    5533        1730 :         ac = talloc_get_type(req->context, struct samldb_ctx);
    5534             : 
    5535        1730 :         if (!ares) {
    5536           0 :                 return ldb_module_done(ac->req, NULL, NULL,
    5537             :                                         LDB_ERR_OPERATIONS_ERROR);
    5538             :         }
    5539        1730 :         if (ares->error != LDB_SUCCESS) {
    5540           0 :                 return ldb_module_done(ac->req, ares->controls,
    5541             :                                         ares->response, ares->error);
    5542             :         }
    5543             : 
    5544        1730 :         switch (ares->type) {
    5545         870 :         case LDB_REPLY_ENTRY:
    5546             :                 /*
    5547             :                  * This is the root entry of the originating move
    5548             :                  * respectively rename request. It has been already
    5549             :                  * stored in the list using "subtree_rename_search()".
    5550             :                  * Only this one is subject to constraint checking.
    5551             :                  */
    5552        1664 :                 ret = check_rename_constraints(ares->message, ac,
    5553         870 :                                                ac->req->op.rename.olddn,
    5554         870 :                                                ac->req->op.rename.newdn);
    5555         870 :                 if (ret != LDB_SUCCESS) {
    5556          10 :                         return ldb_module_done(ac->req, NULL, NULL,
    5557             :                                                ret);
    5558             :                 }
    5559         860 :                 break;
    5560             : 
    5561           0 :         case LDB_REPLY_REFERRAL:
    5562             :                 /* ignore */
    5563           0 :                 break;
    5564             : 
    5565         860 :         case LDB_REPLY_DONE:
    5566             : 
    5567             :                 /*
    5568             :                  * Great, no problem with the rename, so go ahead as
    5569             :                  * if we never were here
    5570             :                  */
    5571         860 :                 ret = ldb_next_request(ac->module, ac->req);
    5572         860 :                 talloc_free(ares);
    5573         860 :                 return ret;
    5574             :         }
    5575             : 
    5576         860 :         talloc_free(ares);
    5577         860 :         return LDB_SUCCESS;
    5578             : }
    5579             : 
    5580             : 
    5581             : /* rename */
    5582         870 : static int samldb_rename(struct ldb_module *module, struct ldb_request *req)
    5583             : {
    5584             :         struct ldb_context *ldb;
    5585             :         static const char * const attrs[] = { "objectClass", "systemFlags",
    5586             :                                               "isDeleted", NULL };
    5587             :         struct ldb_request *search_req;
    5588             :         struct samldb_ctx *ac;
    5589             :         int ret;
    5590             : 
    5591         870 :         if (ldb_dn_is_special(req->op.rename.olddn)) { /* do not manipulate our control entries */
    5592           0 :                 return ldb_next_request(module, req);
    5593             :         }
    5594             : 
    5595         870 :         ldb = ldb_module_get_ctx(module);
    5596             : 
    5597         870 :         ac = samldb_ctx_init(module, req);
    5598         870 :         if (!ac) {
    5599           0 :                 return ldb_oom(ldb);
    5600             :         }
    5601             : 
    5602         870 :         ret = ldb_build_search_req(&search_req, ldb, ac,
    5603             :                                    req->op.rename.olddn,
    5604             :                                    LDB_SCOPE_BASE,
    5605             :                                    "(objectClass=*)",
    5606             :                                    attrs,
    5607             :                                    NULL,
    5608             :                                    ac,
    5609             :                                    samldb_rename_search_base_callback,
    5610             :                                    req);
    5611         870 :         LDB_REQ_SET_LOCATION(search_req);
    5612         870 :         if (ret != LDB_SUCCESS) {
    5613           0 :                 return ret;
    5614             :         }
    5615             : 
    5616         870 :         ret = ldb_request_add_control(search_req, LDB_CONTROL_SHOW_RECYCLED_OID,
    5617             :                                       true, NULL);
    5618         870 :         if (ret != LDB_SUCCESS) {
    5619           0 :                 return ret;
    5620             :         }
    5621             : 
    5622         870 :         return ldb_next_request(ac->module, search_req);
    5623             : }
    5624             : 
    5625             : /* extended */
    5626             : 
    5627          31 : static int samldb_extended_allocate_rid_pool(struct ldb_module *module, struct ldb_request *req)
    5628             : {
    5629          31 :         struct ldb_context *ldb = ldb_module_get_ctx(module);
    5630             :         struct dsdb_fsmo_extended_op *exop;
    5631             :         int ret;
    5632             : 
    5633          31 :         exop = talloc_get_type(req->op.extended.data,
    5634             :                                struct dsdb_fsmo_extended_op);
    5635          31 :         if (!exop) {
    5636           0 :                 ldb_set_errstring(ldb,
    5637             :                                   "samldb_extended_allocate_rid_pool: invalid extended data");
    5638           0 :                 return LDB_ERR_PROTOCOL_ERROR;
    5639             :         }
    5640             : 
    5641          31 :         ret = ridalloc_allocate_rid_pool_fsmo(module, exop, req);
    5642          31 :         if (ret != LDB_SUCCESS) {
    5643           0 :                 return ret;
    5644             :         }
    5645             : 
    5646          31 :         return ldb_module_done(req, NULL, NULL, LDB_SUCCESS);
    5647             : }
    5648             : 
    5649         995 : static int samldb_extended_allocate_rid(struct ldb_module *module, struct ldb_request *req)
    5650             : {
    5651         995 :         struct ldb_context *ldb = ldb_module_get_ctx(module);
    5652             :         struct dsdb_extended_allocate_rid *exop;
    5653             :         int ret;
    5654             : 
    5655         995 :         exop = talloc_get_type(req->op.extended.data,
    5656             :                                struct dsdb_extended_allocate_rid);
    5657         995 :         if (!exop) {
    5658           0 :                 ldb_set_errstring(ldb,
    5659             :                                   "samldb_extended_allocate_rid: invalid extended data");
    5660           0 :                 return LDB_ERR_PROTOCOL_ERROR;
    5661             :         }
    5662             : 
    5663         995 :         ret = ridalloc_allocate_rid(module, &exop->rid, req);
    5664         995 :         if (ret != LDB_SUCCESS) {
    5665           2 :                 return ret;
    5666             :         }
    5667             : 
    5668         993 :         return ldb_module_done(req, NULL, NULL, LDB_SUCCESS);
    5669             : }
    5670             : 
    5671          31 : static int samldb_extended_create_own_rid_set(struct ldb_module *module, struct ldb_request *req)
    5672             : {
    5673          31 :         struct ldb_context *ldb = ldb_module_get_ctx(module);
    5674             :         int ret;
    5675             :         struct ldb_dn *dn;
    5676             : 
    5677          31 :         if (req->op.extended.data != NULL) {
    5678           0 :                 ldb_set_errstring(ldb,
    5679             :                                   "samldb_extended_create_own_rid_set: invalid extended data (should be NULL)");
    5680           0 :                 return LDB_ERR_PROTOCOL_ERROR;
    5681             :         }
    5682             : 
    5683          31 :         ret = ridalloc_create_own_rid_set(module, req,
    5684             :                                           &dn, req);
    5685          31 :         if (ret != LDB_SUCCESS) {
    5686           1 :                 return ret;
    5687             :         }
    5688             : 
    5689          30 :         return ldb_module_done(req, NULL, NULL, LDB_SUCCESS);
    5690             : }
    5691             : 
    5692      892965 : static int samldb_extended(struct ldb_module *module, struct ldb_request *req)
    5693             : {
    5694      892965 :         if (strcmp(req->op.extended.oid, DSDB_EXTENDED_ALLOCATE_RID_POOL) == 0) {
    5695          31 :                 return samldb_extended_allocate_rid_pool(module, req);
    5696             :         }
    5697             : 
    5698      892934 :         if (strcmp(req->op.extended.oid, DSDB_EXTENDED_ALLOCATE_RID) == 0) {
    5699         995 :                 return samldb_extended_allocate_rid(module, req);
    5700             :         }
    5701             : 
    5702      891939 :         if (strcmp(req->op.extended.oid, DSDB_EXTENDED_CREATE_OWN_RID_SET) == 0) {
    5703          31 :                 return samldb_extended_create_own_rid_set(module, req);
    5704             :         }
    5705             : 
    5706      891908 :         return ldb_next_request(module, req);
    5707             : }
    5708             : 
    5709             : 
    5710             : static const struct ldb_module_ops ldb_samldb_module_ops = {
    5711             :         .name          = "samldb",
    5712             :         .add           = samldb_add,
    5713             :         .modify        = samldb_modify,
    5714             :         .del           = samldb_delete,
    5715             :         .rename        = samldb_rename,
    5716             :         .extended      = samldb_extended
    5717             : };
    5718             : 
    5719             : 
    5720        4310 : int ldb_samldb_module_init(const char *version)
    5721             : {
    5722        4310 :         LDB_MODULE_CHECK_VERSION(version);
    5723        4310 :         return ldb_register_module(&ldb_samldb_module_ops);
    5724             : }

Generated by: LCOV version 1.13