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