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/>.
27 * Component: ldb samldb module
29 * Description: various internal DSDB triggers - most for SAM specific objects
35 #include "libcli/ldap/ldap_ndr.h"
36 #include "ldb_module.h"
37 #include "auth/auth.h"
38 #include "dsdb/samdb/samdb.h"
39 #include "dsdb/samdb/ldb_modules/util.h"
40 #include "dsdb/samdb/ldb_modules/ridalloc.h"
41 #include "libcli/security/security.h"
42 #include "librpc/gen_ndr/ndr_security.h"
44 #include "param/param.h"
45 #include "libds/common/flag_mapping.h"
46 #include "system/network.h"
47 #include "librpc/gen_ndr/irpc.h"
48 #include "lib/util/smb_strtox.h"
53 enum samldb_add_type {
60 typedef int (*samldb_step_fn_t)(struct samldb_ctx *);
63 struct samldb_step *next;
68 struct ldb_module *module;
69 struct ldb_request *req;
71 /* used for add operations */
72 enum samldb_add_type type;
75 * should we apply the need_trailing_dollar restriction to
79 bool need_trailing_dollar;
81 /* the resulting message */
82 struct ldb_message *msg;
84 /* used in "samldb_find_for_defaultObjectCategory" */
85 struct ldb_dn *dn, *res_dn;
87 /* all the async steps necessary to complete the operation */
88 struct samldb_step *steps;
89 struct samldb_step *curstep;
91 /* If someone set an ares to forward controls and response back to the caller */
92 struct ldb_reply *ares;
95 static struct samldb_ctx *samldb_ctx_init(struct ldb_module *module,
96 struct ldb_request *req)
98 struct ldb_context *ldb;
99 struct samldb_ctx *ac;
101 ldb = ldb_module_get_ctx(module);
103 ac = talloc_zero(req, struct samldb_ctx);
115 static int samldb_add_step(struct samldb_ctx *ac, samldb_step_fn_t fn)
117 struct samldb_step *step, *stepper;
119 step = talloc_zero(ac, struct samldb_step);
121 return ldb_oom(ldb_module_get_ctx(ac->module));
126 if (ac->steps == NULL) {
130 if (ac->curstep == NULL)
131 return ldb_operr(ldb_module_get_ctx(ac->module));
132 for (stepper = ac->curstep; stepper->next != NULL;
133 stepper = stepper->next);
134 stepper->next = step;
140 static int samldb_first_step(struct samldb_ctx *ac)
142 if (ac->steps == NULL) {
143 return ldb_operr(ldb_module_get_ctx(ac->module));
146 ac->curstep = ac->steps;
147 return ac->curstep->fn(ac);
150 static int samldb_next_step(struct samldb_ctx *ac)
152 if (ac->curstep->next) {
153 ac->curstep = ac->curstep->next;
154 return ac->curstep->fn(ac);
157 /* We exit the samldb module here. If someone set an "ares" to forward
158 * controls and response back to the caller, use them. */
160 return ldb_module_done(ac->req, ac->ares->controls,
161 ac->ares->response, LDB_SUCCESS);
163 return ldb_module_done(ac->req, NULL, NULL, LDB_SUCCESS);
167 static int samldb_get_single_valued_attr(struct ldb_context *ldb,
168 struct samldb_ctx *ac,
173 * The steps we end up going through to get and check a single valued
176 struct ldb_message_element *el = NULL;
181 ret = dsdb_get_expected_new_values(ac,
187 if (ret != LDB_SUCCESS) {
191 /* we are not affected */
195 if (el->num_values > 1) {
196 ldb_asprintf_errstring(
198 "samldb: %s has %u values, should be single-valued!",
199 attr, el->num_values);
200 return LDB_ERR_CONSTRAINT_VIOLATION;
201 } else if (el->num_values == 0) {
202 ldb_asprintf_errstring(
204 "samldb: new value for %s "
205 "not provided for mandatory, single-valued attribute!",
207 return LDB_ERR_OBJECT_CLASS_VIOLATION;
211 if (el->values[0].length == 0) {
212 ldb_asprintf_errstring(
214 "samldb: %s is of zero length, should have a value!",
216 return LDB_ERR_OBJECT_CLASS_VIOLATION;
219 *value = (char *)el->values[0].data;
224 static int samldb_unique_attr_check(struct samldb_ctx *ac, const char *attr,
225 const char *attr_conflict,
226 struct ldb_dn *base_dn)
228 struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
229 const char * const no_attrs[] = { NULL };
230 struct ldb_result *res = NULL;
231 const char *str = NULL;
232 const char *enc_str = NULL;
235 ret = samldb_get_single_valued_attr(ldb, ac, attr, &str);
236 if (ret != LDB_SUCCESS) {
240 /* the attribute wasn't found */
244 enc_str = ldb_binary_encode_string(ac, str);
245 if (enc_str == NULL) {
246 return ldb_module_oom(ac->module);
250 * No other object should have the attribute with this value.
252 if (attr_conflict != NULL) {
253 ret = dsdb_module_search(ac->module, ac, &res,
255 LDB_SCOPE_SUBTREE, no_attrs,
256 DSDB_FLAG_NEXT_MODULE, ac->req,
259 attr_conflict, enc_str);
261 ret = dsdb_module_search(ac->module, ac, &res,
263 LDB_SCOPE_SUBTREE, no_attrs,
264 DSDB_FLAG_NEXT_MODULE, ac->req,
265 "(%s=%s)", attr, enc_str);
267 if (ret != LDB_SUCCESS) {
270 if (res->count > 1) {
271 return ldb_operr(ldb);
272 } else if (res->count == 1) {
273 if (ldb_dn_compare(res->msgs[0]->dn, ac->msg->dn) != 0) {
274 ldb_asprintf_errstring(ldb,
275 "samldb: %s '%s' already in use!",
277 return LDB_ERR_ENTRY_ALREADY_EXISTS;
287 static inline int samldb_sam_account_upn_clash_sub_search(
288 struct samldb_ctx *ac,
290 struct ldb_dn *base_dn,
297 * A very specific helper function for samldb_sam_account_upn_clash(),
298 * where we end up doing this same thing several times in a row.
300 const char * const no_attrs[] = { NULL };
301 struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
302 struct ldb_result *res = NULL;
304 char *enc_value = ldb_binary_encode_string(ac, value);
305 if (enc_value == NULL) {
306 return ldb_module_oom(ac->module);
308 ret = dsdb_module_search(ac->module, mem_ctx, &res,
310 LDB_SCOPE_SUBTREE, no_attrs,
311 DSDB_FLAG_NEXT_MODULE, ac->req,
314 talloc_free(enc_value);
316 if (ret != LDB_SUCCESS) {
318 } else if (res->count > 1) {
319 return ldb_operr(ldb);
320 } else if (res->count == 1) {
321 if (ldb_dn_compare(res->msgs[0]->dn, ac->msg->dn) != 0){
322 ldb_asprintf_errstring(ldb,
324 "is already in use %s",
325 attr, value, err_msg);
326 /* different errors for different attrs */
327 if (strcasecmp("userPrincipalName", attr) == 0) {
328 return LDB_ERR_CONSTRAINT_VIOLATION;
330 return LDB_ERR_ENTRY_ALREADY_EXISTS;
336 static int samaccountname_bad_chars_check(struct samldb_ctx *ac,
340 * The rules here are based on
342 * https://social.technet.microsoft.com/wiki/contents/articles/11216.active-directory-requirements-for-creating-objects.aspx
344 * Windows considers UTF-8 sequences that map to "similar" characters
345 * (e.g. 'a', 'ā') to be the same sAMAccountName, and we don't. Names
346 * that are not valid UTF-8 *are* allowed.
348 * Additionally, Samba collapses multiple spaces, and Windows doesn't.
350 struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
353 for (i = 0; name[i] != '\0'; i++) {
356 if (c < 32 || c == 127) {
357 ldb_asprintf_errstring(
359 "samldb: sAMAccountName contains invalid "
360 "0x%.2x character\n", c);
361 return LDB_ERR_CONSTRAINT_VIOLATION;
363 p = strchr("\"[]:;|=+*?<>/\\,", c);
365 ldb_asprintf_errstring(
367 "samldb: sAMAccountName contains invalid "
368 "'%c' character\n", c);
369 return LDB_ERR_CONSTRAINT_VIOLATION;
374 ldb_asprintf_errstring(
376 "samldb: sAMAccountName is empty\n");
377 return LDB_ERR_CONSTRAINT_VIOLATION;
380 if (name[i - 1] == '.') {
381 ldb_asprintf_errstring(
383 "samldb: sAMAccountName ends with '.'");
384 return LDB_ERR_CONSTRAINT_VIOLATION;
389 static int samldb_sam_account_upn_clash(struct samldb_ctx *ac)
391 struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
393 struct ldb_dn *base_dn = ldb_get_default_basedn(ldb);
394 TALLOC_CTX *tmp_ctx = NULL;
395 const char *real_sam = NULL;
396 const char *real_upn = NULL;
397 char *implied_sam = NULL;
398 char *implied_upn = NULL;
399 const char *realm = NULL;
401 ret = samldb_get_single_valued_attr(ldb, ac,
404 if (ret != LDB_SUCCESS) {
407 ret = samldb_get_single_valued_attr(ldb, ac,
410 if (ret != LDB_SUCCESS) {
413 if (real_upn == NULL && real_sam == NULL) {
414 /* Not changing these things, so we're done */
418 tmp_ctx = talloc_new(ac);
419 realm = samdb_dn_to_dns_domain(tmp_ctx, base_dn);
421 talloc_free(tmp_ctx);
422 return ldb_operr(ldb);
425 if (real_upn != NULL) {
427 * note we take the last @ in the upn because the first (i.e.
428 * sAMAccountName equivalent) part can contain @.
430 * It is also OK (per Windows) for a UPN to have zero @s.
433 char *upn_realm = NULL;
434 implied_sam = talloc_strdup(tmp_ctx, real_upn);
435 if (implied_sam == NULL) {
436 talloc_free(tmp_ctx);
437 return ldb_module_oom(ac->module);
440 at = strrchr(implied_sam, '@');
443 * there is no @ in this UPN, so we treat the whole
444 * thing as a sAMAccountName for the purposes of a
447 DBG_INFO("samldb: userPrincipalName '%s' contains "
448 "no '@' character\n", implied_sam);
451 * Now, this upn only implies a sAMAccountName if the
452 * realm is our realm. So we need to compare the tail
453 * of the upn to the realm.
457 if (strcasecmp(upn_realm, realm) != 0) {
458 /* implied_sam is not the implied
459 * sAMAccountName after all, because it is
460 * from a different realm. */
461 TALLOC_FREE(implied_sam);
466 if (real_sam != NULL) {
467 implied_upn = talloc_asprintf(tmp_ctx, "%s@%s",
469 if (implied_upn == NULL) {
470 talloc_free(tmp_ctx);
471 return ldb_module_oom(ac->module);
476 * Now we have all of the actual and implied names, in which to search
479 if (real_sam != NULL) {
480 ret = samldb_sam_account_upn_clash_sub_search(
481 ac, tmp_ctx, base_dn, "sAMAccountName",
484 if (ret != LDB_SUCCESS) {
485 talloc_free(tmp_ctx);
488 ret = samaccountname_bad_chars_check(ac, real_sam);
489 if (ret != LDB_SUCCESS) {
490 talloc_free(tmp_ctx);
494 if (implied_upn != NULL) {
495 ret = samldb_sam_account_upn_clash_sub_search(
496 ac, tmp_ctx, base_dn, "userPrincipalName", implied_upn,
497 "(implied by sAMAccountName)");
499 if (ret != LDB_SUCCESS) {
500 talloc_free(tmp_ctx);
504 if (real_upn != NULL) {
505 ret = samldb_sam_account_upn_clash_sub_search(
506 ac, tmp_ctx, base_dn, "userPrincipalName",
509 if (ret != LDB_SUCCESS) {
510 talloc_free(tmp_ctx);
514 if (implied_sam != NULL) {
515 ret = samldb_sam_account_upn_clash_sub_search(
516 ac, tmp_ctx, base_dn, "sAMAccountName", implied_sam,
517 "(implied by userPrincipalName)");
518 if (ret != LDB_SUCCESS) {
519 talloc_free(tmp_ctx);
524 talloc_free(tmp_ctx);
529 /* This is run during an add or modify */
530 static int samldb_sam_accountname_valid_check(struct samldb_ctx *ac)
534 struct security_token *user_token = NULL;
535 struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
536 struct ldb_message_element *el = NULL;
538 ret = dsdb_get_expected_new_values(ac,
543 if (ret != LDB_SUCCESS) {
547 if (el == NULL || el->num_values == 0) {
548 ldb_asprintf_errstring(ldb,
549 "%08X: samldb: 'samAccountName' can't be deleted/empty!",
550 W_ERROR_V(WERR_DS_ILLEGAL_MOD_OPERATION));
551 if (ac->req->operation == LDB_ADD) {
552 return LDB_ERR_CONSTRAINT_VIOLATION;
554 return LDB_ERR_UNWILLING_TO_PERFORM;
558 ret = samldb_unique_attr_check(ac, "samAccountName", NULL,
559 ldb_get_default_basedn(
560 ldb_module_get_ctx(ac->module)));
563 * Error code munging to try and match what must be some quite
564 * strange code-paths in Windows
566 if (ret == LDB_ERR_CONSTRAINT_VIOLATION
567 && ac->req->operation == LDB_MODIFY) {
568 ret = LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
569 } else if (ret == LDB_ERR_OBJECT_CLASS_VIOLATION) {
570 ret = LDB_ERR_CONSTRAINT_VIOLATION;
572 if (ret != LDB_SUCCESS) {
576 ret = samldb_sam_account_upn_clash(ac);
577 if (ret != LDB_SUCCESS) {
581 if (!ac->need_trailing_dollar) {
585 /* This does not permit a single $ */
586 if (el->values[0].length < 2) {
587 ldb_asprintf_errstring(ldb,
588 "%08X: samldb: 'samAccountName' "
589 "can't just be one character!",
590 W_ERROR_V(WERR_DS_ILLEGAL_MOD_OPERATION));
591 return LDB_ERR_UNWILLING_TO_PERFORM;
594 user_token = acl_user_token(ac->module);
595 if (user_token == NULL) {
596 return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
600 = security_token_has_builtin_administrators(user_token);
604 * Administrators are allowed to select strange names.
605 * This is poor practice but not prevented.
610 if (el->values[0].data[el->values[0].length - 1] != '$') {
611 ldb_asprintf_errstring(ldb,
612 "%08X: samldb: 'samAccountName' "
613 "must have a trailing $!",
614 W_ERROR_V(WERR_DS_ILLEGAL_MOD_OPERATION));
615 return LDB_ERR_UNWILLING_TO_PERFORM;
617 if (el->values[0].data[el->values[0].length - 2] == '$') {
618 ldb_asprintf_errstring(ldb,
619 "%08X: samldb: 'samAccountName' "
620 "must not have a double trailing $!",
621 W_ERROR_V(WERR_DS_ILLEGAL_MOD_OPERATION));
622 return LDB_ERR_UNWILLING_TO_PERFORM;
628 static int samldb_schema_attributeid_valid_check(struct samldb_ctx *ac)
630 int ret = samldb_unique_attr_check(ac, "attributeID", "governsID",
631 ldb_get_schema_basedn(
632 ldb_module_get_ctx(ac->module)));
633 if (ret == LDB_ERR_ENTRY_ALREADY_EXISTS) {
634 ret = LDB_ERR_UNWILLING_TO_PERFORM;
639 static int samldb_schema_governsid_valid_check(struct samldb_ctx *ac)
641 int ret = samldb_unique_attr_check(ac, "governsID", "attributeID",
642 ldb_get_schema_basedn(
643 ldb_module_get_ctx(ac->module)));
644 if (ret == LDB_ERR_ENTRY_ALREADY_EXISTS) {
645 ret = LDB_ERR_UNWILLING_TO_PERFORM;
650 static int samldb_schema_ldapdisplayname_valid_check(struct samldb_ctx *ac)
652 int ret = samldb_unique_attr_check(ac, "lDAPDisplayName", NULL,
653 ldb_get_schema_basedn(
654 ldb_module_get_ctx(ac->module)));
655 if (ret == LDB_ERR_ENTRY_ALREADY_EXISTS) {
656 ret = LDB_ERR_UNWILLING_TO_PERFORM;
661 static int samldb_check_linkid_used(struct samldb_ctx *ac,
662 struct dsdb_schema *schema,
663 struct ldb_dn *schema_dn,
664 struct ldb_context *ldb,
669 struct ldb_result *ldb_res;
671 if (dsdb_attribute_by_linkID(schema, linkID)) {
676 ret = dsdb_module_search(ac->module, ac,
678 schema_dn, LDB_SCOPE_ONELEVEL, NULL,
679 DSDB_FLAG_NEXT_MODULE,
681 "(linkID=%d)", linkID);
682 if (ret != LDB_SUCCESS) {
683 ldb_debug_set(ldb, LDB_DEBUG_ERROR,
684 __location__": Searching for linkID=%d failed - %s\n",
687 return ldb_operr(ldb);
690 *found = (ldb_res->count != 0);
691 talloc_free(ldb_res);
696 /* Find the next open forward linkID in the schema. */
697 static int samldb_generate_next_linkid(struct samldb_ctx *ac,
698 struct dsdb_schema *schema,
699 int32_t *next_linkID)
702 struct ldb_context *ldb;
703 struct ldb_dn *schema_dn;
704 bool linkID_used = true;
707 * Windows starts at about 0xB0000000 in order to stop potential
708 * collisions with future additions to the schema. We pass this
709 * around as a signed int sometimes, but this should be sufficient.
711 *next_linkID = 0x40000000;
713 ldb = ldb_module_get_ctx(ac->module);
714 schema_dn = ldb_get_schema_basedn(ldb);
716 while (linkID_used) {
718 ret = samldb_check_linkid_used(ac, schema,
720 *next_linkID, &linkID_used);
721 if (ret != LDB_SUCCESS) {
729 static int samldb_schema_add_handle_linkid(struct samldb_ctx *ac)
732 bool ok, found = false;
733 struct ldb_message_element *el;
735 const struct dsdb_attribute *attr;
736 struct ldb_context *ldb;
737 struct ldb_dn *schema_dn;
738 struct dsdb_schema *schema;
739 int32_t new_linkID = 0;
741 ldb = ldb_module_get_ctx(ac->module);
742 schema = dsdb_get_schema(ldb, ac);
743 schema_dn = ldb_get_schema_basedn(ldb);
745 ret = dsdb_get_expected_new_values(ac,
750 if (ret != LDB_SUCCESS) {
758 enc_str = ldb_binary_encode(ac, el->values[0]);
759 if (enc_str == NULL) {
760 return ldb_module_oom(ac->module);
763 ok = (strcmp(enc_str, "0") == 0);
769 * This OID indicates that the caller wants the linkID
770 * to be automatically generated. We therefore assign
771 * it the next open linkID.
773 ok = (strcmp(enc_str, "1.2.840.113556.1.2.50") == 0);
775 ret = samldb_generate_next_linkid(ac, schema, &new_linkID);
776 if (ret != LDB_SUCCESS) {
780 ldb_msg_remove_element(ac->msg, el);
781 ret = samdb_msg_add_int(ldb, ac->msg, ac->msg, "linkID",
787 * Using either the attributeID or lDAPDisplayName of
788 * another attribute in the linkID field indicates that
789 * we should make this the backlink of that attribute.
791 attr = dsdb_attribute_by_attributeID_oid(schema, enc_str);
793 attr = dsdb_attribute_by_lDAPDisplayName(schema, enc_str);
798 * The attribute we're adding this as a backlink of must
801 if (attr->linkID % 2 != 0) {
802 return LDB_ERR_UNWILLING_TO_PERFORM;
805 new_linkID = attr->linkID + 1;
807 /* Make sure that this backlink doesn't already exist. */
808 ret = samldb_check_linkid_used(ac, schema,
811 if (ret != LDB_SUCCESS) {
816 return LDB_ERR_UNWILLING_TO_PERFORM;
819 ldb_msg_remove_element(ac->msg, el);
820 ret = samdb_msg_add_int(ldb, ac->msg, ac->msg, "linkID",
825 schema_dn = ldb_get_schema_basedn(ldb_module_get_ctx(ac->module));
826 ret = samldb_unique_attr_check(ac, "linkID", NULL, schema_dn);
827 if (ret == LDB_ERR_ENTRY_ALREADY_EXISTS) {
828 return LDB_ERR_UNWILLING_TO_PERFORM;
834 static int samldb_check_mapiid_used(struct samldb_ctx *ac,
835 struct dsdb_schema *schema,
836 struct ldb_dn *schema_dn,
837 struct ldb_context *ldb,
842 struct ldb_result *ldb_res;
844 ret = dsdb_module_search(ac->module, ac,
846 schema_dn, LDB_SCOPE_ONELEVEL, NULL,
847 DSDB_FLAG_NEXT_MODULE,
849 "(mAPIID=%d)", mapiid);
850 if (ret != LDB_SUCCESS) {
851 ldb_debug_set(ldb, LDB_DEBUG_ERROR,
852 __location__": Searching for mAPIID=%d failed - %s\n",
855 return ldb_operr(ldb);
858 *found = (ldb_res->count != 0);
859 talloc_free(ldb_res);
864 static int samldb_generate_next_mapiid(struct samldb_ctx *ac,
865 struct dsdb_schema *schema,
866 int32_t *next_mapiid)
869 struct ldb_context *ldb;
870 struct ldb_dn *schema_dn;
871 bool mapiid_used = true;
873 /* Windows' generation seems to start about here */
874 *next_mapiid = 60000;
876 ldb = ldb_module_get_ctx(ac->module);
877 schema_dn = ldb_get_schema_basedn(ldb);
879 while (mapiid_used) {
881 ret = samldb_check_mapiid_used(ac, schema,
883 *next_mapiid, &mapiid_used);
884 if (ret != LDB_SUCCESS) {
892 static int samldb_schema_add_handle_mapiid(struct samldb_ctx *ac)
896 struct ldb_message_element *el;
898 struct ldb_context *ldb;
899 struct ldb_dn *schema_dn;
900 struct dsdb_schema *schema;
901 int32_t new_mapiid = 0;
904 * The mAPIID of a new attribute should be automatically generated
905 * if a specific OID is put as the mAPIID, as according to
906 * [MS-ADTS] 3.1.1.2.3.2.
909 ldb = ldb_module_get_ctx(ac->module);
910 schema = dsdb_get_schema(ldb, ac);
911 schema_dn = ldb_get_schema_basedn(ldb);
913 ret = dsdb_get_expected_new_values(ac,
918 if (ret != LDB_SUCCESS) {
926 enc_str = ldb_binary_encode(ac, el->values[0]);
927 if (enc_str == NULL) {
928 return ldb_module_oom(ac->module);
931 ok = (strcmp(enc_str, "1.2.840.113556.1.2.49") == 0);
933 ret = samldb_generate_next_mapiid(ac, schema,
935 if (ret != LDB_SUCCESS) {
939 ldb_msg_remove_element(ac->msg, el);
940 ret = samdb_msg_add_int(ldb, ac->msg, ac->msg,
941 "mAPIID", new_mapiid);
945 schema_dn = ldb_get_schema_basedn(ldb_module_get_ctx(ac->module));
946 ret = samldb_unique_attr_check(ac, "mAPIID", NULL, schema_dn);
947 if (ret == LDB_ERR_ENTRY_ALREADY_EXISTS) {
948 return LDB_ERR_UNWILLING_TO_PERFORM;
954 /* sAMAccountName handling */
955 static int samldb_generate_sAMAccountName(struct samldb_ctx *ac,
956 struct ldb_message *msg)
958 struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
962 * This is currently a Samba-only behaviour, to add a trailing
963 * $ even for the generated accounts.
966 if (ac->need_trailing_dollar) {
967 /* Format: $000000-00000000000$ */
968 name = talloc_asprintf(msg, "$%.6X-%.6X%.5X$",
969 (unsigned int)generate_random(),
970 (unsigned int)generate_random(),
971 (unsigned int)generate_random());
973 /* Format: $000000-000000000000 */
975 name = talloc_asprintf(msg, "$%.6X-%.6X%.6X",
976 (unsigned int)generate_random(),
977 (unsigned int)generate_random(),
978 (unsigned int)generate_random());
983 return ldb_msg_add_steal_string(msg, "sAMAccountName", name);
986 static int samldb_check_sAMAccountName(struct samldb_ctx *ac)
990 if (ldb_msg_find_element(ac->msg, "sAMAccountName") == NULL) {
991 ret = samldb_generate_sAMAccountName(ac, ac->msg);
992 if (ret != LDB_SUCCESS) {
997 ret = samldb_sam_accountname_valid_check(ac);
998 if (ret != LDB_SUCCESS) {
1002 return samldb_next_step(ac);
1006 static bool samldb_msg_add_sid(struct ldb_message *msg,
1008 const struct dom_sid *sid)
1011 enum ndr_err_code ndr_err;
1013 ndr_err = ndr_push_struct_blob(&v, msg, sid,
1014 (ndr_push_flags_fn_t)ndr_push_dom_sid);
1015 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1018 return (ldb_msg_add_value(msg, name, &v, NULL) == 0);
1022 /* allocate a SID using our RID Set */
1023 static int samldb_allocate_sid(struct samldb_ctx *ac)
1026 struct dom_sid *sid;
1027 struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
1030 ret = ridalloc_allocate_rid(ac->module, &rid, ac->req);
1031 if (ret != LDB_SUCCESS) {
1035 sid = dom_sid_add_rid(ac, samdb_domain_sid(ldb), rid);
1037 return ldb_module_oom(ac->module);
1040 if ( ! samldb_msg_add_sid(ac->msg, "objectSid", sid)) {
1041 return ldb_operr(ldb);
1044 return samldb_next_step(ac);
1048 see if a krbtgt_number is available
1050 static bool samldb_krbtgtnumber_available(struct samldb_ctx *ac,
1051 uint32_t krbtgt_number)
1053 TALLOC_CTX *tmp_ctx = talloc_new(ac);
1054 struct ldb_result *res;
1055 const char * const no_attrs[] = { NULL };
1058 ret = dsdb_module_search(ac->module, tmp_ctx, &res,
1059 ldb_get_default_basedn(ldb_module_get_ctx(ac->module)),
1060 LDB_SCOPE_SUBTREE, no_attrs,
1061 DSDB_FLAG_NEXT_MODULE,
1063 "(msDS-SecondaryKrbTgtNumber=%u)",
1065 if (ret == LDB_SUCCESS && res->count == 0) {
1066 talloc_free(tmp_ctx);
1069 talloc_free(tmp_ctx);
1073 /* special handling for add in RODC join */
1074 static int samldb_rodc_add(struct samldb_ctx *ac)
1076 struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
1077 uint32_t krbtgt_number, i_start, i;
1079 struct ldb_val newpass_utf16;
1081 /* find a unused msDS-SecondaryKrbTgtNumber */
1082 i_start = generate_random() & 0xFFFF;
1087 for (i=i_start; i<=0xFFFF; i++) {
1088 if (samldb_krbtgtnumber_available(ac, i)) {
1093 for (i=1; i<i_start; i++) {
1094 if (samldb_krbtgtnumber_available(ac, i)) {
1100 ldb_asprintf_errstring(ldb,
1101 "%08X: Unable to find available msDS-SecondaryKrbTgtNumber",
1102 W_ERROR_V(WERR_NO_SYSTEM_RESOURCES));
1103 return LDB_ERR_OTHER;
1106 ret = ldb_msg_add_empty(ac->msg, "msDS-SecondaryKrbTgtNumber",
1107 LDB_FLAG_INTERNAL_DISABLE_VALIDATION, NULL);
1108 if (ret != LDB_SUCCESS) {
1109 return ldb_operr(ldb);
1112 ret = samdb_msg_add_uint(ldb, ac->msg, ac->msg,
1113 "msDS-SecondaryKrbTgtNumber", krbtgt_number);
1114 if (ret != LDB_SUCCESS) {
1115 return ldb_operr(ldb);
1118 ret = ldb_msg_add_fmt(ac->msg, "sAMAccountName", "krbtgt_%u",
1120 if (ret != LDB_SUCCESS) {
1121 return ldb_operr(ldb);
1124 newpass_utf16 = data_blob_talloc_zero(ac->module, 256);
1125 if (newpass_utf16.data == NULL) {
1126 return ldb_oom(ldb);
1129 * Note that the password_hash module will ignore
1130 * this value and use it's own generate_secret_buffer()
1131 * that's why we can just use generate_random_buffer()
1134 generate_random_buffer(newpass_utf16.data, newpass_utf16.length);
1135 ret = ldb_msg_add_steal_value(ac->msg, "clearTextPassword", &newpass_utf16);
1136 if (ret != LDB_SUCCESS) {
1137 return ldb_operr(ldb);
1140 return samldb_next_step(ac);
1143 static int samldb_find_for_defaultObjectCategory(struct samldb_ctx *ac)
1145 struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
1146 struct ldb_result *res;
1147 const char * const no_attrs[] = { NULL };
1152 ret = dsdb_module_search(ac->module, ac, &res,
1153 ac->dn, LDB_SCOPE_BASE, no_attrs,
1154 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT
1155 | DSDB_FLAG_NEXT_MODULE,
1157 "(objectClass=classSchema)");
1158 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
1159 /* Don't be pricky when the DN doesn't exist if we have the */
1160 /* RELAX control specified */
1161 if (ldb_request_get_control(ac->req,
1162 LDB_CONTROL_RELAX_OID) == NULL) {
1163 ldb_set_errstring(ldb,
1164 "samldb_find_defaultObjectCategory: "
1165 "Invalid DN for 'defaultObjectCategory'!");
1166 return LDB_ERR_CONSTRAINT_VIOLATION;
1169 if ((ret != LDB_ERR_NO_SUCH_OBJECT) && (ret != LDB_SUCCESS)) {
1173 if (ret == LDB_SUCCESS) {
1174 /* ensure the defaultObjectCategory has a full GUID */
1175 struct ldb_message *m;
1176 m = ldb_msg_new(ac->msg);
1178 return ldb_oom(ldb);
1180 m->dn = ac->msg->dn;
1181 if (ldb_msg_add_string(m, "defaultObjectCategory",
1182 ldb_dn_get_extended_linearized(m, res->msgs[0]->dn, 1)) !=
1184 return ldb_oom(ldb);
1186 m->elements[0].flags = LDB_FLAG_MOD_REPLACE;
1188 ret = dsdb_module_modify(ac->module, m,
1189 DSDB_FLAG_NEXT_MODULE,
1191 if (ret != LDB_SUCCESS) {
1197 ac->res_dn = ac->dn;
1199 return samldb_next_step(ac);
1203 * msDS-IntId attributeSchema attribute handling
1204 * during LDB_ADD request processing
1206 static int samldb_add_handle_msDS_IntId(struct samldb_ctx *ac)
1210 uint32_t msds_intid;
1211 int32_t system_flags;
1212 struct ldb_context *ldb;
1213 struct ldb_result *ldb_res;
1214 struct ldb_dn *schema_dn;
1215 struct samldb_msds_intid_persistant *msds_intid_struct;
1216 struct dsdb_schema *schema;
1218 ldb = ldb_module_get_ctx(ac->module);
1219 schema_dn = ldb_get_schema_basedn(ldb);
1221 /* replicated update should always go through */
1222 if (ldb_request_get_control(ac->req,
1223 DSDB_CONTROL_REPLICATED_UPDATE_OID)) {
1227 /* msDS-IntId is handled by system and should never be
1228 * passed by clients */
1229 if (ldb_msg_find_element(ac->msg, "msDS-IntId")) {
1230 return LDB_ERR_UNWILLING_TO_PERFORM;
1233 /* do not generate msDS-IntId if Relax control is passed */
1234 if (ldb_request_get_control(ac->req, LDB_CONTROL_RELAX_OID)) {
1238 /* check Functional Level */
1239 if (dsdb_functional_level(ldb) < DS_DOMAIN_FUNCTION_2003) {
1243 /* check systemFlags for SCHEMA_BASE_OBJECT flag */
1244 system_flags = ldb_msg_find_attr_as_int(ac->msg, "systemFlags", 0);
1245 if (system_flags & SYSTEM_FLAG_SCHEMA_BASE_OBJECT) {
1248 schema = dsdb_get_schema(ldb, NULL);
1250 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
1251 "samldb_schema_info_update: no dsdb_schema loaded");
1252 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
1253 return ldb_operr(ldb);
1256 msds_intid_struct = (struct samldb_msds_intid_persistant*) ldb_get_opaque(ldb, SAMLDB_MSDS_INTID_OPAQUE);
1257 if (!msds_intid_struct) {
1258 msds_intid_struct = talloc(ldb, struct samldb_msds_intid_persistant);
1259 /* Generate new value for msDs-IntId
1260 * Value should be in 0x80000000..0xBFFFFFFF range */
1261 msds_intid = generate_random() % 0X3FFFFFFF;
1262 msds_intid += 0x80000000;
1263 msds_intid_struct->msds_intid = msds_intid;
1264 DEBUG(2, ("No samldb_msds_intid_persistant struct, allocating a new one\n"));
1266 msds_intid = msds_intid_struct->msds_intid;
1269 /* probe id values until unique one is found */
1272 if (msds_intid > 0xBFFFFFFF) {
1273 msds_intid = 0x80000001;
1276 * We search in the schema if we have already this
1277 * intid (using dsdb_attribute_by_attributeID_id
1278 * because in the range 0x80000000 0xBFFFFFFFF,
1279 * attributeID is a DSDB_ATTID_TYPE_INTID).
1281 * If so generate another random value.
1283 * We have to check the DB in case someone else has
1284 * modified the database while we are doing our
1285 * changes too (this case should be very bery rare) in
1288 if (dsdb_attribute_by_attributeID_id(schema, msds_intid)) {
1290 msds_intid = generate_random() % 0X3FFFFFFF;
1291 msds_intid += 0x80000000;
1296 ret = dsdb_module_search(ac->module, ac,
1298 schema_dn, LDB_SCOPE_ONELEVEL, NULL,
1299 DSDB_FLAG_NEXT_MODULE,
1301 "(msDS-IntId=%d)", msds_intid);
1302 if (ret != LDB_SUCCESS) {
1303 ldb_debug_set(ldb, LDB_DEBUG_ERROR,
1304 __location__": Searching for msDS-IntId=%d failed - %s\n",
1306 ldb_errstring(ldb));
1307 return ldb_operr(ldb);
1309 id_exists = (ldb_res->count > 0);
1310 talloc_free(ldb_res);
1313 msds_intid_struct->msds_intid = msds_intid;
1314 ldb_set_opaque(ldb, SAMLDB_MSDS_INTID_OPAQUE, msds_intid_struct);
1316 return samdb_msg_add_int(ldb, ac->msg, ac->msg, "msDS-IntId",
1322 * samldb_add_entry (async)
1325 static int samldb_add_entry_callback(struct ldb_request *req,
1326 struct ldb_reply *ares)
1328 struct ldb_context *ldb;
1329 struct samldb_ctx *ac;
1332 ac = talloc_get_type(req->context, struct samldb_ctx);
1333 ldb = ldb_module_get_ctx(ac->module);
1336 return ldb_module_done(ac->req, NULL, NULL,
1337 LDB_ERR_OPERATIONS_ERROR);
1340 if (ares->type == LDB_REPLY_REFERRAL) {
1341 return ldb_module_send_referral(ac->req, ares->referral);
1344 if (ares->error != LDB_SUCCESS) {
1345 return ldb_module_done(ac->req, ares->controls,
1346 ares->response, ares->error);
1348 if (ares->type != LDB_REPLY_DONE) {
1349 ldb_asprintf_errstring(ldb, "Invalid LDB reply type %d", ares->type);
1350 return ldb_module_done(ac->req, NULL, NULL,
1351 LDB_ERR_OPERATIONS_ERROR);
1354 /* The caller may wish to get controls back from the add */
1355 ac->ares = talloc_steal(ac, ares);
1357 ret = samldb_next_step(ac);
1358 if (ret != LDB_SUCCESS) {
1359 return ldb_module_done(ac->req, NULL, NULL, ret);
1364 static int samldb_add_entry(struct samldb_ctx *ac)
1366 struct ldb_context *ldb;
1367 struct ldb_request *req;
1370 ldb = ldb_module_get_ctx(ac->module);
1372 ret = ldb_build_add_req(&req, ldb, ac,
1375 ac, samldb_add_entry_callback,
1377 LDB_REQ_SET_LOCATION(req);
1378 if (ret != LDB_SUCCESS) {
1382 return ldb_next_request(ac->module, req);
1386 * return true if msg carries an attributeSchema that is intended to be RODC
1387 * filtered but is also a system-critical attribute.
1389 static bool check_rodc_critical_attribute(struct ldb_message *msg)
1391 uint32_t schemaFlagsEx, searchFlags, rodc_filtered_flags;
1393 schemaFlagsEx = ldb_msg_find_attr_as_uint(msg, "schemaFlagsEx", 0);
1394 searchFlags = ldb_msg_find_attr_as_uint(msg, "searchFlags", 0);
1395 rodc_filtered_flags = (SEARCH_FLAG_RODC_ATTRIBUTE
1396 | SEARCH_FLAG_CONFIDENTIAL);
1398 if ((schemaFlagsEx & SCHEMA_FLAG_ATTR_IS_CRITICAL) &&
1399 ((searchFlags & rodc_filtered_flags) == rodc_filtered_flags)) {
1407 static int samldb_fill_object(struct samldb_ctx *ac)
1409 struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
1412 /* Add information for the different account types */
1414 case SAMLDB_TYPE_USER: {
1415 struct ldb_control *rodc_control = ldb_request_get_control(ac->req,
1416 LDB_CONTROL_RODC_DCPROMO_OID);
1417 if (rodc_control != NULL) {
1418 /* see [MS-ADTS] 3.1.1.3.4.1.23 LDAP_SERVER_RODC_DCPROMO_OID */
1419 rodc_control->critical = false;
1420 ret = samldb_add_step(ac, samldb_rodc_add);
1421 if (ret != LDB_SUCCESS) return ret;
1424 /* check if we have a valid sAMAccountName */
1425 ret = samldb_add_step(ac, samldb_check_sAMAccountName);
1426 if (ret != LDB_SUCCESS) return ret;
1428 ret = samldb_add_step(ac, samldb_add_entry);
1429 if (ret != LDB_SUCCESS) return ret;
1433 case SAMLDB_TYPE_GROUP: {
1434 /* check if we have a valid sAMAccountName */
1435 ret = samldb_add_step(ac, samldb_check_sAMAccountName);
1436 if (ret != LDB_SUCCESS) return ret;
1438 ret = samldb_add_step(ac, samldb_add_entry);
1439 if (ret != LDB_SUCCESS) return ret;
1443 case SAMLDB_TYPE_CLASS: {
1444 const char *lDAPDisplayName = NULL;
1445 const struct ldb_val *rdn_value, *def_obj_cat_val;
1446 unsigned int v = ldb_msg_find_attr_as_uint(ac->msg, "objectClassCategory", -2);
1448 /* As discussed with Microsoft through dochelp in April 2012 this is the behavior of windows*/
1449 if (!ldb_msg_find_element(ac->msg, "subClassOf")) {
1450 ret = ldb_msg_add_string(ac->msg, "subClassOf", "top");
1451 if (ret != LDB_SUCCESS) return ret;
1454 ret = samdb_find_or_add_attribute(ldb, ac->msg,
1456 if (ret != LDB_SUCCESS) return ret;
1458 /* do not allow one to mark an attributeSchema as RODC filtered if it
1459 * is system-critical */
1460 if (check_rodc_critical_attribute(ac->msg)) {
1461 ldb_asprintf_errstring(ldb, "Refusing schema add of %s - cannot combine critical class with RODC filtering",
1462 ldb_dn_get_linearized(ac->msg->dn));
1463 return LDB_ERR_UNWILLING_TO_PERFORM;
1466 rdn_value = ldb_dn_get_rdn_val(ac->msg->dn);
1467 if (rdn_value == NULL) {
1468 return ldb_operr(ldb);
1470 if (!ldb_msg_find_element(ac->msg, "lDAPDisplayName")) {
1471 /* the RDN has prefix "CN" */
1472 ret = ldb_msg_add_string(ac->msg, "lDAPDisplayName",
1473 samdb_cn_to_lDAPDisplayName(ac->msg,
1474 (const char *) rdn_value->data));
1475 if (ret != LDB_SUCCESS) {
1481 lDAPDisplayName = ldb_msg_find_attr_as_string(ac->msg,
1484 ret = ldb_valid_attr_name(lDAPDisplayName);
1486 lDAPDisplayName[0] == '*' ||
1487 lDAPDisplayName[0] == '@')
1489 return dsdb_module_werror(ac->module,
1490 LDB_ERR_UNWILLING_TO_PERFORM,
1491 WERR_DS_INVALID_LDAP_DISPLAY_NAME,
1492 "lDAPDisplayName is invalid");
1495 if (!ldb_msg_find_element(ac->msg, "schemaIDGUID")) {
1498 guid = GUID_random();
1499 ret = dsdb_msg_add_guid(ac->msg, &guid, "schemaIDGUID");
1500 if (ret != LDB_SUCCESS) {
1506 def_obj_cat_val = ldb_msg_find_ldb_val(ac->msg,
1507 "defaultObjectCategory");
1508 if (def_obj_cat_val != NULL) {
1509 /* "defaultObjectCategory" has been set by the caller.
1510 * Do some checks for consistency.
1511 * NOTE: The real constraint check (that
1512 * 'defaultObjectCategory' is the DN of the new
1513 * objectclass or any parent of it) is still incomplete.
1514 * For now we say that 'defaultObjectCategory' is valid
1515 * if it exists and it is of objectclass "classSchema".
1517 ac->dn = ldb_dn_from_ldb_val(ac, ldb, def_obj_cat_val);
1518 if (ac->dn == NULL) {
1519 ldb_set_errstring(ldb,
1520 "Invalid DN for 'defaultObjectCategory'!");
1521 return LDB_ERR_CONSTRAINT_VIOLATION;
1524 /* "defaultObjectCategory" has not been set by the
1525 * caller. Use the entry DN for it. */
1526 ac->dn = ac->msg->dn;
1528 ret = ldb_msg_add_string(ac->msg, "defaultObjectCategory",
1529 ldb_dn_alloc_linearized(ac->msg, ac->dn));
1530 if (ret != LDB_SUCCESS) {
1536 ret = samldb_add_step(ac, samldb_add_entry);
1537 if (ret != LDB_SUCCESS) return ret;
1539 /* Now perform the checks for the 'defaultObjectCategory'. The
1540 * lookup DN was already saved in "ac->dn" */
1541 ret = samldb_add_step(ac, samldb_find_for_defaultObjectCategory);
1542 if (ret != LDB_SUCCESS) return ret;
1544 /* -2 is not a valid objectClassCategory so it means the attribute wasn't present */
1546 /* Windows 2003 does this*/
1547 ret = samdb_msg_add_uint(ldb, ac->msg, ac->msg, "objectClassCategory", 0);
1548 if (ret != LDB_SUCCESS) {
1555 case SAMLDB_TYPE_ATTRIBUTE: {
1556 const char *lDAPDisplayName = NULL;
1557 const struct ldb_val *rdn_value;
1558 struct ldb_message_element *el;
1559 rdn_value = ldb_dn_get_rdn_val(ac->msg->dn);
1560 if (rdn_value == NULL) {
1561 return ldb_operr(ldb);
1563 if (!ldb_msg_find_element(ac->msg, "lDAPDisplayName")) {
1564 /* the RDN has prefix "CN" */
1565 ret = ldb_msg_add_string(ac->msg, "lDAPDisplayName",
1566 samdb_cn_to_lDAPDisplayName(ac->msg,
1567 (const char *) rdn_value->data));
1568 if (ret != LDB_SUCCESS) {
1574 lDAPDisplayName = ldb_msg_find_attr_as_string(ac->msg,
1577 ret = ldb_valid_attr_name(lDAPDisplayName);
1579 lDAPDisplayName[0] == '*' ||
1580 lDAPDisplayName[0] == '@')
1582 return dsdb_module_werror(ac->module,
1583 LDB_ERR_UNWILLING_TO_PERFORM,
1584 WERR_DS_INVALID_LDAP_DISPLAY_NAME,
1585 "lDAPDisplayName is invalid");
1588 /* do not allow one to mark an attributeSchema as RODC filtered if it
1589 * is system-critical */
1590 if (check_rodc_critical_attribute(ac->msg)) {
1591 ldb_asprintf_errstring(ldb,
1592 "samldb: refusing schema add of %s - cannot combine critical attribute with RODC filtering",
1593 ldb_dn_get_linearized(ac->msg->dn));
1594 return LDB_ERR_UNWILLING_TO_PERFORM;
1597 ret = samdb_find_or_add_attribute(ldb, ac->msg,
1598 "isSingleValued", "FALSE");
1599 if (ret != LDB_SUCCESS) return ret;
1601 if (!ldb_msg_find_element(ac->msg, "schemaIDGUID")) {
1604 guid = GUID_random();
1605 ret = dsdb_msg_add_guid(ac->msg, &guid, "schemaIDGUID");
1606 if (ret != LDB_SUCCESS) {
1612 el = ldb_msg_find_element(ac->msg, "attributeSyntax");
1615 * No need to scream if there isn't as we have code later on
1616 * that will take care of it.
1618 const struct dsdb_syntax *syntax = find_syntax_map_by_ad_oid((const char *)el->values[0].data);
1620 DEBUG(9, ("Can't find dsdb_syntax object for attributeSyntax %s\n",
1621 (const char *)el->values[0].data));
1623 unsigned int v = ldb_msg_find_attr_as_uint(ac->msg, "oMSyntax", 0);
1624 const struct ldb_val *val = ldb_msg_find_ldb_val(ac->msg, "oMObjectClass");
1627 ret = samdb_msg_add_uint(ldb, ac->msg, ac->msg, "oMSyntax", syntax->oMSyntax);
1628 if (ret != LDB_SUCCESS) {
1633 struct ldb_val val2 = ldb_val_dup(ldb, &syntax->oMObjectClass);
1634 if (val2.length > 0) {
1635 ret = ldb_msg_add_value(ac->msg, "oMObjectClass", &val2, NULL);
1636 if (ret != LDB_SUCCESS) {
1644 /* handle msDS-IntID attribute */
1645 ret = samldb_add_handle_msDS_IntId(ac);
1646 if (ret != LDB_SUCCESS) return ret;
1648 ret = samldb_add_step(ac, samldb_add_entry);
1649 if (ret != LDB_SUCCESS) return ret;
1654 ldb_asprintf_errstring(ldb, "Invalid entry type!");
1655 return LDB_ERR_OPERATIONS_ERROR;
1659 return samldb_first_step(ac);
1662 static int samldb_fill_foreignSecurityPrincipal_object(struct samldb_ctx *ac)
1664 struct ldb_context *ldb = NULL;
1665 const struct ldb_val *rdn_value = NULL;
1666 struct ldb_message_element *sid_el = NULL;
1667 struct dom_sid *sid = NULL;
1668 struct ldb_control *as_system = NULL;
1669 struct ldb_control *provision = NULL;
1670 bool allowed = false;
1673 ldb = ldb_module_get_ctx(ac->module);
1675 as_system = ldb_request_get_control(ac->req, LDB_CONTROL_AS_SYSTEM_OID);
1676 if (as_system != NULL) {
1680 provision = ldb_request_get_control(ac->req, LDB_CONTROL_PROVISION_OID);
1681 if (provision != NULL) {
1685 sid_el = ldb_msg_find_element(ac->msg, "objectSid");
1687 if (!allowed && sid_el == NULL) {
1688 return dsdb_module_werror(ac->module,
1689 LDB_ERR_OBJECT_CLASS_VIOLATION,
1690 WERR_DS_MISSING_REQUIRED_ATT,
1691 "objectSid missing on foreignSecurityPrincipal");
1695 return dsdb_module_werror(ac->module,
1696 LDB_ERR_UNWILLING_TO_PERFORM,
1697 WERR_DS_ILLEGAL_MOD_OPERATION,
1698 "foreignSecurityPrincipal object not allowed");
1701 if (sid_el != NULL) {
1702 sid = samdb_result_dom_sid(ac->msg, ac->msg, "objectSid");
1704 ldb_set_errstring(ldb,
1705 "samldb: invalid objectSid!");
1706 return LDB_ERR_CONSTRAINT_VIOLATION;
1711 rdn_value = ldb_dn_get_rdn_val(ac->msg->dn);
1712 if (rdn_value == NULL) {
1713 return ldb_operr(ldb);
1715 sid = dom_sid_parse_talloc(ac->msg,
1716 (const char *)rdn_value->data);
1718 ldb_set_errstring(ldb,
1719 "samldb: No valid SID found in ForeignSecurityPrincipal CN!");
1720 return LDB_ERR_CONSTRAINT_VIOLATION;
1722 if (! samldb_msg_add_sid(ac->msg, "objectSid", sid)) {
1723 return ldb_operr(ldb);
1727 /* finally proceed with adding the entry */
1728 ret = samldb_add_step(ac, samldb_add_entry);
1729 if (ret != LDB_SUCCESS) return ret;
1731 return samldb_first_step(ac);
1734 static int samldb_schema_info_update(struct samldb_ctx *ac)
1737 struct ldb_context *ldb;
1738 struct dsdb_schema *schema;
1740 /* replicated update should always go through */
1741 if (ldb_request_get_control(ac->req,
1742 DSDB_CONTROL_REPLICATED_UPDATE_OID)) {
1746 /* do not update schemaInfo during provisioning */
1747 if (ldb_request_get_control(ac->req, LDB_CONTROL_PROVISION_OID)) {
1751 ldb = ldb_module_get_ctx(ac->module);
1752 schema = dsdb_get_schema(ldb, NULL);
1754 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
1755 "samldb_schema_info_update: no dsdb_schema loaded");
1756 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
1757 return ldb_operr(ldb);
1760 ret = dsdb_module_schema_info_update(ac->module, schema,
1761 DSDB_FLAG_NEXT_MODULE|
1762 DSDB_FLAG_AS_SYSTEM,
1764 if (ret != LDB_SUCCESS) {
1765 ldb_asprintf_errstring(ldb,
1766 "samldb_schema_info_update: dsdb_module_schema_info_update failed with %s",
1767 ldb_errstring(ldb));
1774 static int samldb_prim_group_tester(struct samldb_ctx *ac, uint32_t rid);
1775 static int samldb_check_user_account_control_rules(struct samldb_ctx *ac,
1776 struct dom_sid *sid,
1778 uint32_t user_account_control,
1779 uint32_t user_account_control_old,
1780 bool is_computer_objectclass);
1783 * "Objectclass" trigger (MS-SAMR 3.1.1.8.1)
1785 * Has to be invoked on "add" operations on "user", "computer" and
1787 * ac->msg contains the "add"
1788 * ac->type contains the object type (main objectclass)
1790 static int samldb_objectclass_trigger(struct samldb_ctx *ac)
1792 struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
1793 void *skip_allocate_sids = ldb_get_opaque(ldb,
1794 "skip_allocate_sids");
1795 struct ldb_message_element *el, *el2;
1796 struct dom_sid *sid;
1799 /* make sure that "sAMAccountType" is not specified */
1800 el = ldb_msg_find_element(ac->msg, "sAMAccountType");
1802 ldb_set_errstring(ldb,
1803 "samldb: sAMAccountType must not be specified!");
1804 return LDB_ERR_UNWILLING_TO_PERFORM;
1807 /* Step 1: objectSid assignment */
1809 /* Don't allow the objectSid to be changed. But beside the RELAX
1810 * control we have also to guarantee that it can always be set with
1811 * SYSTEM permissions. This is needed for the "samba3sam" backend. */
1812 sid = samdb_result_dom_sid(ac, ac->msg, "objectSid");
1813 if ((sid != NULL) && (!dsdb_module_am_system(ac->module)) &&
1814 (ldb_request_get_control(ac->req, LDB_CONTROL_RELAX_OID) == NULL)) {
1815 ldb_set_errstring(ldb,
1816 "samldb: objectSid must not be specified!");
1817 return LDB_ERR_UNWILLING_TO_PERFORM;
1820 /* but generate a new SID when we do have an add operations */
1821 if ((sid == NULL) && (ac->req->operation == LDB_ADD) && !skip_allocate_sids) {
1822 ret = samldb_add_step(ac, samldb_allocate_sid);
1823 if (ret != LDB_SUCCESS) return ret;
1827 case SAMLDB_TYPE_USER: {
1829 uint32_t user_account_control;
1830 bool is_computer_objectclass;
1831 bool uac_generated = false, uac_add_flags = false;
1832 uint32_t default_user_account_control = UF_NORMAL_ACCOUNT;
1833 /* Step 1.2: Default values */
1834 ret = dsdb_user_obj_set_defaults(ldb, ac->msg, ac->req);
1835 if (ret != LDB_SUCCESS) return ret;
1837 is_computer_objectclass
1838 = (samdb_find_attribute(ldb,
1844 if (is_computer_objectclass) {
1845 default_user_account_control
1846 = UF_WORKSTATION_TRUST_ACCOUNT;
1850 /* On add operations we might need to generate a
1851 * "userAccountControl" (if it isn't specified). */
1852 el = ldb_msg_find_element(ac->msg, "userAccountControl");
1854 ret = samdb_msg_set_uint(ldb, ac->msg, ac->msg,
1855 "userAccountControl",
1856 default_user_account_control);
1857 if (ret != LDB_SUCCESS) {
1860 uac_generated = true;
1861 uac_add_flags = true;
1864 el = ldb_msg_find_element(ac->msg, "userAccountControl");
1865 SMB_ASSERT(el != NULL);
1867 /* Step 1.3: "userAccountControl" -> "sAMAccountType" mapping */
1868 user_account_control = ldb_msg_find_attr_as_uint(ac->msg,
1869 "userAccountControl",
1871 raw_uac = user_account_control;
1873 * "userAccountControl" = 0 or missing one of
1874 * the types means "UF_NORMAL_ACCOUNT"
1875 * or "UF_WORKSTATION_TRUST_ACCOUNT" (if a computer).
1876 * See MS-SAMR 3.1.1.8.10 point 8
1878 if ((user_account_control & UF_ACCOUNT_TYPE_MASK) == 0) {
1879 user_account_control
1880 = default_user_account_control
1881 | user_account_control;
1882 uac_generated = true;
1886 * As per MS-SAMR 3.1.1.8.10 these flags have not to be set
1888 if ((user_account_control & UF_LOCKOUT) != 0) {
1889 user_account_control &= ~UF_LOCKOUT;
1890 uac_generated = true;
1892 if ((user_account_control & UF_PASSWORD_EXPIRED) != 0) {
1893 user_account_control &= ~UF_PASSWORD_EXPIRED;
1894 uac_generated = true;
1897 ret = samldb_check_user_account_control_rules(ac, NULL,
1899 user_account_control,
1901 is_computer_objectclass);
1902 if (ret != LDB_SUCCESS) {
1907 * Require, for non-admin modifications, a trailing $
1908 * for either objectclass=computer or a trust account
1909 * type in userAccountControl
1911 if ((user_account_control
1912 & UF_TRUST_ACCOUNT_MASK) != 0) {
1913 ac->need_trailing_dollar = true;
1916 if (is_computer_objectclass) {
1917 ac->need_trailing_dollar = true;
1920 /* add "sAMAccountType" attribute */
1921 ret = dsdb_user_obj_set_account_type(ldb, ac->msg, user_account_control, NULL);
1922 if (ret != LDB_SUCCESS) {
1926 /* "isCriticalSystemObject" might be set */
1927 if (user_account_control &
1928 (UF_SERVER_TRUST_ACCOUNT | UF_PARTIAL_SECRETS_ACCOUNT)) {
1929 ret = ldb_msg_add_string(ac->msg, "isCriticalSystemObject",
1931 if (ret != LDB_SUCCESS) {
1934 el2 = ldb_msg_find_element(ac->msg,
1935 "isCriticalSystemObject");
1936 el2->flags = LDB_FLAG_MOD_REPLACE;
1937 } else if (user_account_control & UF_WORKSTATION_TRUST_ACCOUNT) {
1938 ret = ldb_msg_add_string(ac->msg, "isCriticalSystemObject",
1940 if (ret != LDB_SUCCESS) {
1943 el2 = ldb_msg_find_element(ac->msg,
1944 "isCriticalSystemObject");
1945 el2->flags = LDB_FLAG_MOD_REPLACE;
1948 /* Step 1.4: "userAccountControl" -> "primaryGroupID" mapping */
1949 if (!ldb_msg_find_element(ac->msg, "primaryGroupID")) {
1952 ret = dsdb_user_obj_set_primary_group_id(ldb, ac->msg, user_account_control, &rid);
1953 if (ret != LDB_SUCCESS) {
1957 * Older AD deployments don't know about the
1960 if (rid == DOMAIN_RID_READONLY_DCS) {
1961 ret = samldb_prim_group_tester(ac, rid);
1962 if (ret != LDB_SUCCESS) {
1968 /* Step 1.5: Add additional flags when needed */
1969 /* Obviously this is done when the "userAccountControl"
1970 * has been generated here (tested against Windows
1972 if (uac_generated) {
1973 if (uac_add_flags) {
1974 user_account_control |= UF_ACCOUNTDISABLE;
1975 user_account_control |= UF_PASSWD_NOTREQD;
1978 ret = samdb_msg_set_uint(ldb, ac->msg, ac->msg,
1979 "userAccountControl",
1980 user_account_control);
1981 if (ret != LDB_SUCCESS) {
1988 case SAMLDB_TYPE_GROUP: {
1989 const char *tempstr;
1991 /* Step 2.2: Default values */
1992 tempstr = talloc_asprintf(ac->msg, "%d",
1993 GTYPE_SECURITY_GLOBAL_GROUP);
1994 if (tempstr == NULL) return ldb_operr(ldb);
1995 ret = samdb_find_or_add_attribute(ldb, ac->msg,
1996 "groupType", tempstr);
1997 if (ret != LDB_SUCCESS) return ret;
1999 /* Step 2.3: "groupType" -> "sAMAccountType" */
2000 el = ldb_msg_find_element(ac->msg, "groupType");
2002 uint32_t group_type, account_type;
2004 group_type = ldb_msg_find_attr_as_uint(ac->msg,
2007 /* The creation of builtin groups requires the
2009 if (group_type == GTYPE_SECURITY_BUILTIN_LOCAL_GROUP) {
2010 if (ldb_request_get_control(ac->req,
2011 LDB_CONTROL_RELAX_OID) == NULL) {
2012 return LDB_ERR_UNWILLING_TO_PERFORM;
2016 account_type = ds_gtype2atype(group_type);
2017 if (account_type == 0) {
2018 ldb_set_errstring(ldb, "samldb: Unrecognized account type!");
2019 return LDB_ERR_UNWILLING_TO_PERFORM;
2021 ret = samdb_msg_add_uint(ldb, ac->msg, ac->msg,
2024 if (ret != LDB_SUCCESS) {
2027 el2 = ldb_msg_find_element(ac->msg, "sAMAccountType");
2028 el2->flags = LDB_FLAG_MOD_REPLACE;
2034 ldb_asprintf_errstring(ldb,
2035 "Invalid entry type!");
2036 return LDB_ERR_OPERATIONS_ERROR;
2044 * "Primary group ID" trigger (MS-SAMR 3.1.1.8.2)
2046 * Has to be invoked on "add" and "modify" operations on "user" and "computer"
2048 * ac->msg contains the "add"/"modify" message
2051 static int samldb_prim_group_tester(struct samldb_ctx *ac, uint32_t rid)
2053 struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
2054 struct dom_sid *sid;
2055 struct ldb_result *res;
2057 const char * const noattrs[] = { NULL };
2059 sid = dom_sid_add_rid(ac, samdb_domain_sid(ldb), rid);
2061 return ldb_operr(ldb);
2064 ret = dsdb_module_search(ac->module, ac, &res,
2065 ldb_get_default_basedn(ldb),
2067 noattrs, DSDB_FLAG_NEXT_MODULE,
2070 ldap_encode_ndr_dom_sid(ac, sid));
2071 if (ret != LDB_SUCCESS) {
2074 if (res->count != 1) {
2076 ldb_asprintf_errstring(ldb,
2077 "Failed to find primary group with RID %u!",
2079 return LDB_ERR_UNWILLING_TO_PERFORM;
2086 static int samldb_prim_group_set(struct samldb_ctx *ac)
2088 struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
2091 rid = ldb_msg_find_attr_as_uint(ac->msg, "primaryGroupID", (uint32_t) -1);
2092 if (rid == (uint32_t) -1) {
2093 /* we aren't affected of any primary group set */
2096 } else if (!ldb_request_get_control(ac->req, LDB_CONTROL_RELAX_OID)) {
2097 ldb_set_errstring(ldb,
2098 "The primary group isn't settable on add operations!");
2099 return LDB_ERR_UNWILLING_TO_PERFORM;
2102 return samldb_prim_group_tester(ac, rid);
2105 static int samldb_prim_group_change(struct samldb_ctx *ac)
2107 struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
2108 const char * const attrs[] = {
2111 "userAccountControl",
2113 struct ldb_result *res, *group_res;
2114 struct ldb_message_element *el;
2115 struct ldb_message *msg;
2116 uint32_t search_flags =
2117 DSDB_FLAG_NEXT_MODULE | DSDB_SEARCH_SHOW_EXTENDED_DN;
2118 uint32_t prev_rid, new_rid, uac;
2119 struct dom_sid *prev_sid, *new_sid;
2120 struct ldb_dn *prev_prim_group_dn, *new_prim_group_dn;
2121 const char *new_prim_group_dn_ext_str = NULL;
2122 struct ldb_dn *user_dn = NULL;
2123 const char *user_dn_ext_str = NULL;
2125 const char * const noattrs[] = { NULL };
2127 ret = dsdb_get_expected_new_values(ac,
2131 ac->req->operation);
2132 if (ret != LDB_SUCCESS) {
2137 /* we are not affected */
2141 /* Fetch information from the existing object */
2143 ret = dsdb_module_search_dn(ac->module, ac, &res, ac->msg->dn, attrs,
2144 search_flags, ac->req);
2145 if (ret != LDB_SUCCESS) {
2148 user_dn = res->msgs[0]->dn;
2149 user_dn_ext_str = ldb_dn_get_extended_linearized(ac, user_dn, 1);
2150 if (user_dn_ext_str == NULL) {
2151 return ldb_operr(ldb);
2154 uac = ldb_msg_find_attr_as_uint(res->msgs[0], "userAccountControl", 0);
2156 /* Finds out the DN of the old primary group */
2158 prev_rid = ldb_msg_find_attr_as_uint(res->msgs[0], "primaryGroupID",
2160 if (prev_rid == (uint32_t) -1) {
2161 /* User objects do always have a mandatory "primaryGroupID"
2162 * attribute. If this doesn't exist then the object is of the
2163 * wrong type. This is the exact Windows error code */
2164 return LDB_ERR_OBJECT_CLASS_VIOLATION;
2167 prev_sid = dom_sid_add_rid(ac, samdb_domain_sid(ldb), prev_rid);
2168 if (prev_sid == NULL) {
2169 return ldb_operr(ldb);
2172 /* Finds out the DN of the new primary group
2173 * Notice: in order to parse the primary group ID correctly we create
2174 * a temporary message here. */
2176 msg = ldb_msg_new(ac->msg);
2178 return ldb_module_oom(ac->module);
2180 ret = ldb_msg_add(msg, el, 0);
2181 if (ret != LDB_SUCCESS) {
2184 new_rid = ldb_msg_find_attr_as_uint(msg, "primaryGroupID", (uint32_t) -1);
2186 if (new_rid == (uint32_t) -1) {
2187 /* we aren't affected of any primary group change */
2191 if (prev_rid == new_rid) {
2195 if ((uac & UF_SERVER_TRUST_ACCOUNT) && new_rid != DOMAIN_RID_DCS) {
2196 ldb_asprintf_errstring(ldb,
2197 "%08X: samldb: UF_SERVER_TRUST_ACCOUNT requires "
2198 "primaryGroupID=%u!",
2199 W_ERROR_V(WERR_DS_CANT_MOD_PRIMARYGROUPID),
2201 return LDB_ERR_UNWILLING_TO_PERFORM;
2204 if ((uac & UF_PARTIAL_SECRETS_ACCOUNT) && new_rid != DOMAIN_RID_READONLY_DCS) {
2205 ldb_asprintf_errstring(ldb,
2206 "%08X: samldb: UF_PARTIAL_SECRETS_ACCOUNT requires "
2207 "primaryGroupID=%u!",
2208 W_ERROR_V(WERR_DS_CANT_MOD_PRIMARYGROUPID),
2209 DOMAIN_RID_READONLY_DCS);
2210 return LDB_ERR_UNWILLING_TO_PERFORM;
2213 ret = dsdb_module_search(ac->module, ac, &group_res,
2214 ldb_get_default_basedn(ldb),
2216 noattrs, search_flags,
2219 ldap_encode_ndr_dom_sid(ac, prev_sid));
2220 if (ret != LDB_SUCCESS) {
2223 if (group_res->count != 1) {
2224 return ldb_operr(ldb);
2226 prev_prim_group_dn = group_res->msgs[0]->dn;
2228 new_sid = dom_sid_add_rid(ac, samdb_domain_sid(ldb), new_rid);
2229 if (new_sid == NULL) {
2230 return ldb_operr(ldb);
2233 ret = dsdb_module_search(ac->module, ac, &group_res,
2234 ldb_get_default_basedn(ldb),
2236 noattrs, search_flags,
2239 ldap_encode_ndr_dom_sid(ac, new_sid));
2240 if (ret != LDB_SUCCESS) {
2243 if (group_res->count != 1) {
2244 /* Here we know if the specified new primary group candidate is
2246 return LDB_ERR_UNWILLING_TO_PERFORM;
2248 new_prim_group_dn = group_res->msgs[0]->dn;
2249 new_prim_group_dn_ext_str = ldb_dn_get_extended_linearized(ac,
2250 new_prim_group_dn, 1);
2251 if (new_prim_group_dn_ext_str == NULL) {
2252 return ldb_operr(ldb);
2255 /* We need to be already a normal member of the new primary
2256 * group in order to be successful. */
2257 el = samdb_find_attribute(ldb, res->msgs[0], "memberOf",
2258 new_prim_group_dn_ext_str);
2260 return LDB_ERR_UNWILLING_TO_PERFORM;
2263 /* Remove the "member" attribute on the new primary group */
2264 msg = ldb_msg_new(ac->msg);
2266 return ldb_module_oom(ac->module);
2268 msg->dn = new_prim_group_dn;
2270 ret = samdb_msg_add_delval(ldb, msg, msg, "member", user_dn_ext_str);
2271 if (ret != LDB_SUCCESS) {
2275 ret = dsdb_module_modify(ac->module, msg, DSDB_FLAG_NEXT_MODULE, ac->req);
2276 if (ret != LDB_SUCCESS) {
2281 /* Add a "member" attribute for the previous primary group */
2282 msg = ldb_msg_new(ac->msg);
2284 return ldb_module_oom(ac->module);
2286 msg->dn = prev_prim_group_dn;
2288 ret = samdb_msg_add_addval(ldb, msg, msg, "member", user_dn_ext_str);
2289 if (ret != LDB_SUCCESS) {
2293 ret = dsdb_module_modify(ac->module, msg, DSDB_FLAG_NEXT_MODULE, ac->req);
2294 if (ret != LDB_SUCCESS) {
2302 static int samldb_prim_group_trigger(struct samldb_ctx *ac)
2306 if (ac->req->operation == LDB_ADD) {
2307 ret = samldb_prim_group_set(ac);
2309 ret = samldb_prim_group_change(ac);
2315 static int samldb_check_user_account_control_invariants(struct samldb_ctx *ac,
2316 uint32_t user_account_control)
2320 bool need_check = false;
2321 const struct uac_to_guid {
2326 const char *error_string;
2329 .uac = UF_TEMP_DUPLICATE_ACCOUNT,
2331 .error_string = "Updating the UF_TEMP_DUPLICATE_ACCOUNT flag is never allowed"
2334 .uac = UF_PARTIAL_SECRETS_ACCOUNT,
2335 .needs = UF_WORKSTATION_TRUST_ACCOUNT,
2336 .error_string = "Setting UF_PARTIAL_SECRETS_ACCOUNT only permitted with UF_WORKSTATION_TRUST_ACCOUNT"
2339 .uac = UF_TRUSTED_FOR_DELEGATION,
2340 .not_with = UF_PARTIAL_SECRETS_ACCOUNT,
2341 .error_string = "Setting UF_TRUSTED_FOR_DELEGATION not allowed with UF_PARTIAL_SECRETS_ACCOUNT"
2344 .uac = UF_NORMAL_ACCOUNT,
2345 .not_with = UF_ACCOUNT_TYPE_MASK & ~UF_NORMAL_ACCOUNT,
2346 .error_string = "Setting more than one account type not permitted"
2349 .uac = UF_WORKSTATION_TRUST_ACCOUNT,
2350 .not_with = UF_ACCOUNT_TYPE_MASK & ~UF_WORKSTATION_TRUST_ACCOUNT,
2351 .error_string = "Setting more than one account type not permitted"
2354 .uac = UF_INTERDOMAIN_TRUST_ACCOUNT,
2355 .not_with = UF_ACCOUNT_TYPE_MASK & ~UF_INTERDOMAIN_TRUST_ACCOUNT,
2356 .error_string = "Setting more than one account type not permitted"
2359 .uac = UF_SERVER_TRUST_ACCOUNT,
2360 .not_with = UF_ACCOUNT_TYPE_MASK & ~UF_SERVER_TRUST_ACCOUNT,
2361 .error_string = "Setting more than one account type not permitted"
2364 .uac = UF_TRUSTED_FOR_DELEGATION,
2365 .not_with = UF_PARTIAL_SECRETS_ACCOUNT,
2366 .error_string = "Setting UF_TRUSTED_FOR_DELEGATION not allowed with UF_PARTIAL_SECRETS_ACCOUNT"
2370 for (i = 0; i < ARRAY_SIZE(map); i++) {
2371 if (user_account_control & map[i].uac) {
2376 if (need_check == false) {
2380 for (i = 0; i < ARRAY_SIZE(map); i++) {
2381 uint32_t this_uac = user_account_control & map[i].uac;
2382 if (this_uac != 0) {
2384 ret = LDB_ERR_OTHER;
2386 } else if (map[i].needs != 0) {
2387 if ((map[i].needs & user_account_control) == 0) {
2388 ret = LDB_ERR_OTHER;
2391 } else if (map[i].not_with != 0) {
2392 if ((map[i].not_with & user_account_control) != 0) {
2393 ret = LDB_ERR_OTHER;
2399 if (ret != LDB_SUCCESS) {
2400 switch (ac->req->operation) {
2402 ldb_asprintf_errstring(ldb_module_get_ctx(ac->module),
2403 "Failed to add %s: %s",
2404 ldb_dn_get_linearized(ac->msg->dn),
2405 map[i].error_string);
2408 ldb_asprintf_errstring(ldb_module_get_ctx(ac->module),
2409 "Failed to modify %s: %s",
2410 ldb_dn_get_linearized(ac->msg->dn),
2411 map[i].error_string);
2414 return ldb_module_operr(ac->module);
2421 * It would be best if these rules apply, always, but for now they
2422 * apply only to non-admins
2424 static int samldb_check_user_account_control_objectclass_invariants(
2425 struct samldb_ctx *ac,
2426 uint32_t user_account_control,
2427 uint32_t user_account_control_old,
2428 bool is_computer_objectclass)
2430 struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
2432 uint32_t old_ufa = user_account_control_old & UF_ACCOUNT_TYPE_MASK;
2433 uint32_t new_ufa = user_account_control & UF_ACCOUNT_TYPE_MASK;
2435 uint32_t old_rodc = user_account_control_old & UF_PARTIAL_SECRETS_ACCOUNT;
2436 uint32_t new_rodc = user_account_control & UF_PARTIAL_SECRETS_ACCOUNT;
2439 struct security_token *user_token
2440 = acl_user_token(ac->module);
2441 if (user_token == NULL) {
2442 return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
2446 = security_token_has_builtin_administrators(user_token);
2450 * We want to allow changes to (eg) disable an account
2451 * that was created wrong, only checking the
2452 * objectclass if the account type changes.
2454 if (old_ufa == new_ufa && old_rodc == new_rodc) {
2459 case UF_NORMAL_ACCOUNT:
2460 if (is_computer_objectclass && !is_admin) {
2461 ldb_asprintf_errstring(ldb,
2462 "%08X: samldb: UF_NORMAL_ACCOUNT "
2463 "requires objectclass 'user' not 'computer'!",
2464 W_ERROR_V(WERR_DS_MACHINE_ACCOUNT_CREATED_PRENT4));
2465 return LDB_ERR_OBJECT_CLASS_VIOLATION;
2469 case UF_INTERDOMAIN_TRUST_ACCOUNT:
2470 if (is_computer_objectclass) {
2471 ldb_asprintf_errstring(ldb,
2472 "%08X: samldb: UF_INTERDOMAIN_TRUST_ACCOUNT "
2473 "requires objectclass 'user' not 'computer'!",
2474 W_ERROR_V(WERR_DS_MACHINE_ACCOUNT_CREATED_PRENT4));
2475 return LDB_ERR_OBJECT_CLASS_VIOLATION;
2479 case UF_WORKSTATION_TRUST_ACCOUNT:
2480 if (!is_computer_objectclass) {
2482 * Modify of a user account account into a
2483 * workstation without objectclass computer
2484 * as an admin is still permitted, but not
2488 && ac->req->operation == LDB_MODIFY
2492 ldb_asprintf_errstring(ldb,
2493 "%08X: samldb: UF_WORKSTATION_TRUST_ACCOUNT "
2494 "requires objectclass 'computer'!",
2495 W_ERROR_V(WERR_DS_MACHINE_ACCOUNT_CREATED_PRENT4));
2496 return LDB_ERR_OBJECT_CLASS_VIOLATION;
2500 case UF_SERVER_TRUST_ACCOUNT:
2501 if (!is_computer_objectclass) {
2502 ldb_asprintf_errstring(ldb,
2503 "%08X: samldb: UF_SERVER_TRUST_ACCOUNT "
2504 "requires objectclass 'computer'!",
2505 W_ERROR_V(WERR_DS_MACHINE_ACCOUNT_CREATED_PRENT4));
2506 return LDB_ERR_OBJECT_CLASS_VIOLATION;
2511 ldb_asprintf_errstring(ldb,
2512 "%08X: samldb: invalid userAccountControl[0x%08X]",
2513 W_ERROR_V(WERR_INVALID_PARAMETER),
2514 user_account_control);
2515 return LDB_ERR_OTHER;
2520 static int samldb_get_domain_secdesc_and_oc(struct samldb_ctx *ac,
2521 struct security_descriptor **domain_sd,
2522 const struct dsdb_class **objectclass)
2524 const char * const sd_attrs[] = {"ntSecurityDescriptor", "objectClass", NULL};
2525 struct ldb_result *res;
2526 struct ldb_dn *domain_dn = ldb_get_default_basedn(ldb_module_get_ctx(ac->module));
2527 const struct dsdb_schema *schema = NULL;
2528 struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
2529 int ret = dsdb_module_search_dn(ac->module, ac, &res,
2532 DSDB_FLAG_NEXT_MODULE | DSDB_SEARCH_SHOW_DELETED,
2534 if (ret != LDB_SUCCESS) {
2537 if (res->count != 1) {
2538 return ldb_module_operr(ac->module);
2541 schema = dsdb_get_schema(ldb, ac->req);
2543 return ldb_module_operr(ac->module);;
2545 *objectclass = dsdb_get_structural_oc_from_msg(schema, res->msgs[0]);
2546 return dsdb_get_sd_from_ldb_message(ldb_module_get_ctx(ac->module),
2547 ac, res->msgs[0], domain_sd);
2552 * Validate that the restriction in point 5 of MS-SAMR 3.1.1.8.10 userAccountControl is honoured
2555 static int samldb_check_user_account_control_acl(struct samldb_ctx *ac,
2556 struct dom_sid *sid,
2557 uint32_t user_account_control,
2558 uint32_t user_account_control_old)
2562 bool need_acl_check = false;
2563 struct security_token *user_token;
2564 struct security_descriptor *domain_sd;
2565 const struct dsdb_class *objectclass = NULL;
2566 const struct uac_to_guid {
2568 uint32_t priv_to_change_from;
2571 enum sec_privilege privilege;
2572 bool delete_is_privileged;
2573 bool admin_required;
2574 const char *error_string;
2577 .uac = UF_PASSWD_NOTREQD,
2578 .guid = GUID_DRS_UPDATE_PASSWORD_NOT_REQUIRED_BIT,
2579 .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"
2582 .uac = UF_DONT_EXPIRE_PASSWD,
2583 .guid = GUID_DRS_UNEXPIRE_PASSWORD,
2584 .error_string = "Adding the UF_DONT_EXPIRE_PASSWD bit in userAccountControl requires the Unexpire-Password right that was not given on the Domain object"
2587 .uac = UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED,
2588 .guid = GUID_DRS_ENABLE_PER_USER_REVERSIBLY_ENCRYPTED_PASSWORD,
2589 .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"
2592 .uac = UF_SERVER_TRUST_ACCOUNT,
2593 .guid = GUID_DRS_DS_INSTALL_REPLICA,
2594 .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"
2597 .uac = UF_PARTIAL_SECRETS_ACCOUNT,
2598 .guid = GUID_DRS_DS_INSTALL_REPLICA,
2599 .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"
2602 .uac = UF_WORKSTATION_TRUST_ACCOUNT,
2603 .priv_to_change_from = UF_NORMAL_ACCOUNT,
2604 .error_string = "Swapping UF_NORMAL_ACCOUNT to UF_WORKSTATION_TRUST_ACCOUNT requires the user to be a member of the domain admins group"
2607 .uac = UF_NORMAL_ACCOUNT,
2608 .priv_to_change_from = UF_WORKSTATION_TRUST_ACCOUNT,
2609 .error_string = "Swapping UF_WORKSTATION_TRUST_ACCOUNT to UF_NORMAL_ACCOUNT requires the user to be a member of the domain admins group"
2612 .uac = UF_INTERDOMAIN_TRUST_ACCOUNT,
2613 .oid = DSDB_CONTROL_PERMIT_INTERDOMAIN_TRUST_UAC_OID,
2614 .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",
2615 .delete_is_privileged = true
2618 .uac = UF_TRUSTED_FOR_DELEGATION,
2619 .privilege = SEC_PRIV_ENABLE_DELEGATION,
2620 .delete_is_privileged = true,
2621 .error_string = "Updating the UF_TRUSTED_FOR_DELEGATION bit in userAccountControl is not permitted without the SeEnableDelegationPrivilege"
2624 .uac = UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION,
2625 .privilege = SEC_PRIV_ENABLE_DELEGATION,
2626 .delete_is_privileged = true,
2627 .error_string = "Updating the UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION bit in userAccountControl is not permitted without the SeEnableDelegationPrivilege"
2632 if (dsdb_module_am_system(ac->module)) {
2636 for (i = 0; i < ARRAY_SIZE(map); i++) {
2637 if (user_account_control & map[i].uac) {
2638 need_acl_check = true;
2642 if (need_acl_check == false) {
2646 user_token = acl_user_token(ac->module);
2647 if (user_token == NULL) {
2648 return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
2651 ret = samldb_get_domain_secdesc_and_oc(ac, &domain_sd, &objectclass);
2652 if (ret != LDB_SUCCESS) {
2656 for (i = 0; i < ARRAY_SIZE(map); i++) {
2657 uint32_t this_uac_new = user_account_control & map[i].uac;
2658 uint32_t this_uac_old = user_account_control_old & map[i].uac;
2659 if (this_uac_new != this_uac_old) {
2660 if (this_uac_old != 0) {
2661 if (map[i].delete_is_privileged == false) {
2666 struct ldb_control *control = ldb_request_get_control(ac->req, map[i].oid);
2667 if (control == NULL) {
2668 ret = LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
2670 } else if (map[i].privilege != SEC_PRIV_INVALID) {
2671 bool have_priv = security_token_has_privilege(user_token,
2673 if (have_priv == false) {
2674 ret = LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
2676 } else if (map[i].priv_to_change_from & user_account_control_old) {
2677 bool is_admin = security_token_has_builtin_administrators(user_token);
2678 if (is_admin == false) {
2679 ret = LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
2681 } else if (map[i].guid) {
2682 ret = acl_check_extended_right(ac,
2689 SEC_ADS_CONTROL_ACCESS,
2694 if (ret != LDB_SUCCESS) {
2699 if (ret == LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS) {
2700 switch (ac->req->operation) {
2702 ldb_asprintf_errstring(ldb_module_get_ctx(ac->module),
2703 "Failed to add %s: %s",
2704 ldb_dn_get_linearized(ac->msg->dn),
2705 map[i].error_string);
2708 ldb_asprintf_errstring(ldb_module_get_ctx(ac->module),
2709 "Failed to modify %s: %s",
2710 ldb_dn_get_linearized(ac->msg->dn),
2711 map[i].error_string);
2714 return ldb_module_operr(ac->module);
2717 struct ldb_dn *domain_dn
2718 = ldb_get_default_basedn(ldb_module_get_ctx(ac->module));
2719 dsdb_acl_debug(domain_sd, acl_user_token(ac->module),
2728 static int samldb_check_user_account_control_rules(struct samldb_ctx *ac,
2729 struct dom_sid *sid,
2731 uint32_t user_account_control,
2732 uint32_t user_account_control_old,
2733 bool is_computer_objectclass)
2736 struct dsdb_control_password_user_account_control *uac = NULL;
2738 ret = samldb_check_user_account_control_invariants(ac, user_account_control);
2739 if (ret != LDB_SUCCESS) {
2742 ret = samldb_check_user_account_control_objectclass_invariants(ac,
2743 user_account_control,
2744 user_account_control_old,
2745 is_computer_objectclass);
2746 if (ret != LDB_SUCCESS) {
2750 ret = samldb_check_user_account_control_acl(ac, sid, user_account_control, user_account_control_old);
2751 if (ret != LDB_SUCCESS) {
2755 uac = talloc_zero(ac->req,
2756 struct dsdb_control_password_user_account_control);
2758 return ldb_module_oom(ac->module);
2761 uac->req_flags = req_uac;
2762 uac->old_flags = user_account_control_old;
2763 uac->new_flags = user_account_control;
2765 ret = ldb_request_add_control(ac->req,
2766 DSDB_CONTROL_PASSWORD_USER_ACCOUNT_CONTROL_OID,
2768 if (ret != LDB_SUCCESS) {
2777 * This function is called on LDB modify operations. It performs some additions/
2778 * replaces on the current LDB message when "userAccountControl" changes.
2780 static int samldb_user_account_control_change(struct samldb_ctx *ac)
2782 struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
2788 uint32_t old_uac_computed;
2794 NTTIME old_lockoutTime;
2795 struct ldb_message_element *el;
2796 struct ldb_val *val;
2797 struct ldb_val computer_val;
2798 struct ldb_message *tmp_msg;
2799 struct dom_sid *sid;
2801 struct ldb_result *res;
2802 const char * const attrs[] = {
2804 "isCriticalSystemObject",
2805 "userAccountControl",
2806 "msDS-User-Account-Control-Computed",
2811 bool is_computer_objectclass = false;
2812 bool old_is_critical = false;
2813 bool new_is_critical = false;
2815 ret = dsdb_get_expected_new_values(ac,
2817 "userAccountControl",
2819 ac->req->operation);
2820 if (ret != LDB_SUCCESS) {
2824 if (el == NULL || el->num_values == 0) {
2825 ldb_asprintf_errstring(ldb,
2826 "%08X: samldb: 'userAccountControl' can't be deleted!",
2827 W_ERROR_V(WERR_DS_ILLEGAL_MOD_OPERATION));
2828 return LDB_ERR_UNWILLING_TO_PERFORM;
2831 /* Create a temporary message for fetching the "userAccountControl" */
2832 tmp_msg = ldb_msg_new(ac->msg);
2833 if (tmp_msg == NULL) {
2834 return ldb_module_oom(ac->module);
2836 ret = ldb_msg_add(tmp_msg, el, 0);
2837 if (ret != LDB_SUCCESS) {
2840 raw_uac = ldb_msg_find_attr_as_uint(tmp_msg,
2841 "userAccountControl",
2843 talloc_free(tmp_msg);
2845 * UF_LOCKOUT, UF_PASSWD_CANT_CHANGE and UF_PASSWORD_EXPIRED
2846 * are only generated and not stored. We ignore them almost
2847 * completely, along with unknown bits and UF_SCRIPT.
2849 * The only exception is ACB_AUTOLOCK, which features in
2850 * clear_acb when the bit is cleared in this modify operation.
2852 * MS-SAMR 2.2.1.13 UF_FLAG Codes states that some bits are
2853 * ignored by clients and servers
2855 new_uac = raw_uac & UF_SETTABLE_BITS;
2857 /* Fetch the old "userAccountControl" and "objectClass" */
2858 ret = dsdb_module_search_dn(ac->module, ac, &res, ac->msg->dn, attrs,
2859 DSDB_FLAG_NEXT_MODULE, ac->req);
2860 if (ret != LDB_SUCCESS) {
2863 old_uac = ldb_msg_find_attr_as_uint(res->msgs[0], "userAccountControl", 0);
2865 return ldb_operr(ldb);
2867 old_uac_computed = ldb_msg_find_attr_as_uint(res->msgs[0],
2868 "msDS-User-Account-Control-Computed", 0);
2869 old_lockoutTime = ldb_msg_find_attr_as_int64(res->msgs[0],
2871 old_is_critical = ldb_msg_find_attr_as_bool(res->msgs[0],
2872 "isCriticalSystemObject", 0);
2874 * When we do not have objectclass "computer" we cannot
2875 * switch to a workstation or (RO)DC
2877 el = ldb_msg_find_element(res->msgs[0], "objectClass");
2879 return ldb_operr(ldb);
2881 computer_val = data_blob_string_const("computer");
2882 val = ldb_msg_find_val(el, &computer_val);
2884 is_computer_objectclass = true;
2887 old_ufa = old_uac & UF_ACCOUNT_TYPE_MASK;
2888 old_atype = ds_uf2atype(old_ufa);
2889 old_pgrid = ds_uf2prim_group_rid(old_uac);
2891 new_ufa = new_uac & UF_ACCOUNT_TYPE_MASK;
2894 * "userAccountControl" = 0 or missing one of the
2895 * types means "UF_NORMAL_ACCOUNT". See MS-SAMR
2896 * 3.1.1.8.10 point 8
2898 new_ufa = UF_NORMAL_ACCOUNT;
2901 sid = samdb_result_dom_sid(res, res->msgs[0], "objectSid");
2903 return ldb_module_operr(ac->module);
2906 ret = samldb_check_user_account_control_rules(ac, sid,
2910 is_computer_objectclass);
2911 if (ret != LDB_SUCCESS) {
2915 new_atype = ds_uf2atype(new_ufa);
2916 new_pgrid = ds_uf2prim_group_rid(new_uac);
2918 clear_uac = (old_uac | old_uac_computed) & ~raw_uac;
2921 case UF_NORMAL_ACCOUNT:
2922 new_is_critical = old_is_critical;
2925 case UF_INTERDOMAIN_TRUST_ACCOUNT:
2926 new_is_critical = true;
2929 case UF_WORKSTATION_TRUST_ACCOUNT:
2930 new_is_critical = false;
2931 if (new_uac & UF_PARTIAL_SECRETS_ACCOUNT) {
2932 new_is_critical = true;
2936 case UF_SERVER_TRUST_ACCOUNT:
2937 new_is_critical = true;
2941 ldb_asprintf_errstring(ldb,
2942 "%08X: samldb: invalid userAccountControl[0x%08X]",
2943 W_ERROR_V(WERR_INVALID_PARAMETER), raw_uac);
2944 return LDB_ERR_OTHER;
2947 if (old_atype != new_atype) {
2948 ret = samdb_msg_add_uint(ldb, ac->msg, ac->msg,
2949 "sAMAccountType", new_atype);
2950 if (ret != LDB_SUCCESS) {
2953 el = ldb_msg_find_element(ac->msg, "sAMAccountType");
2954 el->flags = LDB_FLAG_MOD_REPLACE;
2957 /* As per MS-SAMR 3.1.1.8.10 these flags have not to be set */
2958 if ((clear_uac & UF_LOCKOUT) && (old_lockoutTime != 0)) {
2959 /* "lockoutTime" reset as per MS-SAMR 3.1.1.8.10 */
2960 ldb_msg_remove_attr(ac->msg, "lockoutTime");
2961 ret = samdb_msg_add_uint64(ldb, ac->msg, ac->msg, "lockoutTime",
2963 if (ret != LDB_SUCCESS) {
2966 el = ldb_msg_find_element(ac->msg, "lockoutTime");
2967 el->flags = LDB_FLAG_MOD_REPLACE;
2971 * "isCriticalSystemObject" might be set/changed
2973 * Even a change from UF_NORMAL_ACCOUNT (implicitly FALSE) to
2974 * UF_WORKSTATION_TRUST_ACCOUNT (actually FALSE) triggers
2975 * creating the attribute.
2977 if (old_is_critical != new_is_critical || old_atype != new_atype) {
2978 ret = ldb_msg_add_string(ac->msg, "isCriticalSystemObject",
2979 new_is_critical ? "TRUE": "FALSE");
2980 if (ret != LDB_SUCCESS) {
2983 el = ldb_msg_find_element(ac->msg,
2984 "isCriticalSystemObject");
2985 el->flags = LDB_FLAG_MOD_REPLACE;
2988 if (!ldb_msg_find_element(ac->msg, "primaryGroupID") &&
2989 (old_pgrid != new_pgrid)) {
2990 /* Older AD deployments don't know about the RODC group */
2991 if (new_pgrid == DOMAIN_RID_READONLY_DCS) {
2992 ret = samldb_prim_group_tester(ac, new_pgrid);
2993 if (ret != LDB_SUCCESS) {
2998 ret = samdb_msg_add_uint(ldb, ac->msg, ac->msg,
2999 "primaryGroupID", new_pgrid);
3000 if (ret != LDB_SUCCESS) {
3003 el = ldb_msg_find_element(ac->msg,
3005 el->flags = LDB_FLAG_MOD_REPLACE;
3008 /* Propagate eventual "userAccountControl" attribute changes */
3009 if (old_uac != new_uac) {
3010 char *tempstr = talloc_asprintf(ac->msg, "%d",
3012 if (tempstr == NULL) {
3013 return ldb_module_oom(ac->module);
3016 ret = ldb_msg_add_empty(ac->msg,
3017 "userAccountControl",
3018 LDB_FLAG_MOD_REPLACE,
3020 el->values = talloc(ac->msg, struct ldb_val);
3022 el->values[0].data = (uint8_t *) tempstr;
3023 el->values[0].length = strlen(tempstr);
3025 ldb_msg_remove_attr(ac->msg, "userAccountControl");
3031 static int samldb_check_pwd_last_set_acl(struct samldb_ctx *ac,
3032 struct dom_sid *sid)
3034 struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
3036 struct security_token *user_token = NULL;
3037 struct security_descriptor *domain_sd = NULL;
3038 struct ldb_dn *domain_dn = ldb_get_default_basedn(ldb_module_get_ctx(ac->module));
3039 const char *operation = "";
3040 const struct dsdb_class *objectclass = NULL;
3042 if (dsdb_module_am_system(ac->module)) {
3046 switch (ac->req->operation) {
3051 operation = "modify";
3054 return ldb_module_operr(ac->module);
3057 user_token = acl_user_token(ac->module);
3058 if (user_token == NULL) {
3059 return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
3062 ret = samldb_get_domain_secdesc_and_oc(ac, &domain_sd, &objectclass);
3063 if (ret != LDB_SUCCESS) {
3066 ret = acl_check_extended_right(ac,
3072 GUID_DRS_UNEXPIRE_PASSWORD,
3073 SEC_ADS_CONTROL_ACCESS,
3075 if (ret != LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS) {
3079 ldb_debug_set(ldb, LDB_DEBUG_WARNING,
3081 "Setting pwdLastSet to -1 requires the "
3082 "Unexpire-Password right that was not given "
3083 "on the Domain object",
3085 ldb_dn_get_linearized(ac->msg->dn));
3086 dsdb_acl_debug(domain_sd, user_token,
3087 domain_dn, true, 10);
3093 * This function is called on LDB modify operations. It performs some additions/
3094 * replaces on the current LDB message when "pwdLastSet" changes.
3096 static int samldb_pwd_last_set_change(struct samldb_ctx *ac)
3098 struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
3099 NTTIME last_set = 0;
3100 struct ldb_message_element *el = NULL;
3101 struct ldb_message *tmp_msg = NULL;
3102 struct dom_sid *self_sid = NULL;
3104 struct ldb_result *res = NULL;
3105 const char * const attrs[] = {
3110 el = dsdb_get_single_valued_attr(ac->msg, "pwdLastSet",
3111 ac->req->operation);
3112 if (el == NULL || el->num_values == 0) {
3113 ldb_asprintf_errstring(ldb,
3114 "%08X: samldb: 'pwdLastSet' can't be deleted!",
3115 W_ERROR_V(WERR_DS_ILLEGAL_MOD_OPERATION));
3116 return LDB_ERR_UNWILLING_TO_PERFORM;
3119 /* Create a temporary message for fetching the "userAccountControl" */
3120 tmp_msg = ldb_msg_new(ac->msg);
3121 if (tmp_msg == NULL) {
3122 return ldb_module_oom(ac->module);
3124 ret = ldb_msg_add(tmp_msg, el, 0);
3125 if (ret != LDB_SUCCESS) {
3128 last_set = samdb_result_nttime(tmp_msg, "pwdLastSet", 0);
3129 talloc_free(tmp_msg);
3132 * Setting -1 (0xFFFFFFFFFFFFFFFF) requires the Unexpire-Password right
3134 if (last_set != UINT64_MAX) {
3138 /* Fetch the "objectSid" */
3139 ret = dsdb_module_search_dn(ac->module, ac, &res, ac->msg->dn, attrs,
3140 DSDB_FLAG_NEXT_MODULE, ac->req);
3141 if (ret != LDB_SUCCESS) {
3144 self_sid = samdb_result_dom_sid(res, res->msgs[0], "objectSid");
3145 if (self_sid == NULL) {
3146 return ldb_module_operr(ac->module);
3149 ret = samldb_check_pwd_last_set_acl(ac, self_sid);
3150 if (ret != LDB_SUCCESS) {
3157 static int samldb_lockout_time(struct samldb_ctx *ac)
3159 struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
3161 struct ldb_message_element *el;
3162 struct ldb_message *tmp_msg;
3165 el = dsdb_get_single_valued_attr(ac->msg, "lockoutTime",
3166 ac->req->operation);
3167 if (el == NULL || el->num_values == 0) {
3168 ldb_asprintf_errstring(ldb,
3169 "%08X: samldb: 'lockoutTime' can't be deleted!",
3170 W_ERROR_V(WERR_DS_ILLEGAL_MOD_OPERATION));
3171 return LDB_ERR_UNWILLING_TO_PERFORM;
3174 /* Create a temporary message for fetching the "lockoutTime" */
3175 tmp_msg = ldb_msg_new(ac->msg);
3176 if (tmp_msg == NULL) {
3177 return ldb_module_oom(ac->module);
3179 ret = ldb_msg_add(tmp_msg, el, 0);
3180 if (ret != LDB_SUCCESS) {
3183 lockoutTime = ldb_msg_find_attr_as_int64(tmp_msg,
3186 talloc_free(tmp_msg);
3188 if (lockoutTime != 0) {
3192 /* lockoutTime == 0 resets badPwdCount */
3193 ldb_msg_remove_attr(ac->msg, "badPwdCount");
3194 ret = samdb_msg_add_int(ldb, ac->msg, ac->msg,
3196 if (ret != LDB_SUCCESS) {
3199 el = ldb_msg_find_element(ac->msg, "badPwdCount");
3200 el->flags = LDB_FLAG_MOD_REPLACE;
3205 static int samldb_group_type_change(struct samldb_ctx *ac)
3207 struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
3208 uint32_t group_type, old_group_type, account_type;
3209 struct ldb_message_element *el;
3210 struct ldb_message *tmp_msg;
3212 struct ldb_result *res;
3213 const char * const attrs[] = { "groupType", NULL };
3215 el = dsdb_get_single_valued_attr(ac->msg, "groupType",
3216 ac->req->operation);
3218 /* we are not affected */
3222 /* Create a temporary message for fetching the "groupType" */
3223 tmp_msg = ldb_msg_new(ac->msg);
3224 if (tmp_msg == NULL) {
3225 return ldb_module_oom(ac->module);
3227 ret = ldb_msg_add(tmp_msg, el, 0);
3228 if (ret != LDB_SUCCESS) {
3231 group_type = ldb_msg_find_attr_as_uint(tmp_msg, "groupType", 0);
3232 talloc_free(tmp_msg);
3234 ret = dsdb_module_search_dn(ac->module, ac, &res, ac->msg->dn, attrs,
3235 DSDB_FLAG_NEXT_MODULE |
3236 DSDB_SEARCH_SHOW_DELETED, ac->req);
3237 if (ret != LDB_SUCCESS) {
3240 old_group_type = ldb_msg_find_attr_as_uint(res->msgs[0], "groupType", 0);
3241 if (old_group_type == 0) {
3242 return ldb_operr(ldb);
3245 /* Group type switching isn't so easy as it seems: We can only
3246 * change in this directions: global <-> universal <-> local
3247 * On each step also the group type itself
3248 * (security/distribution) is variable. */
3250 if (ldb_request_get_control(ac->req, LDB_CONTROL_PROVISION_OID) == NULL) {
3251 switch (group_type) {
3252 case GTYPE_SECURITY_GLOBAL_GROUP:
3253 case GTYPE_DISTRIBUTION_GLOBAL_GROUP:
3254 /* change to "universal" allowed */
3255 if ((old_group_type == GTYPE_SECURITY_DOMAIN_LOCAL_GROUP) ||
3256 (old_group_type == GTYPE_DISTRIBUTION_DOMAIN_LOCAL_GROUP)) {
3257 ldb_set_errstring(ldb,
3258 "samldb: Change from security/distribution local group forbidden!");
3259 return LDB_ERR_UNWILLING_TO_PERFORM;
3263 case GTYPE_SECURITY_UNIVERSAL_GROUP:
3264 case GTYPE_DISTRIBUTION_UNIVERSAL_GROUP:
3265 /* each change allowed */
3267 case GTYPE_SECURITY_DOMAIN_LOCAL_GROUP:
3268 case GTYPE_DISTRIBUTION_DOMAIN_LOCAL_GROUP:
3269 /* change to "universal" allowed */
3270 if ((old_group_type == GTYPE_SECURITY_GLOBAL_GROUP) ||
3271 (old_group_type == GTYPE_DISTRIBUTION_GLOBAL_GROUP)) {
3272 ldb_set_errstring(ldb,
3273 "samldb: Change from security/distribution global group forbidden!");
3274 return LDB_ERR_UNWILLING_TO_PERFORM;
3278 case GTYPE_SECURITY_BUILTIN_LOCAL_GROUP:
3280 /* we don't allow this "groupType" values */
3281 return LDB_ERR_UNWILLING_TO_PERFORM;
3286 account_type = ds_gtype2atype(group_type);
3287 if (account_type == 0) {
3288 ldb_set_errstring(ldb, "samldb: Unrecognized account type!");
3289 return LDB_ERR_UNWILLING_TO_PERFORM;
3291 ret = samdb_msg_add_uint(ldb, ac->msg, ac->msg, "sAMAccountType",
3293 if (ret != LDB_SUCCESS) {
3296 el = ldb_msg_find_element(ac->msg, "sAMAccountType");
3297 el->flags = LDB_FLAG_MOD_REPLACE;
3302 static int samldb_member_check(struct samldb_ctx *ac)
3304 const char * const attrs[] = { "objectSid", NULL };
3305 struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
3306 struct ldb_message_element *el;
3307 struct ldb_dn *member_dn;
3308 struct dom_sid *sid;
3309 struct ldb_result *res;
3310 struct dom_sid *group_sid;
3314 /* Fetch information from the existing object */
3316 ret = dsdb_module_search(ac->module, ac, &res, ac->msg->dn, LDB_SCOPE_BASE, attrs,
3317 DSDB_FLAG_NEXT_MODULE | DSDB_SEARCH_SHOW_DELETED, ac->req, NULL);
3318 if (ret != LDB_SUCCESS) {
3321 if (res->count != 1) {
3322 return ldb_operr(ldb);
3325 group_sid = samdb_result_dom_sid(res, res->msgs[0], "objectSid");
3326 if (group_sid == NULL) {
3327 return ldb_operr(ldb);
3330 /* We've to walk over all modification entries and consider the "member"
3332 for (i = 0; i < ac->msg->num_elements; i++) {
3333 if (ldb_attr_cmp(ac->msg->elements[i].name, "member") != 0) {
3337 el = &ac->msg->elements[i];
3338 for (j = 0; j < el->num_values; j++) {
3339 struct ldb_result *group_res;
3340 const char *group_attrs[] = { "primaryGroupID" , NULL };
3341 uint32_t prim_group_rid;
3343 if (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_DELETE) {
3344 /* Deletes will be handled in
3345 * repl_meta_data, and deletes not
3346 * matching a member will return
3347 * LDB_ERR_UNWILLING_TO_PERFORM
3352 member_dn = ldb_dn_from_ldb_val(ac, ldb,
3354 if (!ldb_dn_validate(member_dn)) {
3355 return ldb_operr(ldb);
3358 /* Denies to add "member"s to groups which are primary
3359 * ones for them - in this case return
3360 * ERR_ENTRY_ALREADY_EXISTS. */
3362 ret = dsdb_module_search_dn(ac->module, ac, &group_res,
3363 member_dn, group_attrs,
3364 DSDB_FLAG_NEXT_MODULE, ac->req);
3365 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
3366 /* member DN doesn't exist yet */
3369 if (ret != LDB_SUCCESS) {
3372 prim_group_rid = ldb_msg_find_attr_as_uint(group_res->msgs[0], "primaryGroupID", (uint32_t)-1);
3373 if (prim_group_rid == (uint32_t) -1) {
3374 /* the member hasn't to be a user account ->
3375 * therefore no check needed in this case. */
3379 sid = dom_sid_add_rid(ac, samdb_domain_sid(ldb),
3382 return ldb_operr(ldb);
3385 if (dom_sid_equal(group_sid, sid)) {
3386 ldb_asprintf_errstring(ldb,
3387 "samldb: member %s already set via primaryGroupID %u",
3388 ldb_dn_get_linearized(member_dn), prim_group_rid);
3389 return LDB_ERR_ENTRY_ALREADY_EXISTS;
3399 /* SAM objects have special rules regarding the "description" attribute on
3400 * modify operations. */
3401 static int samldb_description_check(struct samldb_ctx *ac, bool *modified)
3403 struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
3404 const char * const attrs[] = { "objectClass", "description", NULL };
3405 struct ldb_result *res;
3409 /* Fetch information from the existing object */
3410 ret = dsdb_module_search(ac->module, ac, &res, ac->msg->dn, LDB_SCOPE_BASE, attrs,
3411 DSDB_FLAG_NEXT_MODULE | DSDB_SEARCH_SHOW_DELETED, ac->req,
3412 "(|(objectclass=user)(objectclass=group)(objectclass=samDomain)(objectclass=samServer))");
3413 if (ret != LDB_SUCCESS) {
3414 /* don't treat it specially ... let normal error codes
3415 happen from other places */
3416 ldb_reset_err_string(ldb);
3419 if (res->count == 0) {
3420 /* we didn't match the filter */
3425 /* We've to walk over all modification entries and consider the
3426 * "description" ones. */
3427 for (i = 0; i < ac->msg->num_elements; i++) {
3428 if (ldb_attr_cmp(ac->msg->elements[i].name, "description") == 0) {
3429 ac->msg->elements[i].flags |= LDB_FLAG_INTERNAL_FORCE_SINGLE_VALUE_CHECK;
3439 #define SPN_ALIAS_NONE 0
3440 #define SPN_ALIAS_LINK 1
3441 #define SPN_ALIAS_TARGET 2
3443 static int find_spn_aliases(struct ldb_context *ldb,
3444 TALLOC_CTX *mem_ctx,
3445 const char *service_class,
3451 * If you change the way this works, you should also look at changing
3452 * LDB_lookup_spn_alias() in source4/dsdb/samdb/cracknames.c, which
3453 * does some of the same work.
3455 * In particular, note that sPNMappings are resolved on a first come,
3456 * first served basis. For example, if we have
3462 * then 'ldap', 'cifs', and 'host' will resolve to 'host', and
3463 * 'alerter' will resolve to 'cifs'.
3465 * If this resolution method is made more complicated, then the
3466 * cracknames function should also be changed.
3471 struct ldb_result *res = NULL;
3472 struct ldb_message_element *spnmappings = NULL;
3473 TALLOC_CTX *tmp_ctx = NULL;
3474 struct ldb_dn *service_dn = NULL;
3476 const char *attrs[] = {
3481 *direction = SPN_ALIAS_NONE;
3483 tmp_ctx = talloc_new(mem_ctx);
3484 if (tmp_ctx == NULL) {
3485 return ldb_oom(ldb);
3488 service_dn = ldb_dn_new(
3490 "CN=Directory Service,CN=Windows NT,CN=Services");
3491 if (service_dn == NULL) {
3492 talloc_free(tmp_ctx);
3493 return ldb_oom(ldb);
3496 ok = ldb_dn_add_base(service_dn, ldb_get_config_basedn(ldb));
3498 talloc_free(tmp_ctx);
3499 return LDB_ERR_OPERATIONS_ERROR;
3502 ret = ldb_search(ldb, tmp_ctx, &res, service_dn, LDB_SCOPE_BASE,
3503 attrs, "(objectClass=nTDSService)");
3505 if (ret != LDB_SUCCESS || res->count != 1) {
3506 DBG_WARNING("sPNMappings not found.\n");
3507 talloc_free(tmp_ctx);
3511 spnmappings = ldb_msg_find_element(res->msgs[0], "sPNMappings");
3512 if (spnmappings == NULL || spnmappings->num_values == 0) {
3513 DBG_WARNING("no sPNMappings attribute\n");
3514 talloc_free(tmp_ctx);
3515 return LDB_ERR_NO_SUCH_OBJECT;
3519 for (i = 0; i < spnmappings->num_values; i++) {
3521 char *mapping = talloc_strndup(
3523 (char *)spnmappings->values[i].data,
3524 spnmappings->values[i].length);
3525 if (mapping == NULL) {
3526 talloc_free(tmp_ctx);
3527 return ldb_oom(ldb);
3530 p = strchr(mapping, '=');
3532 talloc_free(tmp_ctx);
3533 return LDB_ERR_ALIAS_PROBLEM;
3538 if (strcasecmp(mapping, service_class) == 0) {
3540 * We need to return the reverse aliases for this one.
3542 * typically, this means the service_class is "host"
3543 * and the mapping is "host=alerter,appmgmt,cisvc,..",
3544 * so we get "alerter", "appmgmt", etc in the list of
3548 /* There is one more field than there are commas */
3551 for (j = 0; p[j] != '\0'; j++) {
3557 *aliases = talloc_array(mem_ctx, char*, n);
3558 if (*aliases == NULL) {
3559 talloc_free(tmp_ctx);
3560 return ldb_oom(ldb);
3563 talloc_steal(mem_ctx, mapping);
3564 for (j = 0; j < n; j++) {
3568 talloc_free(tmp_ctx);
3569 *direction = SPN_ALIAS_LINK;
3573 * We need to look along the list to see if service_class is
3574 * there; if so, we return a list of one item (probably "host").
3583 if (strcasecmp(str, service_class) == 0) {
3584 *aliases = talloc_array(mem_ctx, char*, 1);
3585 if (*aliases == NULL) {
3586 talloc_free(tmp_ctx);
3587 return ldb_oom(ldb);
3590 (*aliases)[0] = mapping;
3591 talloc_steal(mem_ctx, mapping);
3592 talloc_free(tmp_ctx);
3593 *direction = SPN_ALIAS_TARGET;
3596 } while (p != NULL);
3598 DBG_INFO("no sPNMappings alias for '%s'\n", service_class);
3599 talloc_free(tmp_ctx);
3606 static int get_spn_dn(struct ldb_context *ldb,
3607 TALLOC_CTX *tmp_ctx,
3608 const char *candidate,
3612 const char *empty_attrs[] = { NULL };
3613 struct ldb_message *msg = NULL;
3614 struct ldb_dn *base_dn = ldb_get_default_basedn(ldb);
3616 const char *enc_candidate = NULL;
3620 enc_candidate = ldb_binary_encode_string(tmp_ctx, candidate);
3621 if (enc_candidate == NULL) {
3622 return ldb_operr(ldb);
3625 ret = dsdb_search_one(ldb,
3632 "(servicePrincipalName=%s)",
3634 if (ret != LDB_SUCCESS) {
3642 static int check_spn_write_rights(struct ldb_context *ldb,
3643 TALLOC_CTX *mem_ctx,
3648 struct ldb_message *msg = NULL;
3649 struct ldb_message_element *del_el = NULL;
3650 struct ldb_message_element *add_el = NULL;
3651 struct ldb_val val = {
3652 .data = discard_const_p(uint8_t, spn),
3653 .length = strlen(spn)
3656 msg = ldb_msg_new(mem_ctx);
3658 return ldb_oom(ldb);
3662 ret = ldb_msg_add_empty(msg,
3663 "servicePrincipalName",
3664 LDB_FLAG_MOD_DELETE,
3666 if (ret != LDB_SUCCESS) {
3671 del_el->values = talloc_array(msg->elements, struct ldb_val, 1);
3672 if (del_el->values == NULL) {
3677 del_el->values[0] = val;
3678 del_el->num_values = 1;
3680 ret = ldb_msg_add_empty(msg,
3681 "servicePrincipalName",
3684 if (ret != LDB_SUCCESS) {
3689 add_el->values = talloc_array(msg->elements, struct ldb_val, 1);
3690 if (add_el->values == NULL) {
3695 add_el->values[0] = val;
3696 add_el->num_values = 1;
3698 ret = ldb_modify(ldb, msg);
3699 if (ret == LDB_ERR_NO_SUCH_ATTRIBUTE) {
3700 DBG_ERR("hmm I think we're OK, but not sure\n");
3701 } else if (ret != LDB_SUCCESS) {
3702 DBG_ERR("SPN write rights check failed with %d\n", ret);
3711 static int check_spn_alias_collision(struct ldb_context *ldb,
3712 TALLOC_CTX *mem_ctx,
3714 struct ldb_dn *target_dn)
3717 char *service_class = NULL;
3718 char *spn_tail = NULL;
3720 char **aliases = NULL;
3721 size_t n_aliases = 0;
3723 TALLOC_CTX *tmp_ctx = NULL;
3724 const char *target_dnstr = ldb_dn_get_linearized(target_dn);
3727 tmp_ctx = talloc_new(mem_ctx);
3728 if (tmp_ctx == NULL) {
3729 return ldb_oom(ldb);
3733 * "dns/example.com/xxx" gives
3734 * service_class = "dns"
3735 * spn_tail = "example.com/xxx"
3737 p = strchr(spn, '/');
3740 talloc_free(tmp_ctx);
3741 return ldb_error(ldb,
3742 LDB_ERR_OPERATIONS_ERROR,
3743 "malformed servicePrincipalName");
3747 service_class = talloc_strndup(tmp_ctx, spn, len);
3748 if (service_class == NULL) {
3749 talloc_free(tmp_ctx);
3750 return ldb_oom(ldb);
3754 ret = find_spn_aliases(ldb,
3760 if (ret != LDB_SUCCESS) {
3761 talloc_free(tmp_ctx);
3766 * we have the list of aliases, and now we need to combined them with
3767 * spn_tail and see if we can find the SPN.
3769 for (i = 0; i < n_aliases; i++) {
3770 struct ldb_dn *colliding_dn = NULL;
3771 const char *colliding_dnstr = NULL;
3773 char *candidate = talloc_asprintf(tmp_ctx,
3777 if (candidate == NULL) {
3778 talloc_free(tmp_ctx);
3779 return ldb_oom(ldb);
3782 ret = get_spn_dn(ldb, tmp_ctx, candidate, &colliding_dn);
3783 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
3784 DBG_DEBUG("SPN alias '%s' not found (good)\n",
3786 talloc_free(candidate);
3789 if (ret != LDB_SUCCESS) {
3790 DBG_ERR("SPN '%s' search error %d\n", candidate, ret);
3791 talloc_free(tmp_ctx);
3795 target_dnstr = ldb_dn_get_linearized(target_dn);
3797 * We have found an existing SPN that matches the alias. That
3798 * is OK only if it is on the object we are trying to add to,
3799 * or if the SPN on the other side is a more generic alias for
3800 * this one and we also have rights to modify it.
3802 * That is, we can put "host/X" and "cifs/X" on the same
3803 * object, but not on different objects, unless we put the
3804 * host/X on first, and could also change that object when we
3805 * add cifs/X. It is forbidden to add the objects in the other
3808 * The rationale for this is that adding "cifs/X" effectively
3809 * changes "host/X" by diverting traffic. If "host/X" can be
3810 * added after "cifs/X", a sneaky person could get "cifs/X" in
3811 * first, making "host/X" have less effect than intended.
3813 * Note: we also can't have "host/X" and "Host/X" on the same
3814 * object, but that is not relevant here.
3817 ret = ldb_dn_compare(colliding_dn, target_dn);
3819 colliding_dnstr = ldb_dn_get_linearized(colliding_dn);
3820 DBG_ERR("trying to add SPN '%s' on '%s' when '%s' is "
3827 if (link_direction == SPN_ALIAS_LINK) {
3828 /* we don't allow host/X if there is a
3830 talloc_free(tmp_ctx);
3831 return LDB_ERR_CONSTRAINT_VIOLATION;
3833 ret = check_spn_write_rights(ldb,
3837 if (ret != LDB_SUCCESS) {
3838 DBG_ERR("SPN '%s' is on '%s' so '%s' can't be "
3844 talloc_free(tmp_ctx);
3845 ldb_asprintf_errstring(ldb,
3846 "samldb: spn[%s] would cause a conflict",
3848 return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
3851 DBG_INFO("SPNs '%s' and '%s' alias both on '%s'\n",
3852 candidate, spn, target_dnstr);
3854 talloc_free(candidate);
3857 talloc_free(tmp_ctx);
3861 static int check_spn_direct_collision(struct ldb_context *ldb,
3862 TALLOC_CTX *mem_ctx,
3864 struct ldb_dn *target_dn)
3867 TALLOC_CTX *tmp_ctx = NULL;
3868 struct ldb_dn *colliding_dn = NULL;
3869 const char *target_dnstr = NULL;
3870 const char *colliding_dnstr = NULL;
3872 tmp_ctx = talloc_new(mem_ctx);
3873 if (tmp_ctx == NULL) {
3874 return ldb_oom(ldb);
3877 ret = get_spn_dn(ldb, tmp_ctx, spn, &colliding_dn);
3878 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
3879 DBG_DEBUG("SPN '%s' not found (good)\n", spn);
3880 talloc_free(tmp_ctx);
3883 if (ret != LDB_SUCCESS) {
3884 DBG_ERR("SPN '%s' search error %d\n", spn, ret);
3885 talloc_free(tmp_ctx);
3886 if (ret == LDB_ERR_COMPARE_TRUE) {
3888 * COMPARE_TRUE has special meaning here and we don't
3889 * want to return it by mistake.
3891 ret = LDB_ERR_OPERATIONS_ERROR;
3896 * We have found this exact SPN. This is mostly harmless (depend on
3897 * ADD vs REPLACE) when the spn is being put on the object that
3898 * already has, so we let it through to succeed or fail as some other
3901 target_dnstr = ldb_dn_get_linearized(target_dn);
3902 ret = ldb_dn_compare(colliding_dn, target_dn);
3904 colliding_dnstr = ldb_dn_get_linearized(colliding_dn);
3905 DBG_ERR("SPN '%s' is on '%s' so it can't be "
3910 ldb_asprintf_errstring(ldb,
3911 "samldb: spn[%s] would cause a conflict",
3913 talloc_free(tmp_ctx);
3914 return LDB_ERR_CONSTRAINT_VIOLATION;
3917 DBG_INFO("SPN '%s' is already on '%s'\n",
3919 talloc_free(tmp_ctx);
3920 return LDB_ERR_COMPARE_TRUE;
3924 static int count_spn_components(struct ldb_val val)
3927 * a 3 part servicePrincipalName has two slashes, like
3928 * ldap/example.com/DomainDNSZones.example.com.
3930 * In krb5_parse_name_flags() we don't count "\/" as a slash (i.e.
3931 * escaped by a backslash), but this is not the behaviour of Windows
3932 * on setting a servicePrincipalName -- slashes are counted regardless
3935 * Accordingly, here we ignore backslashes. This will reject
3936 * multi-slash SPNs that krb5_parse_name_flags() would accept, and
3937 * allow ones in the form "a\/b" that it won't parse.
3941 for (i = 0; i < val.length; i++) {
3942 char c = val.data[i];
3946 /* at this point we don't care */
3955 /* Check that "servicePrincipalName" changes do not introduce a collision
3957 static int samldb_spn_uniqueness_check(struct samldb_ctx *ac,
3958 struct ldb_message_element *spn_el)
3960 struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
3962 const char *spn = NULL;
3964 TALLOC_CTX *tmp_ctx = talloc_new(ac->msg);
3965 if (tmp_ctx == NULL) {
3966 return ldb_oom(ldb);
3969 for (i = 0; i < spn_el->num_values; i++) {
3971 spn = (char *)spn_el->values[i].data;
3973 n_components = count_spn_components(spn_el->values[i]);
3974 if (n_components > 3 || n_components < 2) {
3975 ldb_asprintf_errstring(ldb,
3976 "samldb: spn[%s] invalid with %u components",
3978 talloc_free(tmp_ctx);
3979 return LDB_ERR_CONSTRAINT_VIOLATION;
3982 ret = check_spn_direct_collision(ldb,
3986 if (ret == LDB_ERR_COMPARE_TRUE) {
3987 DBG_INFO("SPN %s re-added to the same object\n", spn);
3988 talloc_free(tmp_ctx);
3991 if (ret != LDB_SUCCESS) {
3992 DBG_ERR("SPN %s failed direct uniqueness check\n", spn);
3993 talloc_free(tmp_ctx);
3997 ret = check_spn_alias_collision(ldb,
4002 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
4003 /* we have no sPNMappings, hence no aliases */
4006 if (ret != LDB_SUCCESS) {
4007 DBG_ERR("SPN %s failed alias uniqueness check\n", spn);
4008 talloc_free(tmp_ctx);
4011 DBG_INFO("SPN %s seems to be unique\n", spn);
4014 talloc_free(tmp_ctx);
4020 /* This trigger adapts the "servicePrincipalName" attributes if the
4021 * "dNSHostName" and/or "sAMAccountName" attribute change(s) */
4022 static int samldb_service_principal_names_change(struct samldb_ctx *ac)
4024 struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
4025 struct ldb_message_element *el = NULL, *el2 = NULL;
4026 struct ldb_message *msg;
4027 const char * const attrs[] = { "servicePrincipalName", NULL };
4028 struct ldb_result *res;
4029 const char *dns_hostname = NULL, *old_dns_hostname = NULL,
4030 *sam_accountname = NULL, *old_sam_accountname = NULL;
4034 el = dsdb_get_single_valued_attr(ac->msg, "dNSHostName",
4035 ac->req->operation);
4036 el2 = dsdb_get_single_valued_attr(ac->msg, "sAMAccountName",
4037 ac->req->operation);
4038 if ((el == NULL) && (el2 == NULL)) {
4039 /* we are not affected */
4043 /* Create a temporary message for fetching the "dNSHostName" */
4045 const char *dns_attrs[] = { "dNSHostName", NULL };
4046 msg = ldb_msg_new(ac->msg);
4048 return ldb_module_oom(ac->module);
4050 ret = ldb_msg_add(msg, el, 0);
4051 if (ret != LDB_SUCCESS) {
4054 dns_hostname = talloc_strdup(ac,
4055 ldb_msg_find_attr_as_string(msg, "dNSHostName", NULL));
4056 if (dns_hostname == NULL) {
4057 return ldb_module_oom(ac->module);
4062 ret = dsdb_module_search_dn(ac->module, ac, &res, ac->msg->dn,
4063 dns_attrs, DSDB_FLAG_NEXT_MODULE, ac->req);
4064 if (ret == LDB_SUCCESS) {
4065 old_dns_hostname = ldb_msg_find_attr_as_string(res->msgs[0], "dNSHostName", NULL);
4069 /* Create a temporary message for fetching the "sAMAccountName" */
4071 char *tempstr, *tempstr2 = NULL;
4072 const char *acct_attrs[] = { "sAMAccountName", NULL };
4074 msg = ldb_msg_new(ac->msg);
4076 return ldb_module_oom(ac->module);
4078 ret = ldb_msg_add(msg, el2, 0);
4079 if (ret != LDB_SUCCESS) {
4082 tempstr = talloc_strdup(ac,
4083 ldb_msg_find_attr_as_string(msg, "sAMAccountName", NULL));
4086 ret = dsdb_module_search_dn(ac->module, ac, &res, ac->msg->dn, acct_attrs,
4087 DSDB_FLAG_NEXT_MODULE, ac->req);
4088 if (ret == LDB_SUCCESS) {
4089 tempstr2 = talloc_strdup(ac,
4090 ldb_msg_find_attr_as_string(res->msgs[0],
4091 "sAMAccountName", NULL));
4095 /* The "sAMAccountName" needs some additional trimming: we need
4096 * to remove the trailing "$"s if they exist. */
4097 if ((tempstr != NULL) && (tempstr[0] != '\0') &&
4098 (tempstr[strlen(tempstr) - 1] == '$')) {
4099 tempstr[strlen(tempstr) - 1] = '\0';
4101 if ((tempstr2 != NULL) && (tempstr2[0] != '\0') &&
4102 (tempstr2[strlen(tempstr2) - 1] == '$')) {
4103 tempstr2[strlen(tempstr2) - 1] = '\0';
4105 sam_accountname = tempstr;
4106 old_sam_accountname = tempstr2;
4109 if (old_dns_hostname == NULL) {
4110 /* we cannot change when the old name is unknown */
4111 dns_hostname = NULL;
4113 if ((old_dns_hostname != NULL) && (dns_hostname != NULL) &&
4114 (strcasecmp_m(old_dns_hostname, dns_hostname) == 0)) {
4115 /* The "dNSHostName" didn't change */
4116 dns_hostname = NULL;
4119 if (old_sam_accountname == NULL) {
4120 /* we cannot change when the old name is unknown */
4121 sam_accountname = NULL;
4123 if ((old_sam_accountname != NULL) && (sam_accountname != NULL) &&
4124 (strcasecmp_m(old_sam_accountname, sam_accountname) == 0)) {
4125 /* The "sAMAccountName" didn't change */
4126 sam_accountname = NULL;
4129 if ((dns_hostname == NULL) && (sam_accountname == NULL)) {
4130 /* Well, there are information missing (old name(s)) or the
4131 * names didn't change. We've nothing to do and can exit here */
4136 * Potential "servicePrincipalName" changes in the same request have
4137 * to be handled before the update (Windows behaviour).
4139 * We extract the SPN changes into a new message and run it through
4140 * the stack from this module, so that it subjects them to the SPN
4141 * checks we have here.
4143 el = ldb_msg_find_element(ac->msg, "servicePrincipalName");
4145 msg = ldb_msg_new(ac->msg);
4147 return ldb_module_oom(ac->module);
4149 msg->dn = ac->msg->dn;
4152 ret = ldb_msg_add(msg, el, el->flags);
4153 if (ret != LDB_SUCCESS) {
4157 ldb_msg_remove_element(ac->msg, el);
4159 el = ldb_msg_find_element(ac->msg,
4160 "servicePrincipalName");
4161 } while (el != NULL);
4163 ret = dsdb_module_modify(ac->module, msg,
4164 DSDB_FLAG_OWN_MODULE, ac->req);
4165 if (ret != LDB_SUCCESS) {
4171 /* Fetch the "servicePrincipalName"s if any */
4172 ret = dsdb_module_search(ac->module, ac, &res, ac->msg->dn, LDB_SCOPE_BASE, attrs,
4173 DSDB_FLAG_NEXT_MODULE, ac->req, NULL);
4174 if (ret != LDB_SUCCESS) {
4177 if ((res->count != 1) || (res->msgs[0]->num_elements > 1)) {
4178 return ldb_operr(ldb);
4181 if (res->msgs[0]->num_elements == 1) {
4183 * Yes, we do have "servicePrincipalName"s. First we update them
4184 * locally, that means we do always substitute the current
4185 * "dNSHostName" with the new one and/or "sAMAccountName"
4186 * without "$" with the new one and then we append the
4187 * modified "servicePrincipalName"s as a message element
4188 * replace to the modification request (Windows behaviour). We
4189 * need also to make sure that the values remain case-
4190 * insensitively unique.
4193 ret = ldb_msg_add_empty(ac->msg, "servicePrincipalName",
4194 LDB_FLAG_MOD_REPLACE, &el);
4195 if (ret != LDB_SUCCESS) {
4199 for (i = 0; i < res->msgs[0]->elements[0].num_values; i++) {
4200 char *old_str, *new_str;
4203 struct ldb_val *vals;
4207 res->msgs[0]->elements[0].values[i].data;
4209 new_str = talloc_strdup(ac->msg,
4210 strtok_r(old_str, "/", &pos));
4211 if (new_str == NULL) {
4212 return ldb_module_oom(ac->module);
4215 while ((tok = strtok_r(NULL, "/", &pos)) != NULL) {
4216 if ((dns_hostname != NULL) &&
4217 (strcasecmp_m(tok, old_dns_hostname) == 0)) {
4220 if ((sam_accountname != NULL) &&
4221 (strcasecmp_m(tok, old_sam_accountname) == 0)) {
4222 tok = sam_accountname;
4225 new_str = talloc_asprintf(ac->msg, "%s/%s",
4227 if (new_str == NULL) {
4228 return ldb_module_oom(ac->module);
4232 /* Uniqueness check */
4233 for (j = 0; (!found) && (j < el->num_values); j++) {
4234 if (strcasecmp_m((char *)el->values[j].data,
4244 * append the new "servicePrincipalName" -
4245 * code derived from ldb_msg_add_value().
4247 * Open coded to make it clear that we must
4248 * append to the MOD_REPLACE el created above.
4250 vals = talloc_realloc(ac->msg, el->values,
4252 el->num_values + 1);
4254 return ldb_module_oom(ac->module);
4257 el->values[el->num_values] = data_blob_string_const(new_str);
4267 /* This checks the "fSMORoleOwner" attributes */
4268 static int samldb_fsmo_role_owner_check(struct samldb_ctx *ac)
4270 struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
4271 const char * const no_attrs[] = { NULL };
4272 struct ldb_message_element *el;
4273 struct ldb_message *tmp_msg;
4274 struct ldb_dn *res_dn;
4275 struct ldb_result *res;
4278 el = dsdb_get_single_valued_attr(ac->msg, "fSMORoleOwner",
4279 ac->req->operation);
4281 /* we are not affected */
4285 /* Create a temporary message for fetching the "fSMORoleOwner" */
4286 tmp_msg = ldb_msg_new(ac->msg);
4287 if (tmp_msg == NULL) {
4288 return ldb_module_oom(ac->module);
4290 ret = ldb_msg_add(tmp_msg, el, 0);
4291 if (ret != LDB_SUCCESS) {
4294 res_dn = ldb_msg_find_attr_as_dn(ldb, ac, tmp_msg, "fSMORoleOwner");
4295 talloc_free(tmp_msg);
4297 if (res_dn == NULL) {
4298 ldb_set_errstring(ldb,
4299 "samldb: 'fSMORoleOwner' attributes have to reference 'nTDSDSA' entries!");
4300 if (ac->req->operation == LDB_ADD) {
4301 return LDB_ERR_CONSTRAINT_VIOLATION;
4303 return LDB_ERR_UNWILLING_TO_PERFORM;
4307 /* Fetched DN has to reference a "nTDSDSA" entry */
4308 ret = dsdb_module_search(ac->module, ac, &res, res_dn, LDB_SCOPE_BASE,
4310 DSDB_FLAG_NEXT_MODULE | DSDB_SEARCH_SHOW_DELETED,
4311 ac->req, "(objectClass=nTDSDSA)");
4312 if (ret != LDB_SUCCESS) {
4315 if (res->count != 1) {
4316 ldb_set_errstring(ldb,
4317 "samldb: 'fSMORoleOwner' attributes have to reference 'nTDSDSA' entries!");
4318 return LDB_ERR_UNWILLING_TO_PERFORM;
4327 * Return zero if the number of zero bits in the address (looking from low to
4328 * high) is equal to or greater than the length minus the mask. Otherwise it
4331 static int check_cidr_zero_bits(uint8_t *address, unsigned int len,
4334 /* <address> is an integer in big-endian form, <len> bits long. All
4335 bits between <mask> and <len> must be zero. */
4337 unsigned int byte_len;
4338 unsigned int byte_mask;
4339 unsigned int bit_mask;
4341 DBG_INFO("Looking at address %02x%02x%02x%02x, mask %u\n",
4342 address[0], address[1], address[2], address[3],
4344 } else if (len == 128){
4345 DBG_INFO("Looking at address "
4346 "%02x%02x-%02x%02x-%02x%02x-%02x%02x-"
4347 "%02x%02x-%02x%02x-%02x%02x-%02x%02x, mask %u\n",
4348 address[0], address[1], address[2], address[3],
4349 address[4], address[5], address[6], address[7],
4350 address[8], address[9], address[10], address[11],
4351 address[12], address[13], address[14], address[15],
4356 DBG_INFO("mask %u is too big (> %u)\n", mask, len);
4360 /* single address subnet.
4361 * In IPv4 all 255s is invalid by the bitmask != address rule
4362 * in MS-ADTS. IPv6 does not suffer.
4365 if (address[0] == 255 &&
4366 address[1] == 255 &&
4367 address[2] == 255 &&
4376 byte_mask = mask / 8;
4378 for (i = byte_len - 1; i > byte_mask; i--){
4379 DBG_DEBUG("checking byte %d %02x\n", i, address[i]);
4380 if (address[i] != 0){
4384 bit_mask = (1 << (8 - (mask & 7))) - 1;
4385 DBG_DEBUG("checking bitmask %02x & %02x overlap %02x\n", bit_mask, address[byte_mask],
4386 bit_mask & address[byte_mask]);
4387 if (address[byte_mask] & bit_mask){
4391 /* According to MS-ADTS, the mask can't exactly equal the bitmask for
4392 * IPv4 (but this is fine for v6). That is 255.255.80.0/17 is bad,
4393 * because the bitmask implied by "/17" is 255.255.80.0.
4395 * The bit_mask used in the previous check is the complement of what
4398 if (len == 32 && address[byte_mask] == (uint8_t)~bit_mask){
4400 for (i = 0; i < byte_mask; i++){
4401 if (address[i] != 255){
4415 static int check_address_roundtrip(const char *address, int family,
4416 const uint8_t *address_bytes,
4417 char *buffer, int buffer_len)
4420 * Check that the address is in the canonical RFC5952 format for IPv6,
4421 * and lacks extra leading zeros for each dotted decimal for IPv4.
4422 * Handily this is what inet_ntop() gives you.
4424 const char *address_redux = inet_ntop(family, address_bytes,
4425 buffer, buffer_len);
4426 if (address_redux == NULL){
4427 DBG_INFO("Address round trip %s failed unexpectedly"
4428 " with errno %d\n", address, errno);
4431 if (strcasecmp(address, address_redux) != 0){
4432 DBG_INFO("Address %s round trips to %s; fail!\n",
4433 address, address_redux);
4434 /* If the address family is IPv6, and the address is in a
4438 if (strchr(address_redux, '.') != NULL){
4439 DEBUG(0, ("The IPv6 address '%s' has the misfortune of "
4440 "lying in a range that was once used for "
4441 "IPv4 embedding (that is, it might also be "
4442 "represented as '%s').\n", address,
4453 * MS-ADTS v20150630 6.1.1.2.2.2.1 Subnet Object, refers to RFC1166 and
4454 * RFC2373. It specifies something seemingly indistinguishable from an RFC4632
4455 * CIDR address range without saying so explicitly. Here we follow the CIDR
4458 * Return 0 on success, -1 on error.
4460 static int verify_cidr(const char *cidr)
4462 char *address = NULL, *slash = NULL;
4463 bool has_colon, has_dot;
4466 uint8_t *address_bytes = NULL;
4467 char *address_redux = NULL;
4468 unsigned int address_len;
4469 TALLOC_CTX *frame = NULL;
4472 DBG_DEBUG("CIDR is %s\n", cidr);
4473 frame = talloc_stackframe();
4474 address = talloc_strdup(frame, cidr);
4475 if (address == NULL){
4479 /* there must be a '/' */
4480 slash = strchr(address, '/');
4484 /* terminate the address for strchr, inet_pton */
4487 mask = smb_strtoul(slash + 1, NULL, 10, &error, SMB_STR_FULL_STR_CONV);
4489 DBG_INFO("Windows does not like the zero mask, "
4490 "so nor do we: %s\n", cidr);
4495 DBG_INFO("CIDR mask is not a proper integer: %s\n", cidr);
4499 address_bytes = talloc_size(frame, sizeof(struct in6_addr));
4500 if (address_bytes == NULL){
4504 address_redux = talloc_size(frame, INET6_ADDRSTRLEN);
4505 if (address_redux == NULL){
4509 DBG_INFO("found address %s, mask %lu\n", address, mask);
4510 has_colon = (strchr(address, ':') == NULL) ? false : true;
4511 has_dot = (strchr(address, '.') == NULL) ? false : true;
4512 if (has_dot && has_colon){
4513 /* This seems to be an IPv4 address embedded in IPv6, which is
4514 icky. We don't support it. */
4515 DBG_INFO("Refusing to consider cidr '%s' with dots and colons\n",
4518 } else if (has_colon){ /* looks like IPv6 */
4519 res = inet_pton(AF_INET6, address, address_bytes);
4521 DBG_INFO("Address in %s fails to parse as IPv6\n", cidr);
4525 if (check_address_roundtrip(address, AF_INET6, address_bytes,
4526 address_redux, INET6_ADDRSTRLEN)){
4529 } else if (has_dot) {
4530 /* looks like IPv4 */
4531 if (strcmp(address, "0.0.0.0") == 0){
4532 DBG_INFO("Windows does not like the zero IPv4 address, "
4536 res = inet_pton(AF_INET, address, address_bytes);
4538 DBG_INFO("Address in %s fails to parse as IPv4\n", cidr);
4543 if (check_address_roundtrip(address, AF_INET, address_bytes,
4544 address_redux, INET_ADDRSTRLEN)){
4548 /* This doesn't look like an IP address at all. */
4552 ret = check_cidr_zero_bits(address_bytes, address_len, mask);
4561 static int samldb_verify_subnet(struct samldb_ctx *ac, struct ldb_dn *dn)
4563 struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
4564 const char *cidr = NULL;
4565 const struct ldb_val *rdn_value = NULL;
4567 rdn_value = ldb_dn_get_rdn_val(dn);
4568 if (rdn_value == NULL) {
4569 ldb_set_errstring(ldb, "samldb: ldb_dn_get_rdn_val "
4571 return LDB_ERR_UNWILLING_TO_PERFORM;
4574 cidr = ldb_dn_escape_value(ac, *rdn_value);
4575 DBG_INFO("looking at cidr '%s'\n", cidr);
4577 ldb_set_errstring(ldb,
4578 "samldb: adding an empty subnet cidr seems wrong");
4579 return LDB_ERR_UNWILLING_TO_PERFORM;
4582 if (verify_cidr(cidr)){
4583 ldb_set_errstring(ldb,
4584 "samldb: subnet value is invalid");
4585 return LDB_ERR_INVALID_DN_SYNTAX;
4591 static char *refer_if_rodc(struct ldb_context *ldb, struct ldb_request *req,
4595 struct loadparm_context *lp_ctx;
4600 if (ldb_request_get_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID) ||
4601 ldb_request_get_control(req, DSDB_CONTROL_DBCHECK_MODIFY_RO_REPLICA)) {
4605 ret = samdb_rodc(ldb, &rodc);
4606 if (ret != LDB_SUCCESS) {
4607 DEBUG(4, (__location__ ": unable to tell if we are an RODC\n"));
4612 const char *domain = NULL;
4613 struct ldb_dn *fsmo_role_dn;
4614 struct ldb_dn *role_owner_dn;
4615 ldb_set_errstring(ldb, "RODC modify is forbidden!");
4616 lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
4617 struct loadparm_context);
4619 err = dsdb_get_fsmo_role_info(req, ldb, DREPL_PDC_MASTER,
4620 &fsmo_role_dn, &role_owner_dn);
4621 if (W_ERROR_IS_OK(err)) {
4622 struct ldb_dn *server_dn = ldb_dn_copy(req, role_owner_dn);
4623 if (server_dn != NULL) {
4624 ldb_dn_remove_child_components(server_dn, 1);
4626 domain = samdb_dn_to_dnshostname(ldb, req,
4630 if (domain == NULL) {
4631 domain = lpcfg_dnsdomain(lp_ctx);
4633 referral = talloc_asprintf(req,
4636 ldb_dn_get_linearized(dn));
4644 * Restrict all access to sensitive attributes.
4646 * We don't want to even inspect the values, so we can use the same
4647 * routine for ADD and MODIFY.
4651 static int samldb_check_sensitive_attributes(struct samldb_ctx *ac)
4653 struct ldb_message_element *el = NULL;
4654 struct security_token *user_token = NULL;
4657 if (dsdb_module_am_system(ac->module)) {
4661 user_token = acl_user_token(ac->module);
4662 if (user_token == NULL) {
4663 return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
4666 el = ldb_msg_find_element(ac->msg, "sidHistory");
4669 * sidHistory is restricted to the (not implemented
4670 * yet in Samba) DsAddSidHistory call (direct LDB access is
4671 * as SYSTEM so will bypass this).
4673 * If you want to modify this, say to merge domains,
4674 * directly modify the sam.ldb as root.
4676 ldb_asprintf_errstring(ldb_module_get_ctx(ac->module),
4678 "(entry %s) cannot be created "
4679 "or changed over LDAP!",
4680 ldb_dn_get_linearized(ac->msg->dn));
4681 return LDB_ERR_UNWILLING_TO_PERFORM;
4684 el = ldb_msg_find_element(ac->msg, "msDS-SecondaryKrbTgtNumber");
4686 struct security_descriptor *domain_sd;
4687 const struct dsdb_class *objectclass = NULL;
4689 * msDS-SecondaryKrbTgtNumber allows the creator to
4690 * become an RODC, this is trusted as an RODC
4693 ret = samldb_get_domain_secdesc_and_oc(ac, &domain_sd, &objectclass);
4694 if (ret != LDB_SUCCESS) {
4697 ret = acl_check_extended_right(ac,
4703 GUID_DRS_DS_INSTALL_REPLICA,
4704 SEC_ADS_CONTROL_ACCESS,
4706 if (ret != LDB_SUCCESS) {
4707 ldb_asprintf_errstring(ldb_module_get_ctx(ac->module),
4708 "msDS-SecondaryKrbTgtNumber "
4709 "(entry %s) cannot be created "
4710 "or changed without "
4711 "DS-Install-Replica extended right!",
4712 ldb_dn_get_linearized(ac->msg->dn));
4717 el = ldb_msg_find_element(ac->msg, "msDS-AllowedToDelegateTo");
4720 * msDS-AllowedToDelegateTo is incredibly powerful,
4721 * given that it allows a server to become ANY USER on
4722 * the target server only listed by SPN so needs to be
4723 * protected just as the userAccountControl
4724 * UF_TRUSTED_FOR_DELEGATION is.
4727 bool have_priv = security_token_has_privilege(user_token,
4728 SEC_PRIV_ENABLE_DELEGATION);
4729 if (have_priv == false) {
4730 ldb_asprintf_errstring(ldb_module_get_ctx(ac->module),
4731 "msDS-AllowedToDelegateTo "
4732 "(entry %s) cannot be created "
4733 "or changed without SePrivEnableDelegation!",
4734 ldb_dn_get_linearized(ac->msg->dn));
4735 return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
4741 static int samldb_add(struct ldb_module *module, struct ldb_request *req)
4743 struct ldb_context *ldb;
4744 struct samldb_ctx *ac;
4745 struct ldb_message_element *el;
4747 char *referral = NULL;
4749 ldb = ldb_module_get_ctx(module);
4750 ldb_debug(ldb, LDB_DEBUG_TRACE, "samldb_add\n");
4752 /* do not manipulate our control entries */
4753 if (ldb_dn_is_special(req->op.add.message->dn)) {
4754 return ldb_next_request(module, req);
4757 referral = refer_if_rodc(ldb, req, req->op.add.message->dn);
4758 if (referral != NULL) {
4759 ret = ldb_module_send_referral(req, referral);
4763 el = ldb_msg_find_element(req->op.add.message, "userParameters");
4764 if (el != NULL && ldb_req_is_untrusted(req)) {
4765 const char *reason = "samldb_add: "
4766 "setting userParameters is not supported over LDAP, "
4767 "see https://bugzilla.samba.org/show_bug.cgi?id=8077";
4768 ldb_debug(ldb, LDB_DEBUG_WARNING, "%s", reason);
4769 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION, reason);
4772 ac = samldb_ctx_init(module, req);
4774 return ldb_operr(ldb);
4777 /* build the new msg */
4778 ac->msg = ldb_msg_copy_shallow(ac, req->op.add.message);
4779 if (ac->msg == NULL) {
4781 ldb_debug(ldb, LDB_DEBUG_FATAL,
4782 "samldb_add: ldb_msg_copy_shallow failed!\n");
4783 return ldb_operr(ldb);
4786 ret = samldb_check_sensitive_attributes(ac);
4787 if (ret != LDB_SUCCESS) {
4792 el = ldb_msg_find_element(ac->msg, "fSMORoleOwner");
4794 ret = samldb_fsmo_role_owner_check(ac);
4795 if (ret != LDB_SUCCESS) {
4800 if (samdb_find_attribute(ldb, ac->msg,
4801 "objectclass", "user") != NULL) {
4802 ac->type = SAMLDB_TYPE_USER;
4804 ret = samldb_prim_group_trigger(ac);
4805 if (ret != LDB_SUCCESS) {
4809 ret = samldb_objectclass_trigger(ac);
4810 if (ret != LDB_SUCCESS) {
4814 return samldb_fill_object(ac);
4817 if (samdb_find_attribute(ldb, ac->msg,
4818 "objectclass", "group") != NULL) {
4819 ac->type = SAMLDB_TYPE_GROUP;
4821 ret = samldb_objectclass_trigger(ac);
4822 if (ret != LDB_SUCCESS) {
4826 return samldb_fill_object(ac);
4829 /* perhaps a foreignSecurityPrincipal? */
4830 if (samdb_find_attribute(ldb, ac->msg,
4832 "foreignSecurityPrincipal") != NULL) {
4833 return samldb_fill_foreignSecurityPrincipal_object(ac);
4836 if (samdb_find_attribute(ldb, ac->msg,
4837 "objectclass", "classSchema") != NULL) {
4838 ac->type = SAMLDB_TYPE_CLASS;
4840 /* If in provision, these checks are too slow to do */
4841 if (!ldb_request_get_control(req, DSDB_CONTROL_SKIP_DUPLICATES_CHECK_OID)) {
4842 ret = samldb_schema_governsid_valid_check(ac);
4843 if (ret != LDB_SUCCESS) {
4848 ret = samldb_schema_ldapdisplayname_valid_check(ac);
4849 if (ret != LDB_SUCCESS) {
4853 ret = samldb_schema_info_update(ac);
4854 if (ret != LDB_SUCCESS) {
4859 return samldb_fill_object(ac);
4862 if (samdb_find_attribute(ldb, ac->msg,
4863 "objectclass", "attributeSchema") != NULL) {
4864 ac->type = SAMLDB_TYPE_ATTRIBUTE;
4866 /* If in provision, these checks are too slow to do */
4867 if (!ldb_request_get_control(req, DSDB_CONTROL_SKIP_DUPLICATES_CHECK_OID)) {
4868 ret = samldb_schema_attributeid_valid_check(ac);
4869 if (ret != LDB_SUCCESS) {
4873 ret = samldb_schema_add_handle_linkid(ac);
4874 if (ret != LDB_SUCCESS) {
4878 ret = samldb_schema_add_handle_mapiid(ac);
4879 if (ret != LDB_SUCCESS) {
4884 ret = samldb_schema_ldapdisplayname_valid_check(ac);
4885 if (ret != LDB_SUCCESS) {
4889 ret = samldb_schema_info_update(ac);
4890 if (ret != LDB_SUCCESS) {
4895 return samldb_fill_object(ac);
4899 el = ldb_msg_find_element(ac->msg, "servicePrincipalName");
4902 * We need to check whether the SPN collides with an existing
4903 * one (anywhere) including via aliases.
4905 ret = samldb_spn_uniqueness_check(ac, el);
4906 if (ret != LDB_SUCCESS) {
4911 if (samdb_find_attribute(ldb, ac->msg,
4912 "objectclass", "subnet") != NULL) {
4913 ret = samldb_verify_subnet(ac, ac->msg->dn);
4914 if (ret != LDB_SUCCESS) {
4918 /* We are just checking the value is valid, and there are no
4919 values to fill in. */
4924 /* nothing matched, go on */
4925 return ldb_next_request(module, req);
4929 static int samldb_modify(struct ldb_module *module, struct ldb_request *req)
4931 struct ldb_context *ldb;
4932 struct samldb_ctx *ac;
4933 struct ldb_message_element *el, *el2;
4934 struct ldb_control *is_undelete;
4935 bool modified = false;
4938 if (ldb_dn_is_special(req->op.mod.message->dn)) {
4939 /* do not manipulate our control entries */
4940 return ldb_next_request(module, req);
4943 ldb = ldb_module_get_ctx(module);
4946 * we are going to need some special handling if in Undelete call.
4947 * Since tombstone_reanimate module will restore certain attributes,
4948 * we need to relax checks for: sAMAccountType, primaryGroupID
4950 is_undelete = ldb_request_get_control(req, DSDB_CONTROL_RESTORE_TOMBSTONE_OID);
4952 /* make sure that "objectSid" is not specified */
4953 el = ldb_msg_find_element(req->op.mod.message, "objectSid");
4955 if (ldb_request_get_control(req, LDB_CONTROL_PROVISION_OID) == NULL) {
4956 ldb_set_errstring(ldb,
4957 "samldb: objectSid must not be specified!");
4958 return LDB_ERR_UNWILLING_TO_PERFORM;
4961 if (is_undelete == NULL) {
4962 /* make sure that "sAMAccountType" is not specified */
4963 el = ldb_msg_find_element(req->op.mod.message, "sAMAccountType");
4965 ldb_set_errstring(ldb,
4966 "samldb: sAMAccountType must not be specified!");
4967 return LDB_ERR_UNWILLING_TO_PERFORM;
4970 /* make sure that "isCriticalSystemObject" is not specified */
4971 el = ldb_msg_find_element(req->op.mod.message, "isCriticalSystemObject");
4973 if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID) == NULL) {
4974 ldb_set_errstring(ldb,
4975 "samldb: isCriticalSystemObject must not be specified!");
4976 return LDB_ERR_UNWILLING_TO_PERFORM;
4980 /* msDS-IntId is not allowed to be modified
4981 * except when modification comes from replication */
4982 if (ldb_msg_find_element(req->op.mod.message, "msDS-IntId")) {
4983 if (!ldb_request_get_control(req,
4984 DSDB_CONTROL_REPLICATED_UPDATE_OID)) {
4985 return LDB_ERR_CONSTRAINT_VIOLATION;
4989 el = ldb_msg_find_element(req->op.mod.message, "userParameters");
4990 if (el != NULL && ldb_req_is_untrusted(req)) {
4991 const char *reason = "samldb: "
4992 "setting userParameters is not supported over LDAP, "
4993 "see https://bugzilla.samba.org/show_bug.cgi?id=8077";
4994 ldb_debug(ldb, LDB_DEBUG_WARNING, "%s", reason);
4995 return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION, reason);
4998 ac = samldb_ctx_init(module, req);
5000 return ldb_operr(ldb);
5003 /* build the new msg */
5004 ac->msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
5005 if (ac->msg == NULL) {
5007 ldb_debug(ldb, LDB_DEBUG_FATAL,
5008 "samldb_modify: ldb_msg_copy_shallow failed!\n");
5009 return ldb_operr(ldb);
5012 ret = samldb_check_sensitive_attributes(ac);
5013 if (ret != LDB_SUCCESS) {
5018 if (is_undelete == NULL) {
5019 el = ldb_msg_find_element(ac->msg, "primaryGroupID");
5021 ret = samldb_prim_group_trigger(ac);
5022 if (ret != LDB_SUCCESS) {
5028 el = ldb_msg_find_element(ac->msg, "userAccountControl");
5031 ret = samldb_user_account_control_change(ac);
5032 if (ret != LDB_SUCCESS) {
5037 el = ldb_msg_find_element(ac->msg, "pwdLastSet");
5040 ret = samldb_pwd_last_set_change(ac);
5041 if (ret != LDB_SUCCESS) {
5046 el = ldb_msg_find_element(ac->msg, "lockoutTime");
5049 ret = samldb_lockout_time(ac);
5050 if (ret != LDB_SUCCESS) {
5055 el = ldb_msg_find_element(ac->msg, "groupType");
5058 ret = samldb_group_type_change(ac);
5059 if (ret != LDB_SUCCESS) {
5064 el = ldb_msg_find_element(ac->msg, "sAMAccountName");
5066 uint32_t user_account_control;
5067 struct ldb_result *res = NULL;
5068 const char * const attrs[] = { "userAccountControl",
5071 ret = dsdb_module_search_dn(ac->module,
5076 DSDB_FLAG_NEXT_MODULE | DSDB_SEARCH_SHOW_DELETED,
5078 if (ret != LDB_SUCCESS) {
5081 user_account_control
5082 = ldb_msg_find_attr_as_uint(res->msgs[0],
5083 "userAccountControl",
5086 if ((user_account_control
5087 & UF_TRUST_ACCOUNT_MASK) != 0) {
5088 ac->need_trailing_dollar = true;
5090 } else if (samdb_find_attribute(ldb,
5095 ac->need_trailing_dollar = true;
5098 ret = samldb_sam_accountname_valid_check(ac);
5099 if (ret != LDB_SUCCESS) {
5104 el = ldb_msg_find_element(ac->msg, "userPrincipalName");
5106 ret = samldb_sam_account_upn_clash(ac);
5107 if (ret != LDB_SUCCESS) {
5113 el = ldb_msg_find_element(ac->msg, "ldapDisplayName");
5115 ret = samldb_schema_ldapdisplayname_valid_check(ac);
5116 if (ret != LDB_SUCCESS) {
5121 el = ldb_msg_find_element(ac->msg, "attributeID");
5123 ldb_asprintf_errstring(ldb_module_get_ctx(ac->module),
5124 "Once set, attributeID values may not be modified");
5125 return LDB_ERR_CONSTRAINT_VIOLATION;
5128 el = ldb_msg_find_element(ac->msg, "governsID");
5130 ldb_asprintf_errstring(ldb_module_get_ctx(ac->module),
5131 "Once set, governsID values may not be modified");
5132 return LDB_ERR_CONSTRAINT_VIOLATION;
5135 el = ldb_msg_find_element(ac->msg, "member");
5137 struct ldb_control *fix_link_sid_ctrl = NULL;
5139 fix_link_sid_ctrl = ldb_request_get_control(ac->req,
5140 DSDB_CONTROL_DBCHECK_FIX_LINK_DN_SID);
5141 if (fix_link_sid_ctrl == NULL) {
5142 ret = samldb_member_check(ac);
5143 if (ret != LDB_SUCCESS) {
5149 el = ldb_msg_find_element(ac->msg, "description");
5151 ret = samldb_description_check(ac, &modified);
5152 if (ret != LDB_SUCCESS) {
5157 el = ldb_msg_find_element(ac->msg, "dNSHostName");
5158 el2 = ldb_msg_find_element(ac->msg, "sAMAccountName");
5159 if ((el != NULL) || (el2 != NULL)) {
5162 * samldb_service_principal_names_change() might add SPN
5163 * changes to the request, so this must come before the SPN
5164 * uniqueness check below.
5166 * Note we ALSO have to do the SPN uniqueness check inside
5167 * samldb_service_principal_names_change(), because it does a
5168 * subrequest to do requested SPN modifications *before* its
5169 * automatic ones are added.
5171 ret = samldb_service_principal_names_change(ac);
5172 if (ret != LDB_SUCCESS) {
5177 el = ldb_msg_find_element(ac->msg, "servicePrincipalName");
5180 * We need to check whether the SPN collides with an existing
5181 * one (anywhere) including via aliases.
5184 ret = samldb_spn_uniqueness_check(ac, el);
5185 if (ret != LDB_SUCCESS) {
5190 el = ldb_msg_find_element(ac->msg, "fSMORoleOwner");
5192 ret = samldb_fsmo_role_owner_check(ac);
5193 if (ret != LDB_SUCCESS) {
5199 struct ldb_request *child_req;
5201 /* Now perform the real modifications as a child request */
5202 ret = ldb_build_mod_req(&child_req, ldb, ac,
5205 req, dsdb_next_callback,
5207 LDB_REQ_SET_LOCATION(child_req);
5208 if (ret != LDB_SUCCESS) {
5212 return ldb_next_request(module, child_req);
5217 /* no change which interests us, go on */
5218 return ldb_next_request(module, req);
5223 static int samldb_prim_group_users_check(struct samldb_ctx *ac)
5225 struct ldb_context *ldb;
5226 struct dom_sid *sid;
5230 struct ldb_result *res = NULL;
5231 struct ldb_result *res_users = NULL;
5232 const char * const attrs[] = { "objectSid", "isDeleted", NULL };
5233 const char * const noattrs[] = { NULL };
5235 ldb = ldb_module_get_ctx(ac->module);
5237 /* Finds out the SID/RID of the SAM object */
5238 ret = dsdb_module_search_dn(ac->module, ac, &res, ac->req->op.del.dn,
5240 DSDB_FLAG_NEXT_MODULE | DSDB_SEARCH_SHOW_DELETED,
5242 if (ret != LDB_SUCCESS) {
5246 if (ldb_msg_check_string_attribute(res->msgs[0], "isDeleted", "TRUE")) {
5250 sid = samdb_result_dom_sid(ac, res->msgs[0], "objectSid");
5252 /* No SID - it might not be a SAM object - therefore ok */
5255 status = dom_sid_split_rid(ac, sid, NULL, &rid);
5256 if (!NT_STATUS_IS_OK(status)) {
5257 return ldb_operr(ldb);
5260 /* Special object (security principal?) */
5263 /* do not allow deletion of well-known sids */
5264 if (rid < DSDB_SAMDB_MINIMUM_ALLOWED_RID &&
5265 (ldb_request_get_control(ac->req, LDB_CONTROL_RELAX_OID) == NULL)) {
5266 return LDB_ERR_OTHER;
5269 /* Deny delete requests from groups which are primary ones */
5270 ret = dsdb_module_search(ac->module, ac, &res_users,
5271 ldb_get_default_basedn(ldb),
5272 LDB_SCOPE_SUBTREE, noattrs,
5273 DSDB_FLAG_NEXT_MODULE,
5275 "(&(primaryGroupID=%u)(objectClass=user))", rid);
5276 if (ret != LDB_SUCCESS) {
5279 if (res_users->count > 0) {
5280 ldb_asprintf_errstring(ldb_module_get_ctx(ac->module),
5281 "Refusing to delete %s, as it "
5282 "is still the primaryGroupID "
5284 ldb_dn_get_linearized(res->msgs[0]->dn),
5288 * Yes, this seems very wrong, but we have a test
5289 * for this exact error code in sam.py
5291 return LDB_ERR_ENTRY_ALREADY_EXISTS;
5297 static int samldb_delete(struct ldb_module *module, struct ldb_request *req)
5299 struct samldb_ctx *ac;
5300 char *referral = NULL;
5302 struct ldb_context *ldb;
5304 if (ldb_dn_is_special(req->op.del.dn)) {
5305 /* do not manipulate our control entries */
5306 return ldb_next_request(module, req);
5309 ldb = ldb_module_get_ctx(module);
5311 referral = refer_if_rodc(ldb, req, req->op.del.dn);
5312 if (referral != NULL) {
5313 ret = ldb_module_send_referral(req, referral);
5317 ac = samldb_ctx_init(module, req);
5319 return ldb_operr(ldb_module_get_ctx(module));
5322 ret = samldb_prim_group_users_check(ac);
5323 if (ret != LDB_SUCCESS) {
5329 return ldb_next_request(module, req);
5334 static int check_rename_constraints(struct ldb_message *msg,
5335 struct samldb_ctx *ac,
5336 struct ldb_dn *olddn, struct ldb_dn *newdn)
5338 struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
5339 struct ldb_dn *dn1, *dn2, *nc_root;
5340 int32_t systemFlags;
5341 bool move_op = false;
5342 bool rename_op = false;
5345 /* Skip the checks if old and new DN are the same, or if we have the
5346 * relax control specified or if the returned objects is already
5347 * deleted and needs only to be moved for consistency. */
5349 if (ldb_dn_compare(olddn, newdn) == 0) {
5352 if (ldb_request_get_control(ac->req, LDB_CONTROL_RELAX_OID) != NULL) {
5356 if (ldb_msg_find_attr_as_bool(msg, "isDeleted", false)) {
5358 * check originating request if we are supposed
5359 * to "see" this record in first place.
5361 if (ldb_request_get_control(ac->req, LDB_CONTROL_SHOW_DELETED_OID) == NULL) {
5362 return LDB_ERR_NO_SUCH_OBJECT;
5364 return LDB_ERR_UNWILLING_TO_PERFORM;
5367 /* Objects under CN=System */
5369 dn1 = ldb_dn_copy(ac, ldb_get_default_basedn(ldb));
5370 if (dn1 == NULL) return ldb_oom(ldb);
5372 if ( ! ldb_dn_add_child_fmt(dn1, "CN=System")) {
5374 return LDB_ERR_OPERATIONS_ERROR;
5377 if ((ldb_dn_compare_base(dn1, olddn) == 0) &&
5378 (ldb_dn_compare_base(dn1, newdn) != 0)) {
5380 ldb_asprintf_errstring(ldb,
5381 "subtree_rename: Cannot move/rename %s. Objects under CN=System have to stay under it!",
5382 ldb_dn_get_linearized(olddn));
5383 return LDB_ERR_OTHER;
5390 if ((samdb_find_attribute(ldb, msg, "objectClass", "secret") != NULL) ||
5391 (samdb_find_attribute(ldb, msg, "objectClass", "trustedDomain") != NULL)) {
5392 ldb_asprintf_errstring(ldb,
5393 "subtree_rename: Cannot move/rename %s. It's an LSA-specific object!",
5394 ldb_dn_get_linearized(olddn));
5395 return LDB_ERR_UNWILLING_TO_PERFORM;
5398 /* subnet objects */
5399 if (samdb_find_attribute(ldb, msg, "objectclass", "subnet") != NULL) {
5400 ret = samldb_verify_subnet(ac, newdn);
5401 if (ret != LDB_SUCCESS) {
5408 dn1 = ldb_dn_get_parent(ac, olddn);
5409 if (dn1 == NULL) return ldb_oom(ldb);
5410 dn2 = ldb_dn_get_parent(ac, newdn);
5411 if (dn2 == NULL) return ldb_oom(ldb);
5413 if (ldb_dn_compare(dn1, dn2) == 0) {
5422 systemFlags = ldb_msg_find_attr_as_int(msg, "systemFlags", 0);
5424 /* Fetch name context */
5426 ret = dsdb_find_nc_root(ldb, ac, olddn, &nc_root);
5427 if (ret != LDB_SUCCESS) {
5431 if (ldb_dn_compare(nc_root, ldb_get_schema_basedn(ldb)) == 0) {
5433 ldb_asprintf_errstring(ldb,
5434 "subtree_rename: Cannot move %s within schema partition",
5435 ldb_dn_get_linearized(olddn));
5436 return LDB_ERR_UNWILLING_TO_PERFORM;
5439 (systemFlags & SYSTEM_FLAG_SCHEMA_BASE_OBJECT) != 0) {
5440 ldb_asprintf_errstring(ldb,
5441 "subtree_rename: Cannot rename %s within schema partition",
5442 ldb_dn_get_linearized(olddn));
5443 return LDB_ERR_UNWILLING_TO_PERFORM;
5445 } else if (ldb_dn_compare(nc_root, ldb_get_config_basedn(ldb)) == 0) {
5447 (systemFlags & SYSTEM_FLAG_CONFIG_ALLOW_MOVE) == 0) {
5448 /* Here we have to do more: control the
5449 * "ALLOW_LIMITED_MOVE" flag. This means that the
5450 * grand-grand-parents of two objects have to be equal
5451 * in order to perform the move (this is used for
5452 * moving "server" objects in the "sites" container). */
5454 systemFlags & SYSTEM_FLAG_CONFIG_ALLOW_LIMITED_MOVE;
5457 dn1 = ldb_dn_copy(ac, olddn);
5458 if (dn1 == NULL) return ldb_oom(ldb);
5459 dn2 = ldb_dn_copy(ac, newdn);
5460 if (dn2 == NULL) return ldb_oom(ldb);
5462 limited_move &= ldb_dn_remove_child_components(dn1, 3);
5463 limited_move &= ldb_dn_remove_child_components(dn2, 3);
5464 limited_move &= ldb_dn_compare(dn1, dn2) == 0;
5471 && ldb_request_get_control(ac->req, DSDB_CONTROL_RESTORE_TOMBSTONE_OID) == NULL) {
5472 ldb_asprintf_errstring(ldb,
5473 "subtree_rename: Cannot move %s to %s in config partition",
5474 ldb_dn_get_linearized(olddn), ldb_dn_get_linearized(newdn));
5475 return LDB_ERR_UNWILLING_TO_PERFORM;
5479 (systemFlags & SYSTEM_FLAG_CONFIG_ALLOW_RENAME) == 0) {
5480 ldb_asprintf_errstring(ldb,
5481 "subtree_rename: Cannot rename %s to %s within config partition",
5482 ldb_dn_get_linearized(olddn), ldb_dn_get_linearized(newdn));
5483 return LDB_ERR_UNWILLING_TO_PERFORM;
5485 } else if (ldb_dn_compare(nc_root, ldb_get_default_basedn(ldb)) == 0) {
5487 (systemFlags & SYSTEM_FLAG_DOMAIN_DISALLOW_MOVE) != 0) {
5488 ldb_asprintf_errstring(ldb,
5489 "subtree_rename: Cannot move %s to %s - DISALLOW_MOVE set",
5490 ldb_dn_get_linearized(olddn), ldb_dn_get_linearized(newdn));
5491 return LDB_ERR_UNWILLING_TO_PERFORM;
5494 (systemFlags & SYSTEM_FLAG_DOMAIN_DISALLOW_RENAME) != 0) {
5495 ldb_asprintf_errstring(ldb,
5496 "subtree_rename: Cannot rename %s to %s - DISALLOW_RENAME set",
5497 ldb_dn_get_linearized(olddn), ldb_dn_get_linearized(newdn));
5498 return LDB_ERR_UNWILLING_TO_PERFORM;
5502 talloc_free(nc_root);
5508 static int samldb_rename_search_base_callback(struct ldb_request *req,
5509 struct ldb_reply *ares)
5511 struct samldb_ctx *ac;
5514 ac = talloc_get_type(req->context, struct samldb_ctx);
5517 return ldb_module_done(ac->req, NULL, NULL,
5518 LDB_ERR_OPERATIONS_ERROR);
5520 if (ares->error != LDB_SUCCESS) {
5521 return ldb_module_done(ac->req, ares->controls,
5522 ares->response, ares->error);
5525 switch (ares->type) {
5526 case LDB_REPLY_ENTRY:
5528 * This is the root entry of the originating move
5529 * respectively rename request. It has been already
5530 * stored in the list using "subtree_rename_search()".
5531 * Only this one is subject to constraint checking.
5533 ret = check_rename_constraints(ares->message, ac,
5534 ac->req->op.rename.olddn,
5535 ac->req->op.rename.newdn);
5536 if (ret != LDB_SUCCESS) {
5537 return ldb_module_done(ac->req, NULL, NULL,
5542 case LDB_REPLY_REFERRAL:
5546 case LDB_REPLY_DONE:
5549 * Great, no problem with the rename, so go ahead as
5550 * if we never were here
5552 ret = ldb_next_request(ac->module, ac->req);
5563 static int samldb_rename(struct ldb_module *module, struct ldb_request *req)
5565 struct ldb_context *ldb;
5566 static const char * const attrs[] = { "objectClass", "systemFlags",
5567 "isDeleted", NULL };
5568 struct ldb_request *search_req;
5569 struct samldb_ctx *ac;
5572 if (ldb_dn_is_special(req->op.rename.olddn)) { /* do not manipulate our control entries */
5573 return ldb_next_request(module, req);
5576 ldb = ldb_module_get_ctx(module);
5578 ac = samldb_ctx_init(module, req);
5580 return ldb_oom(ldb);
5583 ret = ldb_build_search_req(&search_req, ldb, ac,
5584 req->op.rename.olddn,
5590 samldb_rename_search_base_callback,
5592 LDB_REQ_SET_LOCATION(search_req);
5593 if (ret != LDB_SUCCESS) {
5597 ret = ldb_request_add_control(search_req, LDB_CONTROL_SHOW_RECYCLED_OID,
5599 if (ret != LDB_SUCCESS) {
5603 return ldb_next_request(ac->module, search_req);
5608 static int samldb_extended_allocate_rid_pool(struct ldb_module *module, struct ldb_request *req)
5610 struct ldb_context *ldb = ldb_module_get_ctx(module);
5611 struct dsdb_fsmo_extended_op *exop;
5614 exop = talloc_get_type(req->op.extended.data,
5615 struct dsdb_fsmo_extended_op);
5617 ldb_set_errstring(ldb,
5618 "samldb_extended_allocate_rid_pool: invalid extended data");
5619 return LDB_ERR_PROTOCOL_ERROR;
5622 ret = ridalloc_allocate_rid_pool_fsmo(module, exop, req);
5623 if (ret != LDB_SUCCESS) {
5627 return ldb_module_done(req, NULL, NULL, LDB_SUCCESS);
5630 static int samldb_extended_allocate_rid(struct ldb_module *module, struct ldb_request *req)
5632 struct ldb_context *ldb = ldb_module_get_ctx(module);
5633 struct dsdb_extended_allocate_rid *exop;
5636 exop = talloc_get_type(req->op.extended.data,
5637 struct dsdb_extended_allocate_rid);
5639 ldb_set_errstring(ldb,
5640 "samldb_extended_allocate_rid: invalid extended data");
5641 return LDB_ERR_PROTOCOL_ERROR;
5644 ret = ridalloc_allocate_rid(module, &exop->rid, req);
5645 if (ret != LDB_SUCCESS) {
5649 return ldb_module_done(req, NULL, NULL, LDB_SUCCESS);
5652 static int samldb_extended_create_own_rid_set(struct ldb_module *module, struct ldb_request *req)
5654 struct ldb_context *ldb = ldb_module_get_ctx(module);
5658 if (req->op.extended.data != NULL) {
5659 ldb_set_errstring(ldb,
5660 "samldb_extended_create_own_rid_set: invalid extended data (should be NULL)");
5661 return LDB_ERR_PROTOCOL_ERROR;
5664 ret = ridalloc_create_own_rid_set(module, req,
5666 if (ret != LDB_SUCCESS) {
5670 return ldb_module_done(req, NULL, NULL, LDB_SUCCESS);
5673 static int samldb_extended(struct ldb_module *module, struct ldb_request *req)
5675 if (strcmp(req->op.extended.oid, DSDB_EXTENDED_ALLOCATE_RID_POOL) == 0) {
5676 return samldb_extended_allocate_rid_pool(module, req);
5679 if (strcmp(req->op.extended.oid, DSDB_EXTENDED_ALLOCATE_RID) == 0) {
5680 return samldb_extended_allocate_rid(module, req);
5683 if (strcmp(req->op.extended.oid, DSDB_EXTENDED_CREATE_OWN_RID_SET) == 0) {
5684 return samldb_extended_create_own_rid_set(module, req);
5687 return ldb_next_request(module, req);
5691 static const struct ldb_module_ops ldb_samldb_module_ops = {
5694 .modify = samldb_modify,
5695 .del = samldb_delete,
5696 .rename = samldb_rename,
5697 .extended = samldb_extended
5701 int ldb_samldb_module_init(const char *version)
5703 LDB_MODULE_CHECK_VERSION(version);
5704 return ldb_register_module(&ldb_samldb_module_ops);