s3:winbindd: call process_set_title() for locator child
[Samba.git] / source4 / dsdb / samdb / ldb_modules / samldb.c
blob30f4fddc098cefa795655da9a0b414a05c1a641c
1 /*
2 SAM ldb module
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
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.
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.
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/>.
25 * Name: ldb
27 * Component: ldb samldb module
29 * Description: various internal DSDB triggers - most for SAM specific objects
31 * Author: Simo Sorce
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"
57 #undef strcasecmp
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
67 typedef int (*samldb_step_fn_t)(struct samldb_ctx *);
69 struct samldb_step {
70 struct samldb_step *next;
71 samldb_step_fn_t fn;
74 struct samldb_ctx {
75 struct ldb_module *module;
76 struct ldb_request *req;
78 /* used for add operations */
79 enum samldb_add_type type;
82 * should we apply the need_trailing_dollar restriction to
83 * samAccountName
86 bool need_trailing_dollar;
88 /* the resulting message */
89 struct ldb_message *msg;
91 /* used in "samldb_find_for_defaultObjectCategory" */
92 struct ldb_dn *dn, *res_dn;
94 /* the SID to be assigned to the resulting account */
95 const struct dom_sid *sid;
97 /* all the async steps necessary to complete the operation */
98 struct samldb_step *steps;
99 struct samldb_step *curstep;
101 /* If someone set an ares to forward controls and response back to the caller */
102 struct ldb_reply *ares;
105 static struct samldb_ctx *samldb_ctx_init(struct ldb_module *module,
106 struct ldb_request *req)
108 struct ldb_context *ldb;
109 struct samldb_ctx *ac;
111 ldb = ldb_module_get_ctx(module);
113 ac = talloc_zero(req, struct samldb_ctx);
114 if (ac == NULL) {
115 ldb_oom(ldb);
116 return NULL;
119 ac->module = module;
120 ac->req = req;
122 return ac;
125 static int samldb_add_step(struct samldb_ctx *ac, samldb_step_fn_t fn)
127 struct samldb_step *step, *stepper;
129 step = talloc_zero(ac, struct samldb_step);
130 if (step == NULL) {
131 return ldb_oom(ldb_module_get_ctx(ac->module));
134 step->fn = fn;
136 if (ac->steps == NULL) {
137 ac->steps = step;
138 ac->curstep = step;
139 } else {
140 if (ac->curstep == NULL)
141 return ldb_operr(ldb_module_get_ctx(ac->module));
142 for (stepper = ac->curstep; stepper->next != NULL;
143 stepper = stepper->next);
144 stepper->next = step;
147 return LDB_SUCCESS;
150 static int samldb_first_step(struct samldb_ctx *ac)
152 if (ac->steps == NULL) {
153 return ldb_operr(ldb_module_get_ctx(ac->module));
156 ac->curstep = ac->steps;
157 return ac->curstep->fn(ac);
160 static int samldb_next_step(struct samldb_ctx *ac)
162 if (ac->curstep->next) {
163 ac->curstep = ac->curstep->next;
164 return ac->curstep->fn(ac);
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 if (ac->ares) {
170 return ldb_module_done(ac->req, ac->ares->controls,
171 ac->ares->response, LDB_SUCCESS);
172 } else {
173 return ldb_module_done(ac->req, NULL, NULL, LDB_SUCCESS);
177 static int samldb_get_single_valued_attr(struct ldb_context *ldb,
178 struct samldb_ctx *ac,
179 const char *attr,
180 const char **value)
183 * The steps we end up going through to get and check a single valued
184 * attribute.
186 struct ldb_message_element *el = NULL;
187 int ret;
189 *value = NULL;
191 ret = dsdb_get_expected_new_values(ac,
192 ac->msg,
193 attr,
194 &el,
195 ac->req->operation);
197 if (ret != LDB_SUCCESS) {
198 return ret;
200 if (el == NULL) {
201 /* we are not affected */
202 return LDB_SUCCESS;
205 if (el->num_values > 1) {
206 ldb_asprintf_errstring(
207 ldb,
208 "samldb: %s has %u values, should be single-valued!",
209 attr, el->num_values);
210 return LDB_ERR_CONSTRAINT_VIOLATION;
211 } else if (el->num_values == 0) {
212 ldb_asprintf_errstring(
213 ldb,
214 "samldb: new value for %s "
215 "not provided for mandatory, single-valued attribute!",
216 attr);
217 return LDB_ERR_OBJECT_CLASS_VIOLATION;
221 if (el->values[0].length == 0) {
222 ldb_asprintf_errstring(
223 ldb,
224 "samldb: %s is of zero length, should have a value!",
225 attr);
226 return LDB_ERR_OBJECT_CLASS_VIOLATION;
229 *value = (char *)el->values[0].data;
231 return LDB_SUCCESS;
234 static int samldb_unique_attr_check(struct samldb_ctx *ac, const char *attr,
235 const char *attr_conflict,
236 struct ldb_dn *base_dn)
238 struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
239 const char * const no_attrs[] = { NULL };
240 struct ldb_result *res = NULL;
241 const char *str = NULL;
242 const char *enc_str = NULL;
243 int ret;
245 ret = samldb_get_single_valued_attr(ldb, ac, attr, &str);
246 if (ret != LDB_SUCCESS) {
247 return ret;
249 if (str == NULL) {
250 /* the attribute wasn't found */
251 return LDB_SUCCESS;
254 enc_str = ldb_binary_encode_string(ac, str);
255 if (enc_str == NULL) {
256 return ldb_module_oom(ac->module);
260 * No other object should have the attribute with this value.
262 if (attr_conflict != NULL) {
263 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 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);
277 if (ret != LDB_SUCCESS) {
278 return ret;
280 if (res->count > 1) {
281 return ldb_operr(ldb);
282 } else if (res->count == 1) {
283 if (ldb_dn_compare(res->msgs[0]->dn, ac->msg->dn) != 0) {
284 ldb_asprintf_errstring(ldb,
285 "samldb: %s '%s' already in use!",
286 attr, enc_str);
287 return LDB_ERR_ENTRY_ALREADY_EXISTS;
290 talloc_free(res);
292 return LDB_SUCCESS;
297 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
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.
310 const char * const no_attrs[] = { NULL };
311 struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
312 struct ldb_result *res = NULL;
313 int ret;
314 char *enc_value = ldb_binary_encode_string(ac, value);
315 if (enc_value == NULL) {
316 return ldb_module_oom(ac->module);
318 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 talloc_free(enc_value);
326 if (ret != LDB_SUCCESS) {
327 return ret;
328 } else if (res->count > 1) {
329 return ldb_operr(ldb);
330 } else if (res->count == 1) {
331 if (ldb_dn_compare(res->msgs[0]->dn, ac->msg->dn) != 0){
332 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 if (strcasecmp("userPrincipalName", attr) == 0) {
338 return LDB_ERR_CONSTRAINT_VIOLATION;
340 return LDB_ERR_ENTRY_ALREADY_EXISTS;
343 return LDB_SUCCESS;
346 static int samaccountname_bad_chars_check(struct samldb_ctx *ac,
347 const char *name)
350 * The rules here are based on
352 * https://social.technet.microsoft.com/wiki/contents/articles/11216.active-directory-requirements-for-creating-objects.aspx
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.
358 * Additionally, Samba collapses multiple spaces, and Windows doesn't.
360 struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
361 size_t i;
363 for (i = 0; name[i] != '\0'; i++) {
364 uint8_t c = name[i];
365 char *p = NULL;
366 if (c < 32 || c == 127) {
367 ldb_asprintf_errstring(
368 ldb,
369 "samldb: sAMAccountName contains invalid "
370 "0x%.2x character\n", c);
371 return LDB_ERR_CONSTRAINT_VIOLATION;
373 p = strchr("\"[]:;|=+*?<>/\\,", c);
374 if (p != NULL) {
375 ldb_asprintf_errstring(
376 ldb,
377 "samldb: sAMAccountName contains invalid "
378 "'%c' character\n", c);
379 return LDB_ERR_CONSTRAINT_VIOLATION;
383 if (i == 0) {
384 ldb_asprintf_errstring(
385 ldb,
386 "samldb: sAMAccountName is empty\n");
387 return LDB_ERR_CONSTRAINT_VIOLATION;
390 if (name[i - 1] == '.') {
391 ldb_asprintf_errstring(
392 ldb,
393 "samldb: sAMAccountName ends with '.'");
394 return LDB_ERR_CONSTRAINT_VIOLATION;
396 return LDB_SUCCESS;
399 static int samldb_sam_account_upn_clash(struct samldb_ctx *ac)
401 struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
402 int ret;
403 struct ldb_dn *base_dn = ldb_get_default_basedn(ldb);
404 TALLOC_CTX *tmp_ctx = NULL;
405 const char *real_sam = NULL;
406 const char *real_upn = NULL;
407 char *implied_sam = NULL;
408 char *implied_upn = NULL;
409 const char *realm = NULL;
411 ret = samldb_get_single_valued_attr(ldb, ac,
412 "sAMAccountName",
413 &real_sam);
414 if (ret != LDB_SUCCESS) {
415 return ret;
417 ret = samldb_get_single_valued_attr(ldb, ac,
418 "userPrincipalName",
419 &real_upn);
420 if (ret != LDB_SUCCESS) {
421 return ret;
423 if (real_upn == NULL && real_sam == NULL) {
424 /* Not changing these things, so we're done */
425 return LDB_SUCCESS;
428 tmp_ctx = talloc_new(ac);
429 realm = samdb_dn_to_dns_domain(tmp_ctx, base_dn);
430 if (realm == NULL) {
431 talloc_free(tmp_ctx);
432 return ldb_operr(ldb);
435 if (real_upn != NULL) {
437 * note we take the last @ in the upn because the first (i.e.
438 * sAMAccountName equivalent) part can contain @.
440 * It is also OK (per Windows) for a UPN to have zero @s.
442 char *at = NULL;
443 char *upn_realm = NULL;
444 implied_sam = talloc_strdup(tmp_ctx, real_upn);
445 if (implied_sam == NULL) {
446 talloc_free(tmp_ctx);
447 return ldb_module_oom(ac->module);
450 at = strrchr(implied_sam, '@');
451 if (at == NULL) {
453 * there is no @ in this UPN, so we treat the whole
454 * thing as a sAMAccountName for the purposes of a
455 * clash.
457 DBG_INFO("samldb: userPrincipalName '%s' contains "
458 "no '@' character\n", implied_sam);
459 } else {
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.
465 *at = '\0';
466 upn_realm = at + 1;
467 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 TALLOC_FREE(implied_sam);
476 if (real_sam != NULL) {
477 implied_upn = talloc_asprintf(tmp_ctx, "%s@%s",
478 real_sam, realm);
479 if (implied_upn == NULL) {
480 talloc_free(tmp_ctx);
481 return ldb_module_oom(ac->module);
486 * Now we have all of the actual and implied names, in which to search
487 * for conflicts.
489 if (real_sam != NULL) {
490 ret = samldb_sam_account_upn_clash_sub_search(
491 ac, tmp_ctx, base_dn, "sAMAccountName",
492 real_sam, "");
494 if (ret != LDB_SUCCESS) {
495 talloc_free(tmp_ctx);
496 return ret;
498 ret = samaccountname_bad_chars_check(ac, real_sam);
499 if (ret != LDB_SUCCESS) {
500 talloc_free(tmp_ctx);
501 return ret;
504 if (implied_upn != NULL) {
505 ret = samldb_sam_account_upn_clash_sub_search(
506 ac, tmp_ctx, base_dn, "userPrincipalName", implied_upn,
507 "(implied by sAMAccountName)");
509 if (ret != LDB_SUCCESS) {
510 talloc_free(tmp_ctx);
511 return ret;
514 if (real_upn != NULL) {
515 ret = samldb_sam_account_upn_clash_sub_search(
516 ac, tmp_ctx, base_dn, "userPrincipalName",
517 real_upn, "");
519 if (ret != LDB_SUCCESS) {
520 talloc_free(tmp_ctx);
521 return ret;
524 if (implied_sam != NULL) {
525 ret = samldb_sam_account_upn_clash_sub_search(
526 ac, tmp_ctx, base_dn, "sAMAccountName", implied_sam,
527 "(implied by userPrincipalName)");
528 if (ret != LDB_SUCCESS) {
529 talloc_free(tmp_ctx);
530 return ret;
534 talloc_free(tmp_ctx);
535 return LDB_SUCCESS;
539 /* This is run during an add or modify */
540 static int samldb_sam_accountname_valid_check(struct samldb_ctx *ac)
542 int ret = 0;
543 bool is_admin;
544 struct security_token *user_token = NULL;
545 struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
546 struct ldb_message_element *el = NULL;
548 ret = dsdb_get_expected_new_values(ac,
549 ac->msg,
550 "samAccountName",
551 &el,
552 ac->req->operation);
553 if (ret != LDB_SUCCESS) {
554 return ret;
557 if (el == NULL || el->num_values == 0) {
558 ldb_asprintf_errstring(ldb,
559 "%08X: samldb: 'samAccountName' can't be deleted/empty!",
560 W_ERROR_V(WERR_DS_ILLEGAL_MOD_OPERATION));
561 if (ac->req->operation == LDB_ADD) {
562 return LDB_ERR_CONSTRAINT_VIOLATION;
563 } else {
564 return LDB_ERR_UNWILLING_TO_PERFORM;
568 ret = samldb_unique_attr_check(ac, "samAccountName", NULL,
569 ldb_get_default_basedn(
570 ldb_module_get_ctx(ac->module)));
573 * Error code munging to try and match what must be some quite
574 * strange code-paths in Windows
576 if (ret == LDB_ERR_CONSTRAINT_VIOLATION
577 && ac->req->operation == LDB_MODIFY) {
578 ret = LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
579 } else if (ret == LDB_ERR_OBJECT_CLASS_VIOLATION) {
580 ret = LDB_ERR_CONSTRAINT_VIOLATION;
582 if (ret != LDB_SUCCESS) {
583 return ret;
586 ret = samldb_sam_account_upn_clash(ac);
587 if (ret != LDB_SUCCESS) {
588 return ret;
591 if (!ac->need_trailing_dollar) {
592 return LDB_SUCCESS;
595 /* This does not permit a single $ */
596 if (el->values[0].length < 2) {
597 ldb_asprintf_errstring(ldb,
598 "%08X: samldb: 'samAccountName' "
599 "can't just be one character!",
600 W_ERROR_V(WERR_DS_ILLEGAL_MOD_OPERATION));
601 return LDB_ERR_UNWILLING_TO_PERFORM;
604 user_token = acl_user_token(ac->module);
605 if (user_token == NULL) {
606 return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
609 is_admin
610 = security_token_has_builtin_administrators(user_token);
612 if (is_admin) {
614 * Administrators are allowed to select strange names.
615 * This is poor practice but not prevented.
617 return false;
620 if (el->values[0].data[el->values[0].length - 1] != '$') {
621 ldb_asprintf_errstring(ldb,
622 "%08X: samldb: 'samAccountName' "
623 "must have a trailing $!",
624 W_ERROR_V(WERR_DS_ILLEGAL_MOD_OPERATION));
625 return LDB_ERR_UNWILLING_TO_PERFORM;
627 if (el->values[0].data[el->values[0].length - 2] == '$') {
628 ldb_asprintf_errstring(ldb,
629 "%08X: samldb: 'samAccountName' "
630 "must not have a double trailing $!",
631 W_ERROR_V(WERR_DS_ILLEGAL_MOD_OPERATION));
632 return LDB_ERR_UNWILLING_TO_PERFORM;
635 return ret;
638 static int samldb_schema_attributeid_valid_check(struct samldb_ctx *ac)
640 int ret = samldb_unique_attr_check(ac, "attributeID", "governsID",
641 ldb_get_schema_basedn(
642 ldb_module_get_ctx(ac->module)));
643 if (ret == LDB_ERR_ENTRY_ALREADY_EXISTS) {
644 ret = LDB_ERR_UNWILLING_TO_PERFORM;
646 return ret;
649 static int samldb_schema_governsid_valid_check(struct samldb_ctx *ac)
651 int ret = samldb_unique_attr_check(ac, "governsID", "attributeID",
652 ldb_get_schema_basedn(
653 ldb_module_get_ctx(ac->module)));
654 if (ret == LDB_ERR_ENTRY_ALREADY_EXISTS) {
655 ret = LDB_ERR_UNWILLING_TO_PERFORM;
657 return ret;
660 static int samldb_schema_ldapdisplayname_valid_check(struct samldb_ctx *ac)
662 int ret = samldb_unique_attr_check(ac, "lDAPDisplayName", NULL,
663 ldb_get_schema_basedn(
664 ldb_module_get_ctx(ac->module)));
665 if (ret == LDB_ERR_ENTRY_ALREADY_EXISTS) {
666 ret = LDB_ERR_UNWILLING_TO_PERFORM;
668 return ret;
671 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)
678 int ret;
679 struct ldb_result *ldb_res;
681 if (dsdb_attribute_by_linkID(schema, linkID)) {
682 *found = true;
683 return LDB_SUCCESS;
686 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 if (ret != LDB_SUCCESS) {
693 ldb_debug_set(ldb, LDB_DEBUG_ERROR,
694 __location__": Searching for linkID=%d failed - %s\n",
695 linkID,
696 ldb_errstring(ldb));
697 return ldb_operr(ldb);
700 *found = (ldb_res->count != 0);
701 talloc_free(ldb_res);
703 return LDB_SUCCESS;
706 /* Find the next open forward linkID in the schema. */
707 static int samldb_generate_next_linkid(struct samldb_ctx *ac,
708 struct dsdb_schema *schema,
709 int32_t *next_linkID)
711 int ret;
712 struct ldb_context *ldb;
713 struct ldb_dn *schema_dn;
714 bool linkID_used = true;
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.
721 *next_linkID = 0x40000000;
723 ldb = ldb_module_get_ctx(ac->module);
724 schema_dn = ldb_get_schema_basedn(ldb);
726 while (linkID_used) {
727 *next_linkID += 2;
728 ret = samldb_check_linkid_used(ac, schema,
729 schema_dn, ldb,
730 *next_linkID, &linkID_used);
731 if (ret != LDB_SUCCESS) {
732 return ret;
736 return LDB_SUCCESS;
739 static int samldb_schema_add_handle_linkid(struct samldb_ctx *ac)
741 int ret;
742 bool ok, found = false;
743 struct ldb_message_element *el;
744 const char *enc_str;
745 const struct dsdb_attribute *attr;
746 struct ldb_context *ldb;
747 struct ldb_dn *schema_dn;
748 struct dsdb_schema *schema;
749 int32_t new_linkID = 0;
751 ldb = ldb_module_get_ctx(ac->module);
752 schema = dsdb_get_schema(ldb, ac);
753 schema_dn = ldb_get_schema_basedn(ldb);
755 ret = dsdb_get_expected_new_values(ac,
756 ac->msg,
757 "linkID",
758 &el,
759 ac->req->operation);
760 if (ret != LDB_SUCCESS) {
761 return ret;
764 if (el == NULL || el->num_values == 0) {
765 return LDB_SUCCESS;
768 enc_str = ldb_binary_encode(ac, el->values[0]);
769 if (enc_str == NULL) {
770 return ldb_module_oom(ac->module);
773 ok = (strcmp(enc_str, "0") == 0);
774 if (ok) {
775 return LDB_SUCCESS;
779 * This OID indicates that the caller wants the linkID
780 * to be automatically generated. We therefore assign
781 * it the next open linkID.
783 ok = (strcmp(enc_str, "1.2.840.113556.1.2.50") == 0);
784 if (ok) {
785 ret = samldb_generate_next_linkid(ac, schema, &new_linkID);
786 if (ret != LDB_SUCCESS) {
787 return ret;
790 ldb_msg_remove_element(ac->msg, el);
791 ret = samdb_msg_add_int(ldb, ac->msg, ac->msg, "linkID",
792 new_linkID);
793 return ret;
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.
801 attr = dsdb_attribute_by_attributeID_oid(schema, enc_str);
802 if (attr == NULL) {
803 attr = dsdb_attribute_by_lDAPDisplayName(schema, enc_str);
806 if (attr != NULL) {
808 * The attribute we're adding this as a backlink of must
809 * be a forward link.
811 if (attr->linkID % 2 != 0) {
812 return LDB_ERR_UNWILLING_TO_PERFORM;
815 new_linkID = attr->linkID + 1;
817 /* Make sure that this backlink doesn't already exist. */
818 ret = samldb_check_linkid_used(ac, schema,
819 schema_dn, ldb,
820 new_linkID, &found);
821 if (ret != LDB_SUCCESS) {
822 return ret;
825 if (found) {
826 return LDB_ERR_UNWILLING_TO_PERFORM;
829 ldb_msg_remove_element(ac->msg, el);
830 ret = samdb_msg_add_int(ldb, ac->msg, ac->msg, "linkID",
831 new_linkID);
832 return ret;
835 schema_dn = ldb_get_schema_basedn(ldb_module_get_ctx(ac->module));
836 ret = samldb_unique_attr_check(ac, "linkID", NULL, schema_dn);
837 if (ret == LDB_ERR_ENTRY_ALREADY_EXISTS) {
838 return LDB_ERR_UNWILLING_TO_PERFORM;
839 } else {
840 return ret;
844 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)
851 int ret;
852 struct ldb_result *ldb_res;
854 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 if (ret != LDB_SUCCESS) {
861 ldb_debug_set(ldb, LDB_DEBUG_ERROR,
862 __location__": Searching for mAPIID=%d failed - %s\n",
863 mapiid,
864 ldb_errstring(ldb));
865 return ldb_operr(ldb);
868 *found = (ldb_res->count != 0);
869 talloc_free(ldb_res);
871 return LDB_SUCCESS;
874 static int samldb_generate_next_mapiid(struct samldb_ctx *ac,
875 struct dsdb_schema *schema,
876 int32_t *next_mapiid)
878 int ret;
879 struct ldb_context *ldb;
880 struct ldb_dn *schema_dn;
881 bool mapiid_used = true;
883 /* Windows' generation seems to start about here */
884 *next_mapiid = 60000;
886 ldb = ldb_module_get_ctx(ac->module);
887 schema_dn = ldb_get_schema_basedn(ldb);
889 while (mapiid_used) {
890 *next_mapiid += 1;
891 ret = samldb_check_mapiid_used(ac, schema,
892 schema_dn, ldb,
893 *next_mapiid, &mapiid_used);
894 if (ret != LDB_SUCCESS) {
895 return ret;
899 return LDB_SUCCESS;
902 static int samldb_schema_add_handle_mapiid(struct samldb_ctx *ac)
904 int ret;
905 bool ok;
906 struct ldb_message_element *el;
907 const char *enc_str;
908 struct ldb_context *ldb;
909 struct ldb_dn *schema_dn;
910 struct dsdb_schema *schema;
911 int32_t new_mapiid = 0;
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.
919 ldb = ldb_module_get_ctx(ac->module);
920 schema = dsdb_get_schema(ldb, ac);
921 schema_dn = ldb_get_schema_basedn(ldb);
923 ret = dsdb_get_expected_new_values(ac,
924 ac->msg,
925 "mAPIID",
926 &el,
927 ac->req->operation);
928 if (ret != LDB_SUCCESS) {
929 return ret;
932 if (el == NULL || el->num_values == 0) {
933 return LDB_SUCCESS;
936 enc_str = ldb_binary_encode(ac, el->values[0]);
937 if (enc_str == NULL) {
938 return ldb_module_oom(ac->module);
941 ok = (strcmp(enc_str, "1.2.840.113556.1.2.49") == 0);
942 if (ok) {
943 ret = samldb_generate_next_mapiid(ac, schema,
944 &new_mapiid);
945 if (ret != LDB_SUCCESS) {
946 return ret;
949 ldb_msg_remove_element(ac->msg, el);
950 ret = samdb_msg_add_int(ldb, ac->msg, ac->msg,
951 "mAPIID", new_mapiid);
952 return ret;
955 schema_dn = ldb_get_schema_basedn(ldb_module_get_ctx(ac->module));
956 ret = samldb_unique_attr_check(ac, "mAPIID", NULL, schema_dn);
957 if (ret == LDB_ERR_ENTRY_ALREADY_EXISTS) {
958 return LDB_ERR_UNWILLING_TO_PERFORM;
959 } else {
960 return ret;
964 /* sAMAccountName handling */
965 static int samldb_generate_sAMAccountName(struct samldb_ctx *ac,
966 struct ldb_message *msg)
968 struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
969 char *name;
972 * This is currently a Samba-only behaviour, to add a trailing
973 * $ even for the generated accounts.
976 if (ac->need_trailing_dollar) {
977 /* Format: $000000-00000000000$ */
978 name = talloc_asprintf(msg, "$%.6X-%.6X%.5X$",
979 (unsigned int)generate_random(),
980 (unsigned int)generate_random(),
981 (unsigned int)generate_random());
982 } else {
983 /* Format: $000000-000000000000 */
985 name = talloc_asprintf(msg, "$%.6X-%.6X%.6X",
986 (unsigned int)generate_random(),
987 (unsigned int)generate_random(),
988 (unsigned int)generate_random());
990 if (name == NULL) {
991 return ldb_oom(ldb);
993 return ldb_msg_add_steal_string(msg, "sAMAccountName", name);
996 static int samldb_check_sAMAccountName(struct samldb_ctx *ac)
998 int ret;
1000 if (ldb_msg_find_element(ac->msg, "sAMAccountName") == NULL) {
1001 ret = samldb_generate_sAMAccountName(ac, ac->msg);
1002 if (ret != LDB_SUCCESS) {
1003 return ret;
1007 ret = samldb_sam_accountname_valid_check(ac);
1008 if (ret != LDB_SUCCESS) {
1009 return ret;
1012 return samldb_next_step(ac);
1016 static bool samldb_msg_add_sid(struct ldb_message *msg,
1017 const char *name,
1018 const struct dom_sid *sid)
1020 struct ldb_val v;
1021 enum ndr_err_code ndr_err;
1023 ndr_err = ndr_push_struct_blob(&v, msg, sid,
1024 (ndr_push_flags_fn_t)ndr_push_dom_sid);
1025 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1026 return false;
1028 return (ldb_msg_add_value(msg, name, &v, NULL) == 0);
1032 /* allocate a SID using our RID Set */
1033 static int samldb_allocate_sid(struct samldb_ctx *ac)
1035 uint32_t rid;
1036 struct dom_sid *sid;
1037 struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
1038 int ret;
1040 ret = ridalloc_allocate_rid(ac->module, &rid, ac->req);
1041 if (ret != LDB_SUCCESS) {
1042 return ret;
1045 sid = dom_sid_add_rid(ac, samdb_domain_sid(ldb), rid);
1046 if (sid == NULL) {
1047 return ldb_module_oom(ac->module);
1050 if ( ! samldb_msg_add_sid(ac->msg, "objectSid", sid)) {
1051 return ldb_operr(ldb);
1054 ac->sid = sid;
1056 return samldb_next_step(ac);
1060 see if a krbtgt_number is available
1062 static bool samldb_krbtgtnumber_available(struct samldb_ctx *ac,
1063 uint32_t krbtgt_number)
1065 TALLOC_CTX *tmp_ctx = talloc_new(ac);
1066 struct ldb_result *res;
1067 const char * const no_attrs[] = { NULL };
1068 int ret;
1070 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 if (ret == LDB_SUCCESS && res->count == 0) {
1078 talloc_free(tmp_ctx);
1079 return true;
1081 talloc_free(tmp_ctx);
1082 return false;
1085 /* special handling for add in RODC join */
1086 static int samldb_rodc_add(struct samldb_ctx *ac)
1088 struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
1089 uint32_t krbtgt_number, i_start, i;
1090 int ret;
1091 struct ldb_val newpass_utf16;
1093 /* find a unused msDS-SecondaryKrbTgtNumber */
1094 i_start = generate_random() & 0xFFFF;
1095 if (i_start == 0) {
1096 i_start = 1;
1099 for (i=i_start; i<=0xFFFF; i++) {
1100 if (samldb_krbtgtnumber_available(ac, i)) {
1101 krbtgt_number = i;
1102 goto found;
1105 for (i=1; i<i_start; i++) {
1106 if (samldb_krbtgtnumber_available(ac, i)) {
1107 krbtgt_number = i;
1108 goto found;
1112 ldb_asprintf_errstring(ldb,
1113 "%08X: Unable to find available msDS-SecondaryKrbTgtNumber",
1114 W_ERROR_V(WERR_NO_SYSTEM_RESOURCES));
1115 return LDB_ERR_OTHER;
1117 found:
1119 ldb_msg_remove_attr(ac->msg, "msDS-SecondaryKrbTgtNumber");
1120 ret = samdb_msg_append_uint(ldb, ac->msg, ac->msg,
1121 "msDS-SecondaryKrbTgtNumber", krbtgt_number,
1122 LDB_FLAG_INTERNAL_DISABLE_VALIDATION);
1123 if (ret != LDB_SUCCESS) {
1124 return ldb_operr(ldb);
1127 ret = ldb_msg_add_fmt(ac->msg, "sAMAccountName", "krbtgt_%u",
1128 krbtgt_number);
1129 if (ret != LDB_SUCCESS) {
1130 return ldb_operr(ldb);
1133 newpass_utf16 = data_blob_talloc_zero(ac->module, 256);
1134 if (newpass_utf16.data == NULL) {
1135 return ldb_oom(ldb);
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.
1143 generate_random_buffer(newpass_utf16.data, newpass_utf16.length);
1144 ret = ldb_msg_add_steal_value(ac->msg, "clearTextPassword", &newpass_utf16);
1145 if (ret != LDB_SUCCESS) {
1146 return ldb_operr(ldb);
1149 return samldb_next_step(ac);
1152 static int samldb_gmsa_add(struct samldb_ctx *ac)
1154 struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
1155 int ret = LDB_SUCCESS;
1156 NTTIME current_time = 0;
1157 const bool userPassword = dsdb_user_password_support(ac->module,
1158 ac->msg,
1159 ac->req);
1160 bool ok;
1162 ok = dsdb_gmsa_current_time(ldb, &current_time);
1163 if (!ok) {
1164 ret = ldb_operr(ldb);
1165 goto out;
1168 /* Remove any user‐specified passwords. */
1169 dsdb_remove_password_related_attrs(ac->msg, userPassword);
1171 /* Remove any user‐specified password IDs. */
1172 ldb_msg_remove_attr(ac->msg, "msDS-ManagedPasswordId");
1173 ldb_msg_remove_attr(ac->msg, "msDS-ManagedPasswordPreviousId");
1176 DATA_BLOB pwd_id_blob = {};
1177 DATA_BLOB password_blob = {};
1178 struct gmsa_null_terminated_password *password = NULL;
1181 * The account must have a SID allocated for us to be able to
1182 * derive its password.
1184 if (ac->sid == NULL) {
1185 ret = ldb_operr(ldb);
1186 goto out;
1189 /* Calculate the password and ID blobs. */
1190 ret = gmsa_generate_blobs(ldb,
1191 ac->msg,
1192 current_time,
1193 ac->sid,
1194 &pwd_id_blob,
1195 &password);
1196 if (ret) {
1197 goto out;
1200 password_blob = (DATA_BLOB){.data = password->buf,
1201 .length = GMSA_PASSWORD_LEN};
1203 /* Add the new password blob. */
1204 ret = ldb_msg_append_steal_value(ac->msg,
1205 "clearTextPassword",
1206 &password_blob,
1208 if (ret) {
1209 goto out;
1212 /* Add the new password ID blob. */
1213 ret = ldb_msg_append_steal_value(ac->msg,
1214 "msDS-ManagedPasswordId",
1215 &pwd_id_blob,
1217 if (ret) {
1218 goto out;
1222 ret = samldb_next_step(ac);
1223 if (ret) {
1224 goto out;
1227 out:
1228 return ret;
1231 static int samldb_find_for_defaultObjectCategory(struct samldb_ctx *ac)
1233 struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
1234 struct ldb_result *res;
1235 const char * const no_attrs[] = { NULL };
1236 int ret;
1238 ac->res_dn = NULL;
1240 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 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 if (ldb_request_get_control(ac->req,
1250 LDB_CONTROL_RELAX_OID) == NULL) {
1251 ldb_set_errstring(ldb,
1252 "samldb_find_defaultObjectCategory: "
1253 "Invalid DN for 'defaultObjectCategory'!");
1254 return LDB_ERR_CONSTRAINT_VIOLATION;
1257 if ((ret != LDB_ERR_NO_SUCH_OBJECT) && (ret != LDB_SUCCESS)) {
1258 return ret;
1261 if (ret == LDB_SUCCESS) {
1262 /* ensure the defaultObjectCategory has a full GUID */
1263 struct ldb_message *m;
1264 m = ldb_msg_new(ac->msg);
1265 if (m == NULL) {
1266 return ldb_oom(ldb);
1268 m->dn = ac->msg->dn;
1269 if (ldb_msg_add_string(m, "defaultObjectCategory",
1270 ldb_dn_get_extended_linearized(m, res->msgs[0]->dn, 1)) !=
1271 LDB_SUCCESS) {
1272 return ldb_oom(ldb);
1274 m->elements[0].flags = LDB_FLAG_MOD_REPLACE;
1276 ret = dsdb_module_modify(ac->module, m,
1277 DSDB_FLAG_NEXT_MODULE,
1278 ac->req);
1279 if (ret != LDB_SUCCESS) {
1280 return ret;
1285 ac->res_dn = ac->dn;
1287 return samldb_next_step(ac);
1291 * msDS-IntId attributeSchema attribute handling
1292 * during LDB_ADD request processing
1294 static int samldb_add_handle_msDS_IntId(struct samldb_ctx *ac)
1296 int ret;
1297 bool id_exists;
1298 uint32_t msds_intid;
1299 int32_t system_flags;
1300 struct ldb_context *ldb;
1301 struct ldb_result *ldb_res;
1302 struct ldb_dn *schema_dn;
1303 struct samldb_msds_intid_persistant *msds_intid_struct;
1304 struct dsdb_schema *schema;
1306 ldb = ldb_module_get_ctx(ac->module);
1307 schema_dn = ldb_get_schema_basedn(ldb);
1309 /* replicated update should always go through */
1310 if (ldb_request_get_control(ac->req,
1311 DSDB_CONTROL_REPLICATED_UPDATE_OID)) {
1312 return LDB_SUCCESS;
1315 /* msDS-IntId is handled by system and should never be
1316 * passed by clients */
1317 if (ldb_msg_find_element(ac->msg, "msDS-IntId")) {
1318 return LDB_ERR_UNWILLING_TO_PERFORM;
1321 /* do not generate msDS-IntId if Relax control is passed */
1322 if (ldb_request_get_control(ac->req, LDB_CONTROL_RELAX_OID)) {
1323 return LDB_SUCCESS;
1326 /* check Functional Level */
1327 if (dsdb_functional_level(ldb) < DS_DOMAIN_FUNCTION_2003) {
1328 return LDB_SUCCESS;
1331 /* check systemFlags for SCHEMA_BASE_OBJECT flag */
1332 system_flags = ldb_msg_find_attr_as_int(ac->msg, "systemFlags", 0);
1333 if (system_flags & SYSTEM_FLAG_SCHEMA_BASE_OBJECT) {
1334 return LDB_SUCCESS;
1336 schema = dsdb_get_schema(ldb, NULL);
1337 if (!schema) {
1338 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
1339 "samldb_schema_info_update: no dsdb_schema loaded");
1340 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
1341 return ldb_operr(ldb);
1344 msds_intid_struct = (struct samldb_msds_intid_persistant*) ldb_get_opaque(ldb, SAMLDB_MSDS_INTID_OPAQUE);
1345 if (!msds_intid_struct) {
1346 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 msds_intid = generate_random() % 0X3FFFFFFF;
1350 msds_intid += 0x80000000;
1351 msds_intid_struct->msds_intid = msds_intid;
1352 DEBUG(2, ("No samldb_msds_intid_persistant struct, allocating a new one\n"));
1353 } else {
1354 msds_intid = msds_intid_struct->msds_intid;
1357 /* probe id values until unique one is found */
1358 do {
1359 msds_intid++;
1360 if (msds_intid > 0xBFFFFFFF) {
1361 msds_intid = 0x80000001;
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).
1369 * If so generate another random value.
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.
1376 if (dsdb_attribute_by_attributeID_id(schema, msds_intid)) {
1377 id_exists = true;
1378 msds_intid = generate_random() % 0X3FFFFFFF;
1379 msds_intid += 0x80000000;
1380 continue;
1384 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 if (ret != LDB_SUCCESS) {
1391 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 return ldb_operr(ldb);
1397 id_exists = (ldb_res->count > 0);
1398 talloc_free(ldb_res);
1400 } while(id_exists);
1401 msds_intid_struct->msds_intid = msds_intid;
1402 ldb_set_opaque(ldb, SAMLDB_MSDS_INTID_OPAQUE, msds_intid_struct);
1404 return samdb_msg_add_int(ldb, ac->msg, ac->msg, "msDS-IntId",
1405 msds_intid);
1410 * samldb_add_entry (async)
1413 static int samldb_add_entry_callback(struct ldb_request *req,
1414 struct ldb_reply *ares)
1416 struct ldb_context *ldb;
1417 struct samldb_ctx *ac;
1418 int ret;
1420 ac = talloc_get_type(req->context, struct samldb_ctx);
1421 ldb = ldb_module_get_ctx(ac->module);
1423 if (!ares) {
1424 return ldb_module_done(ac->req, NULL, NULL,
1425 LDB_ERR_OPERATIONS_ERROR);
1428 if (ares->type == LDB_REPLY_REFERRAL) {
1429 return ldb_module_send_referral(ac->req, ares->referral);
1432 if (ares->error != LDB_SUCCESS) {
1433 return ldb_module_done(ac->req, ares->controls,
1434 ares->response, ares->error);
1436 if (ares->type != LDB_REPLY_DONE) {
1437 ldb_asprintf_errstring(ldb, "Invalid LDB reply type %d", ares->type);
1438 return ldb_module_done(ac->req, NULL, NULL,
1439 LDB_ERR_OPERATIONS_ERROR);
1442 /* The caller may wish to get controls back from the add */
1443 ac->ares = talloc_steal(ac, ares);
1445 ret = samldb_next_step(ac);
1446 if (ret != LDB_SUCCESS) {
1447 return ldb_module_done(ac->req, NULL, NULL, ret);
1449 return ret;
1452 static int samldb_add_entry(struct samldb_ctx *ac)
1454 struct ldb_context *ldb;
1455 struct ldb_request *req;
1456 int ret;
1458 ldb = ldb_module_get_ctx(ac->module);
1460 ret = ldb_build_add_req(&req, ldb, ac,
1461 ac->msg,
1462 ac->req->controls,
1463 ac, samldb_add_entry_callback,
1464 ac->req);
1465 LDB_REQ_SET_LOCATION(req);
1466 if (ret != LDB_SUCCESS) {
1467 return ret;
1470 return ldb_next_request(ac->module, req);
1474 * return true if msg carries an attributeSchema that is intended to be RODC
1475 * filtered but is also a system-critical attribute.
1477 static bool check_rodc_critical_attribute(struct ldb_message *msg)
1479 uint32_t schemaFlagsEx, searchFlags, rodc_filtered_flags;
1481 schemaFlagsEx = ldb_msg_find_attr_as_uint(msg, "schemaFlagsEx", 0);
1482 searchFlags = ldb_msg_find_attr_as_uint(msg, "searchFlags", 0);
1483 rodc_filtered_flags = (SEARCH_FLAG_RODC_ATTRIBUTE
1484 | SEARCH_FLAG_CONFIDENTIAL);
1486 if ((schemaFlagsEx & SCHEMA_FLAG_ATTR_IS_CRITICAL) &&
1487 ((searchFlags & rodc_filtered_flags) == rodc_filtered_flags)) {
1488 return true;
1489 } else {
1490 return false;
1495 static int samldb_fill_object(struct samldb_ctx *ac)
1497 struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
1498 int ret;
1500 /* Add information for the different account types */
1501 switch(ac->type) {
1502 case SAMLDB_TYPE_USER: {
1503 struct ldb_control *rodc_control = ldb_request_get_control(ac->req,
1504 LDB_CONTROL_RODC_DCPROMO_OID);
1505 if (rodc_control != NULL) {
1506 /* see [MS-ADTS] 3.1.1.3.4.1.23 LDAP_SERVER_RODC_DCPROMO_OID */
1507 rodc_control->critical = false;
1508 ret = samldb_add_step(ac, samldb_rodc_add);
1509 if (ret != LDB_SUCCESS) return ret;
1512 if (dsdb_account_is_gmsa(ldb, ac->msg)) {
1513 ret = samldb_add_step(ac, samldb_gmsa_add);
1514 if (ret != LDB_SUCCESS) {
1515 return ret;
1519 /* check if we have a valid sAMAccountName */
1520 ret = samldb_add_step(ac, samldb_check_sAMAccountName);
1521 if (ret != LDB_SUCCESS) return ret;
1523 ret = samldb_add_step(ac, samldb_add_entry);
1524 if (ret != LDB_SUCCESS) return ret;
1525 break;
1528 case SAMLDB_TYPE_GROUP: {
1529 /* check if we have a valid sAMAccountName */
1530 ret = samldb_add_step(ac, samldb_check_sAMAccountName);
1531 if (ret != LDB_SUCCESS) return ret;
1533 ret = samldb_add_step(ac, samldb_add_entry);
1534 if (ret != LDB_SUCCESS) return ret;
1535 break;
1538 case SAMLDB_TYPE_CLASS: {
1539 const char *lDAPDisplayName = NULL;
1540 const struct ldb_val *rdn_value, *def_obj_cat_val;
1541 unsigned int v = ldb_msg_find_attr_as_uint(ac->msg, "objectClassCategory", -2);
1543 /* As discussed with Microsoft through dochelp in April 2012 this is the behavior of windows*/
1544 if (!ldb_msg_find_element(ac->msg, "subClassOf")) {
1545 ret = ldb_msg_add_string(ac->msg, "subClassOf", "top");
1546 if (ret != LDB_SUCCESS) return ret;
1549 ret = samdb_find_or_add_attribute(ldb, ac->msg,
1550 "rdnAttId", "cn");
1551 if (ret != LDB_SUCCESS) return ret;
1553 /* do not allow one to mark an attributeSchema as RODC filtered if it
1554 * is system-critical */
1555 if (check_rodc_critical_attribute(ac->msg)) {
1556 ldb_asprintf_errstring(ldb, "Refusing schema add of %s - cannot combine critical class with RODC filtering",
1557 ldb_dn_get_linearized(ac->msg->dn));
1558 return LDB_ERR_UNWILLING_TO_PERFORM;
1561 rdn_value = ldb_dn_get_rdn_val(ac->msg->dn);
1562 if (rdn_value == NULL) {
1563 return ldb_operr(ldb);
1565 if (!ldb_msg_find_element(ac->msg, "lDAPDisplayName")) {
1566 /* the RDN has prefix "CN" */
1567 ret = ldb_msg_add_string(ac->msg, "lDAPDisplayName",
1568 samdb_cn_to_lDAPDisplayName(ac->msg,
1569 (const char *) rdn_value->data));
1570 if (ret != LDB_SUCCESS) {
1571 ldb_oom(ldb);
1572 return ret;
1576 lDAPDisplayName = ldb_msg_find_attr_as_string(ac->msg,
1577 "lDAPDisplayName",
1578 NULL);
1579 ret = ldb_valid_attr_name(lDAPDisplayName);
1580 if (ret != 1 ||
1581 lDAPDisplayName[0] == '*' ||
1582 lDAPDisplayName[0] == '@')
1584 return dsdb_module_werror(ac->module,
1585 LDB_ERR_UNWILLING_TO_PERFORM,
1586 WERR_DS_INVALID_LDAP_DISPLAY_NAME,
1587 "lDAPDisplayName is invalid");
1590 if (!ldb_msg_find_element(ac->msg, "schemaIDGUID")) {
1591 struct GUID guid;
1592 /* a new GUID */
1593 guid = GUID_random();
1594 ret = dsdb_msg_add_guid(ac->msg, &guid, "schemaIDGUID");
1595 if (ret != LDB_SUCCESS) {
1596 ldb_oom(ldb);
1597 return ret;
1601 def_obj_cat_val = ldb_msg_find_ldb_val(ac->msg,
1602 "defaultObjectCategory");
1603 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".
1612 ac->dn = ldb_dn_from_ldb_val(ac, ldb, def_obj_cat_val);
1613 if (ac->dn == NULL) {
1614 ldb_set_errstring(ldb,
1615 "Invalid DN for 'defaultObjectCategory'!");
1616 return LDB_ERR_CONSTRAINT_VIOLATION;
1618 } else {
1619 /* "defaultObjectCategory" has not been set by the
1620 * caller. Use the entry DN for it. */
1621 ac->dn = ac->msg->dn;
1623 ret = ldb_msg_add_string(ac->msg, "defaultObjectCategory",
1624 ldb_dn_alloc_linearized(ac->msg, ac->dn));
1625 if (ret != LDB_SUCCESS) {
1626 ldb_oom(ldb);
1627 return ret;
1631 ret = samldb_add_step(ac, samldb_add_entry);
1632 if (ret != LDB_SUCCESS) return ret;
1634 /* Now perform the checks for the 'defaultObjectCategory'. The
1635 * lookup DN was already saved in "ac->dn" */
1636 ret = samldb_add_step(ac, samldb_find_for_defaultObjectCategory);
1637 if (ret != LDB_SUCCESS) return ret;
1639 /* -2 is not a valid objectClassCategory so it means the attribute wasn't present */
1640 if (v == -2) {
1641 /* Windows 2003 does this*/
1642 ret = samdb_msg_add_uint(ldb, ac->msg, ac->msg, "objectClassCategory", 0);
1643 if (ret != LDB_SUCCESS) {
1644 return ret;
1647 break;
1650 case SAMLDB_TYPE_ATTRIBUTE: {
1651 const char *lDAPDisplayName = NULL;
1652 const struct ldb_val *rdn_value;
1653 struct ldb_message_element *el;
1654 rdn_value = ldb_dn_get_rdn_val(ac->msg->dn);
1655 if (rdn_value == NULL) {
1656 return ldb_operr(ldb);
1658 if (!ldb_msg_find_element(ac->msg, "lDAPDisplayName")) {
1659 /* the RDN has prefix "CN" */
1660 ret = ldb_msg_add_string(ac->msg, "lDAPDisplayName",
1661 samdb_cn_to_lDAPDisplayName(ac->msg,
1662 (const char *) rdn_value->data));
1663 if (ret != LDB_SUCCESS) {
1664 ldb_oom(ldb);
1665 return ret;
1669 lDAPDisplayName = ldb_msg_find_attr_as_string(ac->msg,
1670 "lDAPDisplayName",
1671 NULL);
1672 ret = ldb_valid_attr_name(lDAPDisplayName);
1673 if (ret != 1 ||
1674 lDAPDisplayName[0] == '*' ||
1675 lDAPDisplayName[0] == '@')
1677 return dsdb_module_werror(ac->module,
1678 LDB_ERR_UNWILLING_TO_PERFORM,
1679 WERR_DS_INVALID_LDAP_DISPLAY_NAME,
1680 "lDAPDisplayName is invalid");
1683 /* do not allow one to mark an attributeSchema as RODC filtered if it
1684 * is system-critical */
1685 if (check_rodc_critical_attribute(ac->msg)) {
1686 ldb_asprintf_errstring(ldb,
1687 "samldb: refusing schema add of %s - cannot combine critical attribute with RODC filtering",
1688 ldb_dn_get_linearized(ac->msg->dn));
1689 return LDB_ERR_UNWILLING_TO_PERFORM;
1692 ret = samdb_find_or_add_attribute(ldb, ac->msg,
1693 "isSingleValued", "FALSE");
1694 if (ret != LDB_SUCCESS) return ret;
1696 if (!ldb_msg_find_element(ac->msg, "schemaIDGUID")) {
1697 struct GUID guid;
1698 /* a new GUID */
1699 guid = GUID_random();
1700 ret = dsdb_msg_add_guid(ac->msg, &guid, "schemaIDGUID");
1701 if (ret != LDB_SUCCESS) {
1702 ldb_oom(ldb);
1703 return ret;
1707 el = ldb_msg_find_element(ac->msg, "attributeSyntax");
1708 if (el) {
1710 * No need to scream if there isn't as we have code later on
1711 * that will take care of it.
1713 const struct dsdb_syntax *syntax = find_syntax_map_by_ad_oid((const char *)el->values[0].data);
1714 if (!syntax) {
1715 DEBUG(9, ("Can't find dsdb_syntax object for attributeSyntax %s\n",
1716 (const char *)el->values[0].data));
1717 } else {
1718 unsigned int v = ldb_msg_find_attr_as_uint(ac->msg, "oMSyntax", 0);
1719 const struct ldb_val *val = ldb_msg_find_ldb_val(ac->msg, "oMObjectClass");
1721 if (v == 0) {
1722 ret = samdb_msg_add_uint(ldb, ac->msg, ac->msg, "oMSyntax", syntax->oMSyntax);
1723 if (ret != LDB_SUCCESS) {
1724 return ret;
1727 if (!val) {
1728 struct ldb_val val2 = ldb_val_dup(ldb, &syntax->oMObjectClass);
1729 if (val2.length > 0) {
1730 ret = ldb_msg_add_value(ac->msg, "oMObjectClass", &val2, NULL);
1731 if (ret != LDB_SUCCESS) {
1732 return ret;
1739 /* handle msDS-IntID attribute */
1740 ret = samldb_add_handle_msDS_IntId(ac);
1741 if (ret != LDB_SUCCESS) return ret;
1743 ret = samldb_add_step(ac, samldb_add_entry);
1744 if (ret != LDB_SUCCESS) return ret;
1745 break;
1748 default:
1749 ldb_asprintf_errstring(ldb, "Invalid entry type!");
1750 return LDB_ERR_OPERATIONS_ERROR;
1751 break;
1754 return samldb_first_step(ac);
1757 static int samldb_fill_foreignSecurityPrincipal_object(struct samldb_ctx *ac)
1759 struct ldb_context *ldb = NULL;
1760 const struct ldb_val *rdn_value = NULL;
1761 struct ldb_message_element *sid_el = NULL;
1762 struct dom_sid *sid = NULL;
1763 struct ldb_control *as_system = NULL;
1764 struct ldb_control *provision = NULL;
1765 bool allowed = false;
1766 int ret;
1768 ldb = ldb_module_get_ctx(ac->module);
1770 as_system = ldb_request_get_control(ac->req, LDB_CONTROL_AS_SYSTEM_OID);
1771 if (as_system != NULL) {
1772 allowed = true;
1775 provision = ldb_request_get_control(ac->req, LDB_CONTROL_PROVISION_OID);
1776 if (provision != NULL) {
1777 allowed = true;
1780 sid_el = ldb_msg_find_element(ac->msg, "objectSid");
1782 if (!allowed && sid_el == NULL) {
1783 return dsdb_module_werror(ac->module,
1784 LDB_ERR_OBJECT_CLASS_VIOLATION,
1785 WERR_DS_MISSING_REQUIRED_ATT,
1786 "objectSid missing on foreignSecurityPrincipal");
1789 if (!allowed) {
1790 return dsdb_module_werror(ac->module,
1791 LDB_ERR_UNWILLING_TO_PERFORM,
1792 WERR_DS_ILLEGAL_MOD_OPERATION,
1793 "foreignSecurityPrincipal object not allowed");
1796 if (sid_el != NULL) {
1797 sid = samdb_result_dom_sid(ac->msg, ac->msg, "objectSid");
1798 if (sid == NULL) {
1799 ldb_set_errstring(ldb,
1800 "samldb: invalid objectSid!");
1801 return LDB_ERR_CONSTRAINT_VIOLATION;
1805 if (sid == NULL) {
1806 rdn_value = ldb_dn_get_rdn_val(ac->msg->dn);
1807 if (rdn_value == NULL) {
1808 return ldb_operr(ldb);
1810 sid = dom_sid_parse_talloc(ac->msg,
1811 (const char *)rdn_value->data);
1812 if (sid == NULL) {
1813 ldb_set_errstring(ldb,
1814 "samldb: No valid SID found in ForeignSecurityPrincipal CN!");
1815 return LDB_ERR_CONSTRAINT_VIOLATION;
1817 if (! samldb_msg_add_sid(ac->msg, "objectSid", sid)) {
1818 return ldb_operr(ldb);
1822 /* finally proceed with adding the entry */
1823 ret = samldb_add_step(ac, samldb_add_entry);
1824 if (ret != LDB_SUCCESS) return ret;
1826 return samldb_first_step(ac);
1829 static int samldb_schema_info_update(struct samldb_ctx *ac)
1831 int ret;
1832 struct ldb_context *ldb;
1833 struct dsdb_schema *schema;
1835 /* replicated update should always go through */
1836 if (ldb_request_get_control(ac->req,
1837 DSDB_CONTROL_REPLICATED_UPDATE_OID)) {
1838 return LDB_SUCCESS;
1841 /* do not update schemaInfo during provisioning */
1842 if (ldb_request_get_control(ac->req, LDB_CONTROL_PROVISION_OID)) {
1843 return LDB_SUCCESS;
1846 ldb = ldb_module_get_ctx(ac->module);
1847 schema = dsdb_get_schema(ldb, NULL);
1848 if (!schema) {
1849 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
1850 "samldb_schema_info_update: no dsdb_schema loaded");
1851 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
1852 return ldb_operr(ldb);
1855 ret = dsdb_module_schema_info_update(ac->module, schema,
1856 DSDB_FLAG_NEXT_MODULE|
1857 DSDB_FLAG_AS_SYSTEM,
1858 ac->req);
1859 if (ret != LDB_SUCCESS) {
1860 ldb_asprintf_errstring(ldb,
1861 "samldb_schema_info_update: dsdb_module_schema_info_update failed with %s",
1862 ldb_errstring(ldb));
1863 return ret;
1866 return LDB_SUCCESS;
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);
1878 * "Objectclass" trigger (MS-SAMR 3.1.1.8.1)
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)
1885 static int samldb_objectclass_trigger(struct samldb_ctx *ac)
1887 struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
1888 void *skip_allocate_sids = ldb_get_opaque(ldb,
1889 "skip_allocate_sids");
1890 struct ldb_message_element *el;
1891 struct dom_sid *sid;
1892 int ret;
1894 /* make sure that "sAMAccountType" is not specified */
1895 el = ldb_msg_find_element(ac->msg, "sAMAccountType");
1896 if (el != NULL) {
1897 ldb_set_errstring(ldb,
1898 "samldb: sAMAccountType must not be specified!");
1899 return LDB_ERR_UNWILLING_TO_PERFORM;
1902 /* Step 1: objectSid assignment */
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 sid = samdb_result_dom_sid(ac, ac->msg, "objectSid");
1908 if ((sid != NULL) && (!dsdb_module_am_system(ac->module)) &&
1909 (ldb_request_get_control(ac->req, LDB_CONTROL_RELAX_OID) == NULL)) {
1910 ldb_set_errstring(ldb,
1911 "samldb: objectSid must not be specified!");
1912 return LDB_ERR_UNWILLING_TO_PERFORM;
1915 /* but generate a new SID when we do have an add operations */
1916 if ((sid == NULL) && (ac->req->operation == LDB_ADD) && !skip_allocate_sids) {
1917 ret = samldb_add_step(ac, samldb_allocate_sid);
1918 if (ret != LDB_SUCCESS) return ret;
1921 switch(ac->type) {
1922 case SAMLDB_TYPE_USER: {
1923 uint32_t raw_uac;
1924 uint32_t user_account_control;
1925 bool is_computer_objectclass;
1926 bool uac_generated = false, uac_add_flags = false;
1927 uint32_t default_user_account_control = UF_NORMAL_ACCOUNT;
1928 /* Step 1.2: Default values */
1929 ret = dsdb_user_obj_set_defaults(ldb, ac->msg, ac->req);
1930 if (ret != LDB_SUCCESS) return ret;
1932 is_computer_objectclass
1933 = (samdb_find_attribute(ldb,
1934 ac->msg,
1935 "objectclass",
1936 "computer")
1937 != NULL);
1939 if (is_computer_objectclass) {
1940 default_user_account_control
1941 = UF_WORKSTATION_TRUST_ACCOUNT;
1945 /* On add operations we might need to generate a
1946 * "userAccountControl" (if it isn't specified). */
1947 el = ldb_msg_find_element(ac->msg, "userAccountControl");
1948 if (el == NULL) {
1949 ret = samdb_msg_set_uint(ldb, ac->msg, ac->msg,
1950 "userAccountControl",
1951 default_user_account_control);
1952 if (ret != LDB_SUCCESS) {
1953 return ret;
1955 uac_generated = true;
1956 uac_add_flags = true;
1959 el = ldb_msg_find_element(ac->msg, "userAccountControl");
1960 SMB_ASSERT(el != NULL);
1962 /* Step 1.3: "userAccountControl" -> "sAMAccountType" mapping */
1963 user_account_control = ldb_msg_find_attr_as_uint(ac->msg,
1964 "userAccountControl",
1966 raw_uac = user_account_control;
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
1973 if ((user_account_control & UF_ACCOUNT_TYPE_MASK) == 0) {
1974 user_account_control
1975 = default_user_account_control
1976 | user_account_control;
1977 uac_generated = true;
1981 * As per MS-SAMR 3.1.1.8.10 these flags have not to be set
1983 if ((user_account_control & UF_LOCKOUT) != 0) {
1984 user_account_control &= ~UF_LOCKOUT;
1985 uac_generated = true;
1987 if ((user_account_control & UF_PASSWORD_EXPIRED) != 0) {
1988 user_account_control &= ~UF_PASSWORD_EXPIRED;
1989 uac_generated = true;
1992 ret = samldb_check_user_account_control_rules(ac, NULL,
1993 raw_uac,
1994 user_account_control,
1996 is_computer_objectclass);
1997 if (ret != LDB_SUCCESS) {
1998 return ret;
2002 * Require, for non-admin modifications, a trailing $
2003 * for either objectclass=computer or a trust account
2004 * type in userAccountControl
2006 if ((user_account_control
2007 & UF_TRUST_ACCOUNT_MASK) != 0) {
2008 ac->need_trailing_dollar = true;
2011 if (is_computer_objectclass) {
2012 ac->need_trailing_dollar = true;
2015 /* add "sAMAccountType" attribute */
2016 ret = dsdb_user_obj_set_account_type(ldb, ac->msg, user_account_control, NULL);
2017 if (ret != LDB_SUCCESS) {
2018 return ret;
2021 /* "isCriticalSystemObject" might be set */
2022 if (user_account_control &
2023 (UF_SERVER_TRUST_ACCOUNT | UF_PARTIAL_SECRETS_ACCOUNT)) {
2024 ret = ldb_msg_add_string_flags(ac->msg, "isCriticalSystemObject",
2025 "TRUE", LDB_FLAG_MOD_REPLACE);
2026 if (ret != LDB_SUCCESS) {
2027 return ret;
2029 } else if (user_account_control & UF_WORKSTATION_TRUST_ACCOUNT) {
2030 ret = ldb_msg_add_string_flags(ac->msg, "isCriticalSystemObject",
2031 "FALSE", LDB_FLAG_MOD_REPLACE);
2032 if (ret != LDB_SUCCESS) {
2033 return ret;
2037 /* Step 1.4: "userAccountControl" -> "primaryGroupID" mapping */
2038 if (!ldb_msg_find_element(ac->msg, "primaryGroupID")) {
2039 uint32_t rid;
2041 ret = dsdb_user_obj_set_primary_group_id(ldb, ac->msg, user_account_control, &rid);
2042 if (ret != LDB_SUCCESS) {
2043 return ret;
2046 * Older AD deployments don't know about the
2047 * RODC group
2049 if (rid == DOMAIN_RID_READONLY_DCS) {
2050 ret = samldb_prim_group_tester(ac, rid);
2051 if (ret != LDB_SUCCESS) {
2052 return ret;
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 if (uac_generated) {
2062 if (uac_add_flags) {
2063 user_account_control |= UF_ACCOUNTDISABLE;
2064 user_account_control |= UF_PASSWD_NOTREQD;
2067 ret = samdb_msg_set_uint(ldb, ac->msg, ac->msg,
2068 "userAccountControl",
2069 user_account_control);
2070 if (ret != LDB_SUCCESS) {
2071 return ret;
2074 break;
2077 case SAMLDB_TYPE_GROUP: {
2078 const char *tempstr;
2080 /* Step 2.2: Default values */
2081 tempstr = talloc_asprintf(ac->msg, "%d",
2082 GTYPE_SECURITY_GLOBAL_GROUP);
2083 if (tempstr == NULL) return ldb_operr(ldb);
2084 ret = samdb_find_or_add_attribute(ldb, ac->msg,
2085 "groupType", tempstr);
2086 if (ret != LDB_SUCCESS) return ret;
2088 /* Step 2.3: "groupType" -> "sAMAccountType" */
2089 el = ldb_msg_find_element(ac->msg, "groupType");
2090 if (el != NULL) {
2091 uint32_t group_type, account_type;
2093 group_type = ldb_msg_find_attr_as_uint(ac->msg,
2094 "groupType", 0);
2096 /* The creation of builtin groups requires the
2097 * RELAX control */
2098 if (group_type == GTYPE_SECURITY_BUILTIN_LOCAL_GROUP) {
2099 if (ldb_request_get_control(ac->req,
2100 LDB_CONTROL_RELAX_OID) == NULL) {
2101 return LDB_ERR_UNWILLING_TO_PERFORM;
2105 account_type = ds_gtype2atype(group_type);
2106 if (account_type == 0) {
2107 ldb_set_errstring(ldb, "samldb: Unrecognized account type!");
2108 return LDB_ERR_UNWILLING_TO_PERFORM;
2110 ret = samdb_msg_add_uint_flags(ldb, ac->msg, ac->msg,
2111 "sAMAccountType",
2112 account_type,
2113 LDB_FLAG_MOD_REPLACE);
2114 if (ret != LDB_SUCCESS) {
2115 return ret;
2118 break;
2121 default:
2122 ldb_asprintf_errstring(ldb,
2123 "Invalid entry type!");
2124 return LDB_ERR_OPERATIONS_ERROR;
2125 break;
2128 return LDB_SUCCESS;
2132 * "Primary group ID" trigger (MS-SAMR 3.1.1.8.2)
2134 * Has to be invoked on "add" and "modify" operations on "user" and "computer"
2135 * objects.
2136 * ac->msg contains the "add"/"modify" message
2139 static int samldb_prim_group_tester(struct samldb_ctx *ac, uint32_t rid)
2141 struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
2142 struct dom_sid *sid;
2143 struct ldb_result *res;
2144 int ret;
2145 const char * const noattrs[] = { NULL };
2147 sid = dom_sid_add_rid(ac, samdb_domain_sid(ldb), rid);
2148 if (sid == NULL) {
2149 return ldb_operr(ldb);
2152 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 if (ret != LDB_SUCCESS) {
2160 return ret;
2162 if (res->count != 1) {
2163 talloc_free(res);
2164 ldb_asprintf_errstring(ldb,
2165 "Failed to find primary group with RID %u!",
2166 rid);
2167 return LDB_ERR_UNWILLING_TO_PERFORM;
2169 talloc_free(res);
2171 return LDB_SUCCESS;
2174 static int samldb_prim_group_set(struct samldb_ctx *ac)
2176 struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
2177 uint32_t rid;
2179 rid = ldb_msg_find_attr_as_uint(ac->msg, "primaryGroupID", (uint32_t) -1);
2180 if (rid == (uint32_t) -1) {
2181 /* we aren't affected of any primary group set */
2182 return LDB_SUCCESS;
2184 } else if (!ldb_request_get_control(ac->req, LDB_CONTROL_RELAX_OID)) {
2185 ldb_set_errstring(ldb,
2186 "The primary group isn't settable on add operations!");
2187 return LDB_ERR_UNWILLING_TO_PERFORM;
2190 return samldb_prim_group_tester(ac, rid);
2193 static int samldb_prim_group_change(struct samldb_ctx *ac)
2195 struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
2196 const char * const attrs[] = {
2197 "primaryGroupID",
2198 "memberOf",
2199 "userAccountControl",
2200 NULL };
2201 struct ldb_result *res, *group_res;
2202 struct ldb_message_element *el;
2203 struct ldb_message *msg;
2204 uint32_t search_flags =
2205 DSDB_FLAG_NEXT_MODULE | DSDB_SEARCH_SHOW_EXTENDED_DN;
2206 uint32_t prev_rid, new_rid, uac;
2207 struct dom_sid *prev_sid, *new_sid;
2208 struct ldb_dn *prev_prim_group_dn, *new_prim_group_dn;
2209 const char *new_prim_group_dn_ext_str = NULL;
2210 struct ldb_dn *user_dn = NULL;
2211 const char *user_dn_ext_str = NULL;
2212 int ret;
2213 const char * const noattrs[] = { NULL };
2214 const char * const group_type_attrs[] = { "groupType", NULL };
2215 unsigned group_type;
2217 ret = dsdb_get_expected_new_values(ac,
2218 ac->msg,
2219 "primaryGroupID",
2220 &el,
2221 ac->req->operation);
2222 if (ret != LDB_SUCCESS) {
2223 return ret;
2226 if (el == NULL) {
2227 /* we are not affected */
2228 return LDB_SUCCESS;
2231 /* Fetch information from the existing object */
2233 ret = dsdb_module_search_dn(ac->module, ac, &res, ac->msg->dn, attrs,
2234 search_flags, ac->req);
2235 if (ret != LDB_SUCCESS) {
2236 return ret;
2238 user_dn = res->msgs[0]->dn;
2239 user_dn_ext_str = ldb_dn_get_extended_linearized(ac, user_dn, 1);
2240 if (user_dn_ext_str == NULL) {
2241 return ldb_operr(ldb);
2244 uac = ldb_msg_find_attr_as_uint(res->msgs[0], "userAccountControl", 0);
2246 /* Finds out the DN of the old primary group */
2248 prev_rid = ldb_msg_find_attr_as_uint(res->msgs[0], "primaryGroupID",
2249 (uint32_t) -1);
2250 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 return LDB_ERR_OBJECT_CLASS_VIOLATION;
2257 prev_sid = dom_sid_add_rid(ac, samdb_domain_sid(ldb), prev_rid);
2258 if (prev_sid == NULL) {
2259 return ldb_operr(ldb);
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. */
2266 msg = ldb_msg_new(ac->msg);
2267 if (msg == NULL) {
2268 return ldb_module_oom(ac->module);
2270 ret = ldb_msg_add(msg, el, 0);
2271 if (ret != LDB_SUCCESS) {
2272 return ret;
2274 new_rid = ldb_msg_find_attr_as_uint(msg, "primaryGroupID", (uint32_t) -1);
2275 talloc_free(msg);
2276 if (new_rid == (uint32_t) -1) {
2277 /* we aren't affected of any primary group change */
2278 return LDB_SUCCESS;
2281 if (prev_rid == new_rid) {
2282 return LDB_SUCCESS;
2285 if ((uac & UF_SERVER_TRUST_ACCOUNT) && new_rid != DOMAIN_RID_DCS) {
2286 ldb_asprintf_errstring(ldb,
2287 "%08X: samldb: UF_SERVER_TRUST_ACCOUNT requires "
2288 "primaryGroupID=%u!",
2289 W_ERROR_V(WERR_DS_CANT_MOD_PRIMARYGROUPID),
2290 DOMAIN_RID_DCS);
2291 return LDB_ERR_UNWILLING_TO_PERFORM;
2294 if ((uac & UF_PARTIAL_SECRETS_ACCOUNT) && new_rid != DOMAIN_RID_READONLY_DCS) {
2295 ldb_asprintf_errstring(ldb,
2296 "%08X: samldb: UF_PARTIAL_SECRETS_ACCOUNT requires "
2297 "primaryGroupID=%u!",
2298 W_ERROR_V(WERR_DS_CANT_MOD_PRIMARYGROUPID),
2299 DOMAIN_RID_READONLY_DCS);
2300 return LDB_ERR_UNWILLING_TO_PERFORM;
2303 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 if (ret != LDB_SUCCESS) {
2311 return ret;
2313 if (group_res->count != 1) {
2314 return ldb_operr(ldb);
2316 prev_prim_group_dn = group_res->msgs[0]->dn;
2318 new_sid = dom_sid_add_rid(ac, samdb_domain_sid(ldb), new_rid);
2319 if (new_sid == NULL) {
2320 return ldb_operr(ldb);
2323 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 if (ret != LDB_SUCCESS) {
2331 return ret;
2333 if (group_res->count != 1) {
2334 /* Here we know if the specified new primary group candidate is
2335 * valid or not. */
2336 return LDB_ERR_UNWILLING_TO_PERFORM;
2338 new_prim_group_dn = group_res->msgs[0]->dn;
2340 /* The new primary group must not be domain-local. */
2341 group_type = ldb_msg_find_attr_as_uint(group_res->msgs[0], "groupType", 0);
2342 if (group_type & GROUP_TYPE_RESOURCE_GROUP) {
2343 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!");
2349 new_prim_group_dn_ext_str = ldb_dn_get_extended_linearized(ac,
2350 new_prim_group_dn, 1);
2351 if (new_prim_group_dn_ext_str == NULL) {
2352 return ldb_operr(ldb);
2355 /* We need to be already a normal member of the new primary
2356 * group in order to be successful. */
2357 el = samdb_find_attribute(ldb, res->msgs[0], "memberOf",
2358 new_prim_group_dn_ext_str);
2359 if (el == NULL) {
2360 return LDB_ERR_UNWILLING_TO_PERFORM;
2363 /* Remove the "member" attribute on the new primary group */
2364 msg = ldb_msg_new(ac->msg);
2365 if (msg == NULL) {
2366 return ldb_module_oom(ac->module);
2368 msg->dn = new_prim_group_dn;
2370 ret = samdb_msg_add_delval(ldb, msg, msg, "member", user_dn_ext_str);
2371 if (ret != LDB_SUCCESS) {
2372 return ret;
2375 ret = dsdb_module_modify(ac->module, msg, DSDB_FLAG_NEXT_MODULE, ac->req);
2376 if (ret != LDB_SUCCESS) {
2377 return ret;
2379 talloc_free(msg);
2381 /* Add a "member" attribute for the previous primary group */
2382 msg = ldb_msg_new(ac->msg);
2383 if (msg == NULL) {
2384 return ldb_module_oom(ac->module);
2386 msg->dn = prev_prim_group_dn;
2388 ret = samdb_msg_add_addval(ldb, msg, msg, "member", user_dn_ext_str);
2389 if (ret != LDB_SUCCESS) {
2390 return ret;
2393 ret = dsdb_module_modify(ac->module, msg, DSDB_FLAG_NEXT_MODULE, ac->req);
2394 if (ret != LDB_SUCCESS) {
2395 return ret;
2397 talloc_free(msg);
2399 return LDB_SUCCESS;
2402 static int samldb_prim_group_trigger(struct samldb_ctx *ac)
2404 int ret;
2406 if (ac->req->operation == LDB_ADD) {
2407 ret = samldb_prim_group_set(ac);
2408 } else {
2409 ret = samldb_prim_group_change(ac);
2412 return ret;
2415 static int samldb_check_user_account_control_invariants(struct samldb_ctx *ac,
2416 uint32_t user_account_control)
2418 size_t i;
2419 int ret = 0;
2420 bool need_check = false;
2421 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 } map[] = {
2429 .uac = UF_TEMP_DUPLICATE_ACCOUNT,
2430 .never = true,
2431 .error_string = "Updating the UF_TEMP_DUPLICATE_ACCOUNT flag is never allowed"
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"
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"
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"
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"
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"
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"
2465 for (i = 0; i < ARRAY_SIZE(map); i++) {
2466 if (user_account_control & map[i].uac) {
2467 need_check = true;
2468 break;
2471 if (need_check == false) {
2472 return LDB_SUCCESS;
2475 for (i = 0; i < ARRAY_SIZE(map); i++) {
2476 uint32_t this_uac = user_account_control & map[i].uac;
2477 if (this_uac != 0) {
2478 if (map[i].never) {
2479 ret = LDB_ERR_OTHER;
2480 break;
2481 } else if (map[i].needs != 0) {
2482 if ((map[i].needs & user_account_control) == 0) {
2483 ret = LDB_ERR_OTHER;
2484 break;
2486 } else if (map[i].not_with != 0) {
2487 if ((map[i].not_with & user_account_control) != 0) {
2488 ret = LDB_ERR_OTHER;
2489 break;
2494 if (ret != LDB_SUCCESS) {
2495 switch (ac->req->operation) {
2496 case LDB_ADD:
2497 ldb_asprintf_errstring(ldb_module_get_ctx(ac->module),
2498 "Failed to add %s: %s",
2499 ldb_dn_get_linearized(ac->msg->dn),
2500 map[i].error_string);
2501 break;
2502 case LDB_MODIFY:
2503 ldb_asprintf_errstring(ldb_module_get_ctx(ac->module),
2504 "Failed to modify %s: %s",
2505 ldb_dn_get_linearized(ac->msg->dn),
2506 map[i].error_string);
2507 break;
2508 default:
2509 return ldb_module_operr(ac->module);
2512 return ret;
2516 * It would be best if these rules apply, always, but for now they
2517 * apply only to non-admins
2519 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)
2525 struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
2527 uint32_t old_ufa = user_account_control_old & UF_ACCOUNT_TYPE_MASK;
2528 uint32_t new_ufa = user_account_control & UF_ACCOUNT_TYPE_MASK;
2530 uint32_t old_rodc = user_account_control_old & UF_PARTIAL_SECRETS_ACCOUNT;
2531 uint32_t new_rodc = user_account_control & UF_PARTIAL_SECRETS_ACCOUNT;
2533 bool is_admin;
2534 struct security_token *user_token
2535 = acl_user_token(ac->module);
2536 if (user_token == NULL) {
2537 return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
2540 is_admin
2541 = security_token_has_builtin_administrators(user_token);
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.
2549 if (old_ufa == new_ufa && old_rodc == new_rodc) {
2550 return LDB_SUCCESS;
2553 switch (new_ufa) {
2554 case UF_NORMAL_ACCOUNT:
2555 if (is_computer_objectclass && !is_admin) {
2556 ldb_asprintf_errstring(ldb,
2557 "%08X: samldb: UF_NORMAL_ACCOUNT "
2558 "requires objectclass 'user' not 'computer'!",
2559 W_ERROR_V(WERR_DS_MACHINE_ACCOUNT_CREATED_PRENT4));
2560 return LDB_ERR_OBJECT_CLASS_VIOLATION;
2562 break;
2564 case UF_INTERDOMAIN_TRUST_ACCOUNT:
2565 if (is_computer_objectclass) {
2566 ldb_asprintf_errstring(ldb,
2567 "%08X: samldb: UF_INTERDOMAIN_TRUST_ACCOUNT "
2568 "requires objectclass 'user' not 'computer'!",
2569 W_ERROR_V(WERR_DS_MACHINE_ACCOUNT_CREATED_PRENT4));
2570 return LDB_ERR_OBJECT_CLASS_VIOLATION;
2572 break;
2574 case UF_WORKSTATION_TRUST_ACCOUNT:
2575 if (!is_computer_objectclass) {
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
2582 if (is_admin
2583 && ac->req->operation == LDB_MODIFY
2584 && new_rodc == 0) {
2585 break;
2587 ldb_asprintf_errstring(ldb,
2588 "%08X: samldb: UF_WORKSTATION_TRUST_ACCOUNT "
2589 "requires objectclass 'computer'!",
2590 W_ERROR_V(WERR_DS_MACHINE_ACCOUNT_CREATED_PRENT4));
2591 return LDB_ERR_OBJECT_CLASS_VIOLATION;
2593 break;
2595 case UF_SERVER_TRUST_ACCOUNT:
2596 if (!is_computer_objectclass) {
2597 ldb_asprintf_errstring(ldb,
2598 "%08X: samldb: UF_SERVER_TRUST_ACCOUNT "
2599 "requires objectclass 'computer'!",
2600 W_ERROR_V(WERR_DS_MACHINE_ACCOUNT_CREATED_PRENT4));
2601 return LDB_ERR_OBJECT_CLASS_VIOLATION;
2603 break;
2605 default:
2606 ldb_asprintf_errstring(ldb,
2607 "%08X: samldb: invalid userAccountControl[0x%08X]",
2608 W_ERROR_V(WERR_INVALID_PARAMETER),
2609 user_account_control);
2610 return LDB_ERR_OTHER;
2612 return LDB_SUCCESS;
2615 static int samldb_get_domain_secdesc_and_oc(struct samldb_ctx *ac,
2616 struct security_descriptor **domain_sd,
2617 const struct dsdb_class **objectclass)
2619 const char * const sd_attrs[] = {"ntSecurityDescriptor", "objectClass", NULL};
2620 struct ldb_result *res;
2621 struct ldb_dn *domain_dn = ldb_get_default_basedn(ldb_module_get_ctx(ac->module));
2622 const struct dsdb_schema *schema = NULL;
2623 struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
2624 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 if (ret != LDB_SUCCESS) {
2630 return ret;
2632 if (res->count != 1) {
2633 return ldb_module_operr(ac->module);
2636 schema = dsdb_get_schema(ldb, ac->req);
2637 if (!schema) {
2638 return ldb_module_operr(ac->module);;
2640 *objectclass = dsdb_get_structural_oc_from_msg(schema, res->msgs[0]);
2641 return dsdb_get_sd_from_ldb_message(ldb_module_get_ctx(ac->module),
2642 ac, res->msgs[0], domain_sd);
2647 * Validate that the restriction in point 5 of MS-SAMR 3.1.1.8.10 userAccountControl is honoured
2650 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)
2655 size_t i;
2656 int ret = 0;
2657 bool need_acl_check = false;
2658 struct security_token *user_token;
2659 struct security_descriptor *domain_sd;
2660 const struct dsdb_class *objectclass = NULL;
2661 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[] = {
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"
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"
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"
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"
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"
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"
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"
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
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"
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"
2727 if (dsdb_module_am_system(ac->module)) {
2728 return LDB_SUCCESS;
2731 for (i = 0; i < ARRAY_SIZE(map); i++) {
2732 if (user_account_control & map[i].uac) {
2733 need_acl_check = true;
2734 break;
2737 if (need_acl_check == false) {
2738 return LDB_SUCCESS;
2741 user_token = acl_user_token(ac->module);
2742 if (user_token == NULL) {
2743 return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
2746 ret = samldb_get_domain_secdesc_and_oc(ac, &domain_sd, &objectclass);
2747 if (ret != LDB_SUCCESS) {
2748 return ret;
2751 for (i = 0; i < ARRAY_SIZE(map); i++) {
2752 uint32_t this_uac_new = user_account_control & map[i].uac;
2753 uint32_t this_uac_old = user_account_control_old & map[i].uac;
2754 if (this_uac_new != this_uac_old) {
2755 if (this_uac_old != 0) {
2756 if (map[i].delete_is_privileged == false) {
2757 continue;
2760 if (map[i].oid) {
2761 struct ldb_control *control = ldb_request_get_control(ac->req, map[i].oid);
2762 if (control == NULL) {
2763 ret = LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
2765 } else if (map[i].privilege != SEC_PRIV_INVALID) {
2766 bool have_priv = security_token_has_privilege(user_token,
2767 map[i].privilege);
2768 if (have_priv == false) {
2769 ret = LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
2771 } else if (map[i].priv_to_change_from & user_account_control_old) {
2772 bool is_admin = security_token_has_builtin_administrators(user_token);
2773 if (is_admin == false) {
2774 ret = LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
2776 } else if (map[i].guid) {
2777 ret = acl_check_extended_right(ac,
2778 ac->module,
2779 ac->req,
2780 objectclass,
2781 domain_sd,
2782 user_token,
2783 map[i].guid,
2784 SEC_ADS_CONTROL_ACCESS,
2785 sid);
2786 } else {
2787 ret = LDB_SUCCESS;
2789 if (ret != LDB_SUCCESS) {
2790 break;
2794 if (ret == LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS) {
2795 switch (ac->req->operation) {
2796 case LDB_ADD:
2797 ldb_asprintf_errstring(ldb_module_get_ctx(ac->module),
2798 "Failed to add %s: %s",
2799 ldb_dn_get_linearized(ac->msg->dn),
2800 map[i].error_string);
2801 break;
2802 case LDB_MODIFY:
2803 ldb_asprintf_errstring(ldb_module_get_ctx(ac->module),
2804 "Failed to modify %s: %s",
2805 ldb_dn_get_linearized(ac->msg->dn),
2806 map[i].error_string);
2807 break;
2808 default:
2809 return ldb_module_operr(ac->module);
2811 if (map[i].guid) {
2812 struct ldb_dn *domain_dn
2813 = ldb_get_default_basedn(ldb_module_get_ctx(ac->module));
2814 dsdb_acl_debug(domain_sd, acl_user_token(ac->module),
2815 domain_dn,
2816 true,
2817 10);
2820 return ret;
2823 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)
2830 int ret;
2831 struct dsdb_control_password_user_account_control *uac = NULL;
2833 ret = samldb_check_user_account_control_invariants(ac, user_account_control);
2834 if (ret != LDB_SUCCESS) {
2835 return ret;
2837 ret = samldb_check_user_account_control_objectclass_invariants(ac,
2838 user_account_control,
2839 user_account_control_old,
2840 is_computer_objectclass);
2841 if (ret != LDB_SUCCESS) {
2842 return ret;
2845 ret = samldb_check_user_account_control_acl(ac, sid, user_account_control, user_account_control_old);
2846 if (ret != LDB_SUCCESS) {
2847 return ret;
2850 uac = talloc_zero(ac->req,
2851 struct dsdb_control_password_user_account_control);
2852 if (uac == NULL) {
2853 return ldb_module_oom(ac->module);
2856 uac->req_flags = req_uac;
2857 uac->old_flags = user_account_control_old;
2858 uac->new_flags = user_account_control;
2860 ret = ldb_request_add_control(ac->req,
2861 DSDB_CONTROL_PASSWORD_USER_ACCOUNT_CONTROL_OID,
2862 false, uac);
2863 if (ret != LDB_SUCCESS) {
2864 return ret;
2867 return ret;
2872 * This function is called on LDB modify operations. It performs some additions/
2873 * replaces on the current LDB message when "userAccountControl" changes.
2875 static int samldb_user_account_control_change(struct samldb_ctx *ac)
2877 struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
2878 uint32_t old_uac;
2879 uint32_t new_uac;
2880 uint32_t raw_uac;
2881 uint32_t old_ufa;
2882 uint32_t new_ufa;
2883 uint32_t old_uac_computed;
2884 uint32_t clear_uac;
2885 uint32_t old_atype;
2886 uint32_t new_atype;
2887 uint32_t old_pgrid;
2888 uint32_t new_pgrid;
2889 NTTIME old_lockoutTime;
2890 struct ldb_message_element *el;
2891 struct ldb_val *val;
2892 struct ldb_val computer_val;
2893 struct ldb_message *tmp_msg;
2894 struct dom_sid *sid;
2895 int ret;
2896 struct ldb_result *res;
2897 const char * const attrs[] = {
2898 "objectClass",
2899 "isCriticalSystemObject",
2900 "userAccountControl",
2901 "msDS-User-Account-Control-Computed",
2902 "lockoutTime",
2903 "objectSid",
2904 NULL
2906 bool is_computer_objectclass = false;
2907 bool old_is_critical = false;
2908 bool new_is_critical = false;
2910 ret = dsdb_get_expected_new_values(ac,
2911 ac->msg,
2912 "userAccountControl",
2913 &el,
2914 ac->req->operation);
2915 if (ret != LDB_SUCCESS) {
2916 return ret;
2919 if (el == NULL || el->num_values == 0) {
2920 ldb_asprintf_errstring(ldb,
2921 "%08X: samldb: 'userAccountControl' can't be deleted!",
2922 W_ERROR_V(WERR_DS_ILLEGAL_MOD_OPERATION));
2923 return LDB_ERR_UNWILLING_TO_PERFORM;
2926 /* Create a temporary message for fetching the "userAccountControl" */
2927 tmp_msg = ldb_msg_new(ac->msg);
2928 if (tmp_msg == NULL) {
2929 return ldb_module_oom(ac->module);
2931 ret = ldb_msg_add(tmp_msg, el, 0);
2932 if (ret != LDB_SUCCESS) {
2933 return ret;
2935 raw_uac = ldb_msg_find_attr_as_uint(tmp_msg,
2936 "userAccountControl",
2938 talloc_free(tmp_msg);
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.
2944 * The only exception is ACB_AUTOLOCK, which features in
2945 * clear_acb when the bit is cleared in this modify operation.
2947 * MS-SAMR 2.2.1.13 UF_FLAG Codes states that some bits are
2948 * ignored by clients and servers
2950 new_uac = raw_uac & UF_SETTABLE_BITS;
2952 /* Fetch the old "userAccountControl" and "objectClass" */
2953 ret = dsdb_module_search_dn(ac->module, ac, &res, ac->msg->dn, attrs,
2954 DSDB_FLAG_NEXT_MODULE, ac->req);
2955 if (ret != LDB_SUCCESS) {
2956 return ret;
2958 old_uac = ldb_msg_find_attr_as_uint(res->msgs[0], "userAccountControl", 0);
2959 if (old_uac == 0) {
2960 return ldb_operr(ldb);
2962 old_uac_computed = ldb_msg_find_attr_as_uint(res->msgs[0],
2963 "msDS-User-Account-Control-Computed", 0);
2964 old_lockoutTime = ldb_msg_find_attr_as_int64(res->msgs[0],
2965 "lockoutTime", 0);
2966 old_is_critical = ldb_msg_find_attr_as_bool(res->msgs[0],
2967 "isCriticalSystemObject", 0);
2969 * When we do not have objectclass "computer" we cannot
2970 * switch to a workstation or (RO)DC
2972 el = ldb_msg_find_element(res->msgs[0], "objectClass");
2973 if (el == NULL) {
2974 return ldb_operr(ldb);
2976 computer_val = data_blob_string_const("computer");
2977 val = ldb_msg_find_val(el, &computer_val);
2978 if (val != NULL) {
2979 is_computer_objectclass = true;
2982 old_ufa = old_uac & UF_ACCOUNT_TYPE_MASK;
2983 old_atype = ds_uf2atype(old_ufa);
2984 old_pgrid = ds_uf2prim_group_rid(old_uac);
2986 new_ufa = new_uac & UF_ACCOUNT_TYPE_MASK;
2987 if (new_ufa == 0) {
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
2993 new_ufa = UF_NORMAL_ACCOUNT;
2994 new_uac |= new_ufa;
2996 sid = samdb_result_dom_sid(res, res->msgs[0], "objectSid");
2997 if (sid == NULL) {
2998 return ldb_module_operr(ac->module);
3001 ret = samldb_check_user_account_control_rules(ac, sid,
3002 raw_uac,
3003 new_uac,
3004 old_uac,
3005 is_computer_objectclass);
3006 if (ret != LDB_SUCCESS) {
3007 return ret;
3010 new_atype = ds_uf2atype(new_ufa);
3011 new_pgrid = ds_uf2prim_group_rid(new_uac);
3013 clear_uac = (old_uac | old_uac_computed) & ~raw_uac;
3015 switch (new_ufa) {
3016 case UF_NORMAL_ACCOUNT:
3017 new_is_critical = old_is_critical;
3018 break;
3020 case UF_INTERDOMAIN_TRUST_ACCOUNT:
3021 new_is_critical = true;
3022 break;
3024 case UF_WORKSTATION_TRUST_ACCOUNT:
3025 new_is_critical = false;
3026 if (new_uac & UF_PARTIAL_SECRETS_ACCOUNT) {
3027 new_is_critical = true;
3029 break;
3031 case UF_SERVER_TRUST_ACCOUNT:
3032 new_is_critical = true;
3033 break;
3035 default:
3036 ldb_asprintf_errstring(ldb,
3037 "%08X: samldb: invalid userAccountControl[0x%08X]",
3038 W_ERROR_V(WERR_INVALID_PARAMETER), raw_uac);
3039 return LDB_ERR_OTHER;
3042 if (old_atype != new_atype) {
3043 ret = samdb_msg_append_uint(ldb, ac->msg, ac->msg,
3044 "sAMAccountType", new_atype,
3045 LDB_FLAG_MOD_REPLACE);
3046 if (ret != LDB_SUCCESS) {
3047 return ret;
3051 /* As per MS-SAMR 3.1.1.8.10 these flags have not to be set */
3052 if ((clear_uac & UF_LOCKOUT) && (old_lockoutTime != 0)) {
3053 /* "lockoutTime" reset as per MS-SAMR 3.1.1.8.10 */
3054 ldb_msg_remove_attr(ac->msg, "lockoutTime");
3055 ret = samdb_msg_append_uint64(ldb, ac->msg, ac->msg, "lockoutTime",
3056 (NTTIME)0, LDB_FLAG_MOD_REPLACE);
3057 if (ret != LDB_SUCCESS) {
3058 return ret;
3063 * "isCriticalSystemObject" might be set/changed
3065 * Even a change from UF_NORMAL_ACCOUNT (implicitly FALSE) to
3066 * UF_WORKSTATION_TRUST_ACCOUNT (actually FALSE) triggers
3067 * creating the attribute.
3069 if (old_is_critical != new_is_critical || old_atype != new_atype) {
3070 ret = ldb_msg_append_string(ac->msg, "isCriticalSystemObject",
3071 new_is_critical ? "TRUE": "FALSE",
3072 LDB_FLAG_MOD_REPLACE);
3073 if (ret != LDB_SUCCESS) {
3074 return ret;
3078 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 if (new_pgrid == DOMAIN_RID_READONLY_DCS) {
3082 ret = samldb_prim_group_tester(ac, new_pgrid);
3083 if (ret != LDB_SUCCESS) {
3084 return ret;
3088 ret = samdb_msg_append_uint(ldb, ac->msg, ac->msg,
3089 "primaryGroupID", new_pgrid,
3090 LDB_FLAG_MOD_REPLACE);
3091 if (ret != LDB_SUCCESS) {
3092 return ret;
3096 /* Propagate eventual "userAccountControl" attribute changes */
3097 if (old_uac != new_uac) {
3098 char *tempstr = talloc_asprintf(ac->msg, "%d",
3099 new_uac);
3100 if (tempstr == NULL) {
3101 return ldb_module_oom(ac->module);
3104 ret = ldb_msg_add_empty(ac->msg,
3105 "userAccountControl",
3106 LDB_FLAG_MOD_REPLACE,
3107 &el);
3108 el->values = talloc(ac->msg, struct ldb_val);
3109 el->num_values = 1;
3110 el->values[0].data = (uint8_t *) tempstr;
3111 el->values[0].length = strlen(tempstr);
3112 } else {
3113 ldb_msg_remove_attr(ac->msg, "userAccountControl");
3116 return LDB_SUCCESS;
3119 static int samldb_check_pwd_last_set_acl(struct samldb_ctx *ac,
3120 struct dom_sid *sid)
3122 struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
3123 int ret = 0;
3124 struct security_token *user_token = NULL;
3125 struct security_descriptor *domain_sd = NULL;
3126 struct ldb_dn *domain_dn = ldb_get_default_basedn(ldb_module_get_ctx(ac->module));
3127 const char *operation = "";
3128 const struct dsdb_class *objectclass = NULL;
3130 if (dsdb_module_am_system(ac->module)) {
3131 return LDB_SUCCESS;
3134 switch (ac->req->operation) {
3135 case LDB_ADD:
3136 operation = "add";
3137 break;
3138 case LDB_MODIFY:
3139 operation = "modify";
3140 break;
3141 default:
3142 return ldb_module_operr(ac->module);
3145 user_token = acl_user_token(ac->module);
3146 if (user_token == NULL) {
3147 return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
3150 ret = samldb_get_domain_secdesc_and_oc(ac, &domain_sd, &objectclass);
3151 if (ret != LDB_SUCCESS) {
3152 return ret;
3154 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 if (ret != LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS) {
3164 return ret;
3167 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 ldb_dn_get_linearized(ac->msg->dn));
3174 dsdb_acl_debug(domain_sd, user_token,
3175 domain_dn, true, 10);
3177 return ret;
3181 * This function is called on LDB modify operations. It performs some additions/
3182 * replaces on the current LDB message when "pwdLastSet" changes.
3184 static int samldb_pwd_last_set_change(struct samldb_ctx *ac)
3186 struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
3187 NTTIME last_set = 0;
3188 struct ldb_message_element *el = NULL;
3189 struct ldb_message *tmp_msg = NULL;
3190 struct dom_sid *self_sid = NULL;
3191 int ret;
3192 struct ldb_result *res = NULL;
3193 const char * const attrs[] = {
3194 "objectSid",
3195 NULL
3198 ret = dsdb_get_expected_new_values(ac,
3199 ac->msg,
3200 "pwdLastSet",
3201 &el,
3202 ac->req->operation);
3203 if (ret != LDB_SUCCESS) {
3204 return ret;
3207 if (el == NULL || el->num_values == 0) {
3208 ldb_asprintf_errstring(ldb,
3209 "%08X: samldb: 'pwdLastSet' can't be deleted!",
3210 W_ERROR_V(WERR_DS_ILLEGAL_MOD_OPERATION));
3211 return LDB_ERR_UNWILLING_TO_PERFORM;
3214 /* Create a temporary message for fetching the "userAccountControl" */
3215 tmp_msg = ldb_msg_new(ac->msg);
3216 if (tmp_msg == NULL) {
3217 return ldb_module_oom(ac->module);
3219 ret = ldb_msg_add(tmp_msg, el, 0);
3220 if (ret != LDB_SUCCESS) {
3221 return ret;
3223 last_set = samdb_result_nttime(tmp_msg, "pwdLastSet", 0);
3224 talloc_free(tmp_msg);
3227 * Setting -1 (0xFFFFFFFFFFFFFFFF) requires the Unexpire-Password right
3229 if (last_set != UINT64_MAX) {
3230 return LDB_SUCCESS;
3233 /* Fetch the "objectSid" */
3234 ret = dsdb_module_search_dn(ac->module, ac, &res, ac->msg->dn, attrs,
3235 DSDB_FLAG_NEXT_MODULE, ac->req);
3236 if (ret != LDB_SUCCESS) {
3237 return ret;
3239 self_sid = samdb_result_dom_sid(res, res->msgs[0], "objectSid");
3240 if (self_sid == NULL) {
3241 return ldb_module_operr(ac->module);
3244 ret = samldb_check_pwd_last_set_acl(ac, self_sid);
3245 if (ret != LDB_SUCCESS) {
3246 return ret;
3249 return LDB_SUCCESS;
3252 static int samldb_lockout_time(struct samldb_ctx *ac)
3254 struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
3255 NTTIME lockoutTime;
3256 struct ldb_message_element *el;
3257 struct ldb_message *tmp_msg;
3258 int ret;
3260 ret = dsdb_get_expected_new_values(ac,
3261 ac->msg,
3262 "lockoutTime",
3263 &el,
3264 ac->req->operation);
3265 if (ret != LDB_SUCCESS) {
3266 return ret;
3269 if (el == NULL || el->num_values == 0) {
3270 ldb_asprintf_errstring(ldb,
3271 "%08X: samldb: 'lockoutTime' can't be deleted!",
3272 W_ERROR_V(WERR_DS_ILLEGAL_MOD_OPERATION));
3273 return LDB_ERR_UNWILLING_TO_PERFORM;
3276 /* Create a temporary message for fetching the "lockoutTime" */
3277 tmp_msg = ldb_msg_new(ac->msg);
3278 if (tmp_msg == NULL) {
3279 return ldb_module_oom(ac->module);
3281 ret = ldb_msg_add(tmp_msg, el, 0);
3282 if (ret != LDB_SUCCESS) {
3283 return ret;
3285 lockoutTime = ldb_msg_find_attr_as_int64(tmp_msg,
3286 "lockoutTime",
3288 talloc_free(tmp_msg);
3290 if (lockoutTime != 0) {
3291 return LDB_SUCCESS;
3294 /* lockoutTime == 0 resets badPwdCount */
3295 ldb_msg_remove_attr(ac->msg, "badPwdCount");
3296 ret = samdb_msg_append_int(ldb, ac->msg, ac->msg,
3297 "badPwdCount", 0,
3298 LDB_FLAG_MOD_REPLACE);
3299 if (ret != LDB_SUCCESS) {
3300 return ret;
3303 return LDB_SUCCESS;
3306 static int samldb_group_type_change(struct samldb_ctx *ac)
3308 struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
3309 uint32_t group_type, old_group_type, account_type;
3310 struct ldb_message_element *el;
3311 struct ldb_message *tmp_msg;
3312 int ret;
3313 struct ldb_result *res;
3314 const char * const attrs[] = { "groupType", NULL };
3316 ret = dsdb_get_expected_new_values(ac,
3317 ac->msg,
3318 "groupType",
3319 &el,
3320 ac->req->operation);
3321 if (ret != LDB_SUCCESS) {
3322 return ret;
3325 if (el == NULL) {
3326 /* we are not affected */
3327 return LDB_SUCCESS;
3330 /* Create a temporary message for fetching the "groupType" */
3331 tmp_msg = ldb_msg_new(ac->msg);
3332 if (tmp_msg == NULL) {
3333 return ldb_module_oom(ac->module);
3335 ret = ldb_msg_add(tmp_msg, el, 0);
3336 if (ret != LDB_SUCCESS) {
3337 return ret;
3339 group_type = ldb_msg_find_attr_as_uint(tmp_msg, "groupType", 0);
3340 talloc_free(tmp_msg);
3342 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 if (ret != LDB_SUCCESS) {
3346 return ret;
3348 old_group_type = ldb_msg_find_attr_as_uint(res->msgs[0], "groupType", 0);
3349 if (old_group_type == 0) {
3350 return ldb_operr(ldb);
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. */
3358 if (ldb_request_get_control(ac->req, LDB_CONTROL_PROVISION_OID) == NULL) {
3359 switch (group_type) {
3360 case GTYPE_SECURITY_GLOBAL_GROUP:
3361 case GTYPE_DISTRIBUTION_GLOBAL_GROUP:
3362 /* change to "universal" allowed */
3363 if ((old_group_type == GTYPE_SECURITY_DOMAIN_LOCAL_GROUP) ||
3364 (old_group_type == GTYPE_DISTRIBUTION_DOMAIN_LOCAL_GROUP)) {
3365 ldb_set_errstring(ldb,
3366 "samldb: Change from security/distribution local group forbidden!");
3367 return LDB_ERR_UNWILLING_TO_PERFORM;
3369 break;
3371 case GTYPE_SECURITY_UNIVERSAL_GROUP:
3372 case GTYPE_DISTRIBUTION_UNIVERSAL_GROUP:
3373 /* each change allowed */
3374 break;
3375 case GTYPE_SECURITY_DOMAIN_LOCAL_GROUP:
3376 case GTYPE_DISTRIBUTION_DOMAIN_LOCAL_GROUP:
3377 /* change to "universal" allowed */
3378 if ((old_group_type == GTYPE_SECURITY_GLOBAL_GROUP) ||
3379 (old_group_type == GTYPE_DISTRIBUTION_GLOBAL_GROUP)) {
3380 ldb_set_errstring(ldb,
3381 "samldb: Change from security/distribution global group forbidden!");
3382 return LDB_ERR_UNWILLING_TO_PERFORM;
3384 break;
3386 case GTYPE_SECURITY_BUILTIN_LOCAL_GROUP:
3387 default:
3388 /* we don't allow this "groupType" values */
3389 return LDB_ERR_UNWILLING_TO_PERFORM;
3390 break;
3394 account_type = ds_gtype2atype(group_type);
3395 if (account_type == 0) {
3396 ldb_set_errstring(ldb, "samldb: Unrecognized account type!");
3397 return LDB_ERR_UNWILLING_TO_PERFORM;
3399 ret = samdb_msg_append_uint(ldb, ac->msg, ac->msg, "sAMAccountType",
3400 account_type, LDB_FLAG_MOD_REPLACE);
3401 if (ret != LDB_SUCCESS) {
3402 return ret;
3405 return LDB_SUCCESS;
3408 static int samldb_member_check(struct samldb_ctx *ac)
3410 const char * const attrs[] = { "objectSid", NULL };
3411 struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
3412 struct ldb_message_element *el;
3413 struct ldb_dn *member_dn;
3414 struct dom_sid *sid;
3415 struct ldb_result *res;
3416 struct dom_sid *group_sid;
3417 unsigned int i, j;
3418 int ret;
3420 /* Fetch information from the existing object */
3422 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 if (ret != LDB_SUCCESS) {
3425 return ret;
3427 if (res->count != 1) {
3428 return ldb_operr(ldb);
3431 group_sid = samdb_result_dom_sid(res, res->msgs[0], "objectSid");
3432 if (group_sid == NULL) {
3433 return ldb_operr(ldb);
3436 /* We've to walk over all modification entries and consider the "member"
3437 * ones. */
3438 for (i = 0; i < ac->msg->num_elements; i++) {
3439 if (ldb_attr_cmp(ac->msg->elements[i].name, "member") != 0) {
3440 continue;
3443 el = &ac->msg->elements[i];
3444 for (j = 0; j < el->num_values; j++) {
3445 struct ldb_result *group_res;
3446 const char *group_attrs[] = { "primaryGroupID" , NULL };
3447 uint32_t prim_group_rid;
3449 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 continue;
3458 member_dn = ldb_dn_from_ldb_val(ac, ldb,
3459 &el->values[j]);
3460 if (!ldb_dn_validate(member_dn)) {
3461 return ldb_operr(ldb);
3464 /* Denies to add "member"s to groups which are primary
3465 * ones for them - in this case return
3466 * ERR_ENTRY_ALREADY_EXISTS. */
3468 ret = dsdb_module_search_dn(ac->module, ac, &group_res,
3469 member_dn, group_attrs,
3470 DSDB_FLAG_NEXT_MODULE, ac->req);
3471 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
3472 /* member DN doesn't exist yet */
3473 continue;
3475 if (ret != LDB_SUCCESS) {
3476 return ret;
3478 prim_group_rid = ldb_msg_find_attr_as_uint(group_res->msgs[0], "primaryGroupID", (uint32_t)-1);
3479 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 continue;
3485 sid = dom_sid_add_rid(ac, samdb_domain_sid(ldb),
3486 prim_group_rid);
3487 if (sid == NULL) {
3488 return ldb_operr(ldb);
3491 if (dom_sid_equal(group_sid, sid)) {
3492 ldb_asprintf_errstring(ldb,
3493 "samldb: member %s already set via primaryGroupID %u",
3494 ldb_dn_get_linearized(member_dn), prim_group_rid);
3495 return LDB_ERR_ENTRY_ALREADY_EXISTS;
3500 talloc_free(res);
3502 return LDB_SUCCESS;
3505 /* SAM objects have special rules regarding the "description" attribute on
3506 * modify operations. */
3507 static int samldb_description_check(struct samldb_ctx *ac, bool *modified)
3509 struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
3510 const char * const attrs[] = { "objectClass", "description", NULL };
3511 struct ldb_result *res;
3512 unsigned int i;
3513 int ret;
3515 /* Fetch information from the existing object */
3516 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 if (ret != LDB_SUCCESS) {
3520 /* don't treat it specially ... let normal error codes
3521 happen from other places */
3522 ldb_reset_err_string(ldb);
3523 return LDB_SUCCESS;
3525 if (res->count == 0) {
3526 /* we didn't match the filter */
3527 talloc_free(res);
3528 return LDB_SUCCESS;
3531 /* We've to walk over all modification entries and consider the
3532 * "description" ones. */
3533 for (i = 0; i < ac->msg->num_elements; i++) {
3534 if (ldb_attr_cmp(ac->msg->elements[i].name, "description") == 0) {
3535 ac->msg->elements[i].flags |= LDB_FLAG_INTERNAL_FORCE_SINGLE_VALUE_CHECK;
3536 *modified = true;
3540 talloc_free(res);
3542 return LDB_SUCCESS;
3545 #define SPN_ALIAS_NONE 0
3546 #define SPN_ALIAS_LINK 1
3547 #define SPN_ALIAS_TARGET 2
3549 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)
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.
3561 * In particular, note that sPNMappings are resolved on a first come,
3562 * first served basis. For example, if we have
3564 * host=ldap,cifs
3565 * foo=ldap
3566 * cifs=host,alerter
3568 * then 'ldap', 'cifs', and 'host' will resolve to 'host', and
3569 * 'alerter' will resolve to 'cifs'.
3571 * If this resolution method is made more complicated, then the
3572 * cracknames function should also be changed.
3574 size_t i, j;
3575 int ret;
3576 bool ok;
3577 struct ldb_result *res = NULL;
3578 struct ldb_message_element *spnmappings = NULL;
3579 TALLOC_CTX *tmp_ctx = NULL;
3580 struct ldb_dn *service_dn = NULL;
3582 const char *attrs[] = {
3583 "sPNMappings",
3584 NULL
3587 *direction = SPN_ALIAS_NONE;
3589 tmp_ctx = talloc_new(mem_ctx);
3590 if (tmp_ctx == NULL) {
3591 return ldb_oom(ldb);
3594 service_dn = ldb_dn_new(
3595 tmp_ctx, ldb,
3596 "CN=Directory Service,CN=Windows NT,CN=Services");
3597 if (service_dn == NULL) {
3598 talloc_free(tmp_ctx);
3599 return ldb_oom(ldb);
3602 ok = ldb_dn_add_base(service_dn, ldb_get_config_basedn(ldb));
3603 if (! ok) {
3604 talloc_free(tmp_ctx);
3605 return LDB_ERR_OPERATIONS_ERROR;
3608 ret = ldb_search(ldb, tmp_ctx, &res, service_dn, LDB_SCOPE_BASE,
3609 attrs, "(objectClass=nTDSService)");
3611 if (ret != LDB_SUCCESS || res->count != 1) {
3612 DBG_WARNING("sPNMappings not found.\n");
3613 talloc_free(tmp_ctx);
3614 return ret;
3617 spnmappings = ldb_msg_find_element(res->msgs[0], "sPNMappings");
3618 if (spnmappings == NULL || spnmappings->num_values == 0) {
3619 DBG_WARNING("no sPNMappings attribute\n");
3620 talloc_free(tmp_ctx);
3621 return LDB_ERR_NO_SUCH_OBJECT;
3623 *n_aliases = 0;
3625 for (i = 0; i < spnmappings->num_values; i++) {
3626 char *p = NULL;
3627 char *mapping = talloc_strndup(
3628 tmp_ctx,
3629 (char *)spnmappings->values[i].data,
3630 spnmappings->values[i].length);
3631 if (mapping == NULL) {
3632 talloc_free(tmp_ctx);
3633 return ldb_oom(ldb);
3636 p = strchr(mapping, '=');
3637 if (p == NULL) {
3638 talloc_free(tmp_ctx);
3639 return LDB_ERR_ALIAS_PROBLEM;
3641 p[0] = '\0';
3642 p++;
3644 if (strcasecmp(mapping, service_class) == 0) {
3646 * We need to return the reverse aliases for this one.
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.
3654 /* There is one more field than there are commas */
3655 size_t n = 1;
3657 for (j = 0; p[j] != '\0'; j++) {
3658 if (p[j] == ',') {
3659 n++;
3660 p[j] = '\0';
3663 *aliases = talloc_array(mem_ctx, char*, n);
3664 if (*aliases == NULL) {
3665 talloc_free(tmp_ctx);
3666 return ldb_oom(ldb);
3668 *n_aliases = n;
3669 talloc_steal(mem_ctx, mapping);
3670 for (j = 0; j < n; j++) {
3671 (*aliases)[j] = p;
3672 p += strlen(p) + 1;
3674 talloc_free(tmp_ctx);
3675 *direction = SPN_ALIAS_LINK;
3676 return LDB_SUCCESS;
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").
3682 do {
3683 char *str = p;
3684 p = strchr(p, ',');
3685 if (p != NULL) {
3686 p[0] = '\0';
3687 p++;
3689 if (strcasecmp(str, service_class) == 0) {
3690 *aliases = talloc_array(mem_ctx, char*, 1);
3691 if (*aliases == NULL) {
3692 talloc_free(tmp_ctx);
3693 return ldb_oom(ldb);
3695 *n_aliases = 1;
3696 (*aliases)[0] = mapping;
3697 talloc_steal(mem_ctx, mapping);
3698 talloc_free(tmp_ctx);
3699 *direction = SPN_ALIAS_TARGET;
3700 return LDB_SUCCESS;
3702 } while (p != NULL);
3704 DBG_INFO("no sPNMappings alias for '%s'\n", service_class);
3705 talloc_free(tmp_ctx);
3706 *aliases = NULL;
3707 *n_aliases = 0;
3708 return LDB_SUCCESS;
3712 static int get_spn_dn(struct ldb_context *ldb,
3713 TALLOC_CTX *tmp_ctx,
3714 const char *candidate,
3715 struct ldb_dn **dn)
3717 int ret;
3718 const char *empty_attrs[] = { NULL };
3719 struct ldb_message *msg = NULL;
3720 struct ldb_dn *base_dn = ldb_get_default_basedn(ldb);
3722 const char *enc_candidate = NULL;
3724 *dn = NULL;
3726 enc_candidate = ldb_binary_encode_string(tmp_ctx, candidate);
3727 if (enc_candidate == NULL) {
3728 return ldb_operr(ldb);
3731 ret = dsdb_search_one(ldb,
3732 tmp_ctx,
3733 &msg,
3734 base_dn,
3735 LDB_SCOPE_SUBTREE,
3736 empty_attrs,
3738 "(servicePrincipalName=%s)",
3739 enc_candidate);
3740 if (ret != LDB_SUCCESS) {
3741 return ret;
3743 *dn = msg->dn;
3744 return LDB_SUCCESS;
3748 static int check_spn_write_rights(struct ldb_context *ldb,
3749 TALLOC_CTX *mem_ctx,
3750 const char *spn,
3751 struct ldb_dn *dn)
3753 int ret;
3754 struct ldb_message *msg = NULL;
3755 struct ldb_message_element *del_el = NULL;
3756 struct ldb_message_element *add_el = NULL;
3757 struct ldb_val val = {
3758 .data = discard_const_p(uint8_t, spn),
3759 .length = strlen(spn)
3762 msg = ldb_msg_new(mem_ctx);
3763 if (msg == NULL) {
3764 return ldb_oom(ldb);
3766 msg->dn = dn;
3768 ret = ldb_msg_add_empty(msg,
3769 "servicePrincipalName",
3770 LDB_FLAG_MOD_DELETE,
3771 &del_el);
3772 if (ret != LDB_SUCCESS) {
3773 talloc_free(msg);
3774 return ret;
3777 del_el->values = talloc_array(msg->elements, struct ldb_val, 1);
3778 if (del_el->values == NULL) {
3779 talloc_free(msg);
3780 return ret;
3783 del_el->values[0] = val;
3784 del_el->num_values = 1;
3786 ret = ldb_msg_add_empty(msg,
3787 "servicePrincipalName",
3788 LDB_FLAG_MOD_ADD,
3789 &add_el);
3790 if (ret != LDB_SUCCESS) {
3791 talloc_free(msg);
3792 return ret;
3795 add_el->values = talloc_array(msg->elements, struct ldb_val, 1);
3796 if (add_el->values == NULL) {
3797 talloc_free(msg);
3798 return ret;
3801 add_el->values[0] = val;
3802 add_el->num_values = 1;
3804 ret = ldb_modify(ldb, msg);
3805 if (ret == LDB_ERR_NO_SUCH_ATTRIBUTE) {
3806 DBG_ERR("hmm I think we're OK, but not sure\n");
3807 } else if (ret != LDB_SUCCESS) {
3808 DBG_ERR("SPN write rights check failed with %d\n", ret);
3809 talloc_free(msg);
3810 return ret;
3812 talloc_free(msg);
3813 return LDB_SUCCESS;
3817 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)
3822 int ret;
3823 char *service_class = NULL;
3824 char *spn_tail = NULL;
3825 char *p = NULL;
3826 char **aliases = NULL;
3827 size_t n_aliases = 0;
3828 size_t i, len;
3829 TALLOC_CTX *tmp_ctx = NULL;
3830 const char *target_dnstr = ldb_dn_get_linearized(target_dn);
3831 int link_direction;
3833 tmp_ctx = talloc_new(mem_ctx);
3834 if (tmp_ctx == NULL) {
3835 return ldb_oom(ldb);
3839 * "dns/example.com/xxx" gives
3840 * service_class = "dns"
3841 * spn_tail = "example.com/xxx"
3843 p = strchr(spn, '/');
3844 if (p == NULL) {
3845 /* bad SPN */
3846 talloc_free(tmp_ctx);
3847 return ldb_error(ldb,
3848 LDB_ERR_OPERATIONS_ERROR,
3849 "malformed servicePrincipalName");
3851 len = p - spn;
3853 service_class = talloc_strndup(tmp_ctx, spn, len);
3854 if (service_class == NULL) {
3855 talloc_free(tmp_ctx);
3856 return ldb_oom(ldb);
3858 spn_tail = p + 1;
3860 ret = find_spn_aliases(ldb,
3861 tmp_ctx,
3862 service_class,
3863 &aliases,
3864 &n_aliases,
3865 &link_direction);
3866 if (ret != LDB_SUCCESS) {
3867 talloc_free(tmp_ctx);
3868 return ret;
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.
3875 for (i = 0; i < n_aliases; i++) {
3876 struct ldb_dn *colliding_dn = NULL;
3877 const char *colliding_dnstr = NULL;
3879 char *candidate = talloc_asprintf(tmp_ctx,
3880 "%s/%s",
3881 aliases[i],
3882 spn_tail);
3883 if (candidate == NULL) {
3884 talloc_free(tmp_ctx);
3885 return ldb_oom(ldb);
3888 ret = get_spn_dn(ldb, tmp_ctx, candidate, &colliding_dn);
3889 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
3890 DBG_DEBUG("SPN alias '%s' not found (good)\n",
3891 candidate);
3892 talloc_free(candidate);
3893 continue;
3895 if (ret != LDB_SUCCESS) {
3896 DBG_ERR("SPN '%s' search error %d\n", candidate, ret);
3897 talloc_free(tmp_ctx);
3898 return ret;
3901 target_dnstr = ldb_dn_get_linearized(target_dn);
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.
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.
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.
3919 * Note: we also can't have "host/X" and "Host/X" on the same
3920 * object, but that is not relevant here.
3923 ret = ldb_dn_compare(colliding_dn, target_dn);
3924 if (ret != 0) {
3925 colliding_dnstr = ldb_dn_get_linearized(colliding_dn);
3926 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);
3933 if (link_direction == SPN_ALIAS_LINK) {
3934 /* we don't allow host/X if there is a
3935 * cifs/X */
3936 talloc_free(tmp_ctx);
3937 return LDB_ERR_CONSTRAINT_VIOLATION;
3939 ret = check_spn_write_rights(ldb,
3940 tmp_ctx,
3941 candidate,
3942 colliding_dn);
3943 if (ret != LDB_SUCCESS) {
3944 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 talloc_free(tmp_ctx);
3951 ldb_asprintf_errstring(ldb,
3952 "samldb: spn[%s] would cause a conflict",
3953 spn);
3954 return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
3956 } else {
3957 DBG_INFO("SPNs '%s' and '%s' alias both on '%s'\n",
3958 candidate, spn, target_dnstr);
3960 talloc_free(candidate);
3963 talloc_free(tmp_ctx);
3964 return LDB_SUCCESS;
3967 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)
3972 int ret;
3973 TALLOC_CTX *tmp_ctx = NULL;
3974 struct ldb_dn *colliding_dn = NULL;
3975 const char *target_dnstr = NULL;
3976 const char *colliding_dnstr = NULL;
3978 tmp_ctx = talloc_new(mem_ctx);
3979 if (tmp_ctx == NULL) {
3980 return ldb_oom(ldb);
3983 ret = get_spn_dn(ldb, tmp_ctx, spn, &colliding_dn);
3984 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
3985 DBG_DEBUG("SPN '%s' not found (good)\n", spn);
3986 talloc_free(tmp_ctx);
3987 return LDB_SUCCESS;
3989 if (ret != LDB_SUCCESS) {
3990 DBG_ERR("SPN '%s' search error %d\n", spn, ret);
3991 talloc_free(tmp_ctx);
3992 if (ret == LDB_ERR_COMPARE_TRUE) {
3994 * COMPARE_TRUE has special meaning here and we don't
3995 * want to return it by mistake.
3997 ret = LDB_ERR_OPERATIONS_ERROR;
3999 return ret;
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.
4007 target_dnstr = ldb_dn_get_linearized(target_dn);
4008 ret = ldb_dn_compare(colliding_dn, target_dn);
4009 if (ret != 0) {
4010 colliding_dnstr = ldb_dn_get_linearized(colliding_dn);
4011 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 ldb_asprintf_errstring(ldb,
4017 "samldb: spn[%s] would cause a conflict",
4018 spn);
4019 talloc_free(tmp_ctx);
4020 return LDB_ERR_CONSTRAINT_VIOLATION;
4023 DBG_INFO("SPN '%s' is already on '%s'\n",
4024 spn, target_dnstr);
4025 talloc_free(tmp_ctx);
4026 return LDB_ERR_COMPARE_TRUE;
4030 static int count_spn_components(struct ldb_val val)
4033 * a 3 part servicePrincipalName has two slashes, like
4034 * ldap/example.com/DomainDNSZones.example.com.
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.
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.
4045 size_t i;
4046 int slashes = 0;
4047 for (i = 0; i < val.length; i++) {
4048 char c = val.data[i];
4049 if (c == '/') {
4050 slashes++;
4051 if (slashes == 3) {
4052 /* at this point we don't care */
4053 return 4;
4057 return slashes + 1;
4061 /* Check that "servicePrincipalName" changes do not introduce a collision
4062 * globally. */
4063 static int samldb_spn_uniqueness_check(struct samldb_ctx *ac,
4064 struct ldb_message_element *spn_el)
4066 struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
4067 int ret;
4068 const char *spn = NULL;
4069 size_t i;
4070 TALLOC_CTX *tmp_ctx = talloc_new(ac->msg);
4071 if (tmp_ctx == NULL) {
4072 return ldb_oom(ldb);
4075 for (i = 0; i < spn_el->num_values; i++) {
4076 int n_components;
4077 spn = (char *)spn_el->values[i].data;
4079 n_components = count_spn_components(spn_el->values[i]);
4080 if (n_components > 3 || n_components < 2) {
4081 ldb_asprintf_errstring(ldb,
4082 "samldb: spn[%s] invalid with %u components",
4083 spn, n_components);
4084 talloc_free(tmp_ctx);
4085 return LDB_ERR_CONSTRAINT_VIOLATION;
4088 ret = check_spn_direct_collision(ldb,
4089 tmp_ctx,
4090 spn,
4091 ac->msg->dn);
4092 if (ret == LDB_ERR_COMPARE_TRUE) {
4093 DBG_INFO("SPN %s re-added to the same object\n", spn);
4094 continue;
4096 if (ret != LDB_SUCCESS) {
4097 DBG_ERR("SPN %s failed direct uniqueness check\n", spn);
4098 talloc_free(tmp_ctx);
4099 return ret;
4102 ret = check_spn_alias_collision(ldb,
4103 tmp_ctx,
4104 spn,
4105 ac->msg->dn);
4107 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
4108 /* we have no sPNMappings, hence no aliases */
4109 break;
4111 if (ret != LDB_SUCCESS) {
4112 DBG_ERR("SPN %s failed alias uniqueness check\n", spn);
4113 talloc_free(tmp_ctx);
4114 return ret;
4116 DBG_INFO("SPN %s seems to be unique\n", spn);
4119 talloc_free(tmp_ctx);
4120 return LDB_SUCCESS;
4125 /* This trigger adapts the "servicePrincipalName" attributes if the
4126 * "dNSHostName" and/or "sAMAccountName" attribute change(s) */
4127 static int samldb_service_principal_names_change(struct samldb_ctx *ac)
4129 struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
4130 struct ldb_message_element *el = NULL, *el2 = NULL;
4131 struct ldb_message *msg;
4132 const char * const attrs[] = { "servicePrincipalName", NULL };
4133 struct ldb_result *res;
4134 const char *dns_hostname = NULL, *old_dns_hostname = NULL,
4135 *sam_accountname = NULL, *old_sam_accountname = NULL;
4136 unsigned int i, j;
4137 int ret;
4139 ret = dsdb_get_expected_new_values(ac,
4140 ac->msg,
4141 "dNSHostName",
4142 &el,
4143 ac->req->operation);
4144 if (ret != LDB_SUCCESS) {
4145 return ret;
4147 ret = dsdb_get_expected_new_values(ac,
4148 ac->msg,
4149 "sAMAccountName",
4150 &el2,
4151 ac->req->operation);
4152 if (ret != LDB_SUCCESS) {
4153 return ret;
4155 if ((el == NULL) && (el2 == NULL)) {
4156 /* we are not affected */
4157 return LDB_SUCCESS;
4160 /* Create a temporary message for fetching the "dNSHostName" */
4161 if (el != NULL) {
4162 const char *dns_attrs[] = { "dNSHostName", NULL };
4163 msg = ldb_msg_new(ac->msg);
4164 if (msg == NULL) {
4165 return ldb_module_oom(ac->module);
4167 ret = ldb_msg_add(msg, el, 0);
4168 if (ret != LDB_SUCCESS) {
4169 return ret;
4171 dns_hostname = talloc_strdup(ac,
4172 ldb_msg_find_attr_as_string(msg, "dNSHostName", NULL));
4173 if (dns_hostname == NULL) {
4174 return ldb_module_oom(ac->module);
4177 talloc_free(msg);
4179 ret = dsdb_module_search_dn(ac->module, ac, &res, ac->msg->dn,
4180 dns_attrs, DSDB_FLAG_NEXT_MODULE, ac->req);
4181 if (ret == LDB_SUCCESS) {
4182 old_dns_hostname = ldb_msg_find_attr_as_string(res->msgs[0], "dNSHostName", NULL);
4186 /* Create a temporary message for fetching the "sAMAccountName" */
4187 if (el2 != NULL) {
4188 char *tempstr, *tempstr2 = NULL;
4189 const char *acct_attrs[] = { "sAMAccountName", NULL };
4191 msg = ldb_msg_new(ac->msg);
4192 if (msg == NULL) {
4193 return ldb_module_oom(ac->module);
4195 ret = ldb_msg_add(msg, el2, 0);
4196 if (ret != LDB_SUCCESS) {
4197 return ret;
4199 tempstr = talloc_strdup(ac,
4200 ldb_msg_find_attr_as_string(msg, "sAMAccountName", NULL));
4201 talloc_free(msg);
4203 ret = dsdb_module_search_dn(ac->module, ac, &res, ac->msg->dn, acct_attrs,
4204 DSDB_FLAG_NEXT_MODULE, ac->req);
4205 if (ret == LDB_SUCCESS) {
4206 tempstr2 = talloc_strdup(ac,
4207 ldb_msg_find_attr_as_string(res->msgs[0],
4208 "sAMAccountName", NULL));
4212 /* The "sAMAccountName" needs some additional trimming: we need
4213 * to remove the trailing "$"s if they exist. */
4214 if ((tempstr != NULL) && (tempstr[0] != '\0') &&
4215 (tempstr[strlen(tempstr) - 1] == '$')) {
4216 tempstr[strlen(tempstr) - 1] = '\0';
4218 if ((tempstr2 != NULL) && (tempstr2[0] != '\0') &&
4219 (tempstr2[strlen(tempstr2) - 1] == '$')) {
4220 tempstr2[strlen(tempstr2) - 1] = '\0';
4222 sam_accountname = tempstr;
4223 old_sam_accountname = tempstr2;
4226 if (old_dns_hostname == NULL) {
4227 /* we cannot change when the old name is unknown */
4228 dns_hostname = NULL;
4230 if ((old_dns_hostname != NULL) && (dns_hostname != NULL) &&
4231 (strcasecmp_m(old_dns_hostname, dns_hostname) == 0)) {
4232 /* The "dNSHostName" didn't change */
4233 dns_hostname = NULL;
4236 if (old_sam_accountname == NULL) {
4237 /* we cannot change when the old name is unknown */
4238 sam_accountname = NULL;
4240 if ((old_sam_accountname != NULL) && (sam_accountname != NULL) &&
4241 (strcasecmp_m(old_sam_accountname, sam_accountname) == 0)) {
4242 /* The "sAMAccountName" didn't change */
4243 sam_accountname = NULL;
4246 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 return LDB_SUCCESS;
4253 * Potential "servicePrincipalName" changes in the same request have
4254 * to be handled before the update (Windows behaviour).
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.
4260 el = ldb_msg_find_element(ac->msg, "servicePrincipalName");
4261 if (el != NULL) {
4262 msg = ldb_msg_new(ac->msg);
4263 if (msg == NULL) {
4264 return ldb_module_oom(ac->module);
4266 msg->dn = ac->msg->dn;
4268 do {
4269 ret = ldb_msg_add(msg, el, el->flags);
4270 if (ret != LDB_SUCCESS) {
4271 return ret;
4274 ldb_msg_remove_element(ac->msg, el);
4276 el = ldb_msg_find_element(ac->msg,
4277 "servicePrincipalName");
4278 } while (el != NULL);
4280 ret = dsdb_module_modify(ac->module, msg,
4281 DSDB_FLAG_OWN_MODULE, ac->req);
4282 if (ret != LDB_SUCCESS) {
4283 return ret;
4285 talloc_free(msg);
4288 /* Fetch the "servicePrincipalName"s if any */
4289 ret = dsdb_module_search(ac->module, ac, &res, ac->msg->dn, LDB_SCOPE_BASE, attrs,
4290 DSDB_FLAG_NEXT_MODULE, ac->req, NULL);
4291 if (ret != LDB_SUCCESS) {
4292 return ret;
4294 if ((res->count != 1) || (res->msgs[0]->num_elements > 1)) {
4295 return ldb_operr(ldb);
4298 if (res->msgs[0]->num_elements == 1) {
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.
4310 ret = ldb_msg_add_empty(ac->msg, "servicePrincipalName",
4311 LDB_FLAG_MOD_REPLACE, &el);
4312 if (ret != LDB_SUCCESS) {
4313 return ret;
4316 for (i = 0; i < res->msgs[0]->elements[0].num_values; i++) {
4317 char *old_str, *new_str;
4318 char *pos = NULL;
4319 const char *tok;
4320 struct ldb_val *vals;
4321 bool found = false;
4323 old_str = (char *)
4324 res->msgs[0]->elements[0].values[i].data;
4326 new_str = talloc_strdup(ac->msg,
4327 strtok_r(old_str, "/", &pos));
4328 if (new_str == NULL) {
4329 return ldb_module_oom(ac->module);
4332 while ((tok = strtok_r(NULL, "/", &pos)) != NULL) {
4333 if ((dns_hostname != NULL) &&
4334 (strcasecmp_m(tok, old_dns_hostname) == 0)) {
4335 tok = dns_hostname;
4337 if ((sam_accountname != NULL) &&
4338 (strcasecmp_m(tok, old_sam_accountname) == 0)) {
4339 tok = sam_accountname;
4342 new_str = talloc_asprintf(ac->msg, "%s/%s",
4343 new_str, tok);
4344 if (new_str == NULL) {
4345 return ldb_module_oom(ac->module);
4349 /* Uniqueness check */
4350 for (j = 0; (!found) && (j < el->num_values); j++) {
4351 if (strcasecmp_m((char *)el->values[j].data,
4352 new_str) == 0) {
4353 found = true;
4356 if (found) {
4357 continue;
4361 * append the new "servicePrincipalName" -
4362 * code derived from ldb_msg_add_value().
4364 * Open coded to make it clear that we must
4365 * append to the MOD_REPLACE el created above.
4367 vals = talloc_realloc(ac->msg, el->values,
4368 struct ldb_val,
4369 el->num_values + 1);
4370 if (vals == NULL) {
4371 return ldb_module_oom(ac->module);
4373 el->values = vals;
4374 el->values[el->num_values] = data_blob_string_const(new_str);
4375 ++(el->num_values);
4379 talloc_free(res);
4381 return LDB_SUCCESS;
4384 /* This checks the "fSMORoleOwner" attributes */
4385 static int samldb_fsmo_role_owner_check(struct samldb_ctx *ac)
4387 struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
4388 const char * const no_attrs[] = { NULL };
4389 struct ldb_message_element *el;
4390 struct ldb_message *tmp_msg;
4391 struct ldb_dn *res_dn;
4392 struct ldb_result *res;
4393 int ret;
4394 ret = dsdb_get_expected_new_values(ac,
4395 ac->msg,
4396 "fSMORoleOwner",
4397 &el,
4398 ac->req->operation);
4399 if (ret != LDB_SUCCESS) {
4400 return ret;
4403 if (el == NULL) {
4404 /* we are not affected */
4405 return LDB_SUCCESS;
4407 if (el->num_values != 1) {
4408 goto choose_error_code;
4411 /* Create a temporary message for fetching the "fSMORoleOwner" */
4412 tmp_msg = ldb_msg_new(ac->msg);
4413 if (tmp_msg == NULL) {
4414 return ldb_module_oom(ac->module);
4416 ret = ldb_msg_add(tmp_msg, el, 0);
4417 if (ret != LDB_SUCCESS) {
4418 return ret;
4420 res_dn = ldb_msg_find_attr_as_dn(ldb, ac, tmp_msg, "fSMORoleOwner");
4421 talloc_free(tmp_msg);
4423 if (res_dn == NULL) {
4424 ldb_set_errstring(ldb,
4425 "samldb: 'fSMORoleOwner' attributes have to reference 'nTDSDSA' entries!");
4426 goto choose_error_code;
4429 /* Fetched DN has to reference a "nTDSDSA" entry */
4430 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 if (ret != LDB_SUCCESS) {
4435 return ret;
4437 if (res->count != 1) {
4438 ldb_set_errstring(ldb,
4439 "samldb: 'fSMORoleOwner' attributes have to reference 'nTDSDSA' entries!");
4440 return LDB_ERR_UNWILLING_TO_PERFORM;
4443 talloc_free(res);
4445 return LDB_SUCCESS;
4447 choose_error_code:
4448 /* this is just how it is */
4449 if (ac->req->operation == LDB_ADD) {
4450 return LDB_ERR_CONSTRAINT_VIOLATION;
4451 } else {
4452 return LDB_ERR_UNWILLING_TO_PERFORM;
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.
4461 static int check_cidr_zero_bits(uint8_t *address, unsigned int len,
4462 unsigned int mask)
4464 /* <address> is an integer in big-endian form, <len> bits long. All
4465 bits between <mask> and <len> must be zero. */
4466 int i;
4467 unsigned int byte_len;
4468 unsigned int byte_mask;
4469 unsigned int bit_mask;
4470 if (len == 32) {
4471 DBG_INFO("Looking at address %02x%02x%02x%02x, mask %u\n",
4472 address[0], address[1], address[2], address[3],
4473 mask);
4474 } else if (len == 128){
4475 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);
4485 if (mask > len){
4486 DBG_INFO("mask %u is too big (> %u)\n", mask, len);
4487 return -1;
4489 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.
4494 if (len == 32){
4495 if (address[0] == 255 &&
4496 address[1] == 255 &&
4497 address[2] == 255 &&
4498 address[3] == 255){
4499 return -1;
4502 return 0;
4505 byte_len = len / 8;
4506 byte_mask = mask / 8;
4508 for (i = byte_len - 1; i > byte_mask; i--){
4509 DBG_DEBUG("checking byte %d %02x\n", i, address[i]);
4510 if (address[i] != 0){
4511 return -1;
4514 bit_mask = (1 << (8 - (mask & 7))) - 1;
4515 DBG_DEBUG("checking bitmask %02x & %02x overlap %02x\n", bit_mask, address[byte_mask],
4516 bit_mask & address[byte_mask]);
4517 if (address[byte_mask] & bit_mask){
4518 return -1;
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.
4525 * The bit_mask used in the previous check is the complement of what
4526 * we want here.
4528 if (len == 32 && address[byte_mask] == (uint8_t)~bit_mask){
4529 bool ok = false;
4530 for (i = 0; i < byte_mask; i++){
4531 if (address[i] != 255){
4532 ok = true;
4533 break;
4536 if (ok == false){
4537 return -1;
4540 return 0;
4545 static int check_address_roundtrip(const char *address, int family,
4546 const uint8_t *address_bytes,
4547 char *buffer, int buffer_len)
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.
4554 const char *address_redux = inet_ntop(family, address_bytes,
4555 buffer, buffer_len);
4556 if (address_redux == NULL){
4557 DBG_INFO("Address round trip %s failed unexpectedly"
4558 " with errno %d\n", address, errno);
4559 return -1;
4561 if (strcasecmp(address, address_redux) != 0){
4562 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
4568 if (strchr(address_redux, '.') != NULL){
4569 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));
4575 return -1;
4577 return 0;
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.
4588 * Return 0 on success, -1 on error.
4590 static int verify_cidr(const char *cidr)
4592 char *address = NULL, *slash = NULL;
4593 bool has_colon, has_dot;
4594 int res, ret;
4595 unsigned long mask;
4596 uint8_t *address_bytes = NULL;
4597 char *address_redux = NULL;
4598 unsigned int address_len;
4599 TALLOC_CTX *frame = NULL;
4600 int error = 0;
4602 DBG_DEBUG("CIDR is %s\n", cidr);
4603 frame = talloc_stackframe();
4604 address = talloc_strdup(frame, cidr);
4605 if (address == NULL){
4606 goto error;
4609 /* there must be a '/' */
4610 slash = strchr(address, '/');
4611 if (slash == NULL){
4612 goto error;
4614 /* terminate the address for strchr, inet_pton */
4615 *slash = '\0';
4617 mask = smb_strtoul(slash + 1, NULL, 10, &error, SMB_STR_FULL_STR_CONV);
4618 if (mask == 0){
4619 DBG_INFO("Windows does not like the zero mask, "
4620 "so nor do we: %s\n", cidr);
4621 goto error;
4624 if (error != 0){
4625 DBG_INFO("CIDR mask is not a proper integer: %s\n", cidr);
4626 goto error;
4629 address_bytes = talloc_size(frame, sizeof(struct in6_addr));
4630 if (address_bytes == NULL){
4631 goto error;
4634 address_redux = talloc_size(frame, INET6_ADDRSTRLEN);
4635 if (address_redux == NULL){
4636 goto error;
4639 DBG_INFO("found address %s, mask %lu\n", address, mask);
4640 has_colon = (strchr(address, ':') == NULL) ? false : true;
4641 has_dot = (strchr(address, '.') == NULL) ? false : true;
4642 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 DBG_INFO("Refusing to consider cidr '%s' with dots and colons\n",
4646 cidr);
4647 goto error;
4648 } else if (has_colon){ /* looks like IPv6 */
4649 res = inet_pton(AF_INET6, address, address_bytes);
4650 if (res != 1) {
4651 DBG_INFO("Address in %s fails to parse as IPv6\n", cidr);
4652 goto error;
4654 address_len = 128;
4655 if (check_address_roundtrip(address, AF_INET6, address_bytes,
4656 address_redux, INET6_ADDRSTRLEN)){
4657 goto error;
4659 } else if (has_dot) {
4660 /* looks like IPv4 */
4661 if (strcmp(address, "0.0.0.0") == 0){
4662 DBG_INFO("Windows does not like the zero IPv4 address, "
4663 "so nor do we.\n");
4664 goto error;
4666 res = inet_pton(AF_INET, address, address_bytes);
4667 if (res != 1) {
4668 DBG_INFO("Address in %s fails to parse as IPv4\n", cidr);
4669 goto error;
4671 address_len = 32;
4673 if (check_address_roundtrip(address, AF_INET, address_bytes,
4674 address_redux, INET_ADDRSTRLEN)){
4675 goto error;
4677 } else {
4678 /* This doesn't look like an IP address at all. */
4679 goto error;
4682 ret = check_cidr_zero_bits(address_bytes, address_len, mask);
4683 talloc_free(frame);
4684 return ret;
4685 error:
4686 talloc_free(frame);
4687 return -1;
4691 static int samldb_verify_subnet(struct samldb_ctx *ac, struct ldb_dn *dn)
4693 struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
4694 const char *cidr = NULL;
4695 const struct ldb_val *rdn_value = NULL;
4697 rdn_value = ldb_dn_get_rdn_val(dn);
4698 if (rdn_value == NULL) {
4699 ldb_set_errstring(ldb, "samldb: ldb_dn_get_rdn_val "
4700 "failed");
4701 return LDB_ERR_UNWILLING_TO_PERFORM;
4704 cidr = ldb_dn_escape_value(ac, *rdn_value);
4705 DBG_INFO("looking at cidr '%s'\n", cidr);
4706 if (cidr == NULL) {
4707 ldb_set_errstring(ldb,
4708 "samldb: adding an empty subnet cidr seems wrong");
4709 return LDB_ERR_UNWILLING_TO_PERFORM;
4712 if (verify_cidr(cidr)){
4713 ldb_set_errstring(ldb,
4714 "samldb: subnet value is invalid");
4715 return LDB_ERR_INVALID_DN_SYNTAX;
4718 return LDB_SUCCESS;
4721 static char *refer_if_rodc(struct ldb_context *ldb, struct ldb_request *req,
4722 struct ldb_dn *dn)
4724 bool rodc = false;
4725 struct loadparm_context *lp_ctx;
4726 char *referral;
4727 int ret;
4728 WERROR err;
4730 if (ldb_request_get_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID) ||
4731 ldb_request_get_control(req, DSDB_CONTROL_DBCHECK_MODIFY_RO_REPLICA)) {
4732 return NULL;
4735 ret = samdb_rodc(ldb, &rodc);
4736 if (ret != LDB_SUCCESS) {
4737 DEBUG(4, (__location__ ": unable to tell if we are an RODC\n"));
4738 return NULL;
4741 if (rodc) {
4742 const char *domain = NULL;
4743 struct ldb_dn *fsmo_role_dn;
4744 struct ldb_dn *role_owner_dn;
4745 ldb_set_errstring(ldb, "RODC modify is forbidden!");
4746 lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
4747 struct loadparm_context);
4749 err = dsdb_get_fsmo_role_info(req, ldb, DREPL_PDC_MASTER,
4750 &fsmo_role_dn, &role_owner_dn);
4751 if (W_ERROR_IS_OK(err)) {
4752 struct ldb_dn *server_dn = ldb_dn_copy(req, role_owner_dn);
4753 if (server_dn != NULL) {
4754 ldb_dn_remove_child_components(server_dn, 1);
4756 domain = samdb_dn_to_dnshostname(ldb, req,
4757 server_dn);
4760 if (domain == NULL) {
4761 domain = lpcfg_dnsdomain(lp_ctx);
4763 referral = talloc_asprintf(req,
4764 "ldap://%s/%s",
4765 domain,
4766 ldb_dn_get_linearized(dn));
4767 return referral;
4770 return NULL;
4774 * Restrict all access to sensitive attributes.
4776 * We don't want to even inspect the values, so we can use the same
4777 * routine for ADD and MODIFY.
4781 static int samldb_check_sensitive_attributes(struct samldb_ctx *ac)
4783 struct ldb_message_element *el = NULL;
4784 struct security_token *user_token = NULL;
4785 int ret;
4787 if (dsdb_module_am_system(ac->module)) {
4788 return LDB_SUCCESS;
4791 user_token = acl_user_token(ac->module);
4792 if (user_token == NULL) {
4793 return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
4796 el = ldb_msg_find_element(ac->msg, "sidHistory");
4797 if (el) {
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).
4803 * If you want to modify this, say to merge domains,
4804 * directly modify the sam.ldb as root.
4806 ldb_asprintf_errstring(ldb_module_get_ctx(ac->module),
4807 "sidHistory "
4808 "(entry %s) cannot be created "
4809 "or changed over LDAP!",
4810 ldb_dn_get_linearized(ac->msg->dn));
4811 return LDB_ERR_UNWILLING_TO_PERFORM;
4814 el = ldb_msg_find_element(ac->msg, "msDS-SecondaryKrbTgtNumber");
4815 if (el) {
4816 struct security_descriptor *domain_sd;
4817 const struct dsdb_class *objectclass = NULL;
4819 * msDS-SecondaryKrbTgtNumber allows the creator to
4820 * become an RODC, this is trusted as an RODC
4821 * account
4823 ret = samldb_get_domain_secdesc_and_oc(ac, &domain_sd, &objectclass);
4824 if (ret != LDB_SUCCESS) {
4825 return ret;
4827 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 if (ret != LDB_SUCCESS) {
4837 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 ldb_dn_get_linearized(ac->msg->dn));
4843 return ret;
4847 el = ldb_msg_find_element(ac->msg, "msDS-AllowedToDelegateTo");
4848 if (el) {
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.
4857 bool have_priv = security_token_has_privilege(user_token,
4858 SEC_PRIV_ENABLE_DELEGATION);
4859 if (have_priv == false) {
4860 ldb_asprintf_errstring(ldb_module_get_ctx(ac->module),
4861 "msDS-AllowedToDelegateTo "
4862 "(entry %s) cannot be created "
4863 "or changed without SePrivEnableDelegation!",
4864 ldb_dn_get_linearized(ac->msg->dn));
4865 return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
4868 return LDB_SUCCESS;
4870 /* add */
4871 static int samldb_add(struct ldb_module *module, struct ldb_request *req)
4873 struct ldb_context *ldb;
4874 struct samldb_ctx *ac;
4875 struct ldb_message_element *el;
4876 int ret;
4877 char *referral = NULL;
4879 ldb = ldb_module_get_ctx(module);
4880 ldb_debug(ldb, LDB_DEBUG_TRACE, "samldb_add\n");
4882 /* do not manipulate our control entries */
4883 if (ldb_dn_is_special(req->op.add.message->dn)) {
4884 return ldb_next_request(module, req);
4887 referral = refer_if_rodc(ldb, req, req->op.add.message->dn);
4888 if (referral != NULL) {
4889 ret = ldb_module_send_referral(req, referral);
4890 return ret;
4893 el = ldb_msg_find_element(req->op.add.message, "userParameters");
4894 if (el != NULL && ldb_req_is_untrusted(req)) {
4895 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 ldb_debug(ldb, LDB_DEBUG_WARNING, "%s", reason);
4899 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION, reason);
4902 ac = samldb_ctx_init(module, req);
4903 if (ac == NULL) {
4904 return ldb_operr(ldb);
4907 /* build the new msg */
4908 ac->msg = ldb_msg_copy_shallow(ac, req->op.add.message);
4909 if (ac->msg == NULL) {
4910 talloc_free(ac);
4911 ldb_debug(ldb, LDB_DEBUG_FATAL,
4912 "samldb_add: ldb_msg_copy_shallow failed!\n");
4913 return ldb_operr(ldb);
4916 ret = samldb_check_sensitive_attributes(ac);
4917 if (ret != LDB_SUCCESS) {
4918 talloc_free(ac);
4919 return ret;
4922 el = ldb_msg_find_element(ac->msg, "fSMORoleOwner");
4923 if (el != NULL) {
4924 ret = samldb_fsmo_role_owner_check(ac);
4925 if (ret != LDB_SUCCESS) {
4926 return ret;
4930 el = ldb_msg_find_element(ac->msg, "servicePrincipalName");
4931 if ((el != NULL)) {
4933 * We need to check whether the SPN collides with an existing
4934 * one (anywhere) including via aliases.
4936 ret = samldb_spn_uniqueness_check(ac, el);
4937 if (ret != LDB_SUCCESS) {
4938 return ret;
4942 if (samdb_find_attribute(ldb, ac->msg,
4943 "objectclass", "user") != NULL) {
4944 ac->type = SAMLDB_TYPE_USER;
4946 ret = samldb_prim_group_trigger(ac);
4947 if (ret != LDB_SUCCESS) {
4948 return ret;
4951 ret = samldb_objectclass_trigger(ac);
4952 if (ret != LDB_SUCCESS) {
4953 return ret;
4956 return samldb_fill_object(ac);
4959 if (samdb_find_attribute(ldb, ac->msg,
4960 "objectclass", "group") != NULL) {
4961 ac->type = SAMLDB_TYPE_GROUP;
4963 ret = samldb_objectclass_trigger(ac);
4964 if (ret != LDB_SUCCESS) {
4965 return ret;
4968 return samldb_fill_object(ac);
4971 /* perhaps a foreignSecurityPrincipal? */
4972 if (samdb_find_attribute(ldb, ac->msg,
4973 "objectclass",
4974 "foreignSecurityPrincipal") != NULL) {
4975 return samldb_fill_foreignSecurityPrincipal_object(ac);
4978 if (samdb_find_attribute(ldb, ac->msg,
4979 "objectclass", "classSchema") != NULL) {
4980 ac->type = SAMLDB_TYPE_CLASS;
4982 /* If in provision, these checks are too slow to do */
4983 if (!ldb_request_get_control(req, DSDB_CONTROL_SKIP_DUPLICATES_CHECK_OID)) {
4984 ret = samldb_schema_governsid_valid_check(ac);
4985 if (ret != LDB_SUCCESS) {
4986 return ret;
4990 ret = samldb_schema_ldapdisplayname_valid_check(ac);
4991 if (ret != LDB_SUCCESS) {
4992 return ret;
4995 ret = samldb_schema_info_update(ac);
4996 if (ret != LDB_SUCCESS) {
4997 talloc_free(ac);
4998 return ret;
5001 return samldb_fill_object(ac);
5004 if (samdb_find_attribute(ldb, ac->msg,
5005 "objectclass", "attributeSchema") != NULL) {
5006 ac->type = SAMLDB_TYPE_ATTRIBUTE;
5008 /* If in provision, these checks are too slow to do */
5009 if (!ldb_request_get_control(req, DSDB_CONTROL_SKIP_DUPLICATES_CHECK_OID)) {
5010 ret = samldb_schema_attributeid_valid_check(ac);
5011 if (ret != LDB_SUCCESS) {
5012 return ret;
5015 ret = samldb_schema_add_handle_linkid(ac);
5016 if (ret != LDB_SUCCESS) {
5017 return ret;
5020 ret = samldb_schema_add_handle_mapiid(ac);
5021 if (ret != LDB_SUCCESS) {
5022 return ret;
5026 ret = samldb_schema_ldapdisplayname_valid_check(ac);
5027 if (ret != LDB_SUCCESS) {
5028 return ret;
5031 ret = samldb_schema_info_update(ac);
5032 if (ret != LDB_SUCCESS) {
5033 talloc_free(ac);
5034 return ret;
5037 return samldb_fill_object(ac);
5040 if (samdb_find_attribute(ldb, ac->msg,
5041 "objectclass", "subnet") != NULL) {
5042 ret = samldb_verify_subnet(ac, ac->msg->dn);
5043 if (ret != LDB_SUCCESS) {
5044 talloc_free(ac);
5045 return ret;
5047 /* We are just checking the value is valid, and there are no
5048 values to fill in. */
5051 talloc_free(ac);
5053 /* nothing matched, go on */
5054 return ldb_next_request(module, req);
5057 /* modify */
5058 static int samldb_modify(struct ldb_module *module, struct ldb_request *req)
5060 struct ldb_context *ldb;
5061 struct samldb_ctx *ac;
5062 struct ldb_message_element *el, *el2;
5063 struct ldb_control *is_undelete;
5064 bool modified = false;
5065 int ret;
5067 if (ldb_dn_is_special(req->op.mod.message->dn)) {
5068 /* do not manipulate our control entries */
5069 return ldb_next_request(module, req);
5072 ldb = ldb_module_get_ctx(module);
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
5079 is_undelete = ldb_request_get_control(req, DSDB_CONTROL_RESTORE_TOMBSTONE_OID);
5081 /* make sure that "objectSid" is not specified */
5082 el = ldb_msg_find_element(req->op.mod.message, "objectSid");
5083 if (el != NULL) {
5084 if (ldb_request_get_control(req, LDB_CONTROL_PROVISION_OID) == NULL) {
5085 ldb_set_errstring(ldb,
5086 "samldb: objectSid must not be specified!");
5087 return LDB_ERR_UNWILLING_TO_PERFORM;
5090 if (is_undelete == NULL) {
5091 /* make sure that "sAMAccountType" is not specified */
5092 el = ldb_msg_find_element(req->op.mod.message, "sAMAccountType");
5093 if (el != NULL) {
5094 ldb_set_errstring(ldb,
5095 "samldb: sAMAccountType must not be specified!");
5096 return LDB_ERR_UNWILLING_TO_PERFORM;
5099 /* make sure that "isCriticalSystemObject" is not specified */
5100 el = ldb_msg_find_element(req->op.mod.message, "isCriticalSystemObject");
5101 if (el != NULL) {
5102 if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID) == NULL) {
5103 ldb_set_errstring(ldb,
5104 "samldb: isCriticalSystemObject must not be specified!");
5105 return LDB_ERR_UNWILLING_TO_PERFORM;
5109 /* msDS-IntId is not allowed to be modified
5110 * except when modification comes from replication */
5111 if (ldb_msg_find_element(req->op.mod.message, "msDS-IntId")) {
5112 if (!ldb_request_get_control(req,
5113 DSDB_CONTROL_REPLICATED_UPDATE_OID)) {
5114 return LDB_ERR_CONSTRAINT_VIOLATION;
5118 el = ldb_msg_find_element(req->op.mod.message, "userParameters");
5119 if (el != NULL && ldb_req_is_untrusted(req)) {
5120 const char *reason = "samldb: "
5121 "setting userParameters is not supported over LDAP, "
5122 "see https://bugzilla.samba.org/show_bug.cgi?id=8077";
5123 ldb_debug(ldb, LDB_DEBUG_WARNING, "%s", reason);
5124 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION, reason);
5127 ac = samldb_ctx_init(module, req);
5128 if (ac == NULL) {
5129 return ldb_operr(ldb);
5132 /* build the new msg */
5133 ac->msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
5134 if (ac->msg == NULL) {
5135 talloc_free(ac);
5136 ldb_debug(ldb, LDB_DEBUG_FATAL,
5137 "samldb_modify: ldb_msg_copy_shallow failed!\n");
5138 return ldb_operr(ldb);
5141 ret = samldb_check_sensitive_attributes(ac);
5142 if (ret != LDB_SUCCESS) {
5143 talloc_free(ac);
5144 return ret;
5147 if (is_undelete == NULL) {
5148 el = ldb_msg_find_element(ac->msg, "primaryGroupID");
5149 if (el != NULL) {
5150 ret = samldb_prim_group_trigger(ac);
5151 if (ret != LDB_SUCCESS) {
5152 return ret;
5157 el = ldb_msg_find_element(ac->msg, "userAccountControl");
5158 if (el != NULL) {
5159 modified = true;
5160 ret = samldb_user_account_control_change(ac);
5161 if (ret != LDB_SUCCESS) {
5162 return ret;
5166 el = ldb_msg_find_element(ac->msg, "pwdLastSet");
5167 if (el != NULL) {
5168 modified = true;
5169 ret = samldb_pwd_last_set_change(ac);
5170 if (ret != LDB_SUCCESS) {
5171 return ret;
5175 el = ldb_msg_find_element(ac->msg, "lockoutTime");
5176 if (el != NULL) {
5177 modified = true;
5178 ret = samldb_lockout_time(ac);
5179 if (ret != LDB_SUCCESS) {
5180 return ret;
5184 el = ldb_msg_find_element(ac->msg, "groupType");
5185 if (el != NULL) {
5186 modified = true;
5187 ret = samldb_group_type_change(ac);
5188 if (ret != LDB_SUCCESS) {
5189 return ret;
5193 el = ldb_msg_find_element(ac->msg, "sAMAccountName");
5194 if (el != NULL) {
5195 uint32_t user_account_control;
5196 struct ldb_result *res = NULL;
5197 const char * const attrs[] = { "userAccountControl",
5198 "objectclass",
5199 NULL };
5200 ret = dsdb_module_search_dn(ac->module,
5202 &res,
5203 ac->msg->dn,
5204 attrs,
5205 DSDB_FLAG_NEXT_MODULE | DSDB_SEARCH_SHOW_DELETED,
5206 ac->req);
5207 if (ret != LDB_SUCCESS) {
5208 return ret;
5210 user_account_control
5211 = ldb_msg_find_attr_as_uint(res->msgs[0],
5212 "userAccountControl",
5215 if ((user_account_control
5216 & UF_TRUST_ACCOUNT_MASK) != 0) {
5217 ac->need_trailing_dollar = true;
5219 } else if (samdb_find_attribute(ldb,
5220 res->msgs[0],
5221 "objectclass",
5222 "computer")
5223 != NULL) {
5224 ac->need_trailing_dollar = true;
5227 ret = samldb_sam_accountname_valid_check(ac);
5228 if (ret != LDB_SUCCESS) {
5229 return ret;
5233 el = ldb_msg_find_element(ac->msg, "userPrincipalName");
5234 if (el != NULL) {
5235 ret = samldb_sam_account_upn_clash(ac);
5236 if (ret != LDB_SUCCESS) {
5237 talloc_free(ac);
5238 return ret;
5242 el = ldb_msg_find_element(ac->msg, "ldapDisplayName");
5243 if (el != NULL) {
5244 ret = samldb_schema_ldapdisplayname_valid_check(ac);
5245 if (ret != LDB_SUCCESS) {
5246 return ret;
5250 el = ldb_msg_find_element(ac->msg, "attributeID");
5251 if (el != NULL) {
5252 ldb_asprintf_errstring(ldb_module_get_ctx(ac->module),
5253 "Once set, attributeID values may not be modified");
5254 return LDB_ERR_CONSTRAINT_VIOLATION;
5257 el = ldb_msg_find_element(ac->msg, "governsID");
5258 if (el != NULL) {
5259 ldb_asprintf_errstring(ldb_module_get_ctx(ac->module),
5260 "Once set, governsID values may not be modified");
5261 return LDB_ERR_CONSTRAINT_VIOLATION;
5264 el = ldb_msg_find_element(ac->msg, "member");
5265 if (el != NULL) {
5266 struct ldb_control *fix_link_sid_ctrl = NULL;
5268 fix_link_sid_ctrl = ldb_request_get_control(ac->req,
5269 DSDB_CONTROL_DBCHECK_FIX_LINK_DN_SID);
5270 if (fix_link_sid_ctrl == NULL) {
5271 ret = samldb_member_check(ac);
5272 if (ret != LDB_SUCCESS) {
5273 return ret;
5278 el = ldb_msg_find_element(ac->msg, "description");
5279 if (el != NULL) {
5280 ret = samldb_description_check(ac, &modified);
5281 if (ret != LDB_SUCCESS) {
5282 return ret;
5286 el = ldb_msg_find_element(ac->msg, "dNSHostName");
5287 el2 = ldb_msg_find_element(ac->msg, "sAMAccountName");
5288 if ((el != NULL) || (el2 != NULL)) {
5289 modified = true;
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.
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.
5300 ret = samldb_service_principal_names_change(ac);
5301 if (ret != LDB_SUCCESS) {
5302 return ret;
5306 el = ldb_msg_find_element(ac->msg, "servicePrincipalName");
5307 if ((el != NULL)) {
5309 * We need to check whether the SPN collides with an existing
5310 * one (anywhere) including via aliases.
5312 modified = true;
5313 ret = samldb_spn_uniqueness_check(ac, el);
5314 if (ret != LDB_SUCCESS) {
5315 return ret;
5319 el = ldb_msg_find_element(ac->msg, "fSMORoleOwner");
5320 if (el != NULL) {
5321 ret = samldb_fsmo_role_owner_check(ac);
5322 if (ret != LDB_SUCCESS) {
5323 return ret;
5327 if (modified) {
5328 struct ldb_request *child_req;
5330 /* Now perform the real modifications as a child request */
5331 ret = ldb_build_mod_req(&child_req, ldb, ac,
5332 ac->msg,
5333 req->controls,
5334 req, dsdb_next_callback,
5335 req);
5336 LDB_REQ_SET_LOCATION(child_req);
5337 if (ret != LDB_SUCCESS) {
5338 return ret;
5341 return ldb_next_request(module, child_req);
5344 talloc_free(ac);
5346 /* no change which interests us, go on */
5347 return ldb_next_request(module, req);
5350 /* delete */
5352 static int samldb_prim_group_users_check(struct samldb_ctx *ac)
5354 struct ldb_context *ldb;
5355 struct dom_sid *sid;
5356 uint32_t rid;
5357 NTSTATUS status;
5358 int ret;
5359 struct ldb_result *res = NULL;
5360 struct ldb_result *res_users = NULL;
5361 const char * const attrs[] = { "objectSid", "isDeleted", NULL };
5362 const char * const noattrs[] = { NULL };
5364 ldb = ldb_module_get_ctx(ac->module);
5366 /* Finds out the SID/RID of the SAM object */
5367 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 if (ret != LDB_SUCCESS) {
5372 return ret;
5375 if (ldb_msg_check_string_attribute(res->msgs[0], "isDeleted", "TRUE")) {
5376 return LDB_SUCCESS;
5379 sid = samdb_result_dom_sid(ac, res->msgs[0], "objectSid");
5380 if (sid == NULL) {
5381 /* No SID - it might not be a SAM object - therefore ok */
5382 return LDB_SUCCESS;
5384 status = dom_sid_split_rid(ac, sid, NULL, &rid);
5385 if (!NT_STATUS_IS_OK(status)) {
5386 return ldb_operr(ldb);
5388 if (rid == 0) {
5389 /* Special object (security principal?) */
5390 return LDB_SUCCESS;
5392 /* do not allow deletion of well-known sids */
5393 if (rid < DSDB_SAMDB_MINIMUM_ALLOWED_RID &&
5394 (ldb_request_get_control(ac->req, LDB_CONTROL_RELAX_OID) == NULL)) {
5395 return LDB_ERR_OTHER;
5398 /* Deny delete requests from groups which are primary ones */
5399 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 if (ret != LDB_SUCCESS) {
5406 return ret;
5408 if (res_users->count > 0) {
5409 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 ldb_dn_get_linearized(res->msgs[0]->dn),
5414 res_users->count);
5417 * Yes, this seems very wrong, but we have a test
5418 * for this exact error code in sam.py
5420 return LDB_ERR_ENTRY_ALREADY_EXISTS;
5423 return LDB_SUCCESS;
5426 static int samldb_delete(struct ldb_module *module, struct ldb_request *req)
5428 struct samldb_ctx *ac;
5429 char *referral = NULL;
5430 int ret;
5431 struct ldb_context *ldb;
5433 if (ldb_dn_is_special(req->op.del.dn)) {
5434 /* do not manipulate our control entries */
5435 return ldb_next_request(module, req);
5438 ldb = ldb_module_get_ctx(module);
5440 referral = refer_if_rodc(ldb, req, req->op.del.dn);
5441 if (referral != NULL) {
5442 ret = ldb_module_send_referral(req, referral);
5443 return ret;
5446 ac = samldb_ctx_init(module, req);
5447 if (ac == NULL) {
5448 return ldb_operr(ldb_module_get_ctx(module));
5451 ret = samldb_prim_group_users_check(ac);
5452 if (ret != LDB_SUCCESS) {
5453 return ret;
5456 talloc_free(ac);
5458 return ldb_next_request(module, req);
5461 /* rename */
5463 static int check_rename_constraints(struct ldb_message *msg,
5464 struct samldb_ctx *ac,
5465 struct ldb_dn *olddn, struct ldb_dn *newdn)
5467 struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
5468 struct ldb_dn *dn1, *dn2, *nc_root;
5469 int32_t systemFlags;
5470 bool move_op = false;
5471 bool rename_op = false;
5472 int ret;
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. */
5478 if (ldb_dn_compare(olddn, newdn) == 0) {
5479 return LDB_SUCCESS;
5481 if (ldb_request_get_control(ac->req, LDB_CONTROL_RELAX_OID) != NULL) {
5482 return LDB_SUCCESS;
5485 if (ldb_msg_find_attr_as_bool(msg, "isDeleted", false)) {
5487 * check originating request if we are supposed
5488 * to "see" this record in first place.
5490 if (ldb_request_get_control(ac->req, LDB_CONTROL_SHOW_DELETED_OID) == NULL) {
5491 return LDB_ERR_NO_SUCH_OBJECT;
5493 return LDB_ERR_UNWILLING_TO_PERFORM;
5496 /* Objects under CN=System */
5498 dn1 = samdb_system_container_dn(ldb, ac);
5499 if (dn1 == NULL) return ldb_oom(ldb);
5501 if ((ldb_dn_compare_base(dn1, olddn) == 0) &&
5502 (ldb_dn_compare_base(dn1, newdn) != 0)) {
5503 talloc_free(dn1);
5504 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 return LDB_ERR_OTHER;
5510 talloc_free(dn1);
5512 /* LSA objects */
5514 if ((samdb_find_attribute(ldb, msg, "objectClass", "secret") != NULL) ||
5515 (samdb_find_attribute(ldb, msg, "objectClass", "trustedDomain") != NULL)) {
5516 ldb_asprintf_errstring(ldb,
5517 "subtree_rename: Cannot move/rename %s. It's an LSA-specific object!",
5518 ldb_dn_get_linearized(olddn));
5519 return LDB_ERR_UNWILLING_TO_PERFORM;
5522 /* subnet objects */
5523 if (samdb_find_attribute(ldb, msg, "objectclass", "subnet") != NULL) {
5524 ret = samldb_verify_subnet(ac, newdn);
5525 if (ret != LDB_SUCCESS) {
5526 return ret;
5530 /* systemFlags */
5532 dn1 = ldb_dn_get_parent(ac, olddn);
5533 if (dn1 == NULL) return ldb_oom(ldb);
5534 dn2 = ldb_dn_get_parent(ac, newdn);
5535 if (dn2 == NULL) return ldb_oom(ldb);
5537 if (ldb_dn_compare(dn1, dn2) == 0) {
5538 rename_op = true;
5539 } else {
5540 move_op = true;
5543 talloc_free(dn1);
5544 talloc_free(dn2);
5546 systemFlags = ldb_msg_find_attr_as_int(msg, "systemFlags", 0);
5548 /* Fetch name context */
5550 ret = dsdb_find_nc_root(ldb, ac, olddn, &nc_root);
5551 if (ret != LDB_SUCCESS) {
5552 return ret;
5555 if (ldb_dn_compare(nc_root, ldb_get_schema_basedn(ldb)) == 0) {
5556 if (move_op) {
5557 ldb_asprintf_errstring(ldb,
5558 "subtree_rename: Cannot move %s within schema partition",
5559 ldb_dn_get_linearized(olddn));
5560 return LDB_ERR_UNWILLING_TO_PERFORM;
5562 if (rename_op &&
5563 (systemFlags & SYSTEM_FLAG_SCHEMA_BASE_OBJECT) != 0) {
5564 ldb_asprintf_errstring(ldb,
5565 "subtree_rename: Cannot rename %s within schema partition",
5566 ldb_dn_get_linearized(olddn));
5567 return LDB_ERR_UNWILLING_TO_PERFORM;
5569 } else if (ldb_dn_compare(nc_root, ldb_get_config_basedn(ldb)) == 0) {
5570 if (move_op &&
5571 (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 bool limited_move =
5578 systemFlags & SYSTEM_FLAG_CONFIG_ALLOW_LIMITED_MOVE;
5580 if (limited_move) {
5581 dn1 = ldb_dn_copy(ac, olddn);
5582 if (dn1 == NULL) return ldb_oom(ldb);
5583 dn2 = ldb_dn_copy(ac, newdn);
5584 if (dn2 == NULL) return ldb_oom(ldb);
5586 limited_move &= ldb_dn_remove_child_components(dn1, 3);
5587 limited_move &= ldb_dn_remove_child_components(dn2, 3);
5588 limited_move &= ldb_dn_compare(dn1, dn2) == 0;
5590 talloc_free(dn1);
5591 talloc_free(dn2);
5594 if (!limited_move
5595 && ldb_request_get_control(ac->req, DSDB_CONTROL_RESTORE_TOMBSTONE_OID) == NULL) {
5596 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 return LDB_ERR_UNWILLING_TO_PERFORM;
5602 if (rename_op &&
5603 (systemFlags & SYSTEM_FLAG_CONFIG_ALLOW_RENAME) == 0) {
5604 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 return LDB_ERR_UNWILLING_TO_PERFORM;
5609 } else if (ldb_dn_compare(nc_root, ldb_get_default_basedn(ldb)) == 0) {
5610 if (move_op &&
5611 (systemFlags & SYSTEM_FLAG_DOMAIN_DISALLOW_MOVE) != 0) {
5612 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 return LDB_ERR_UNWILLING_TO_PERFORM;
5617 if (rename_op &&
5618 (systemFlags & SYSTEM_FLAG_DOMAIN_DISALLOW_RENAME) != 0) {
5619 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 return LDB_ERR_UNWILLING_TO_PERFORM;
5626 talloc_free(nc_root);
5628 return LDB_SUCCESS;
5632 static int samldb_rename_search_base_callback(struct ldb_request *req,
5633 struct ldb_reply *ares)
5635 struct samldb_ctx *ac;
5636 int ret;
5638 ac = talloc_get_type(req->context, struct samldb_ctx);
5640 if (!ares) {
5641 return ldb_module_done(ac->req, NULL, NULL,
5642 LDB_ERR_OPERATIONS_ERROR);
5644 if (ares->error != LDB_SUCCESS) {
5645 return ldb_module_done(ac->req, ares->controls,
5646 ares->response, ares->error);
5649 switch (ares->type) {
5650 case LDB_REPLY_ENTRY:
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.
5657 ret = check_rename_constraints(ares->message, ac,
5658 ac->req->op.rename.olddn,
5659 ac->req->op.rename.newdn);
5660 if (ret != LDB_SUCCESS) {
5661 return ldb_module_done(ac->req, NULL, NULL,
5662 ret);
5664 break;
5666 case LDB_REPLY_REFERRAL:
5667 /* ignore */
5668 break;
5670 case LDB_REPLY_DONE:
5673 * Great, no problem with the rename, so go ahead as
5674 * if we never were here
5676 ret = ldb_next_request(ac->module, ac->req);
5677 talloc_free(ares);
5678 return ret;
5681 talloc_free(ares);
5682 return LDB_SUCCESS;
5686 /* rename */
5687 static int samldb_rename(struct ldb_module *module, struct ldb_request *req)
5689 struct ldb_context *ldb;
5690 static const char * const attrs[] = { "objectClass", "systemFlags",
5691 "isDeleted", NULL };
5692 struct ldb_request *search_req;
5693 struct samldb_ctx *ac;
5694 int ret;
5696 if (ldb_dn_is_special(req->op.rename.olddn)) { /* do not manipulate our control entries */
5697 return ldb_next_request(module, req);
5700 ldb = ldb_module_get_ctx(module);
5702 ac = samldb_ctx_init(module, req);
5703 if (!ac) {
5704 return ldb_oom(ldb);
5707 ret = ldb_build_search_req(&search_req, ldb, ac,
5708 req->op.rename.olddn,
5709 LDB_SCOPE_BASE,
5710 "(objectClass=*)",
5711 attrs,
5712 NULL,
5714 samldb_rename_search_base_callback,
5715 req);
5716 LDB_REQ_SET_LOCATION(search_req);
5717 if (ret != LDB_SUCCESS) {
5718 return ret;
5721 ret = ldb_request_add_control(search_req, LDB_CONTROL_SHOW_RECYCLED_OID,
5722 true, NULL);
5723 if (ret != LDB_SUCCESS) {
5724 return ret;
5727 return ldb_next_request(ac->module, search_req);
5730 /* extended */
5732 static int samldb_extended_allocate_rid_pool(struct ldb_module *module, struct ldb_request *req)
5734 struct ldb_context *ldb = ldb_module_get_ctx(module);
5735 struct dsdb_fsmo_extended_op *exop;
5736 int ret;
5738 exop = talloc_get_type(req->op.extended.data,
5739 struct dsdb_fsmo_extended_op);
5740 if (!exop) {
5741 ldb_set_errstring(ldb,
5742 "samldb_extended_allocate_rid_pool: invalid extended data");
5743 return LDB_ERR_PROTOCOL_ERROR;
5746 ret = ridalloc_allocate_rid_pool_fsmo(module, exop, req);
5747 if (ret != LDB_SUCCESS) {
5748 return ret;
5751 return ldb_module_done(req, NULL, NULL, LDB_SUCCESS);
5754 static int samldb_extended_allocate_rid(struct ldb_module *module, struct ldb_request *req)
5756 struct ldb_context *ldb = ldb_module_get_ctx(module);
5757 struct dsdb_extended_allocate_rid *exop;
5758 int ret;
5760 exop = talloc_get_type(req->op.extended.data,
5761 struct dsdb_extended_allocate_rid);
5762 if (!exop) {
5763 ldb_set_errstring(ldb,
5764 "samldb_extended_allocate_rid: invalid extended data");
5765 return LDB_ERR_PROTOCOL_ERROR;
5768 ret = ridalloc_allocate_rid(module, &exop->rid, req);
5769 if (ret != LDB_SUCCESS) {
5770 return ret;
5773 return ldb_module_done(req, NULL, NULL, LDB_SUCCESS);
5776 static int samldb_extended_create_own_rid_set(struct ldb_module *module, struct ldb_request *req)
5778 struct ldb_context *ldb = ldb_module_get_ctx(module);
5779 int ret;
5780 struct ldb_dn *dn;
5782 if (req->op.extended.data != NULL) {
5783 ldb_set_errstring(ldb,
5784 "samldb_extended_create_own_rid_set: invalid extended data (should be NULL)");
5785 return LDB_ERR_PROTOCOL_ERROR;
5788 ret = ridalloc_create_own_rid_set(module, req,
5789 &dn, req);
5790 if (ret != LDB_SUCCESS) {
5791 return ret;
5794 return ldb_module_done(req, NULL, NULL, LDB_SUCCESS);
5797 static int samldb_extended(struct ldb_module *module, struct ldb_request *req)
5799 if (strcmp(req->op.extended.oid, DSDB_EXTENDED_ALLOCATE_RID_POOL) == 0) {
5800 return samldb_extended_allocate_rid_pool(module, req);
5803 if (strcmp(req->op.extended.oid, DSDB_EXTENDED_ALLOCATE_RID) == 0) {
5804 return samldb_extended_allocate_rid(module, req);
5807 if (strcmp(req->op.extended.oid, DSDB_EXTENDED_CREATE_OWN_RID_SET) == 0) {
5808 return samldb_extended_create_own_rid_set(module, req);
5811 return ldb_next_request(module, req);
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
5825 int ldb_samldb_module_init(const char *version)
5827 LDB_MODULE_CHECK_VERSION(version);
5828 return ldb_register_module(&ldb_samldb_module_ops);