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

Generated by: LCOV version 1.14