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