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

Generated by: LCOV version 1.14