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 1205517 : static struct samldb_ctx *samldb_ctx_init(struct ldb_module *module,
96 : struct ldb_request *req)
97 : {
98 105289 : struct ldb_context *ldb;
99 105289 : struct samldb_ctx *ac;
100 :
101 1205517 : ldb = ldb_module_get_ctx(module);
102 :
103 1205517 : ac = talloc_zero(req, struct samldb_ctx);
104 1205517 : if (ac == NULL) {
105 0 : ldb_oom(ldb);
106 0 : return NULL;
107 : }
108 :
109 1205517 : ac->module = module;
110 1205517 : ac->req = req;
111 :
112 1205517 : return ac;
113 : }
114 :
115 366670 : static int samldb_add_step(struct samldb_ctx *ac, samldb_step_fn_t fn)
116 : {
117 47778 : struct samldb_step *step, *stepper;
118 :
119 366670 : step = talloc_zero(ac, struct samldb_step);
120 366670 : if (step == NULL) {
121 0 : return ldb_oom(ldb_module_get_ctx(ac->module));
122 : }
123 :
124 366670 : step->fn = fn;
125 :
126 366670 : if (ac->steps == NULL) {
127 260661 : ac->steps = step;
128 260661 : ac->curstep = step;
129 : } else {
130 106009 : if (ac->curstep == NULL)
131 0 : return ldb_operr(ldb_module_get_ctx(ac->module));
132 139729 : for (stepper = ac->curstep; stepper->next != NULL;
133 33548 : stepper = stepper->next);
134 106009 : stepper->next = step;
135 : }
136 :
137 318892 : return LDB_SUCCESS;
138 : }
139 :
140 260546 : static int samldb_first_step(struct samldb_ctx *ac)
141 : {
142 260546 : if (ac->steps == NULL) {
143 0 : return ldb_operr(ldb_module_get_ctx(ac->module));
144 : }
145 :
146 260546 : ac->curstep = ac->steps;
147 260546 : return ac->curstep->fn(ac);
148 : }
149 :
150 366315 : static int samldb_next_step(struct samldb_ctx *ac)
151 : {
152 366315 : if (ac->curstep->next) {
153 105969 : ac->curstep = ac->curstep->next;
154 105969 : 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 260346 : if (ac->ares) {
160 260346 : return ldb_module_done(ac->req, ac->ares->controls,
161 219725 : ac->ares->response, LDB_SUCCESS);
162 : } else {
163 0 : return ldb_module_done(ac->req, NULL, NULL, LDB_SUCCESS);
164 : }
165 : }
166 :
167 339686 : 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 339686 : struct ldb_message_element *el = NULL;
177 42140 : int ret;
178 :
179 339686 : *value = NULL;
180 :
181 381826 : ret = dsdb_get_expected_new_values(ac,
182 339686 : ac->msg,
183 : attr,
184 : &el,
185 339686 : ac->req->operation);
186 :
187 339686 : if (ret != LDB_SUCCESS) {
188 0 : return ret;
189 : }
190 339686 : if (el == NULL) {
191 : /* we are not affected */
192 25986 : return LDB_SUCCESS;
193 : }
194 :
195 312643 : 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 312641 : } 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 312632 : 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 312632 : *value = (char *)el->values[0].data;
220 :
221 312632 : return LDB_SUCCESS;
222 : }
223 :
224 259718 : 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 259718 : struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
229 259718 : const char * const no_attrs[] = { NULL };
230 259718 : struct ldb_result *res = NULL;
231 259718 : const char *str = NULL;
232 259718 : const char *enc_str = NULL;
233 39972 : int ret;
234 :
235 259718 : ret = samldb_get_single_valued_attr(ldb, ac, attr, &str);
236 259718 : if (ret != LDB_SUCCESS) {
237 11 : return ret;
238 : }
239 259707 : if (str == NULL) {
240 : /* the attribute wasn't found */
241 734 : return LDB_SUCCESS;
242 : }
243 :
244 258973 : enc_str = ldb_binary_encode_string(ac, str);
245 258973 : 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 258973 : if (attr_conflict != NULL) {
253 1905 : 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 257068 : 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 258973 : if (ret != LDB_SUCCESS) {
268 0 : return ret;
269 : }
270 258973 : if (res->count > 1) {
271 0 : return ldb_operr(ldb);
272 258973 : } else if (res->count == 1) {
273 568 : 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 258889 : talloc_free(res);
281 :
282 258889 : return LDB_SUCCESS;
283 : }
284 :
285 :
286 :
287 107145 : 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 107145 : const char * const no_attrs[] = { NULL };
301 107145 : struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
302 107145 : struct ldb_result *res = NULL;
303 2220 : int ret;
304 107145 : char *enc_value = ldb_binary_encode_string(ac, value);
305 107145 : if (enc_value == NULL) {
306 0 : return ldb_module_oom(ac->module);
307 : }
308 107145 : 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 107145 : talloc_free(enc_value);
315 :
316 107145 : if (ret != LDB_SUCCESS) {
317 0 : return ret;
318 107145 : } else if (res->count > 1) {
319 0 : return ldb_operr(ldb);
320 107145 : } else if (res->count == 1) {
321 818 : 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 104903 : return LDB_SUCCESS;
334 : }
335 :
336 39565 : 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 39565 : struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
351 1076 : size_t i;
352 :
353 685105 : for (i = 0; name[i] != '\0'; i++) {
354 644470 : uint8_t c = name[i];
355 644470 : char *p = NULL;
356 644470 : 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 644464 : p = strchr("\"[]:;|=+*?<>/\\,", c);
364 644464 : 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 39559 : 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 39559 : 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 38483 : return LDB_SUCCESS;
387 : }
388 :
389 39984 : static int samldb_sam_account_upn_clash(struct samldb_ctx *ac)
390 : {
391 39984 : struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
392 1084 : int ret;
393 39984 : struct ldb_dn *base_dn = ldb_get_default_basedn(ldb);
394 39984 : TALLOC_CTX *tmp_ctx = NULL;
395 39984 : const char *real_sam = NULL;
396 39984 : const char *real_upn = NULL;
397 39984 : char *implied_sam = NULL;
398 39984 : char *implied_upn = NULL;
399 39984 : const char *realm = NULL;
400 :
401 39984 : ret = samldb_get_single_valued_attr(ldb, ac,
402 : "sAMAccountName",
403 : &real_sam);
404 39984 : if (ret != LDB_SUCCESS) {
405 0 : return ret;
406 : }
407 39984 : ret = samldb_get_single_valued_attr(ldb, ac,
408 : "userPrincipalName",
409 : &real_upn);
410 39984 : if (ret != LDB_SUCCESS) {
411 0 : return ret;
412 : }
413 39984 : if (real_upn == NULL && real_sam == NULL) {
414 : /* Not changing these things, so we're done */
415 0 : return LDB_SUCCESS;
416 : }
417 :
418 39984 : tmp_ctx = talloc_new(ac);
419 39984 : realm = samdb_dn_to_dns_domain(tmp_ctx, base_dn);
420 39984 : if (realm == NULL) {
421 0 : talloc_free(tmp_ctx);
422 0 : return ldb_operr(ldb);
423 : }
424 :
425 39984 : 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 14094 : char *at = NULL;
433 14094 : char *upn_realm = NULL;
434 14094 : implied_sam = talloc_strdup(tmp_ctx, real_upn);
435 14094 : if (implied_sam == NULL) {
436 0 : talloc_free(tmp_ctx);
437 0 : return ldb_module_oom(ac->module);
438 : }
439 :
440 14094 : at = strrchr(implied_sam, '@');
441 14094 : 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 75 : 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 14019 : *at = '\0';
456 14019 : upn_realm = at + 1;
457 14019 : 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 159 : TALLOC_FREE(implied_sam);
462 : }
463 : }
464 : }
465 :
466 39984 : if (real_sam != NULL) {
467 39565 : implied_upn = talloc_asprintf(tmp_ctx, "%s@%s",
468 : real_sam, realm);
469 39565 : 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 39984 : if (real_sam != NULL) {
480 39565 : ret = samldb_sam_account_upn_clash_sub_search(
481 : ac, tmp_ctx, base_dn, "sAMAccountName",
482 : real_sam, "");
483 :
484 39565 : if (ret != LDB_SUCCESS) {
485 0 : talloc_free(tmp_ctx);
486 0 : return ret;
487 : }
488 39565 : ret = samaccountname_bad_chars_check(ac, real_sam);
489 39565 : if (ret != LDB_SUCCESS) {
490 6 : talloc_free(tmp_ctx);
491 6 : return ret;
492 : }
493 : }
494 39978 : if (implied_upn != NULL) {
495 39559 : ret = samldb_sam_account_upn_clash_sub_search(
496 : ac, tmp_ctx, base_dn, "userPrincipalName", implied_upn,
497 : "(implied by sAMAccountName)");
498 :
499 39559 : if (ret != LDB_SUCCESS) {
500 6 : talloc_free(tmp_ctx);
501 6 : return ret;
502 : }
503 : }
504 39972 : if (real_upn != NULL) {
505 14093 : ret = samldb_sam_account_upn_clash_sub_search(
506 : ac, tmp_ctx, base_dn, "userPrincipalName",
507 : real_upn, "");
508 :
509 14093 : if (ret != LDB_SUCCESS) {
510 10 : talloc_free(tmp_ctx);
511 10 : return ret;
512 : }
513 : }
514 39962 : if (implied_sam != NULL) {
515 13928 : ret = samldb_sam_account_upn_clash_sub_search(
516 : ac, tmp_ctx, base_dn, "sAMAccountName", implied_sam,
517 : "(implied by userPrincipalName)");
518 13928 : if (ret != LDB_SUCCESS) {
519 6 : talloc_free(tmp_ctx);
520 6 : return ret;
521 : }
522 : }
523 :
524 39956 : talloc_free(tmp_ctx);
525 39956 : return LDB_SUCCESS;
526 : }
527 :
528 :
529 : /* This is run during an add or modify */
530 39589 : static int samldb_sam_accountname_valid_check(struct samldb_ctx *ac)
531 : {
532 39589 : int ret = 0;
533 1076 : bool is_admin;
534 39589 : struct security_token *user_token = NULL;
535 39589 : struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
536 39589 : struct ldb_message_element *el = NULL;
537 :
538 40665 : ret = dsdb_get_expected_new_values(ac,
539 39589 : ac->msg,
540 : "samAccountName",
541 : &el,
542 39589 : ac->req->operation);
543 39589 : if (ret != LDB_SUCCESS) {
544 0 : return ret;
545 : }
546 :
547 39589 : 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 39574 : 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 39574 : if (ret == LDB_ERR_CONSTRAINT_VIOLATION
567 2 : && ac->req->operation == LDB_MODIFY) {
568 1 : ret = LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
569 39573 : } else if (ret == LDB_ERR_OBJECT_CLASS_VIOLATION) {
570 0 : ret = LDB_ERR_CONSTRAINT_VIOLATION;
571 : }
572 39574 : if (ret != LDB_SUCCESS) {
573 14 : return ret;
574 : }
575 :
576 39560 : ret = samldb_sam_account_upn_clash(ac);
577 39560 : if (ret != LDB_SUCCESS) {
578 12 : return ret;
579 : }
580 :
581 39548 : if (!ac->need_trailing_dollar) {
582 34096 : return LDB_SUCCESS;
583 : }
584 :
585 : /* This does not permit a single $ */
586 4472 : 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 4472 : user_token = acl_user_token(ac->module);
595 4472 : if (user_token == NULL) {
596 0 : return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
597 : }
598 :
599 96 : is_admin
600 4472 : = security_token_has_builtin_administrators(user_token);
601 :
602 4472 : if (is_admin) {
603 : /*
604 : * Administrators are allowed to select strange names.
605 : * This is poor practice but not prevented.
606 : */
607 3545 : return false;
608 : }
609 :
610 833 : 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 820 : 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 818 : return ret;
626 : }
627 :
628 1023 : static int samldb_schema_attributeid_valid_check(struct samldb_ctx *ac)
629 : {
630 1023 : int ret = samldb_unique_attr_check(ac, "attributeID", "governsID",
631 : ldb_get_schema_basedn(
632 : ldb_module_get_ctx(ac->module)));
633 1023 : if (ret == LDB_ERR_ENTRY_ALREADY_EXISTS) {
634 9 : ret = LDB_ERR_UNWILLING_TO_PERFORM;
635 : }
636 1023 : return ret;
637 : }
638 :
639 882 : static int samldb_schema_governsid_valid_check(struct samldb_ctx *ac)
640 : {
641 882 : int ret = samldb_unique_attr_check(ac, "governsID", "attributeID",
642 : ldb_get_schema_basedn(
643 : ldb_module_get_ctx(ac->module)));
644 882 : if (ret == LDB_ERR_ENTRY_ALREADY_EXISTS) {
645 9 : ret = LDB_ERR_UNWILLING_TO_PERFORM;
646 : }
647 882 : return ret;
648 : }
649 :
650 218088 : static int samldb_schema_ldapdisplayname_valid_check(struct samldb_ctx *ac)
651 : {
652 218088 : int ret = samldb_unique_attr_check(ac, "lDAPDisplayName", NULL,
653 : ldb_get_schema_basedn(
654 : ldb_module_get_ctx(ac->module)));
655 218088 : if (ret == LDB_ERR_ENTRY_ALREADY_EXISTS) {
656 36 : ret = LDB_ERR_UNWILLING_TO_PERFORM;
657 : }
658 218088 : 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 0 : int ret;
669 0 : 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 0 : int ret;
702 0 : struct ldb_context *ldb;
703 0 : 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 69 : 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 1014 : static int samldb_schema_add_handle_linkid(struct samldb_ctx *ac)
730 : {
731 0 : int ret;
732 1014 : bool ok, found = false;
733 0 : struct ldb_message_element *el;
734 0 : const char *enc_str;
735 0 : const struct dsdb_attribute *attr;
736 0 : struct ldb_context *ldb;
737 0 : struct ldb_dn *schema_dn;
738 0 : struct dsdb_schema *schema;
739 1014 : int32_t new_linkID = 0;
740 :
741 1014 : ldb = ldb_module_get_ctx(ac->module);
742 1014 : schema = dsdb_get_schema(ldb, ac);
743 1014 : schema_dn = ldb_get_schema_basedn(ldb);
744 :
745 1014 : ret = dsdb_get_expected_new_values(ac,
746 1014 : ac->msg,
747 : "linkID",
748 : &el,
749 1014 : ac->req->operation);
750 1014 : if (ret != LDB_SUCCESS) {
751 0 : return ret;
752 : }
753 :
754 1014 : if (el == NULL || el->num_values == 0) {
755 806 : return LDB_SUCCESS;
756 : }
757 :
758 208 : enc_str = ldb_binary_encode(ac, el->values[0]);
759 208 : if (enc_str == NULL) {
760 0 : return ldb_module_oom(ac->module);
761 : }
762 :
763 208 : ok = (strcmp(enc_str, "0") == 0);
764 208 : 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 208 : ok = (strcmp(enc_str, "1.2.840.113556.1.2.50") == 0);
774 208 : 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 181 : attr = dsdb_attribute_by_attributeID_oid(schema, enc_str);
792 181 : if (attr == NULL) {
793 163 : attr = dsdb_attribute_by_lDAPDisplayName(schema, enc_str);
794 : }
795 :
796 181 : 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 142 : schema_dn = ldb_get_schema_basedn(ldb_module_get_ctx(ac->module));
826 142 : ret = samldb_unique_attr_check(ac, "linkID", NULL, schema_dn);
827 142 : if (ret == LDB_ERR_ENTRY_ALREADY_EXISTS) {
828 9 : return LDB_ERR_UNWILLING_TO_PERFORM;
829 : } else {
830 133 : 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 0 : int ret;
842 0 : 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 0 : int ret;
869 0 : struct ldb_context *ldb;
870 0 : 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 18 : 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 978 : static int samldb_schema_add_handle_mapiid(struct samldb_ctx *ac)
893 : {
894 0 : int ret;
895 0 : bool ok;
896 0 : struct ldb_message_element *el;
897 0 : const char *enc_str;
898 0 : struct ldb_context *ldb;
899 0 : struct ldb_dn *schema_dn;
900 0 : struct dsdb_schema *schema;
901 978 : 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 978 : ldb = ldb_module_get_ctx(ac->module);
910 978 : schema = dsdb_get_schema(ldb, ac);
911 978 : schema_dn = ldb_get_schema_basedn(ldb);
912 :
913 978 : ret = dsdb_get_expected_new_values(ac,
914 978 : ac->msg,
915 : "mAPIID",
916 : &el,
917 978 : ac->req->operation);
918 978 : if (ret != LDB_SUCCESS) {
919 0 : return ret;
920 : }
921 :
922 978 : if (el == NULL || el->num_values == 0) {
923 960 : 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 6541 : static int samldb_generate_sAMAccountName(struct samldb_ctx *ac,
956 : struct ldb_message *msg)
957 : {
958 6541 : struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
959 3 : 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 6541 : if (ac->need_trailing_dollar) {
967 : /* Format: $000000-00000000000$ */
968 350 : name = talloc_asprintf(msg, "$%.6X-%.6X%.5X$",
969 350 : (unsigned int)generate_random(),
970 350 : (unsigned int)generate_random(),
971 350 : (unsigned int)generate_random());
972 : } else {
973 : /* Format: $000000-000000000000 */
974 :
975 6191 : name = talloc_asprintf(msg, "$%.6X-%.6X%.6X",
976 6191 : (unsigned int)generate_random(),
977 6191 : (unsigned int)generate_random(),
978 6191 : (unsigned int)generate_random());
979 : }
980 6541 : if (name == NULL) {
981 0 : return ldb_oom(ldb);
982 : }
983 6541 : return ldb_msg_add_steal_string(msg, "sAMAccountName", name);
984 : }
985 :
986 38677 : static int samldb_check_sAMAccountName(struct samldb_ctx *ac)
987 : {
988 1066 : int ret;
989 :
990 38677 : if (ldb_msg_find_element(ac->msg, "sAMAccountName") == NULL) {
991 6541 : ret = samldb_generate_sAMAccountName(ac, ac->msg);
992 6541 : if (ret != LDB_SUCCESS) {
993 0 : return ret;
994 : }
995 : }
996 :
997 38677 : ret = samldb_sam_accountname_valid_check(ac);
998 38677 : if (ret != LDB_SUCCESS) {
999 8 : return ret;
1000 : }
1001 :
1002 38669 : return samldb_next_step(ac);
1003 : }
1004 :
1005 :
1006 33581 : static bool samldb_msg_add_sid(struct ldb_message *msg,
1007 : const char *name,
1008 : const struct dom_sid *sid)
1009 : {
1010 172 : struct ldb_val v;
1011 172 : enum ndr_err_code ndr_err;
1012 :
1013 33581 : ndr_err = ndr_push_struct_blob(&v, msg, sid,
1014 : (ndr_push_flags_fn_t)ndr_push_dom_sid);
1015 33581 : if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1016 0 : return false;
1017 : }
1018 33581 : return (ldb_msg_add_value(msg, name, &v, NULL) == 0);
1019 : }
1020 :
1021 :
1022 : /* allocate a SID using our RID Set */
1023 33536 : static int samldb_allocate_sid(struct samldb_ctx *ac)
1024 : {
1025 172 : uint32_t rid;
1026 172 : struct dom_sid *sid;
1027 33536 : struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
1028 172 : int ret;
1029 :
1030 33536 : ret = ridalloc_allocate_rid(ac->module, &rid, ac->req);
1031 33536 : if (ret != LDB_SUCCESS) {
1032 0 : return ret;
1033 : }
1034 :
1035 33536 : sid = dom_sid_add_rid(ac, samdb_domain_sid(ldb), rid);
1036 33536 : if (sid == NULL) {
1037 0 : return ldb_module_oom(ac->module);
1038 : }
1039 :
1040 33536 : if ( ! samldb_msg_add_sid(ac->msg, "objectSid", sid)) {
1041 0 : return ldb_operr(ldb);
1042 : }
1043 :
1044 33536 : return samldb_next_step(ac);
1045 : }
1046 :
1047 : /*
1048 : see if a krbtgt_number is available
1049 : */
1050 92 : static bool samldb_krbtgtnumber_available(struct samldb_ctx *ac,
1051 : uint32_t krbtgt_number)
1052 : {
1053 92 : TALLOC_CTX *tmp_ctx = talloc_new(ac);
1054 0 : struct ldb_result *res;
1055 92 : const char * const no_attrs[] = { NULL };
1056 0 : int ret;
1057 :
1058 92 : 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 92 : if (ret == LDB_SUCCESS && res->count == 0) {
1066 92 : talloc_free(tmp_ctx);
1067 92 : return true;
1068 : }
1069 0 : talloc_free(tmp_ctx);
1070 0 : return false;
1071 : }
1072 :
1073 : /* special handling for add in RODC join */
1074 92 : static int samldb_rodc_add(struct samldb_ctx *ac)
1075 : {
1076 92 : struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
1077 0 : uint32_t krbtgt_number, i_start, i;
1078 0 : int ret;
1079 0 : struct ldb_val newpass_utf16;
1080 :
1081 : /* find a unused msDS-SecondaryKrbTgtNumber */
1082 92 : i_start = generate_random() & 0xFFFF;
1083 92 : if (i_start == 0) {
1084 0 : i_start = 1;
1085 : }
1086 :
1087 92 : for (i=i_start; i<=0xFFFF; i++) {
1088 92 : if (samldb_krbtgtnumber_available(ac, i)) {
1089 92 : krbtgt_number = i;
1090 92 : 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 92 : found:
1106 :
1107 92 : ldb_msg_remove_attr(ac->msg, "msDS-SecondaryKrbTgtNumber");
1108 92 : ret = samdb_msg_append_uint(ldb, ac->msg, ac->msg,
1109 : "msDS-SecondaryKrbTgtNumber", krbtgt_number,
1110 : LDB_FLAG_INTERNAL_DISABLE_VALIDATION);
1111 92 : if (ret != LDB_SUCCESS) {
1112 0 : return ldb_operr(ldb);
1113 : }
1114 :
1115 92 : ret = ldb_msg_add_fmt(ac->msg, "sAMAccountName", "krbtgt_%u",
1116 : krbtgt_number);
1117 92 : if (ret != LDB_SUCCESS) {
1118 0 : return ldb_operr(ldb);
1119 : }
1120 :
1121 92 : newpass_utf16 = data_blob_talloc_zero(ac->module, 256);
1122 92 : 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 92 : generate_random_buffer(newpass_utf16.data, newpass_utf16.length);
1132 92 : ret = ldb_msg_add_steal_value(ac->msg, "clearTextPassword", &newpass_utf16);
1133 92 : if (ret != LDB_SUCCESS) {
1134 0 : return ldb_operr(ldb);
1135 : }
1136 :
1137 92 : return samldb_next_step(ac);
1138 : }
1139 :
1140 33672 : static int samldb_find_for_defaultObjectCategory(struct samldb_ctx *ac)
1141 : {
1142 33672 : struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
1143 5918 : struct ldb_result *res;
1144 33672 : const char * const no_attrs[] = { NULL };
1145 5918 : int ret;
1146 :
1147 33672 : ac->res_dn = NULL;
1148 :
1149 33672 : 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 33672 : 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 264 : 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 33672 : if ((ret != LDB_ERR_NO_SUCH_OBJECT) && (ret != LDB_SUCCESS)) {
1167 0 : return ret;
1168 : }
1169 :
1170 33672 : if (ret == LDB_SUCCESS) {
1171 : /* ensure the defaultObjectCategory has a full GUID */
1172 5874 : struct ldb_message *m;
1173 33408 : m = ldb_msg_new(ac->msg);
1174 33408 : if (m == NULL) {
1175 0 : return ldb_oom(ldb);
1176 : }
1177 33408 : m->dn = ac->msg->dn;
1178 33408 : if (ldb_msg_add_string(m, "defaultObjectCategory",
1179 33408 : ldb_dn_get_extended_linearized(m, res->msgs[0]->dn, 1)) !=
1180 : LDB_SUCCESS) {
1181 0 : return ldb_oom(ldb);
1182 : }
1183 33408 : m->elements[0].flags = LDB_FLAG_MOD_REPLACE;
1184 :
1185 33408 : ret = dsdb_module_modify(ac->module, m,
1186 : DSDB_FLAG_NEXT_MODULE,
1187 : ac->req);
1188 33408 : if (ret != LDB_SUCCESS) {
1189 0 : return ret;
1190 : }
1191 : }
1192 :
1193 :
1194 33672 : ac->res_dn = ac->dn;
1195 :
1196 33672 : return samldb_next_step(ac);
1197 : }
1198 :
1199 : /**
1200 : * msDS-IntId attributeSchema attribute handling
1201 : * during LDB_ADD request processing
1202 : */
1203 184328 : static int samldb_add_handle_msDS_IntId(struct samldb_ctx *ac)
1204 : {
1205 32978 : int ret;
1206 32978 : bool id_exists;
1207 32978 : uint32_t msds_intid;
1208 32978 : int32_t system_flags;
1209 32978 : struct ldb_context *ldb;
1210 32978 : struct ldb_result *ldb_res;
1211 32978 : struct ldb_dn *schema_dn;
1212 32978 : struct samldb_msds_intid_persistant *msds_intid_struct;
1213 32978 : struct dsdb_schema *schema;
1214 :
1215 184328 : ldb = ldb_module_get_ctx(ac->module);
1216 184328 : schema_dn = ldb_get_schema_basedn(ldb);
1217 :
1218 : /* replicated update should always go through */
1219 184328 : 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 184328 : 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 184310 : if (ldb_request_get_control(ac->req, LDB_CONTROL_RELAX_OID)) {
1232 151087 : 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 0 : 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 0xBFFFFFFF,
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 very 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 260550 : static int samldb_add_entry_callback(struct ldb_request *req,
1323 : struct ldb_reply *ares)
1324 : {
1325 40622 : struct ldb_context *ldb;
1326 40622 : struct samldb_ctx *ac;
1327 40622 : int ret;
1328 :
1329 260550 : ac = talloc_get_type(req->context, struct samldb_ctx);
1330 260550 : ldb = ldb_module_get_ctx(ac->module);
1331 :
1332 260550 : if (!ares) {
1333 0 : return ldb_module_done(ac->req, NULL, NULL,
1334 : LDB_ERR_OPERATIONS_ERROR);
1335 : }
1336 :
1337 260550 : if (ares->type == LDB_REPLY_REFERRAL) {
1338 0 : return ldb_module_send_referral(ac->req, ares->referral);
1339 : }
1340 :
1341 260550 : if (ares->error != LDB_SUCCESS) {
1342 204 : return ldb_module_done(ac->req, ares->controls,
1343 : ares->response, ares->error);
1344 : }
1345 260346 : 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 260346 : ac->ares = talloc_steal(ac, ares);
1353 :
1354 260346 : ret = samldb_next_step(ac);
1355 260346 : if (ret != LDB_SUCCESS) {
1356 0 : return ldb_module_done(ac->req, NULL, NULL, ret);
1357 : }
1358 219725 : return ret;
1359 : }
1360 :
1361 260538 : static int samldb_add_entry(struct samldb_ctx *ac)
1362 : {
1363 40622 : struct ldb_context *ldb;
1364 40622 : struct ldb_request *req;
1365 40622 : int ret;
1366 :
1367 260538 : ldb = ldb_module_get_ctx(ac->module);
1368 :
1369 301160 : ret = ldb_build_add_req(&req, ldb, ac,
1370 260538 : ac->msg,
1371 219916 : ac->req->controls,
1372 : ac, samldb_add_entry_callback,
1373 : ac->req);
1374 260538 : LDB_REQ_SET_LOCATION(req);
1375 260538 : if (ret != LDB_SUCCESS) {
1376 0 : return ret;
1377 : }
1378 :
1379 260538 : 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 218032 : static bool check_rodc_critical_attribute(struct ldb_message *msg)
1387 : {
1388 38896 : uint32_t schemaFlagsEx, searchFlags, rodc_filtered_flags;
1389 :
1390 218032 : schemaFlagsEx = ldb_msg_find_attr_as_uint(msg, "schemaFlagsEx", 0);
1391 218032 : searchFlags = ldb_msg_find_attr_as_uint(msg, "searchFlags", 0);
1392 218032 : rodc_filtered_flags = (SEARCH_FLAG_RODC_ATTRIBUTE
1393 : | SEARCH_FLAG_CONFIDENTIAL);
1394 :
1395 218032 : if ((schemaFlagsEx & SCHEMA_FLAG_ATTR_IS_CRITICAL) &&
1396 49350 : ((searchFlags & rodc_filtered_flags) == rodc_filtered_flags)) {
1397 0 : return true;
1398 : } else {
1399 218032 : return false;
1400 : }
1401 : }
1402 :
1403 :
1404 256709 : static int samldb_fill_object(struct samldb_ctx *ac)
1405 : {
1406 256709 : struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
1407 39962 : int ret;
1408 :
1409 : /* Add information for the different account types */
1410 256709 : switch(ac->type) {
1411 29973 : case SAMLDB_TYPE_USER: {
1412 29973 : struct ldb_control *rodc_control = ldb_request_get_control(ac->req,
1413 : LDB_CONTROL_RODC_DCPROMO_OID);
1414 29973 : if (rodc_control != NULL) {
1415 : /* see [MS-ADTS] 3.1.1.3.4.1.23 LDAP_SERVER_RODC_DCPROMO_OID */
1416 92 : rodc_control->critical = false;
1417 92 : ret = samldb_add_step(ac, samldb_rodc_add);
1418 92 : if (ret != LDB_SUCCESS) return ret;
1419 : }
1420 :
1421 : /* check if we have a valid sAMAccountName */
1422 29973 : ret = samldb_add_step(ac, samldb_check_sAMAccountName);
1423 29973 : if (ret != LDB_SUCCESS) return ret;
1424 :
1425 29973 : ret = samldb_add_step(ac, samldb_add_entry);
1426 29973 : if (ret != LDB_SUCCESS) return ret;
1427 29752 : break;
1428 : }
1429 :
1430 8704 : case SAMLDB_TYPE_GROUP: {
1431 : /* check if we have a valid sAMAccountName */
1432 8704 : ret = samldb_add_step(ac, samldb_check_sAMAccountName);
1433 8704 : if (ret != LDB_SUCCESS) return ret;
1434 :
1435 8704 : ret = samldb_add_step(ac, samldb_add_entry);
1436 8704 : if (ret != LDB_SUCCESS) return ret;
1437 7859 : break;
1438 : }
1439 :
1440 33704 : case SAMLDB_TYPE_CLASS: {
1441 33704 : const char *lDAPDisplayName = NULL;
1442 5918 : const struct ldb_val *rdn_value, *def_obj_cat_val;
1443 33704 : 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 33704 : 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 33704 : ret = samdb_find_or_add_attribute(ldb, ac->msg,
1452 : "rdnAttId", "cn");
1453 33704 : 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 33704 : 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 33704 : rdn_value = ldb_dn_get_rdn_val(ac->msg->dn);
1464 33704 : if (rdn_value == NULL) {
1465 0 : return ldb_operr(ldb);
1466 : }
1467 33704 : if (!ldb_msg_find_element(ac->msg, "lDAPDisplayName")) {
1468 : /* the RDN has prefix "CN" */
1469 628 : ret = ldb_msg_add_string(ac->msg, "lDAPDisplayName",
1470 628 : samdb_cn_to_lDAPDisplayName(ac->msg,
1471 628 : (const char *) rdn_value->data));
1472 628 : if (ret != LDB_SUCCESS) {
1473 0 : ldb_oom(ldb);
1474 0 : return ret;
1475 : }
1476 : }
1477 :
1478 33704 : lDAPDisplayName = ldb_msg_find_attr_as_string(ac->msg,
1479 : "lDAPDisplayName",
1480 : NULL);
1481 33704 : ret = ldb_valid_attr_name(lDAPDisplayName);
1482 33704 : if (ret != 1 ||
1483 33704 : lDAPDisplayName[0] == '*' ||
1484 27786 : 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 33704 : if (!ldb_msg_find_element(ac->msg, "schemaIDGUID")) {
1493 0 : struct GUID guid;
1494 : /* a new GUID */
1495 702 : guid = GUID_random();
1496 702 : ret = dsdb_msg_add_guid(ac->msg, &guid, "schemaIDGUID");
1497 702 : if (ret != LDB_SUCCESS) {
1498 0 : ldb_oom(ldb);
1499 0 : return ret;
1500 : }
1501 : }
1502 :
1503 33704 : def_obj_cat_val = ldb_msg_find_ldb_val(ac->msg,
1504 : "defaultObjectCategory");
1505 33704 : 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 33120 : ac->dn = ldb_dn_from_ldb_val(ac, ldb, def_obj_cat_val);
1515 33120 : 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 584 : ac->dn = ac->msg->dn;
1524 :
1525 584 : ret = ldb_msg_add_string(ac->msg, "defaultObjectCategory",
1526 584 : ldb_dn_alloc_linearized(ac->msg, ac->dn));
1527 584 : if (ret != LDB_SUCCESS) {
1528 0 : ldb_oom(ldb);
1529 0 : return ret;
1530 : }
1531 : }
1532 :
1533 33704 : ret = samldb_add_step(ac, samldb_add_entry);
1534 33704 : 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 33704 : ret = samldb_add_step(ac, samldb_find_for_defaultObjectCategory);
1539 33704 : if (ret != LDB_SUCCESS) return ret;
1540 :
1541 : /* -2 is not a valid objectClassCategory so it means the attribute wasn't present */
1542 33704 : if (v == -2) {
1543 : /* Windows 2003 does this*/
1544 284 : ret = samdb_msg_add_uint(ldb, ac->msg, ac->msg, "objectClassCategory", 0);
1545 284 : if (ret != LDB_SUCCESS) {
1546 0 : return ret;
1547 : }
1548 : }
1549 27786 : break;
1550 : }
1551 :
1552 184328 : case SAMLDB_TYPE_ATTRIBUTE: {
1553 184328 : const char *lDAPDisplayName = NULL;
1554 32978 : const struct ldb_val *rdn_value;
1555 32978 : struct ldb_message_element *el;
1556 184328 : rdn_value = ldb_dn_get_rdn_val(ac->msg->dn);
1557 184328 : if (rdn_value == NULL) {
1558 0 : return ldb_operr(ldb);
1559 : }
1560 184328 : 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 184328 : lDAPDisplayName = ldb_msg_find_attr_as_string(ac->msg,
1572 : "lDAPDisplayName",
1573 : NULL);
1574 184328 : ret = ldb_valid_attr_name(lDAPDisplayName);
1575 184328 : if (ret != 1 ||
1576 184328 : lDAPDisplayName[0] == '*' ||
1577 151350 : 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 184328 : 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 184328 : ret = samdb_find_or_add_attribute(ldb, ac->msg,
1595 : "isSingleValued", "FALSE");
1596 184328 : if (ret != LDB_SUCCESS) return ret;
1597 :
1598 184328 : if (!ldb_msg_find_element(ac->msg, "schemaIDGUID")) {
1599 0 : 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 184328 : el = ldb_msg_find_element(ac->msg, "attributeSyntax");
1610 184328 : 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 184328 : const struct dsdb_syntax *syntax = find_syntax_map_by_ad_oid((const char *)el->values[0].data);
1616 184328 : 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 184328 : unsigned int v = ldb_msg_find_attr_as_uint(ac->msg, "oMSyntax", 0);
1621 184328 : const struct ldb_val *val = ldb_msg_find_ldb_val(ac->msg, "oMObjectClass");
1622 :
1623 184328 : 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 184328 : if (!val) {
1630 158469 : struct ldb_val val2 = ldb_val_dup(ldb, &syntax->oMObjectClass);
1631 158469 : if (val2.length > 0) {
1632 59 : ret = ldb_msg_add_value(ac->msg, "oMObjectClass", &val2, NULL);
1633 59 : if (ret != LDB_SUCCESS) {
1634 0 : return ret;
1635 : }
1636 : }
1637 : }
1638 : }
1639 : }
1640 :
1641 : /* handle msDS-IntID attribute */
1642 184328 : ret = samldb_add_handle_msDS_IntId(ac);
1643 184328 : if (ret != LDB_SUCCESS) return ret;
1644 :
1645 184310 : ret = samldb_add_step(ac, samldb_add_entry);
1646 184310 : if (ret != LDB_SUCCESS) return ret;
1647 151332 : break;
1648 : }
1649 :
1650 0 : default:
1651 0 : ldb_asprintf_errstring(ldb, "Invalid entry type!");
1652 0 : return LDB_ERR_OPERATIONS_ERROR;
1653 39962 : break;
1654 : }
1655 :
1656 256691 : return samldb_first_step(ac);
1657 : }
1658 :
1659 3857 : static int samldb_fill_foreignSecurityPrincipal_object(struct samldb_ctx *ac)
1660 : {
1661 3857 : struct ldb_context *ldb = NULL;
1662 3857 : const struct ldb_val *rdn_value = NULL;
1663 3857 : struct ldb_message_element *sid_el = NULL;
1664 3857 : struct dom_sid *sid = NULL;
1665 3857 : struct ldb_control *as_system = NULL;
1666 3857 : struct ldb_control *provision = NULL;
1667 3857 : bool allowed = false;
1668 660 : int ret;
1669 :
1670 3857 : ldb = ldb_module_get_ctx(ac->module);
1671 :
1672 3857 : as_system = ldb_request_get_control(ac->req, LDB_CONTROL_AS_SYSTEM_OID);
1673 3857 : if (as_system != NULL) {
1674 43 : allowed = true;
1675 : }
1676 :
1677 3857 : provision = ldb_request_get_control(ac->req, LDB_CONTROL_PROVISION_OID);
1678 3857 : if (provision != NULL) {
1679 3812 : allowed = true;
1680 : }
1681 :
1682 3857 : sid_el = ldb_msg_find_element(ac->msg, "objectSid");
1683 :
1684 3857 : 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 3856 : 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 3855 : if (sid_el != NULL) {
1699 3810 : sid = samdb_result_dom_sid(ac->msg, ac->msg, "objectSid");
1700 3810 : if (sid == NULL) {
1701 0 : ldb_set_errstring(ldb,
1702 : "samldb: invalid objectSid!");
1703 0 : return LDB_ERR_CONSTRAINT_VIOLATION;
1704 : }
1705 : }
1706 :
1707 3195 : if (sid == NULL) {
1708 45 : rdn_value = ldb_dn_get_rdn_val(ac->msg->dn);
1709 45 : if (rdn_value == NULL) {
1710 0 : return ldb_operr(ldb);
1711 : }
1712 45 : sid = dom_sid_parse_talloc(ac->msg,
1713 45 : (const char *)rdn_value->data);
1714 45 : 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 45 : 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 3855 : ret = samldb_add_step(ac, samldb_add_entry);
1726 3855 : if (ret != LDB_SUCCESS) return ret;
1727 :
1728 3855 : return samldb_first_step(ac);
1729 : }
1730 :
1731 218032 : static int samldb_schema_info_update(struct samldb_ctx *ac)
1732 : {
1733 38896 : int ret;
1734 38896 : struct ldb_context *ldb;
1735 38896 : struct dsdb_schema *schema;
1736 :
1737 : /* replicated update should always go through */
1738 218032 : 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 218032 : if (ldb_request_get_control(ac->req, LDB_CONTROL_PROVISION_OID)) {
1745 177321 : return LDB_SUCCESS;
1746 : }
1747 :
1748 1815 : ldb = ldb_module_get_ctx(ac->module);
1749 1815 : schema = dsdb_get_schema(ldb, NULL);
1750 1815 : 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 1815 : ret = dsdb_module_schema_info_update(ac->module, schema,
1758 : DSDB_FLAG_NEXT_MODULE|
1759 : DSDB_FLAG_AS_SYSTEM,
1760 : ac->req);
1761 1815 : 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 1815 : 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 38793 : static int samldb_objectclass_trigger(struct samldb_ctx *ac)
1788 : {
1789 38793 : struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
1790 38793 : void *skip_allocate_sids = ldb_get_opaque(ldb,
1791 : "skip_allocate_sids");
1792 1066 : struct ldb_message_element *el;
1793 1066 : struct dom_sid *sid;
1794 1066 : int ret;
1795 :
1796 : /* make sure that "sAMAccountType" is not specified */
1797 38793 : el = ldb_msg_find_element(ac->msg, "sAMAccountType");
1798 38793 : 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 38792 : sid = samdb_result_dom_sid(ac, ac->msg, "objectSid");
1810 43875 : if ((sid != NULL) && (!dsdb_module_am_system(ac->module)) &&
1811 5083 : (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 38792 : if ((sid == NULL) && (ac->req->operation == LDB_ADD) && !skip_allocate_sids) {
1819 33651 : ret = samldb_add_step(ac, samldb_allocate_sid);
1820 33651 : if (ret != LDB_SUCCESS) return ret;
1821 : }
1822 :
1823 38792 : switch(ac->type) {
1824 30082 : case SAMLDB_TYPE_USER: {
1825 221 : uint32_t raw_uac;
1826 221 : uint32_t user_account_control;
1827 221 : bool is_computer_objectclass;
1828 30082 : bool uac_generated = false, uac_add_flags = false;
1829 30082 : uint32_t default_user_account_control = UF_NORMAL_ACCOUNT;
1830 : /* Step 1.2: Default values */
1831 30082 : ret = dsdb_user_obj_set_defaults(ldb, ac->msg, ac->req);
1832 30082 : if (ret != LDB_SUCCESS) return ret;
1833 :
1834 221 : is_computer_objectclass
1835 60164 : = (samdb_find_attribute(ldb,
1836 30082 : ac->msg,
1837 : "objectclass",
1838 : "computer")
1839 : != NULL);
1840 :
1841 30082 : if (is_computer_objectclass) {
1842 94 : default_user_account_control
1843 4121 : = 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 30082 : el = ldb_msg_find_element(ac->msg, "userAccountControl");
1850 30082 : if (el == NULL) {
1851 23937 : ret = samdb_msg_set_uint(ldb, ac->msg, ac->msg,
1852 : "userAccountControl",
1853 : default_user_account_control);
1854 23937 : if (ret != LDB_SUCCESS) {
1855 0 : return ret;
1856 : }
1857 23884 : uac_generated = true;
1858 23884 : uac_add_flags = true;
1859 : }
1860 :
1861 30082 : el = ldb_msg_find_element(ac->msg, "userAccountControl");
1862 30082 : SMB_ASSERT(el != NULL);
1863 :
1864 : /* Step 1.3: "userAccountControl" -> "sAMAccountType" mapping */
1865 30082 : user_account_control = ldb_msg_find_attr_as_uint(ac->msg,
1866 : "userAccountControl",
1867 : 0);
1868 30082 : 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 30082 : if ((user_account_control & UF_ACCOUNT_TYPE_MASK) == 0) {
1876 0 : user_account_control
1877 30 : = default_user_account_control
1878 : | user_account_control;
1879 30 : 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 30082 : if ((user_account_control & UF_LOCKOUT) != 0) {
1886 7 : user_account_control &= ~UF_LOCKOUT;
1887 7 : uac_generated = true;
1888 : }
1889 30082 : if ((user_account_control & UF_PASSWORD_EXPIRED) != 0) {
1890 7 : user_account_control &= ~UF_PASSWORD_EXPIRED;
1891 7 : uac_generated = true;
1892 : }
1893 :
1894 30082 : ret = samldb_check_user_account_control_rules(ac, NULL,
1895 : raw_uac,
1896 : user_account_control,
1897 : 0,
1898 : is_computer_objectclass);
1899 30082 : 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 29973 : if ((user_account_control
1909 29973 : & UF_TRUST_ACCOUNT_MASK) != 0) {
1910 4078 : ac->need_trailing_dollar = true;
1911 : }
1912 :
1913 29973 : if (is_computer_objectclass) {
1914 4079 : ac->need_trailing_dollar = true;
1915 : }
1916 :
1917 : /* add "sAMAccountType" attribute */
1918 29973 : ret = dsdb_user_obj_set_account_type(ldb, ac->msg, user_account_control, NULL);
1919 29973 : if (ret != LDB_SUCCESS) {
1920 0 : return ret;
1921 : }
1922 :
1923 : /* "isCriticalSystemObject" might be set */
1924 29973 : if (user_account_control &
1925 : (UF_SERVER_TRUST_ACCOUNT | UF_PARTIAL_SECRETS_ACCOUNT)) {
1926 1228 : ret = ldb_msg_add_string_flags(ac->msg, "isCriticalSystemObject",
1927 : "TRUE", LDB_FLAG_MOD_REPLACE);
1928 1228 : if (ret != LDB_SUCCESS) {
1929 0 : return ret;
1930 : }
1931 28745 : } else if (user_account_control & UF_WORKSTATION_TRUST_ACCOUNT) {
1932 2779 : ret = ldb_msg_add_string_flags(ac->msg, "isCriticalSystemObject",
1933 : "FALSE", LDB_FLAG_MOD_REPLACE);
1934 2779 : if (ret != LDB_SUCCESS) {
1935 0 : return ret;
1936 : }
1937 : }
1938 :
1939 : /* Step 1.4: "userAccountControl" -> "primaryGroupID" mapping */
1940 29973 : if (!ldb_msg_find_element(ac->msg, "primaryGroupID")) {
1941 199 : uint32_t rid;
1942 :
1943 29845 : ret = dsdb_user_obj_set_primary_group_id(ldb, ac->msg, user_account_control, &rid);
1944 29845 : if (ret != LDB_SUCCESS) {
1945 0 : return ret;
1946 : }
1947 : /*
1948 : * Older AD deployments don't know about the
1949 : * RODC group
1950 : */
1951 29845 : if (rid == DOMAIN_RID_READONLY_DCS) {
1952 158 : ret = samldb_prim_group_tester(ac, rid);
1953 158 : 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 29973 : if (uac_generated) {
1964 23975 : if (uac_add_flags) {
1965 23937 : user_account_control |= UF_ACCOUNTDISABLE;
1966 23937 : user_account_control |= UF_PASSWD_NOTREQD;
1967 : }
1968 :
1969 23975 : ret = samdb_msg_set_uint(ldb, ac->msg, ac->msg,
1970 : "userAccountControl",
1971 : user_account_control);
1972 23975 : if (ret != LDB_SUCCESS) {
1973 0 : return ret;
1974 : }
1975 : }
1976 29752 : break;
1977 : }
1978 :
1979 8710 : case SAMLDB_TYPE_GROUP: {
1980 845 : const char *tempstr;
1981 :
1982 : /* Step 2.2: Default values */
1983 8710 : tempstr = talloc_asprintf(ac->msg, "%d",
1984 : GTYPE_SECURITY_GLOBAL_GROUP);
1985 8710 : if (tempstr == NULL) return ldb_operr(ldb);
1986 8710 : ret = samdb_find_or_add_attribute(ldb, ac->msg,
1987 : "groupType", tempstr);
1988 8710 : if (ret != LDB_SUCCESS) return ret;
1989 :
1990 : /* Step 2.3: "groupType" -> "sAMAccountType" */
1991 8710 : el = ldb_msg_find_element(ac->msg, "groupType");
1992 8710 : if (el != NULL) {
1993 845 : uint32_t group_type, account_type;
1994 :
1995 8710 : 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 8710 : if (group_type == GTYPE_SECURITY_BUILTIN_LOCAL_GROUP) {
2001 2670 : 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 8707 : account_type = ds_gtype2atype(group_type);
2008 8707 : if (account_type == 0) {
2009 3 : ldb_set_errstring(ldb, "samldb: Unrecognized account type!");
2010 3 : return LDB_ERR_UNWILLING_TO_PERFORM;
2011 : }
2012 8704 : ret = samdb_msg_add_uint_flags(ldb, ac->msg, ac->msg,
2013 : "sAMAccountType",
2014 : account_type,
2015 : LDB_FLAG_MOD_REPLACE);
2016 8704 : if (ret != LDB_SUCCESS) {
2017 0 : return ret;
2018 : }
2019 : }
2020 7859 : 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 37611 : 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 307 : static int samldb_prim_group_tester(struct samldb_ctx *ac, uint32_t rid)
2042 : {
2043 307 : struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
2044 22 : struct dom_sid *sid;
2045 22 : struct ldb_result *res;
2046 22 : int ret;
2047 307 : const char * const noattrs[] = { NULL };
2048 :
2049 307 : sid = dom_sid_add_rid(ac, samdb_domain_sid(ldb), rid);
2050 307 : if (sid == NULL) {
2051 0 : return ldb_operr(ldb);
2052 : }
2053 :
2054 307 : 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 307 : if (ret != LDB_SUCCESS) {
2062 0 : return ret;
2063 : }
2064 307 : 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 307 : talloc_free(res);
2072 :
2073 307 : return LDB_SUCCESS;
2074 : }
2075 :
2076 30109 : static int samldb_prim_group_set(struct samldb_ctx *ac)
2077 : {
2078 30109 : struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
2079 221 : uint32_t rid;
2080 :
2081 30109 : rid = ldb_msg_find_attr_as_uint(ac->msg, "primaryGroupID", (uint32_t) -1);
2082 30109 : if (rid == (uint32_t) -1) {
2083 : /* we aren't affected of any primary group set */
2084 29756 : return LDB_SUCCESS;
2085 :
2086 154 : } 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 128 : return samldb_prim_group_tester(ac, rid);
2093 : }
2094 :
2095 202 : static int samldb_prim_group_change(struct samldb_ctx *ac)
2096 : {
2097 202 : struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
2098 202 : const char * const attrs[] = {
2099 : "primaryGroupID",
2100 : "memberOf",
2101 : "userAccountControl",
2102 : NULL };
2103 2 : struct ldb_result *res, *group_res;
2104 2 : struct ldb_message_element *el;
2105 2 : struct ldb_message *msg;
2106 202 : uint32_t search_flags =
2107 : DSDB_FLAG_NEXT_MODULE | DSDB_SEARCH_SHOW_EXTENDED_DN;
2108 2 : uint32_t prev_rid, new_rid, uac;
2109 2 : struct dom_sid *prev_sid, *new_sid;
2110 2 : struct ldb_dn *prev_prim_group_dn, *new_prim_group_dn;
2111 202 : const char *new_prim_group_dn_ext_str = NULL;
2112 202 : struct ldb_dn *user_dn = NULL;
2113 202 : const char *user_dn_ext_str = NULL;
2114 2 : int ret;
2115 202 : const char * const noattrs[] = { NULL };
2116 202 : const char * const group_type_attrs[] = { "groupType", NULL };
2117 2 : unsigned group_type;
2118 :
2119 204 : ret = dsdb_get_expected_new_values(ac,
2120 202 : ac->msg,
2121 : "primaryGroupID",
2122 : &el,
2123 202 : ac->req->operation);
2124 202 : if (ret != LDB_SUCCESS) {
2125 0 : return ret;
2126 : }
2127 :
2128 202 : if (el == NULL) {
2129 : /* we are not affected */
2130 3 : return LDB_SUCCESS;
2131 : }
2132 :
2133 : /* Fetch information from the existing object */
2134 :
2135 199 : ret = dsdb_module_search_dn(ac->module, ac, &res, ac->msg->dn, attrs,
2136 : search_flags, ac->req);
2137 199 : if (ret != LDB_SUCCESS) {
2138 0 : return ret;
2139 : }
2140 199 : user_dn = res->msgs[0]->dn;
2141 199 : user_dn_ext_str = ldb_dn_get_extended_linearized(ac, user_dn, 1);
2142 199 : if (user_dn_ext_str == NULL) {
2143 0 : return ldb_operr(ldb);
2144 : }
2145 :
2146 199 : uac = ldb_msg_find_attr_as_uint(res->msgs[0], "userAccountControl", 0);
2147 :
2148 : /* Finds out the DN of the old primary group */
2149 :
2150 199 : prev_rid = ldb_msg_find_attr_as_uint(res->msgs[0], "primaryGroupID",
2151 : (uint32_t) -1);
2152 199 : if (prev_rid == (uint32_t) -1) {
2153 : /* User objects do always have a mandatory "primaryGroupID"
2154 : * attribute. If this doesn't exist then the object is of the
2155 : * wrong type. This is the exact Windows error code */
2156 3 : return LDB_ERR_OBJECT_CLASS_VIOLATION;
2157 : }
2158 :
2159 196 : prev_sid = dom_sid_add_rid(ac, samdb_domain_sid(ldb), prev_rid);
2160 196 : if (prev_sid == NULL) {
2161 0 : return ldb_operr(ldb);
2162 : }
2163 :
2164 : /* Finds out the DN of the new primary group
2165 : * Notice: in order to parse the primary group ID correctly we create
2166 : * a temporary message here. */
2167 :
2168 196 : msg = ldb_msg_new(ac->msg);
2169 196 : if (msg == NULL) {
2170 0 : return ldb_module_oom(ac->module);
2171 : }
2172 196 : ret = ldb_msg_add(msg, el, 0);
2173 196 : if (ret != LDB_SUCCESS) {
2174 0 : return ret;
2175 : }
2176 196 : new_rid = ldb_msg_find_attr_as_uint(msg, "primaryGroupID", (uint32_t) -1);
2177 196 : talloc_free(msg);
2178 196 : if (new_rid == (uint32_t) -1) {
2179 : /* we aren't affected of any primary group change */
2180 3 : return LDB_SUCCESS;
2181 : }
2182 :
2183 193 : if (prev_rid == new_rid) {
2184 13 : return LDB_SUCCESS;
2185 : }
2186 :
2187 180 : if ((uac & UF_SERVER_TRUST_ACCOUNT) && new_rid != DOMAIN_RID_DCS) {
2188 1 : ldb_asprintf_errstring(ldb,
2189 : "%08X: samldb: UF_SERVER_TRUST_ACCOUNT requires "
2190 : "primaryGroupID=%u!",
2191 1 : W_ERROR_V(WERR_DS_CANT_MOD_PRIMARYGROUPID),
2192 : DOMAIN_RID_DCS);
2193 1 : return LDB_ERR_UNWILLING_TO_PERFORM;
2194 : }
2195 :
2196 179 : if ((uac & UF_PARTIAL_SECRETS_ACCOUNT) && new_rid != DOMAIN_RID_READONLY_DCS) {
2197 1 : ldb_asprintf_errstring(ldb,
2198 : "%08X: samldb: UF_PARTIAL_SECRETS_ACCOUNT requires "
2199 : "primaryGroupID=%u!",
2200 1 : W_ERROR_V(WERR_DS_CANT_MOD_PRIMARYGROUPID),
2201 : DOMAIN_RID_READONLY_DCS);
2202 1 : return LDB_ERR_UNWILLING_TO_PERFORM;
2203 : }
2204 :
2205 178 : ret = dsdb_module_search(ac->module, ac, &group_res,
2206 : ldb_get_default_basedn(ldb),
2207 : LDB_SCOPE_SUBTREE,
2208 : noattrs, search_flags,
2209 : ac->req,
2210 : "(objectSid=%s)",
2211 : ldap_encode_ndr_dom_sid(ac, prev_sid));
2212 178 : if (ret != LDB_SUCCESS) {
2213 0 : return ret;
2214 : }
2215 178 : if (group_res->count != 1) {
2216 0 : return ldb_operr(ldb);
2217 : }
2218 178 : prev_prim_group_dn = group_res->msgs[0]->dn;
2219 :
2220 178 : new_sid = dom_sid_add_rid(ac, samdb_domain_sid(ldb), new_rid);
2221 178 : if (new_sid == NULL) {
2222 0 : return ldb_operr(ldb);
2223 : }
2224 :
2225 178 : ret = dsdb_module_search(ac->module, ac, &group_res,
2226 : ldb_get_default_basedn(ldb),
2227 : LDB_SCOPE_SUBTREE,
2228 : group_type_attrs, search_flags,
2229 : ac->req,
2230 : "(objectSid=%s)",
2231 : ldap_encode_ndr_dom_sid(ac, new_sid));
2232 178 : if (ret != LDB_SUCCESS) {
2233 0 : return ret;
2234 : }
2235 178 : if (group_res->count != 1) {
2236 : /* Here we know if the specified new primary group candidate is
2237 : * valid or not. */
2238 3 : return LDB_ERR_UNWILLING_TO_PERFORM;
2239 : }
2240 175 : new_prim_group_dn = group_res->msgs[0]->dn;
2241 :
2242 : /* The new primary group must not be domain-local. */
2243 175 : group_type = ldb_msg_find_attr_as_uint(group_res->msgs[0], "groupType", 0);
2244 175 : if (group_type & GROUP_TYPE_RESOURCE_GROUP) {
2245 3 : return dsdb_module_werror(ac->module,
2246 : LDB_ERR_UNWILLING_TO_PERFORM,
2247 : WERR_MEMBER_NOT_IN_GROUP,
2248 : "may not set resource group as primary group!");
2249 : }
2250 :
2251 172 : new_prim_group_dn_ext_str = ldb_dn_get_extended_linearized(ac,
2252 : new_prim_group_dn, 1);
2253 172 : if (new_prim_group_dn_ext_str == NULL) {
2254 0 : return ldb_operr(ldb);
2255 : }
2256 :
2257 : /* We need to be already a normal member of the new primary
2258 : * group in order to be successful. */
2259 172 : el = samdb_find_attribute(ldb, res->msgs[0], "memberOf",
2260 : new_prim_group_dn_ext_str);
2261 172 : if (el == NULL) {
2262 3 : return LDB_ERR_UNWILLING_TO_PERFORM;
2263 : }
2264 :
2265 : /* Remove the "member" attribute on the new primary group */
2266 169 : msg = ldb_msg_new(ac->msg);
2267 169 : if (msg == NULL) {
2268 0 : return ldb_module_oom(ac->module);
2269 : }
2270 169 : msg->dn = new_prim_group_dn;
2271 :
2272 169 : ret = samdb_msg_add_delval(ldb, msg, msg, "member", user_dn_ext_str);
2273 169 : if (ret != LDB_SUCCESS) {
2274 0 : return ret;
2275 : }
2276 :
2277 169 : ret = dsdb_module_modify(ac->module, msg, DSDB_FLAG_NEXT_MODULE, ac->req);
2278 169 : if (ret != LDB_SUCCESS) {
2279 0 : return ret;
2280 : }
2281 169 : talloc_free(msg);
2282 :
2283 : /* Add a "member" attribute for the previous primary group */
2284 169 : msg = ldb_msg_new(ac->msg);
2285 169 : if (msg == NULL) {
2286 0 : return ldb_module_oom(ac->module);
2287 : }
2288 169 : msg->dn = prev_prim_group_dn;
2289 :
2290 169 : ret = samdb_msg_add_addval(ldb, msg, msg, "member", user_dn_ext_str);
2291 169 : if (ret != LDB_SUCCESS) {
2292 0 : return ret;
2293 : }
2294 :
2295 169 : ret = dsdb_module_modify(ac->module, msg, DSDB_FLAG_NEXT_MODULE, ac->req);
2296 169 : if (ret != LDB_SUCCESS) {
2297 0 : return ret;
2298 : }
2299 169 : talloc_free(msg);
2300 :
2301 169 : return LDB_SUCCESS;
2302 : }
2303 :
2304 30311 : static int samldb_prim_group_trigger(struct samldb_ctx *ac)
2305 : {
2306 223 : int ret;
2307 :
2308 30311 : if (ac->req->operation == LDB_ADD) {
2309 30109 : ret = samldb_prim_group_set(ac);
2310 : } else {
2311 202 : ret = samldb_prim_group_change(ac);
2312 : }
2313 :
2314 30311 : return ret;
2315 : }
2316 :
2317 46060 : static int samldb_check_user_account_control_invariants(struct samldb_ctx *ac,
2318 : uint32_t user_account_control)
2319 : {
2320 330 : size_t i;
2321 46060 : int ret = 0;
2322 46060 : bool need_check = false;
2323 330 : const struct uac_to_guid {
2324 : uint32_t uac;
2325 : bool never;
2326 : uint32_t needs;
2327 : uint32_t not_with;
2328 : const char *error_string;
2329 46060 : } map[] = {
2330 : {
2331 : .uac = UF_TEMP_DUPLICATE_ACCOUNT,
2332 : .never = true,
2333 : .error_string = "Updating the UF_TEMP_DUPLICATE_ACCOUNT flag is never allowed"
2334 : },
2335 : {
2336 : .uac = UF_PARTIAL_SECRETS_ACCOUNT,
2337 : .needs = UF_WORKSTATION_TRUST_ACCOUNT,
2338 : .error_string = "Setting UF_PARTIAL_SECRETS_ACCOUNT only permitted with UF_WORKSTATION_TRUST_ACCOUNT"
2339 : },
2340 : {
2341 : .uac = UF_TRUSTED_FOR_DELEGATION,
2342 : .not_with = UF_PARTIAL_SECRETS_ACCOUNT,
2343 : .error_string = "Setting UF_TRUSTED_FOR_DELEGATION not allowed with UF_PARTIAL_SECRETS_ACCOUNT"
2344 : },
2345 : {
2346 : .uac = UF_NORMAL_ACCOUNT,
2347 : .not_with = UF_ACCOUNT_TYPE_MASK & ~UF_NORMAL_ACCOUNT,
2348 : .error_string = "Setting more than one account type not permitted"
2349 : },
2350 : {
2351 : .uac = UF_WORKSTATION_TRUST_ACCOUNT,
2352 : .not_with = UF_ACCOUNT_TYPE_MASK & ~UF_WORKSTATION_TRUST_ACCOUNT,
2353 : .error_string = "Setting more than one account type not permitted"
2354 : },
2355 : {
2356 : .uac = UF_INTERDOMAIN_TRUST_ACCOUNT,
2357 : .not_with = UF_ACCOUNT_TYPE_MASK & ~UF_INTERDOMAIN_TRUST_ACCOUNT,
2358 : .error_string = "Setting more than one account type not permitted"
2359 : },
2360 : {
2361 : .uac = UF_SERVER_TRUST_ACCOUNT,
2362 : .not_with = UF_ACCOUNT_TYPE_MASK & ~UF_SERVER_TRUST_ACCOUNT,
2363 : .error_string = "Setting more than one account type not permitted"
2364 : },
2365 : {
2366 : .uac = UF_TRUSTED_FOR_DELEGATION,
2367 : .not_with = UF_PARTIAL_SECRETS_ACCOUNT,
2368 : .error_string = "Setting UF_TRUSTED_FOR_DELEGATION not allowed with UF_PARTIAL_SECRETS_ACCOUNT"
2369 : }
2370 : };
2371 :
2372 188495 : for (i = 0; i < ARRAY_SIZE(map); i++) {
2373 188495 : if (user_account_control & map[i].uac) {
2374 45730 : need_check = true;
2375 45730 : break;
2376 : }
2377 : }
2378 46060 : if (need_check == false) {
2379 0 : return LDB_SUCCESS;
2380 : }
2381 :
2382 414049 : for (i = 0; i < ARRAY_SIZE(map); i++) {
2383 368057 : uint32_t this_uac = user_account_control & map[i].uac;
2384 368057 : if (this_uac != 0) {
2385 48276 : if (map[i].never) {
2386 16 : ret = LDB_ERR_OTHER;
2387 16 : break;
2388 48260 : } else if (map[i].needs != 0) {
2389 439 : if ((map[i].needs & user_account_control) == 0) {
2390 51 : ret = LDB_ERR_OTHER;
2391 51 : break;
2392 : }
2393 47821 : } else if (map[i].not_with != 0) {
2394 47821 : if ((map[i].not_with & user_account_control) != 0) {
2395 1 : ret = LDB_ERR_OTHER;
2396 1 : break;
2397 : }
2398 : }
2399 : }
2400 : }
2401 46060 : if (ret != LDB_SUCCESS) {
2402 68 : switch (ac->req->operation) {
2403 7 : case LDB_ADD:
2404 7 : ldb_asprintf_errstring(ldb_module_get_ctx(ac->module),
2405 : "Failed to add %s: %s",
2406 7 : ldb_dn_get_linearized(ac->msg->dn),
2407 7 : map[i].error_string);
2408 7 : break;
2409 61 : case LDB_MODIFY:
2410 61 : ldb_asprintf_errstring(ldb_module_get_ctx(ac->module),
2411 : "Failed to modify %s: %s",
2412 61 : ldb_dn_get_linearized(ac->msg->dn),
2413 61 : map[i].error_string);
2414 61 : break;
2415 0 : default:
2416 0 : return ldb_module_operr(ac->module);
2417 : }
2418 : }
2419 45730 : return ret;
2420 : }
2421 :
2422 : /*
2423 : * It would be best if these rules apply, always, but for now they
2424 : * apply only to non-admins
2425 : */
2426 45992 : static int samldb_check_user_account_control_objectclass_invariants(
2427 : struct samldb_ctx *ac,
2428 : uint32_t user_account_control,
2429 : uint32_t user_account_control_old,
2430 : bool is_computer_objectclass)
2431 : {
2432 45992 : struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
2433 :
2434 45992 : uint32_t old_ufa = user_account_control_old & UF_ACCOUNT_TYPE_MASK;
2435 45992 : uint32_t new_ufa = user_account_control & UF_ACCOUNT_TYPE_MASK;
2436 :
2437 45992 : uint32_t old_rodc = user_account_control_old & UF_PARTIAL_SECRETS_ACCOUNT;
2438 45992 : uint32_t new_rodc = user_account_control & UF_PARTIAL_SECRETS_ACCOUNT;
2439 :
2440 330 : bool is_admin;
2441 330 : struct security_token *user_token
2442 45992 : = acl_user_token(ac->module);
2443 45992 : if (user_token == NULL) {
2444 0 : return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
2445 : }
2446 :
2447 330 : is_admin
2448 45992 : = security_token_has_builtin_administrators(user_token);
2449 :
2450 :
2451 : /*
2452 : * We want to allow changes to (eg) disable an account
2453 : * that was created wrong, only checking the
2454 : * objectclass if the account type changes.
2455 : */
2456 45992 : if (old_ufa == new_ufa && old_rodc == new_rodc) {
2457 15484 : return LDB_SUCCESS;
2458 : }
2459 :
2460 30399 : switch (new_ufa) {
2461 26007 : case UF_NORMAL_ACCOUNT:
2462 26007 : if (is_computer_objectclass && !is_admin) {
2463 39 : ldb_asprintf_errstring(ldb,
2464 : "%08X: samldb: UF_NORMAL_ACCOUNT "
2465 : "requires objectclass 'user' not 'computer'!",
2466 39 : W_ERROR_V(WERR_DS_MACHINE_ACCOUNT_CREATED_PRENT4));
2467 39 : return LDB_ERR_OBJECT_CLASS_VIOLATION;
2468 : }
2469 25841 : break;
2470 :
2471 87 : case UF_INTERDOMAIN_TRUST_ACCOUNT:
2472 87 : if (is_computer_objectclass) {
2473 8 : ldb_asprintf_errstring(ldb,
2474 : "%08X: samldb: UF_INTERDOMAIN_TRUST_ACCOUNT "
2475 : "requires objectclass 'user' not 'computer'!",
2476 8 : W_ERROR_V(WERR_DS_MACHINE_ACCOUNT_CREATED_PRENT4));
2477 8 : return LDB_ERR_OBJECT_CLASS_VIOLATION;
2478 : }
2479 79 : break;
2480 :
2481 3137 : case UF_WORKSTATION_TRUST_ACCOUNT:
2482 3137 : if (!is_computer_objectclass) {
2483 : /*
2484 : * Modify of a user account account into a
2485 : * workstation without objectclass computer
2486 : * as an admin is still permitted, but not
2487 : * to make an RODC
2488 : */
2489 70 : if (is_admin
2490 47 : && ac->req->operation == LDB_MODIFY
2491 25 : && new_rodc == 0) {
2492 22 : break;
2493 : }
2494 48 : ldb_asprintf_errstring(ldb,
2495 : "%08X: samldb: UF_WORKSTATION_TRUST_ACCOUNT "
2496 : "requires objectclass 'computer'!",
2497 48 : W_ERROR_V(WERR_DS_MACHINE_ACCOUNT_CREATED_PRENT4));
2498 48 : return LDB_ERR_OBJECT_CLASS_VIOLATION;
2499 : }
2500 3031 : break;
2501 :
2502 1168 : case UF_SERVER_TRUST_ACCOUNT:
2503 1168 : if (!is_computer_objectclass) {
2504 38 : ldb_asprintf_errstring(ldb,
2505 : "%08X: samldb: UF_SERVER_TRUST_ACCOUNT "
2506 : "requires objectclass 'computer'!",
2507 38 : W_ERROR_V(WERR_DS_MACHINE_ACCOUNT_CREATED_PRENT4));
2508 38 : return LDB_ERR_OBJECT_CLASS_VIOLATION;
2509 : }
2510 1072 : break;
2511 :
2512 0 : default:
2513 0 : ldb_asprintf_errstring(ldb,
2514 : "%08X: samldb: invalid userAccountControl[0x%08X]",
2515 0 : W_ERROR_V(WERR_INVALID_PARAMETER),
2516 : user_account_control);
2517 0 : return LDB_ERR_OTHER;
2518 : }
2519 30045 : return LDB_SUCCESS;
2520 : }
2521 :
2522 43795 : static int samldb_get_domain_secdesc_and_oc(struct samldb_ctx *ac,
2523 : struct security_descriptor **domain_sd,
2524 : const struct dsdb_class **objectclass)
2525 : {
2526 43795 : const char * const sd_attrs[] = {"ntSecurityDescriptor", "objectClass", NULL};
2527 233 : struct ldb_result *res;
2528 43795 : struct ldb_dn *domain_dn = ldb_get_default_basedn(ldb_module_get_ctx(ac->module));
2529 43795 : const struct dsdb_schema *schema = NULL;
2530 43795 : struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
2531 43795 : int ret = dsdb_module_search_dn(ac->module, ac, &res,
2532 : domain_dn,
2533 : sd_attrs,
2534 : DSDB_FLAG_NEXT_MODULE | DSDB_SEARCH_SHOW_DELETED,
2535 : ac->req);
2536 43795 : if (ret != LDB_SUCCESS) {
2537 0 : return ret;
2538 : }
2539 43795 : if (res->count != 1) {
2540 0 : return ldb_module_operr(ac->module);
2541 : }
2542 :
2543 43795 : schema = dsdb_get_schema(ldb, ac->req);
2544 43795 : if (!schema) {
2545 0 : return ldb_module_operr(ac->module);;
2546 : }
2547 43795 : *objectclass = dsdb_get_structural_oc_from_msg(schema, res->msgs[0]);
2548 43795 : return dsdb_get_sd_from_ldb_message(ldb_module_get_ctx(ac->module),
2549 43795 : ac, res->msgs[0], domain_sd);
2550 :
2551 : }
2552 :
2553 : /**
2554 : * Validate that the restriction in point 5 of MS-SAMR 3.1.1.8.10 userAccountControl is honoured
2555 : *
2556 : */
2557 45859 : static int samldb_check_user_account_control_acl(struct samldb_ctx *ac,
2558 : struct dom_sid *sid,
2559 : uint32_t user_account_control,
2560 : uint32_t user_account_control_old)
2561 : {
2562 330 : size_t i;
2563 45859 : int ret = 0;
2564 45859 : bool need_acl_check = false;
2565 330 : struct security_token *user_token;
2566 330 : struct security_descriptor *domain_sd;
2567 45859 : const struct dsdb_class *objectclass = NULL;
2568 330 : const struct uac_to_guid {
2569 : uint32_t uac;
2570 : uint32_t priv_to_change_from;
2571 : const char *oid;
2572 : const char *guid;
2573 : enum sec_privilege privilege;
2574 : bool delete_is_privileged;
2575 : bool admin_required;
2576 : const char *error_string;
2577 45859 : } map[] = {
2578 : {
2579 : .uac = UF_PASSWD_NOTREQD,
2580 : .guid = GUID_DRS_UPDATE_PASSWORD_NOT_REQUIRED_BIT,
2581 : .error_string = "Adding the UF_PASSWD_NOTREQD bit in userAccountControl requires the Update-Password-Not-Required-Bit right that was not given on the Domain object"
2582 : },
2583 : {
2584 : .uac = UF_DONT_EXPIRE_PASSWD,
2585 : .guid = GUID_DRS_UNEXPIRE_PASSWORD,
2586 : .error_string = "Adding the UF_DONT_EXPIRE_PASSWD bit in userAccountControl requires the Unexpire-Password right that was not given on the Domain object"
2587 : },
2588 : {
2589 : .uac = UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED,
2590 : .guid = GUID_DRS_ENABLE_PER_USER_REVERSIBLY_ENCRYPTED_PASSWORD,
2591 : .error_string = "Adding the UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED bit in userAccountControl requires the Enable-Per-User-Reversibly-Encrypted-Password right that was not given on the Domain object"
2592 : },
2593 : {
2594 : .uac = UF_SERVER_TRUST_ACCOUNT,
2595 : .guid = GUID_DRS_DS_INSTALL_REPLICA,
2596 : .error_string = "Adding the UF_SERVER_TRUST_ACCOUNT bit in userAccountControl requires the DS-Install-Replica right that was not given on the Domain object"
2597 : },
2598 : {
2599 : .uac = UF_PARTIAL_SECRETS_ACCOUNT,
2600 : .guid = GUID_DRS_DS_INSTALL_REPLICA,
2601 : .error_string = "Adding the UF_PARTIAL_SECRETS_ACCOUNT bit in userAccountControl requires the DS-Install-Replica right that was not given on the Domain object"
2602 : },
2603 : {
2604 : .uac = UF_WORKSTATION_TRUST_ACCOUNT,
2605 : .priv_to_change_from = UF_NORMAL_ACCOUNT,
2606 : .error_string = "Swapping UF_NORMAL_ACCOUNT to UF_WORKSTATION_TRUST_ACCOUNT requires the user to be a member of the domain admins group"
2607 : },
2608 : {
2609 : .uac = UF_NORMAL_ACCOUNT,
2610 : .priv_to_change_from = UF_WORKSTATION_TRUST_ACCOUNT,
2611 : .error_string = "Swapping UF_WORKSTATION_TRUST_ACCOUNT to UF_NORMAL_ACCOUNT requires the user to be a member of the domain admins group"
2612 : },
2613 : {
2614 : .uac = UF_INTERDOMAIN_TRUST_ACCOUNT,
2615 : .oid = DSDB_CONTROL_PERMIT_INTERDOMAIN_TRUST_UAC_OID,
2616 : .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",
2617 : .delete_is_privileged = true
2618 : },
2619 : {
2620 : .uac = UF_TRUSTED_FOR_DELEGATION,
2621 : .privilege = SEC_PRIV_ENABLE_DELEGATION,
2622 : .delete_is_privileged = true,
2623 : .error_string = "Updating the UF_TRUSTED_FOR_DELEGATION bit in userAccountControl is not permitted without the SeEnableDelegationPrivilege"
2624 : },
2625 : {
2626 : .uac = UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION,
2627 : .privilege = SEC_PRIV_ENABLE_DELEGATION,
2628 : .delete_is_privileged = true,
2629 : .error_string = "Updating the UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION bit in userAccountControl is not permitted without the SeEnableDelegationPrivilege"
2630 : }
2631 :
2632 : };
2633 :
2634 45859 : if (dsdb_module_am_system(ac->module)) {
2635 2041 : return LDB_SUCCESS;
2636 : }
2637 :
2638 286971 : for (i = 0; i < ARRAY_SIZE(map); i++) {
2639 286971 : if (user_account_control & map[i].uac) {
2640 43488 : need_acl_check = true;
2641 43488 : break;
2642 : }
2643 : }
2644 43721 : if (need_acl_check == false) {
2645 0 : return LDB_SUCCESS;
2646 : }
2647 :
2648 43721 : user_token = acl_user_token(ac->module);
2649 43721 : if (user_token == NULL) {
2650 0 : return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
2651 : }
2652 :
2653 43721 : ret = samldb_get_domain_secdesc_and_oc(ac, &domain_sd, &objectclass);
2654 43721 : if (ret != LDB_SUCCESS) {
2655 0 : return ret;
2656 : }
2657 :
2658 480583 : for (i = 0; i < ARRAY_SIZE(map); i++) {
2659 436945 : uint32_t this_uac_new = user_account_control & map[i].uac;
2660 436945 : uint32_t this_uac_old = user_account_control_old & map[i].uac;
2661 436945 : if (this_uac_new != this_uac_old) {
2662 45518 : if (this_uac_old != 0) {
2663 13985 : if (map[i].delete_is_privileged == false) {
2664 13971 : continue;
2665 : }
2666 : }
2667 31547 : if (map[i].oid) {
2668 71 : struct ldb_control *control = ldb_request_get_control(ac->req, map[i].oid);
2669 71 : if (control == NULL) {
2670 8 : ret = LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
2671 : }
2672 31476 : } else if (map[i].privilege != SEC_PRIV_INVALID) {
2673 792 : bool have_priv = security_token_has_privilege(user_token,
2674 770 : map[i].privilege);
2675 792 : if (have_priv == false) {
2676 32 : ret = LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
2677 : }
2678 30684 : } else if (map[i].priv_to_change_from & user_account_control_old) {
2679 120 : bool is_admin = security_token_has_builtin_administrators(user_token);
2680 120 : if (is_admin == false) {
2681 7 : ret = LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
2682 : }
2683 30564 : } else if (map[i].guid) {
2684 2950 : ret = acl_check_extended_right(ac,
2685 : ac->module,
2686 : ac->req,
2687 : objectclass,
2688 : domain_sd,
2689 : user_token,
2690 2776 : map[i].guid,
2691 : SEC_ADS_CONTROL_ACCESS,
2692 : sid);
2693 : } else {
2694 27511 : ret = LDB_SUCCESS;
2695 : }
2696 31444 : if (ret != LDB_SUCCESS) {
2697 83 : break;
2698 : }
2699 : }
2700 : }
2701 43721 : if (ret == LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS) {
2702 83 : switch (ac->req->operation) {
2703 33 : case LDB_ADD:
2704 33 : ldb_asprintf_errstring(ldb_module_get_ctx(ac->module),
2705 : "Failed to add %s: %s",
2706 33 : ldb_dn_get_linearized(ac->msg->dn),
2707 33 : map[i].error_string);
2708 33 : break;
2709 50 : case LDB_MODIFY:
2710 50 : ldb_asprintf_errstring(ldb_module_get_ctx(ac->module),
2711 : "Failed to modify %s: %s",
2712 50 : ldb_dn_get_linearized(ac->msg->dn),
2713 50 : map[i].error_string);
2714 50 : break;
2715 0 : default:
2716 0 : return ldb_module_operr(ac->module);
2717 : }
2718 83 : if (map[i].guid) {
2719 0 : struct ldb_dn *domain_dn
2720 36 : = ldb_get_default_basedn(ldb_module_get_ctx(ac->module));
2721 36 : dsdb_acl_debug(domain_sd, acl_user_token(ac->module),
2722 : domain_dn,
2723 : true,
2724 : 10);
2725 : }
2726 : }
2727 43488 : return ret;
2728 : }
2729 :
2730 46060 : static int samldb_check_user_account_control_rules(struct samldb_ctx *ac,
2731 : struct dom_sid *sid,
2732 : uint32_t req_uac,
2733 : uint32_t user_account_control,
2734 : uint32_t user_account_control_old,
2735 : bool is_computer_objectclass)
2736 : {
2737 330 : int ret;
2738 46060 : struct dsdb_control_password_user_account_control *uac = NULL;
2739 :
2740 46060 : ret = samldb_check_user_account_control_invariants(ac, user_account_control);
2741 46060 : if (ret != LDB_SUCCESS) {
2742 68 : return ret;
2743 : }
2744 45992 : ret = samldb_check_user_account_control_objectclass_invariants(ac,
2745 : user_account_control,
2746 : user_account_control_old,
2747 : is_computer_objectclass);
2748 45992 : if (ret != LDB_SUCCESS) {
2749 133 : return ret;
2750 : }
2751 :
2752 45859 : ret = samldb_check_user_account_control_acl(ac, sid, user_account_control, user_account_control_old);
2753 45859 : if (ret != LDB_SUCCESS) {
2754 83 : return ret;
2755 : }
2756 :
2757 45776 : uac = talloc_zero(ac->req,
2758 : struct dsdb_control_password_user_account_control);
2759 45776 : if (uac == NULL) {
2760 0 : return ldb_module_oom(ac->module);
2761 : }
2762 :
2763 45776 : uac->req_flags = req_uac;
2764 45776 : uac->old_flags = user_account_control_old;
2765 45776 : uac->new_flags = user_account_control;
2766 :
2767 45776 : ret = ldb_request_add_control(ac->req,
2768 : DSDB_CONTROL_PASSWORD_USER_ACCOUNT_CONTROL_OID,
2769 : false, uac);
2770 45776 : if (ret != LDB_SUCCESS) {
2771 0 : return ret;
2772 : }
2773 :
2774 45446 : return ret;
2775 : }
2776 :
2777 :
2778 : /**
2779 : * This function is called on LDB modify operations. It performs some additions/
2780 : * replaces on the current LDB message when "userAccountControl" changes.
2781 : */
2782 15984 : static int samldb_user_account_control_change(struct samldb_ctx *ac)
2783 : {
2784 15984 : struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
2785 109 : uint32_t old_uac;
2786 109 : uint32_t new_uac;
2787 109 : uint32_t raw_uac;
2788 109 : uint32_t old_ufa;
2789 109 : uint32_t new_ufa;
2790 109 : uint32_t old_uac_computed;
2791 109 : uint32_t clear_uac;
2792 109 : uint32_t old_atype;
2793 109 : uint32_t new_atype;
2794 109 : uint32_t old_pgrid;
2795 109 : uint32_t new_pgrid;
2796 109 : NTTIME old_lockoutTime;
2797 109 : struct ldb_message_element *el;
2798 109 : struct ldb_val *val;
2799 109 : struct ldb_val computer_val;
2800 109 : struct ldb_message *tmp_msg;
2801 109 : struct dom_sid *sid;
2802 109 : int ret;
2803 109 : struct ldb_result *res;
2804 15984 : const char * const attrs[] = {
2805 : "objectClass",
2806 : "isCriticalSystemObject",
2807 : "userAccountControl",
2808 : "msDS-User-Account-Control-Computed",
2809 : "lockoutTime",
2810 : "objectSid",
2811 : NULL
2812 : };
2813 15984 : bool is_computer_objectclass = false;
2814 15984 : bool old_is_critical = false;
2815 15984 : bool new_is_critical = false;
2816 :
2817 16093 : ret = dsdb_get_expected_new_values(ac,
2818 15984 : ac->msg,
2819 : "userAccountControl",
2820 : &el,
2821 15984 : ac->req->operation);
2822 15984 : if (ret != LDB_SUCCESS) {
2823 0 : return ret;
2824 : }
2825 :
2826 15984 : if (el == NULL || el->num_values == 0) {
2827 6 : ldb_asprintf_errstring(ldb,
2828 : "%08X: samldb: 'userAccountControl' can't be deleted!",
2829 6 : W_ERROR_V(WERR_DS_ILLEGAL_MOD_OPERATION));
2830 6 : return LDB_ERR_UNWILLING_TO_PERFORM;
2831 : }
2832 :
2833 : /* Create a temporary message for fetching the "userAccountControl" */
2834 15978 : tmp_msg = ldb_msg_new(ac->msg);
2835 15978 : if (tmp_msg == NULL) {
2836 0 : return ldb_module_oom(ac->module);
2837 : }
2838 15978 : ret = ldb_msg_add(tmp_msg, el, 0);
2839 15978 : if (ret != LDB_SUCCESS) {
2840 0 : return ret;
2841 : }
2842 15978 : raw_uac = ldb_msg_find_attr_as_uint(tmp_msg,
2843 : "userAccountControl",
2844 : 0);
2845 15978 : talloc_free(tmp_msg);
2846 : /*
2847 : * UF_LOCKOUT, UF_PASSWD_CANT_CHANGE and UF_PASSWORD_EXPIRED
2848 : * are only generated and not stored. We ignore them almost
2849 : * completely, along with unknown bits and UF_SCRIPT.
2850 : *
2851 : * The only exception is ACB_AUTOLOCK, which features in
2852 : * clear_acb when the bit is cleared in this modify operation.
2853 : *
2854 : * MS-SAMR 2.2.1.13 UF_FLAG Codes states that some bits are
2855 : * ignored by clients and servers
2856 : */
2857 15978 : new_uac = raw_uac & UF_SETTABLE_BITS;
2858 :
2859 : /* Fetch the old "userAccountControl" and "objectClass" */
2860 15978 : ret = dsdb_module_search_dn(ac->module, ac, &res, ac->msg->dn, attrs,
2861 : DSDB_FLAG_NEXT_MODULE, ac->req);
2862 15978 : if (ret != LDB_SUCCESS) {
2863 0 : return ret;
2864 : }
2865 15978 : old_uac = ldb_msg_find_attr_as_uint(res->msgs[0], "userAccountControl", 0);
2866 15978 : if (old_uac == 0) {
2867 0 : return ldb_operr(ldb);
2868 : }
2869 15978 : old_uac_computed = ldb_msg_find_attr_as_uint(res->msgs[0],
2870 : "msDS-User-Account-Control-Computed", 0);
2871 15978 : old_lockoutTime = ldb_msg_find_attr_as_int64(res->msgs[0],
2872 : "lockoutTime", 0);
2873 15978 : old_is_critical = ldb_msg_find_attr_as_bool(res->msgs[0],
2874 : "isCriticalSystemObject", 0);
2875 : /*
2876 : * When we do not have objectclass "computer" we cannot
2877 : * switch to a workstation or (RO)DC
2878 : */
2879 15978 : el = ldb_msg_find_element(res->msgs[0], "objectClass");
2880 15978 : if (el == NULL) {
2881 0 : return ldb_operr(ldb);
2882 : }
2883 15978 : computer_val = data_blob_string_const("computer");
2884 15978 : val = ldb_msg_find_val(el, &computer_val);
2885 15978 : if (val != NULL) {
2886 1533 : is_computer_objectclass = true;
2887 : }
2888 :
2889 15978 : old_ufa = old_uac & UF_ACCOUNT_TYPE_MASK;
2890 15978 : old_atype = ds_uf2atype(old_ufa);
2891 15978 : old_pgrid = ds_uf2prim_group_rid(old_uac);
2892 :
2893 15978 : new_ufa = new_uac & UF_ACCOUNT_TYPE_MASK;
2894 15978 : if (new_ufa == 0) {
2895 : /*
2896 : * "userAccountControl" = 0 or missing one of the
2897 : * types means "UF_NORMAL_ACCOUNT". See MS-SAMR
2898 : * 3.1.1.8.10 point 8
2899 : */
2900 267 : new_ufa = UF_NORMAL_ACCOUNT;
2901 267 : new_uac |= new_ufa;
2902 : }
2903 15978 : sid = samdb_result_dom_sid(res, res->msgs[0], "objectSid");
2904 15978 : if (sid == NULL) {
2905 0 : return ldb_module_operr(ac->module);
2906 : }
2907 :
2908 15978 : ret = samldb_check_user_account_control_rules(ac, sid,
2909 : raw_uac,
2910 : new_uac,
2911 : old_uac,
2912 : is_computer_objectclass);
2913 15978 : if (ret != LDB_SUCCESS) {
2914 175 : return ret;
2915 : }
2916 :
2917 15803 : new_atype = ds_uf2atype(new_ufa);
2918 15803 : new_pgrid = ds_uf2prim_group_rid(new_uac);
2919 :
2920 15803 : clear_uac = (old_uac | old_uac_computed) & ~raw_uac;
2921 :
2922 15803 : switch (new_ufa) {
2923 14458 : case UF_NORMAL_ACCOUNT:
2924 14458 : new_is_critical = old_is_critical;
2925 14458 : break;
2926 :
2927 0 : case UF_INTERDOMAIN_TRUST_ACCOUNT:
2928 0 : new_is_critical = true;
2929 0 : break;
2930 :
2931 706 : case UF_WORKSTATION_TRUST_ACCOUNT:
2932 706 : new_is_critical = false;
2933 706 : if (new_uac & UF_PARTIAL_SECRETS_ACCOUNT) {
2934 205 : new_is_critical = true;
2935 : }
2936 670 : break;
2937 :
2938 602 : case UF_SERVER_TRUST_ACCOUNT:
2939 602 : new_is_critical = true;
2940 602 : break;
2941 :
2942 0 : default:
2943 0 : ldb_asprintf_errstring(ldb,
2944 : "%08X: samldb: invalid userAccountControl[0x%08X]",
2945 0 : W_ERROR_V(WERR_INVALID_PARAMETER), raw_uac);
2946 0 : return LDB_ERR_OTHER;
2947 : }
2948 :
2949 15803 : if (old_atype != new_atype) {
2950 142 : ret = samdb_msg_append_uint(ldb, ac->msg, ac->msg,
2951 : "sAMAccountType", new_atype,
2952 : LDB_FLAG_MOD_REPLACE);
2953 142 : if (ret != LDB_SUCCESS) {
2954 0 : return ret;
2955 : }
2956 : }
2957 :
2958 : /* As per MS-SAMR 3.1.1.8.10 these flags have not to be set */
2959 15803 : if ((clear_uac & UF_LOCKOUT) && (old_lockoutTime != 0)) {
2960 : /* "lockoutTime" reset as per MS-SAMR 3.1.1.8.10 */
2961 10 : ldb_msg_remove_attr(ac->msg, "lockoutTime");
2962 10 : ret = samdb_msg_append_uint64(ldb, ac->msg, ac->msg, "lockoutTime",
2963 : (NTTIME)0, LDB_FLAG_MOD_REPLACE);
2964 10 : if (ret != LDB_SUCCESS) {
2965 0 : return ret;
2966 : }
2967 : }
2968 :
2969 : /*
2970 : * "isCriticalSystemObject" might be set/changed
2971 : *
2972 : * Even a change from UF_NORMAL_ACCOUNT (implicitly FALSE) to
2973 : * UF_WORKSTATION_TRUST_ACCOUNT (actually FALSE) triggers
2974 : * creating the attribute.
2975 : */
2976 15803 : if (old_is_critical != new_is_critical || old_atype != new_atype) {
2977 221 : ret = ldb_msg_append_string(ac->msg, "isCriticalSystemObject",
2978 : new_is_critical ? "TRUE": "FALSE",
2979 : LDB_FLAG_MOD_REPLACE);
2980 221 : if (ret != LDB_SUCCESS) {
2981 0 : return ret;
2982 : }
2983 : }
2984 :
2985 15803 : if (!ldb_msg_find_element(ac->msg, "primaryGroupID") &&
2986 : (old_pgrid != new_pgrid)) {
2987 : /* Older AD deployments don't know about the RODC group */
2988 224 : if (new_pgrid == DOMAIN_RID_READONLY_DCS) {
2989 21 : ret = samldb_prim_group_tester(ac, new_pgrid);
2990 21 : if (ret != LDB_SUCCESS) {
2991 0 : return ret;
2992 : }
2993 : }
2994 :
2995 224 : ret = samdb_msg_append_uint(ldb, ac->msg, ac->msg,
2996 : "primaryGroupID", new_pgrid,
2997 : LDB_FLAG_MOD_REPLACE);
2998 224 : if (ret != LDB_SUCCESS) {
2999 0 : return ret;
3000 : }
3001 : }
3002 :
3003 : /* Propagate eventual "userAccountControl" attribute changes */
3004 15803 : if (old_uac != new_uac) {
3005 15196 : char *tempstr = talloc_asprintf(ac->msg, "%d",
3006 : new_uac);
3007 15196 : if (tempstr == NULL) {
3008 0 : return ldb_module_oom(ac->module);
3009 : }
3010 :
3011 15196 : ret = ldb_msg_add_empty(ac->msg,
3012 : "userAccountControl",
3013 : LDB_FLAG_MOD_REPLACE,
3014 : &el);
3015 15196 : el->values = talloc(ac->msg, struct ldb_val);
3016 15196 : el->num_values = 1;
3017 15196 : el->values[0].data = (uint8_t *) tempstr;
3018 15196 : el->values[0].length = strlen(tempstr);
3019 : } else {
3020 607 : ldb_msg_remove_attr(ac->msg, "userAccountControl");
3021 : }
3022 :
3023 15694 : return LDB_SUCCESS;
3024 : }
3025 :
3026 59 : static int samldb_check_pwd_last_set_acl(struct samldb_ctx *ac,
3027 : struct dom_sid *sid)
3028 : {
3029 59 : struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
3030 59 : int ret = 0;
3031 59 : struct security_token *user_token = NULL;
3032 59 : struct security_descriptor *domain_sd = NULL;
3033 59 : struct ldb_dn *domain_dn = ldb_get_default_basedn(ldb_module_get_ctx(ac->module));
3034 59 : const char *operation = "";
3035 59 : const struct dsdb_class *objectclass = NULL;
3036 :
3037 59 : if (dsdb_module_am_system(ac->module)) {
3038 1 : return LDB_SUCCESS;
3039 : }
3040 :
3041 58 : switch (ac->req->operation) {
3042 0 : case LDB_ADD:
3043 0 : operation = "add";
3044 0 : break;
3045 58 : case LDB_MODIFY:
3046 58 : operation = "modify";
3047 58 : break;
3048 0 : default:
3049 0 : return ldb_module_operr(ac->module);
3050 : }
3051 :
3052 58 : user_token = acl_user_token(ac->module);
3053 58 : if (user_token == NULL) {
3054 0 : return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
3055 : }
3056 :
3057 58 : ret = samldb_get_domain_secdesc_and_oc(ac, &domain_sd, &objectclass);
3058 58 : if (ret != LDB_SUCCESS) {
3059 0 : return ret;
3060 : }
3061 58 : ret = acl_check_extended_right(ac,
3062 : ac->module,
3063 : ac->req,
3064 : objectclass,
3065 : domain_sd,
3066 : user_token,
3067 : GUID_DRS_UNEXPIRE_PASSWORD,
3068 : SEC_ADS_CONTROL_ACCESS,
3069 : sid);
3070 58 : if (ret != LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS) {
3071 58 : return ret;
3072 : }
3073 :
3074 0 : ldb_debug_set(ldb, LDB_DEBUG_WARNING,
3075 : "Failed to %s %s: "
3076 : "Setting pwdLastSet to -1 requires the "
3077 : "Unexpire-Password right that was not given "
3078 : "on the Domain object",
3079 : operation,
3080 0 : ldb_dn_get_linearized(ac->msg->dn));
3081 0 : dsdb_acl_debug(domain_sd, user_token,
3082 : domain_dn, true, 10);
3083 :
3084 0 : return ret;
3085 : }
3086 :
3087 : /**
3088 : * This function is called on LDB modify operations. It performs some additions/
3089 : * replaces on the current LDB message when "pwdLastSet" changes.
3090 : */
3091 321 : static int samldb_pwd_last_set_change(struct samldb_ctx *ac)
3092 : {
3093 321 : struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
3094 321 : NTTIME last_set = 0;
3095 321 : struct ldb_message_element *el = NULL;
3096 321 : struct ldb_message *tmp_msg = NULL;
3097 321 : struct dom_sid *self_sid = NULL;
3098 11 : int ret;
3099 321 : struct ldb_result *res = NULL;
3100 321 : const char * const attrs[] = {
3101 : "objectSid",
3102 : NULL
3103 : };
3104 :
3105 332 : ret = dsdb_get_expected_new_values(ac,
3106 321 : ac->msg,
3107 : "pwdLastSet",
3108 : &el,
3109 321 : ac->req->operation);
3110 321 : if (ret != LDB_SUCCESS) {
3111 0 : return ret;
3112 : }
3113 :
3114 321 : if (el == NULL || el->num_values == 0) {
3115 6 : ldb_asprintf_errstring(ldb,
3116 : "%08X: samldb: 'pwdLastSet' can't be deleted!",
3117 6 : W_ERROR_V(WERR_DS_ILLEGAL_MOD_OPERATION));
3118 6 : return LDB_ERR_UNWILLING_TO_PERFORM;
3119 : }
3120 :
3121 : /* Create a temporary message for fetching the "userAccountControl" */
3122 315 : tmp_msg = ldb_msg_new(ac->msg);
3123 315 : if (tmp_msg == NULL) {
3124 0 : return ldb_module_oom(ac->module);
3125 : }
3126 315 : ret = ldb_msg_add(tmp_msg, el, 0);
3127 315 : if (ret != LDB_SUCCESS) {
3128 0 : return ret;
3129 : }
3130 315 : last_set = samdb_result_nttime(tmp_msg, "pwdLastSet", 0);
3131 315 : talloc_free(tmp_msg);
3132 :
3133 : /*
3134 : * Setting -1 (0xFFFFFFFFFFFFFFFF) requires the Unexpire-Password right
3135 : */
3136 315 : if (last_set != UINT64_MAX) {
3137 245 : return LDB_SUCCESS;
3138 : }
3139 :
3140 : /* Fetch the "objectSid" */
3141 59 : ret = dsdb_module_search_dn(ac->module, ac, &res, ac->msg->dn, attrs,
3142 : DSDB_FLAG_NEXT_MODULE, ac->req);
3143 59 : if (ret != LDB_SUCCESS) {
3144 0 : return ret;
3145 : }
3146 59 : self_sid = samdb_result_dom_sid(res, res->msgs[0], "objectSid");
3147 59 : if (self_sid == NULL) {
3148 0 : return ldb_module_operr(ac->module);
3149 : }
3150 :
3151 59 : ret = samldb_check_pwd_last_set_acl(ac, self_sid);
3152 59 : if (ret != LDB_SUCCESS) {
3153 0 : return ret;
3154 : }
3155 :
3156 59 : return LDB_SUCCESS;
3157 : }
3158 :
3159 189 : static int samldb_lockout_time(struct samldb_ctx *ac)
3160 : {
3161 189 : struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
3162 0 : NTTIME lockoutTime;
3163 0 : struct ldb_message_element *el;
3164 0 : struct ldb_message *tmp_msg;
3165 0 : int ret;
3166 :
3167 189 : ret = dsdb_get_expected_new_values(ac,
3168 189 : ac->msg,
3169 : "lockoutTime",
3170 : &el,
3171 189 : ac->req->operation);
3172 189 : if (ret != LDB_SUCCESS) {
3173 0 : return ret;
3174 : }
3175 :
3176 189 : if (el == NULL || el->num_values == 0) {
3177 0 : ldb_asprintf_errstring(ldb,
3178 : "%08X: samldb: 'lockoutTime' can't be deleted!",
3179 0 : W_ERROR_V(WERR_DS_ILLEGAL_MOD_OPERATION));
3180 0 : return LDB_ERR_UNWILLING_TO_PERFORM;
3181 : }
3182 :
3183 : /* Create a temporary message for fetching the "lockoutTime" */
3184 189 : tmp_msg = ldb_msg_new(ac->msg);
3185 189 : if (tmp_msg == NULL) {
3186 0 : return ldb_module_oom(ac->module);
3187 : }
3188 189 : ret = ldb_msg_add(tmp_msg, el, 0);
3189 189 : if (ret != LDB_SUCCESS) {
3190 0 : return ret;
3191 : }
3192 189 : lockoutTime = ldb_msg_find_attr_as_int64(tmp_msg,
3193 : "lockoutTime",
3194 : 0);
3195 189 : talloc_free(tmp_msg);
3196 :
3197 189 : if (lockoutTime != 0) {
3198 63 : return LDB_SUCCESS;
3199 : }
3200 :
3201 : /* lockoutTime == 0 resets badPwdCount */
3202 126 : ldb_msg_remove_attr(ac->msg, "badPwdCount");
3203 126 : ret = samdb_msg_append_int(ldb, ac->msg, ac->msg,
3204 : "badPwdCount", 0,
3205 : LDB_FLAG_MOD_REPLACE);
3206 126 : if (ret != LDB_SUCCESS) {
3207 0 : return ret;
3208 : }
3209 :
3210 126 : return LDB_SUCCESS;
3211 : }
3212 :
3213 146 : static int samldb_group_type_change(struct samldb_ctx *ac)
3214 : {
3215 146 : struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
3216 0 : uint32_t group_type, old_group_type, account_type;
3217 0 : struct ldb_message_element *el;
3218 0 : struct ldb_message *tmp_msg;
3219 0 : int ret;
3220 0 : struct ldb_result *res;
3221 146 : const char * const attrs[] = { "groupType", NULL };
3222 :
3223 146 : ret = dsdb_get_expected_new_values(ac,
3224 146 : ac->msg,
3225 : "groupType",
3226 : &el,
3227 146 : ac->req->operation);
3228 146 : if (ret != LDB_SUCCESS) {
3229 0 : return ret;
3230 : }
3231 :
3232 146 : if (el == NULL) {
3233 : /* we are not affected */
3234 3 : return LDB_SUCCESS;
3235 : }
3236 :
3237 : /* Create a temporary message for fetching the "groupType" */
3238 143 : tmp_msg = ldb_msg_new(ac->msg);
3239 143 : if (tmp_msg == NULL) {
3240 0 : return ldb_module_oom(ac->module);
3241 : }
3242 143 : ret = ldb_msg_add(tmp_msg, el, 0);
3243 143 : if (ret != LDB_SUCCESS) {
3244 0 : return ret;
3245 : }
3246 143 : group_type = ldb_msg_find_attr_as_uint(tmp_msg, "groupType", 0);
3247 143 : talloc_free(tmp_msg);
3248 :
3249 143 : ret = dsdb_module_search_dn(ac->module, ac, &res, ac->msg->dn, attrs,
3250 : DSDB_FLAG_NEXT_MODULE |
3251 : DSDB_SEARCH_SHOW_DELETED, ac->req);
3252 143 : if (ret != LDB_SUCCESS) {
3253 0 : return ret;
3254 : }
3255 143 : old_group_type = ldb_msg_find_attr_as_uint(res->msgs[0], "groupType", 0);
3256 143 : if (old_group_type == 0) {
3257 0 : return ldb_operr(ldb);
3258 : }
3259 :
3260 : /* Group type switching isn't so easy as it seems: We can only
3261 : * change in this directions: global <-> universal <-> local
3262 : * On each step also the group type itself
3263 : * (security/distribution) is variable. */
3264 :
3265 143 : if (ldb_request_get_control(ac->req, LDB_CONTROL_PROVISION_OID) == NULL) {
3266 143 : switch (group_type) {
3267 41 : case GTYPE_SECURITY_GLOBAL_GROUP:
3268 : case GTYPE_DISTRIBUTION_GLOBAL_GROUP:
3269 : /* change to "universal" allowed */
3270 41 : if ((old_group_type == GTYPE_SECURITY_DOMAIN_LOCAL_GROUP) ||
3271 0 : (old_group_type == GTYPE_DISTRIBUTION_DOMAIN_LOCAL_GROUP)) {
3272 9 : ldb_set_errstring(ldb,
3273 : "samldb: Change from security/distribution local group forbidden!");
3274 9 : return LDB_ERR_UNWILLING_TO_PERFORM;
3275 : }
3276 32 : break;
3277 :
3278 43 : case GTYPE_SECURITY_UNIVERSAL_GROUP:
3279 : case GTYPE_DISTRIBUTION_UNIVERSAL_GROUP:
3280 : /* each change allowed */
3281 43 : break;
3282 44 : case GTYPE_SECURITY_DOMAIN_LOCAL_GROUP:
3283 : case GTYPE_DISTRIBUTION_DOMAIN_LOCAL_GROUP:
3284 : /* change to "universal" allowed */
3285 44 : if ((old_group_type == GTYPE_SECURITY_GLOBAL_GROUP) ||
3286 0 : (old_group_type == GTYPE_DISTRIBUTION_GLOBAL_GROUP)) {
3287 9 : ldb_set_errstring(ldb,
3288 : "samldb: Change from security/distribution global group forbidden!");
3289 9 : return LDB_ERR_UNWILLING_TO_PERFORM;
3290 : }
3291 35 : break;
3292 :
3293 15 : case GTYPE_SECURITY_BUILTIN_LOCAL_GROUP:
3294 : default:
3295 : /* we don't allow this "groupType" values */
3296 15 : return LDB_ERR_UNWILLING_TO_PERFORM;
3297 0 : break;
3298 : }
3299 : }
3300 :
3301 110 : account_type = ds_gtype2atype(group_type);
3302 110 : if (account_type == 0) {
3303 0 : ldb_set_errstring(ldb, "samldb: Unrecognized account type!");
3304 0 : return LDB_ERR_UNWILLING_TO_PERFORM;
3305 : }
3306 110 : ret = samdb_msg_append_uint(ldb, ac->msg, ac->msg, "sAMAccountType",
3307 : account_type, LDB_FLAG_MOD_REPLACE);
3308 110 : if (ret != LDB_SUCCESS) {
3309 0 : return ret;
3310 : }
3311 :
3312 110 : return LDB_SUCCESS;
3313 : }
3314 :
3315 5159 : static int samldb_member_check(struct samldb_ctx *ac)
3316 : {
3317 5159 : const char * const attrs[] = { "objectSid", NULL };
3318 5159 : struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
3319 39 : struct ldb_message_element *el;
3320 39 : struct ldb_dn *member_dn;
3321 39 : struct dom_sid *sid;
3322 39 : struct ldb_result *res;
3323 39 : struct dom_sid *group_sid;
3324 39 : unsigned int i, j;
3325 39 : int ret;
3326 :
3327 : /* Fetch information from the existing object */
3328 :
3329 5159 : ret = dsdb_module_search(ac->module, ac, &res, ac->msg->dn, LDB_SCOPE_BASE, attrs,
3330 : DSDB_FLAG_NEXT_MODULE | DSDB_SEARCH_SHOW_DELETED, ac->req, NULL);
3331 5159 : if (ret != LDB_SUCCESS) {
3332 0 : return ret;
3333 : }
3334 5159 : if (res->count != 1) {
3335 0 : return ldb_operr(ldb);
3336 : }
3337 :
3338 5159 : group_sid = samdb_result_dom_sid(res, res->msgs[0], "objectSid");
3339 5159 : if (group_sid == NULL) {
3340 0 : return ldb_operr(ldb);
3341 : }
3342 :
3343 : /* We've to walk over all modification entries and consider the "member"
3344 : * ones. */
3345 14394 : for (i = 0; i < ac->msg->num_elements; i++) {
3346 9238 : if (ldb_attr_cmp(ac->msg->elements[i].name, "member") != 0) {
3347 0 : continue;
3348 : }
3349 :
3350 9199 : el = &ac->msg->elements[i];
3351 18810 : for (j = 0; j < el->num_values; j++) {
3352 40 : struct ldb_result *group_res;
3353 9575 : const char *group_attrs[] = { "primaryGroupID" , NULL };
3354 40 : uint32_t prim_group_rid;
3355 :
3356 9575 : if (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_DELETE) {
3357 : /* Deletes will be handled in
3358 : * repl_meta_data, and deletes not
3359 : * matching a member will return
3360 : * LDB_ERR_UNWILLING_TO_PERFORM
3361 : * there */
3362 1026 : continue;
3363 : }
3364 :
3365 8988 : member_dn = ldb_dn_from_ldb_val(ac, ldb,
3366 8979 : &el->values[j]);
3367 8979 : if (!ldb_dn_validate(member_dn)) {
3368 3 : return ldb_operr(ldb);
3369 : }
3370 :
3371 : /* Denies to add "member"s to groups which are primary
3372 : * ones for them - in this case return
3373 : * ERR_ENTRY_ALREADY_EXISTS. */
3374 :
3375 8979 : ret = dsdb_module_search_dn(ac->module, ac, &group_res,
3376 : member_dn, group_attrs,
3377 : DSDB_FLAG_NEXT_MODULE, ac->req);
3378 8979 : if (ret == LDB_ERR_NO_SUCH_OBJECT) {
3379 : /* member DN doesn't exist yet */
3380 0 : continue;
3381 : }
3382 8979 : if (ret != LDB_SUCCESS) {
3383 0 : return ret;
3384 : }
3385 8979 : prim_group_rid = ldb_msg_find_attr_as_uint(group_res->msgs[0], "primaryGroupID", (uint32_t)-1);
3386 8979 : if (prim_group_rid == (uint32_t) -1) {
3387 : /* the member hasn't to be a user account ->
3388 : * therefore no check needed in this case. */
3389 430 : continue;
3390 : }
3391 :
3392 8549 : sid = dom_sid_add_rid(ac, samdb_domain_sid(ldb),
3393 : prim_group_rid);
3394 8549 : if (sid == NULL) {
3395 0 : return ldb_operr(ldb);
3396 : }
3397 :
3398 8549 : if (dom_sid_equal(group_sid, sid)) {
3399 3 : ldb_asprintf_errstring(ldb,
3400 : "samldb: member %s already set via primaryGroupID %u",
3401 : ldb_dn_get_linearized(member_dn), prim_group_rid);
3402 3 : return LDB_ERR_ENTRY_ALREADY_EXISTS;
3403 : }
3404 : }
3405 : }
3406 :
3407 5156 : talloc_free(res);
3408 :
3409 5156 : return LDB_SUCCESS;
3410 : }
3411 :
3412 : /* SAM objects have special rules regarding the "description" attribute on
3413 : * modify operations. */
3414 1037 : static int samldb_description_check(struct samldb_ctx *ac, bool *modified)
3415 : {
3416 1037 : struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
3417 1037 : const char * const attrs[] = { "objectClass", "description", NULL };
3418 123 : struct ldb_result *res;
3419 123 : unsigned int i;
3420 123 : int ret;
3421 :
3422 : /* Fetch information from the existing object */
3423 1037 : ret = dsdb_module_search(ac->module, ac, &res, ac->msg->dn, LDB_SCOPE_BASE, attrs,
3424 : DSDB_FLAG_NEXT_MODULE | DSDB_SEARCH_SHOW_DELETED, ac->req,
3425 : "(|(objectclass=user)(objectclass=group)(objectclass=samDomain)(objectclass=samServer))");
3426 1037 : if (ret != LDB_SUCCESS) {
3427 : /* don't treat it specially ... let normal error codes
3428 : happen from other places */
3429 0 : ldb_reset_err_string(ldb);
3430 0 : return LDB_SUCCESS;
3431 : }
3432 1037 : if (res->count == 0) {
3433 : /* we didn't match the filter */
3434 315 : talloc_free(res);
3435 315 : return LDB_SUCCESS;
3436 : }
3437 :
3438 : /* We've to walk over all modification entries and consider the
3439 : * "description" ones. */
3440 2536 : for (i = 0; i < ac->msg->num_elements; i++) {
3441 1814 : if (ldb_attr_cmp(ac->msg->elements[i].name, "description") == 0) {
3442 725 : ac->msg->elements[i].flags |= LDB_FLAG_INTERNAL_FORCE_SINGLE_VALUE_CHECK;
3443 725 : *modified = true;
3444 : }
3445 : }
3446 :
3447 722 : talloc_free(res);
3448 :
3449 722 : return LDB_SUCCESS;
3450 : }
3451 :
3452 : #define SPN_ALIAS_NONE 0
3453 : #define SPN_ALIAS_LINK 1
3454 : #define SPN_ALIAS_TARGET 2
3455 :
3456 5838 : static int find_spn_aliases(struct ldb_context *ldb,
3457 : TALLOC_CTX *mem_ctx,
3458 : const char *service_class,
3459 : char ***aliases,
3460 : size_t *n_aliases,
3461 : int *direction)
3462 : {
3463 : /*
3464 : * If you change the way this works, you should also look at changing
3465 : * LDB_lookup_spn_alias() in source4/dsdb/samdb/cracknames.c, which
3466 : * does some of the same work.
3467 : *
3468 : * In particular, note that sPNMappings are resolved on a first come,
3469 : * first served basis. For example, if we have
3470 : *
3471 : * host=ldap,cifs
3472 : * foo=ldap
3473 : * cifs=host,alerter
3474 : *
3475 : * then 'ldap', 'cifs', and 'host' will resolve to 'host', and
3476 : * 'alerter' will resolve to 'cifs'.
3477 : *
3478 : * If this resolution method is made more complicated, then the
3479 : * cracknames function should also be changed.
3480 : */
3481 272 : size_t i, j;
3482 272 : int ret;
3483 272 : bool ok;
3484 5838 : struct ldb_result *res = NULL;
3485 5838 : struct ldb_message_element *spnmappings = NULL;
3486 5838 : TALLOC_CTX *tmp_ctx = NULL;
3487 5838 : struct ldb_dn *service_dn = NULL;
3488 :
3489 5838 : const char *attrs[] = {
3490 : "sPNMappings",
3491 : NULL
3492 : };
3493 :
3494 5838 : *direction = SPN_ALIAS_NONE;
3495 :
3496 5838 : tmp_ctx = talloc_new(mem_ctx);
3497 5838 : if (tmp_ctx == NULL) {
3498 0 : return ldb_oom(ldb);
3499 : }
3500 :
3501 5838 : service_dn = ldb_dn_new(
3502 : tmp_ctx, ldb,
3503 : "CN=Directory Service,CN=Windows NT,CN=Services");
3504 5838 : if (service_dn == NULL) {
3505 0 : talloc_free(tmp_ctx);
3506 0 : return ldb_oom(ldb);
3507 : }
3508 :
3509 5838 : ok = ldb_dn_add_base(service_dn, ldb_get_config_basedn(ldb));
3510 5838 : if (! ok) {
3511 0 : talloc_free(tmp_ctx);
3512 0 : return LDB_ERR_OPERATIONS_ERROR;
3513 : }
3514 :
3515 5838 : ret = ldb_search(ldb, tmp_ctx, &res, service_dn, LDB_SCOPE_BASE,
3516 : attrs, "(objectClass=nTDSService)");
3517 :
3518 5838 : if (ret != LDB_SUCCESS || res->count != 1) {
3519 0 : DBG_WARNING("sPNMappings not found.\n");
3520 0 : talloc_free(tmp_ctx);
3521 0 : return ret;
3522 : }
3523 :
3524 5838 : spnmappings = ldb_msg_find_element(res->msgs[0], "sPNMappings");
3525 5838 : if (spnmappings == NULL || spnmappings->num_values == 0) {
3526 0 : DBG_WARNING("no sPNMappings attribute\n");
3527 0 : talloc_free(tmp_ctx);
3528 0 : return LDB_ERR_NO_SUCH_OBJECT;
3529 : }
3530 5838 : *n_aliases = 0;
3531 :
3532 7445 : for (i = 0; i < spnmappings->num_values; i++) {
3533 5838 : char *p = NULL;
3534 6110 : char *mapping = talloc_strndup(
3535 : tmp_ctx,
3536 5838 : (char *)spnmappings->values[i].data,
3537 5838 : spnmappings->values[i].length);
3538 5838 : if (mapping == NULL) {
3539 0 : talloc_free(tmp_ctx);
3540 0 : return ldb_oom(ldb);
3541 : }
3542 :
3543 5838 : p = strchr(mapping, '=');
3544 5838 : if (p == NULL) {
3545 0 : talloc_free(tmp_ctx);
3546 0 : return LDB_ERR_ALIAS_PROBLEM;
3547 : }
3548 5838 : p[0] = '\0';
3549 5838 : p++;
3550 :
3551 5838 : if (strcasecmp(mapping, service_class) == 0) {
3552 : /*
3553 : * We need to return the reverse aliases for this one.
3554 : *
3555 : * typically, this means the service_class is "host"
3556 : * and the mapping is "host=alerter,appmgmt,cisvc,..",
3557 : * so we get "alerter", "appmgmt", etc in the list of
3558 : * aliases.
3559 : */
3560 :
3561 : /* There is one more field than there are commas */
3562 3716 : size_t n = 1;
3563 :
3564 1560240 : for (j = 0; p[j] != '\0'; j++) {
3565 1556300 : if (p[j] == ',') {
3566 204880 : n++;
3567 204880 : p[j] = '\0';
3568 : }
3569 : }
3570 3940 : *aliases = talloc_array(mem_ctx, char*, n);
3571 3940 : if (*aliases == NULL) {
3572 0 : talloc_free(tmp_ctx);
3573 0 : return ldb_oom(ldb);
3574 : }
3575 3940 : *n_aliases = n;
3576 3940 : talloc_steal(mem_ctx, mapping);
3577 212984 : for (j = 0; j < n; j++) {
3578 208820 : (*aliases)[j] = p;
3579 208820 : p += strlen(p) + 1;
3580 : }
3581 3940 : talloc_free(tmp_ctx);
3582 3940 : *direction = SPN_ALIAS_LINK;
3583 3940 : return LDB_SUCCESS;
3584 : }
3585 : /*
3586 : * We need to look along the list to see if service_class is
3587 : * there; if so, we return a list of one item (probably "host").
3588 : */
3589 2460 : do {
3590 96539 : char *str = p;
3591 96539 : p = strchr(p, ',');
3592 96539 : if (p != NULL) {
3593 94932 : p[0] = '\0';
3594 94932 : p++;
3595 : }
3596 96539 : if (strcasecmp(str, service_class) == 0) {
3597 291 : *aliases = talloc_array(mem_ctx, char*, 1);
3598 291 : if (*aliases == NULL) {
3599 0 : talloc_free(tmp_ctx);
3600 0 : return ldb_oom(ldb);
3601 : }
3602 291 : *n_aliases = 1;
3603 291 : (*aliases)[0] = mapping;
3604 291 : talloc_steal(mem_ctx, mapping);
3605 291 : talloc_free(tmp_ctx);
3606 291 : *direction = SPN_ALIAS_TARGET;
3607 291 : return LDB_SUCCESS;
3608 : }
3609 96248 : } while (p != NULL);
3610 : }
3611 1607 : DBG_INFO("no sPNMappings alias for '%s'\n", service_class);
3612 1607 : talloc_free(tmp_ctx);
3613 1607 : *aliases = NULL;
3614 1607 : *n_aliases = 0;
3615 1607 : return LDB_SUCCESS;
3616 : }
3617 :
3618 :
3619 215069 : static int get_spn_dn(struct ldb_context *ldb,
3620 : TALLOC_CTX *tmp_ctx,
3621 : const char *candidate,
3622 : struct ldb_dn **dn)
3623 : {
3624 12150 : int ret;
3625 215069 : const char *empty_attrs[] = { NULL };
3626 215069 : struct ldb_message *msg = NULL;
3627 215069 : struct ldb_dn *base_dn = ldb_get_default_basedn(ldb);
3628 :
3629 215069 : const char *enc_candidate = NULL;
3630 :
3631 215069 : *dn = NULL;
3632 :
3633 215069 : enc_candidate = ldb_binary_encode_string(tmp_ctx, candidate);
3634 215069 : if (enc_candidate == NULL) {
3635 0 : return ldb_operr(ldb);
3636 : }
3637 :
3638 215069 : ret = dsdb_search_one(ldb,
3639 : tmp_ctx,
3640 : &msg,
3641 : base_dn,
3642 : LDB_SCOPE_SUBTREE,
3643 : empty_attrs,
3644 : 0,
3645 : "(servicePrincipalName=%s)",
3646 : enc_candidate);
3647 215069 : if (ret != LDB_SUCCESS) {
3648 202315 : return ret;
3649 : }
3650 608 : *dn = msg->dn;
3651 608 : return LDB_SUCCESS;
3652 : }
3653 :
3654 :
3655 68 : static int check_spn_write_rights(struct ldb_context *ldb,
3656 : TALLOC_CTX *mem_ctx,
3657 : const char *spn,
3658 : struct ldb_dn *dn)
3659 : {
3660 2 : int ret;
3661 68 : struct ldb_message *msg = NULL;
3662 68 : struct ldb_message_element *del_el = NULL;
3663 68 : struct ldb_message_element *add_el = NULL;
3664 68 : struct ldb_val val = {
3665 : .data = discard_const_p(uint8_t, spn),
3666 68 : .length = strlen(spn)
3667 : };
3668 :
3669 68 : msg = ldb_msg_new(mem_ctx);
3670 68 : if (msg == NULL) {
3671 0 : return ldb_oom(ldb);
3672 : }
3673 68 : msg->dn = dn;
3674 :
3675 68 : ret = ldb_msg_add_empty(msg,
3676 : "servicePrincipalName",
3677 : LDB_FLAG_MOD_DELETE,
3678 : &del_el);
3679 68 : if (ret != LDB_SUCCESS) {
3680 0 : talloc_free(msg);
3681 0 : return ret;
3682 : }
3683 :
3684 68 : del_el->values = talloc_array(msg->elements, struct ldb_val, 1);
3685 68 : if (del_el->values == NULL) {
3686 0 : talloc_free(msg);
3687 0 : return ret;
3688 : }
3689 :
3690 68 : del_el->values[0] = val;
3691 68 : del_el->num_values = 1;
3692 :
3693 68 : ret = ldb_msg_add_empty(msg,
3694 : "servicePrincipalName",
3695 : LDB_FLAG_MOD_ADD,
3696 : &add_el);
3697 68 : if (ret != LDB_SUCCESS) {
3698 0 : talloc_free(msg);
3699 0 : return ret;
3700 : }
3701 :
3702 68 : add_el->values = talloc_array(msg->elements, struct ldb_val, 1);
3703 68 : if (add_el->values == NULL) {
3704 0 : talloc_free(msg);
3705 0 : return ret;
3706 : }
3707 :
3708 68 : add_el->values[0] = val;
3709 68 : add_el->num_values = 1;
3710 :
3711 68 : ret = ldb_modify(ldb, msg);
3712 68 : if (ret == LDB_ERR_NO_SUCH_ATTRIBUTE) {
3713 0 : DBG_ERR("hmm I think we're OK, but not sure\n");
3714 68 : } else if (ret != LDB_SUCCESS) {
3715 14 : DBG_ERR("SPN write rights check failed with %d\n", ret);
3716 14 : talloc_free(msg);
3717 14 : return ret;
3718 : }
3719 54 : talloc_free(msg);
3720 54 : return LDB_SUCCESS;
3721 : }
3722 :
3723 :
3724 5838 : static int check_spn_alias_collision(struct ldb_context *ldb,
3725 : TALLOC_CTX *mem_ctx,
3726 : const char *spn,
3727 : struct ldb_dn *target_dn)
3728 : {
3729 272 : int ret;
3730 5838 : char *service_class = NULL;
3731 5838 : char *spn_tail = NULL;
3732 5838 : char *p = NULL;
3733 5838 : char **aliases = NULL;
3734 5838 : size_t n_aliases = 0;
3735 272 : size_t i, len;
3736 5838 : TALLOC_CTX *tmp_ctx = NULL;
3737 5838 : const char *target_dnstr = ldb_dn_get_linearized(target_dn);
3738 272 : int link_direction;
3739 :
3740 5838 : tmp_ctx = talloc_new(mem_ctx);
3741 5838 : if (tmp_ctx == NULL) {
3742 0 : return ldb_oom(ldb);
3743 : }
3744 :
3745 : /*
3746 : * "dns/example.com/xxx" gives
3747 : * service_class = "dns"
3748 : * spn_tail = "example.com/xxx"
3749 : */
3750 5838 : p = strchr(spn, '/');
3751 5838 : if (p == NULL) {
3752 : /* bad SPN */
3753 0 : talloc_free(tmp_ctx);
3754 0 : return ldb_error(ldb,
3755 : LDB_ERR_OPERATIONS_ERROR,
3756 : "malformed servicePrincipalName");
3757 : }
3758 5838 : len = p - spn;
3759 :
3760 5838 : service_class = talloc_strndup(tmp_ctx, spn, len);
3761 5838 : if (service_class == NULL) {
3762 0 : talloc_free(tmp_ctx);
3763 0 : return ldb_oom(ldb);
3764 : }
3765 5838 : spn_tail = p + 1;
3766 :
3767 5838 : ret = find_spn_aliases(ldb,
3768 : tmp_ctx,
3769 : service_class,
3770 : &aliases,
3771 : &n_aliases,
3772 : &link_direction);
3773 5838 : if (ret != LDB_SUCCESS) {
3774 0 : talloc_free(tmp_ctx);
3775 0 : return ret;
3776 : }
3777 :
3778 : /*
3779 : * we have the list of aliases, and now we need to combined them with
3780 : * spn_tail and see if we can find the SPN.
3781 : */
3782 214555 : for (i = 0; i < n_aliases; i++) {
3783 208759 : struct ldb_dn *colliding_dn = NULL;
3784 208759 : const char *colliding_dnstr = NULL;
3785 :
3786 220635 : char *candidate = talloc_asprintf(tmp_ctx,
3787 : "%s/%s",
3788 208759 : aliases[i],
3789 : spn_tail);
3790 208759 : if (candidate == NULL) {
3791 0 : talloc_free(tmp_ctx);
3792 42 : return ldb_oom(ldb);
3793 : }
3794 :
3795 208759 : ret = get_spn_dn(ldb, tmp_ctx, candidate, &colliding_dn);
3796 208759 : if (ret == LDB_ERR_NO_SUCH_OBJECT) {
3797 208623 : DBG_DEBUG("SPN alias '%s' not found (good)\n",
3798 : candidate);
3799 208623 : talloc_free(candidate);
3800 208623 : continue;
3801 : }
3802 136 : if (ret != LDB_SUCCESS) {
3803 0 : DBG_ERR("SPN '%s' search error %d\n", candidate, ret);
3804 0 : talloc_free(tmp_ctx);
3805 0 : return ret;
3806 : }
3807 :
3808 136 : target_dnstr = ldb_dn_get_linearized(target_dn);
3809 : /*
3810 : * We have found an existing SPN that matches the alias. That
3811 : * is OK only if it is on the object we are trying to add to,
3812 : * or if the SPN on the other side is a more generic alias for
3813 : * this one and we also have rights to modify it.
3814 : *
3815 : * That is, we can put "host/X" and "cifs/X" on the same
3816 : * object, but not on different objects, unless we put the
3817 : * host/X on first, and could also change that object when we
3818 : * add cifs/X. It is forbidden to add the objects in the other
3819 : * order.
3820 : *
3821 : * The rationale for this is that adding "cifs/X" effectively
3822 : * changes "host/X" by diverting traffic. If "host/X" can be
3823 : * added after "cifs/X", a sneaky person could get "cifs/X" in
3824 : * first, making "host/X" have less effect than intended.
3825 : *
3826 : * Note: we also can't have "host/X" and "Host/X" on the same
3827 : * object, but that is not relevant here.
3828 : */
3829 :
3830 136 : ret = ldb_dn_compare(colliding_dn, target_dn);
3831 136 : if (ret != 0) {
3832 96 : colliding_dnstr = ldb_dn_get_linearized(colliding_dn);
3833 96 : DBG_ERR("trying to add SPN '%s' on '%s' when '%s' is "
3834 : "on '%s'\n",
3835 : spn,
3836 : target_dnstr,
3837 : candidate,
3838 : colliding_dnstr);
3839 :
3840 96 : if (link_direction == SPN_ALIAS_LINK) {
3841 : /* we don't allow host/X if there is a
3842 : * cifs/X */
3843 28 : talloc_free(tmp_ctx);
3844 28 : return LDB_ERR_CONSTRAINT_VIOLATION;
3845 : }
3846 68 : ret = check_spn_write_rights(ldb,
3847 : tmp_ctx,
3848 : candidate,
3849 : colliding_dn);
3850 68 : if (ret != LDB_SUCCESS) {
3851 14 : DBG_ERR("SPN '%s' is on '%s' so '%s' can't be "
3852 : "added to '%s'\n",
3853 : candidate,
3854 : colliding_dnstr,
3855 : spn,
3856 : target_dnstr);
3857 14 : talloc_free(tmp_ctx);
3858 14 : ldb_asprintf_errstring(ldb,
3859 : "samldb: spn[%s] would cause a conflict",
3860 : spn);
3861 14 : return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
3862 : }
3863 : } else {
3864 40 : DBG_INFO("SPNs '%s' and '%s' alias both on '%s'\n",
3865 : candidate, spn, target_dnstr);
3866 : }
3867 94 : talloc_free(candidate);
3868 : }
3869 :
3870 5796 : talloc_free(tmp_ctx);
3871 5796 : return LDB_SUCCESS;
3872 : }
3873 :
3874 6310 : static int check_spn_direct_collision(struct ldb_context *ldb,
3875 : TALLOC_CTX *mem_ctx,
3876 : const char *spn,
3877 : struct ldb_dn *target_dn)
3878 : {
3879 274 : int ret;
3880 6310 : TALLOC_CTX *tmp_ctx = NULL;
3881 6310 : struct ldb_dn *colliding_dn = NULL;
3882 6310 : const char *target_dnstr = NULL;
3883 6310 : const char *colliding_dnstr = NULL;
3884 :
3885 6310 : tmp_ctx = talloc_new(mem_ctx);
3886 6310 : if (tmp_ctx == NULL) {
3887 0 : return ldb_oom(ldb);
3888 : }
3889 :
3890 6310 : ret = get_spn_dn(ldb, tmp_ctx, spn, &colliding_dn);
3891 6310 : if (ret == LDB_ERR_NO_SUCH_OBJECT) {
3892 5838 : DBG_DEBUG("SPN '%s' not found (good)\n", spn);
3893 5838 : talloc_free(tmp_ctx);
3894 5838 : return LDB_SUCCESS;
3895 : }
3896 472 : if (ret != LDB_SUCCESS) {
3897 0 : DBG_ERR("SPN '%s' search error %d\n", spn, ret);
3898 0 : talloc_free(tmp_ctx);
3899 0 : if (ret == LDB_ERR_COMPARE_TRUE) {
3900 : /*
3901 : * COMPARE_TRUE has special meaning here and we don't
3902 : * want to return it by mistake.
3903 : */
3904 0 : ret = LDB_ERR_OPERATIONS_ERROR;
3905 : }
3906 0 : return ret;
3907 : }
3908 : /*
3909 : * We have found this exact SPN. This is mostly harmless (depend on
3910 : * ADD vs REPLACE) when the spn is being put on the object that
3911 : * already has, so we let it through to succeed or fail as some other
3912 : * module sees fit.
3913 : */
3914 472 : target_dnstr = ldb_dn_get_linearized(target_dn);
3915 472 : ret = ldb_dn_compare(colliding_dn, target_dn);
3916 472 : if (ret != 0) {
3917 23 : colliding_dnstr = ldb_dn_get_linearized(colliding_dn);
3918 23 : DBG_ERR("SPN '%s' is on '%s' so it can't be "
3919 : "added to '%s'\n",
3920 : spn,
3921 : colliding_dnstr,
3922 : target_dnstr);
3923 23 : ldb_asprintf_errstring(ldb,
3924 : "samldb: spn[%s] would cause a conflict",
3925 : spn);
3926 23 : talloc_free(tmp_ctx);
3927 23 : return LDB_ERR_CONSTRAINT_VIOLATION;
3928 : }
3929 :
3930 449 : DBG_INFO("SPN '%s' is already on '%s'\n",
3931 : spn, target_dnstr);
3932 449 : talloc_free(tmp_ctx);
3933 449 : return LDB_ERR_COMPARE_TRUE;
3934 : }
3935 :
3936 :
3937 6326 : static int count_spn_components(struct ldb_val val)
3938 : {
3939 : /*
3940 : * a 3 part servicePrincipalName has two slashes, like
3941 : * ldap/example.com/DomainDNSZones.example.com.
3942 : *
3943 : * In krb5_parse_name_flags() we don't count "\/" as a slash (i.e.
3944 : * escaped by a backslash), but this is not the behaviour of Windows
3945 : * on setting a servicePrincipalName -- slashes are counted regardless
3946 : * of backslashes.
3947 : *
3948 : * Accordingly, here we ignore backslashes. This will reject
3949 : * multi-slash SPNs that krb5_parse_name_flags() would accept, and
3950 : * allow ones in the form "a\/b" that it won't parse.
3951 : */
3952 274 : size_t i;
3953 6326 : int slashes = 0;
3954 209725 : for (i = 0; i < val.length; i++) {
3955 203407 : char c = val.data[i];
3956 203407 : if (c == '/') {
3957 7691 : slashes++;
3958 7691 : if (slashes == 3) {
3959 : /* at this point we don't care */
3960 8 : return 4;
3961 : }
3962 : }
3963 : }
3964 6318 : return slashes + 1;
3965 : }
3966 :
3967 :
3968 : /* Check that "servicePrincipalName" changes do not introduce a collision
3969 : * globally. */
3970 3823 : static int samldb_spn_uniqueness_check(struct samldb_ctx *ac,
3971 : struct ldb_message_element *spn_el)
3972 : {
3973 3823 : struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
3974 130 : int ret;
3975 3823 : const char *spn = NULL;
3976 130 : size_t i;
3977 3823 : TALLOC_CTX *tmp_ctx = talloc_new(ac->msg);
3978 3823 : if (tmp_ctx == NULL) {
3979 0 : return ldb_oom(ldb);
3980 : }
3981 :
3982 10068 : for (i = 0; i < spn_el->num_values; i++) {
3983 274 : int n_components;
3984 6326 : spn = (char *)spn_el->values[i].data;
3985 :
3986 6326 : n_components = count_spn_components(spn_el->values[i]);
3987 6326 : if (n_components > 3 || n_components < 2) {
3988 16 : ldb_asprintf_errstring(ldb,
3989 : "samldb: spn[%s] invalid with %u components",
3990 : spn, n_components);
3991 16 : talloc_free(tmp_ctx);
3992 16 : return LDB_ERR_CONSTRAINT_VIOLATION;
3993 : }
3994 :
3995 6584 : ret = check_spn_direct_collision(ldb,
3996 : tmp_ctx,
3997 : spn,
3998 6310 : ac->msg->dn);
3999 6310 : if (ret == LDB_ERR_COMPARE_TRUE) {
4000 449 : DBG_INFO("SPN %s re-added to the same object\n", spn);
4001 449 : continue;
4002 : }
4003 5861 : if (ret != LDB_SUCCESS) {
4004 23 : DBG_ERR("SPN %s failed direct uniqueness check\n", spn);
4005 23 : talloc_free(tmp_ctx);
4006 23 : return ret;
4007 : }
4008 :
4009 6110 : ret = check_spn_alias_collision(ldb,
4010 : tmp_ctx,
4011 : spn,
4012 5838 : ac->msg->dn);
4013 :
4014 5838 : if (ret == LDB_ERR_NO_SUCH_OBJECT) {
4015 : /* we have no sPNMappings, hence no aliases */
4016 0 : break;
4017 : }
4018 5838 : if (ret != LDB_SUCCESS) {
4019 42 : DBG_ERR("SPN %s failed alias uniqueness check\n", spn);
4020 42 : talloc_free(tmp_ctx);
4021 42 : return ret;
4022 : }
4023 5796 : DBG_INFO("SPN %s seems to be unique\n", spn);
4024 : }
4025 :
4026 3742 : talloc_free(tmp_ctx);
4027 3742 : return LDB_SUCCESS;
4028 : }
4029 :
4030 :
4031 :
4032 : /* This trigger adapts the "servicePrincipalName" attributes if the
4033 : * "dNSHostName" and/or "sAMAccountName" attribute change(s) */
4034 1611 : static int samldb_service_principal_names_change(struct samldb_ctx *ac)
4035 : {
4036 1611 : struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
4037 1611 : struct ldb_message_element *el = NULL, *el2 = NULL;
4038 84 : struct ldb_message *msg;
4039 1611 : const char * const attrs[] = { "servicePrincipalName", NULL };
4040 84 : struct ldb_result *res;
4041 1611 : const char *dns_hostname = NULL, *old_dns_hostname = NULL,
4042 1611 : *sam_accountname = NULL, *old_sam_accountname = NULL;
4043 84 : unsigned int i, j;
4044 84 : int ret;
4045 :
4046 1695 : ret = dsdb_get_expected_new_values(ac,
4047 1611 : ac->msg,
4048 : "dNSHostName",
4049 : &el,
4050 1611 : ac->req->operation);
4051 1611 : if (ret != LDB_SUCCESS) {
4052 0 : return ret;
4053 : }
4054 1695 : ret = dsdb_get_expected_new_values(ac,
4055 1611 : ac->msg,
4056 : "sAMAccountName",
4057 : &el2,
4058 1611 : ac->req->operation);
4059 1611 : if (ret != LDB_SUCCESS) {
4060 0 : return ret;
4061 : }
4062 1611 : if ((el == NULL) && (el2 == NULL)) {
4063 : /* we are not affected */
4064 3 : return LDB_SUCCESS;
4065 : }
4066 :
4067 : /* Create a temporary message for fetching the "dNSHostName" */
4068 1608 : if (el != NULL) {
4069 764 : const char *dns_attrs[] = { "dNSHostName", NULL };
4070 764 : msg = ldb_msg_new(ac->msg);
4071 764 : if (msg == NULL) {
4072 0 : return ldb_module_oom(ac->module);
4073 : }
4074 764 : ret = ldb_msg_add(msg, el, 0);
4075 764 : if (ret != LDB_SUCCESS) {
4076 0 : return ret;
4077 : }
4078 764 : dns_hostname = talloc_strdup(ac,
4079 : ldb_msg_find_attr_as_string(msg, "dNSHostName", NULL));
4080 764 : if (dns_hostname == NULL) {
4081 0 : return ldb_module_oom(ac->module);
4082 : }
4083 :
4084 764 : talloc_free(msg);
4085 :
4086 764 : ret = dsdb_module_search_dn(ac->module, ac, &res, ac->msg->dn,
4087 : dns_attrs, DSDB_FLAG_NEXT_MODULE, ac->req);
4088 764 : if (ret == LDB_SUCCESS) {
4089 764 : old_dns_hostname = ldb_msg_find_attr_as_string(res->msgs[0], "dNSHostName", NULL);
4090 : }
4091 : }
4092 :
4093 : /* Create a temporary message for fetching the "sAMAccountName" */
4094 1608 : if (el2 != NULL) {
4095 866 : char *tempstr, *tempstr2 = NULL;
4096 866 : const char *acct_attrs[] = { "sAMAccountName", NULL };
4097 :
4098 866 : msg = ldb_msg_new(ac->msg);
4099 866 : if (msg == NULL) {
4100 0 : return ldb_module_oom(ac->module);
4101 : }
4102 866 : ret = ldb_msg_add(msg, el2, 0);
4103 866 : if (ret != LDB_SUCCESS) {
4104 0 : return ret;
4105 : }
4106 866 : tempstr = talloc_strdup(ac,
4107 : ldb_msg_find_attr_as_string(msg, "sAMAccountName", NULL));
4108 866 : talloc_free(msg);
4109 :
4110 866 : ret = dsdb_module_search_dn(ac->module, ac, &res, ac->msg->dn, acct_attrs,
4111 : DSDB_FLAG_NEXT_MODULE, ac->req);
4112 866 : if (ret == LDB_SUCCESS) {
4113 866 : tempstr2 = talloc_strdup(ac,
4114 866 : ldb_msg_find_attr_as_string(res->msgs[0],
4115 : "sAMAccountName", NULL));
4116 : }
4117 :
4118 :
4119 : /* The "sAMAccountName" needs some additional trimming: we need
4120 : * to remove the trailing "$"s if they exist. */
4121 866 : if ((tempstr != NULL) && (tempstr[0] != '\0') &&
4122 866 : (tempstr[strlen(tempstr) - 1] == '$')) {
4123 184 : tempstr[strlen(tempstr) - 1] = '\0';
4124 : }
4125 866 : if ((tempstr2 != NULL) && (tempstr2[0] != '\0') &&
4126 866 : (tempstr2[strlen(tempstr2) - 1] == '$')) {
4127 231 : tempstr2[strlen(tempstr2) - 1] = '\0';
4128 : }
4129 866 : sam_accountname = tempstr;
4130 866 : old_sam_accountname = tempstr2;
4131 : }
4132 :
4133 1608 : if (old_dns_hostname == NULL) {
4134 : /* we cannot change when the old name is unknown */
4135 1492 : dns_hostname = NULL;
4136 : }
4137 1724 : if ((old_dns_hostname != NULL) && (dns_hostname != NULL) &&
4138 116 : (strcasecmp_m(old_dns_hostname, dns_hostname) == 0)) {
4139 : /* The "dNSHostName" didn't change */
4140 51 : dns_hostname = NULL;
4141 : }
4142 :
4143 1608 : if (old_sam_accountname == NULL) {
4144 : /* we cannot change when the old name is unknown */
4145 742 : sam_accountname = NULL;
4146 : }
4147 2474 : if ((old_sam_accountname != NULL) && (sam_accountname != NULL) &&
4148 866 : (strcasecmp_m(old_sam_accountname, sam_accountname) == 0)) {
4149 : /* The "sAMAccountName" didn't change */
4150 443 : sam_accountname = NULL;
4151 : }
4152 :
4153 1608 : if ((dns_hostname == NULL) && (sam_accountname == NULL)) {
4154 : /* Well, there are information missing (old name(s)) or the
4155 : * names didn't change. We've nothing to do and can exit here */
4156 1050 : return LDB_SUCCESS;
4157 : }
4158 :
4159 : /*
4160 : * Potential "servicePrincipalName" changes in the same request have
4161 : * to be handled before the update (Windows behaviour).
4162 : *
4163 : * We extract the SPN changes into a new message and run it through
4164 : * the stack from this module, so that it subjects them to the SPN
4165 : * checks we have here.
4166 : */
4167 480 : el = ldb_msg_find_element(ac->msg, "servicePrincipalName");
4168 480 : if (el != NULL) {
4169 34 : msg = ldb_msg_new(ac->msg);
4170 34 : if (msg == NULL) {
4171 0 : return ldb_module_oom(ac->module);
4172 : }
4173 34 : msg->dn = ac->msg->dn;
4174 :
4175 0 : do {
4176 34 : ret = ldb_msg_add(msg, el, el->flags);
4177 34 : if (ret != LDB_SUCCESS) {
4178 0 : return ret;
4179 : }
4180 :
4181 34 : ldb_msg_remove_element(ac->msg, el);
4182 :
4183 34 : el = ldb_msg_find_element(ac->msg,
4184 : "servicePrincipalName");
4185 34 : } while (el != NULL);
4186 :
4187 34 : ret = dsdb_module_modify(ac->module, msg,
4188 : DSDB_FLAG_OWN_MODULE, ac->req);
4189 34 : if (ret != LDB_SUCCESS) {
4190 0 : return ret;
4191 : }
4192 34 : talloc_free(msg);
4193 : }
4194 :
4195 : /* Fetch the "servicePrincipalName"s if any */
4196 480 : ret = dsdb_module_search(ac->module, ac, &res, ac->msg->dn, LDB_SCOPE_BASE, attrs,
4197 : DSDB_FLAG_NEXT_MODULE, ac->req, NULL);
4198 480 : if (ret != LDB_SUCCESS) {
4199 0 : return ret;
4200 : }
4201 480 : if ((res->count != 1) || (res->msgs[0]->num_elements > 1)) {
4202 0 : return ldb_operr(ldb);
4203 : }
4204 :
4205 480 : if (res->msgs[0]->num_elements == 1) {
4206 : /*
4207 : * Yes, we do have "servicePrincipalName"s. First we update them
4208 : * locally, that means we do always substitute the current
4209 : * "dNSHostName" with the new one and/or "sAMAccountName"
4210 : * without "$" with the new one and then we append the
4211 : * modified "servicePrincipalName"s as a message element
4212 : * replace to the modification request (Windows behaviour). We
4213 : * need also to make sure that the values remain case-
4214 : * insensitively unique.
4215 : */
4216 :
4217 71 : ret = ldb_msg_add_empty(ac->msg, "servicePrincipalName",
4218 : LDB_FLAG_MOD_REPLACE, &el);
4219 71 : if (ret != LDB_SUCCESS) {
4220 0 : return ret;
4221 : }
4222 :
4223 199 : for (i = 0; i < res->msgs[0]->elements[0].num_values; i++) {
4224 6 : char *old_str, *new_str;
4225 128 : char *pos = NULL;
4226 6 : const char *tok;
4227 6 : struct ldb_val *vals;
4228 128 : bool found = false;
4229 :
4230 128 : old_str = (char *)
4231 128 : res->msgs[0]->elements[0].values[i].data;
4232 :
4233 128 : new_str = talloc_strdup(ac->msg,
4234 128 : strtok_r(old_str, "/", &pos));
4235 128 : if (new_str == NULL) {
4236 0 : return ldb_module_oom(ac->module);
4237 : }
4238 :
4239 262 : while ((tok = strtok_r(NULL, "/", &pos)) != NULL) {
4240 247 : if ((dns_hostname != NULL) &&
4241 113 : (strcasecmp_m(tok, old_dns_hostname) == 0)) {
4242 57 : tok = dns_hostname;
4243 : }
4244 175 : if ((sam_accountname != NULL) &&
4245 41 : (strcasecmp_m(tok, old_sam_accountname) == 0)) {
4246 17 : tok = sam_accountname;
4247 : }
4248 :
4249 134 : new_str = talloc_asprintf(ac->msg, "%s/%s",
4250 : new_str, tok);
4251 134 : if (new_str == NULL) {
4252 0 : return ldb_module_oom(ac->module);
4253 : }
4254 : }
4255 :
4256 : /* Uniqueness check */
4257 208 : for (j = 0; (!found) && (j < el->num_values); j++) {
4258 80 : if (strcasecmp_m((char *)el->values[j].data,
4259 : new_str) == 0) {
4260 19 : found = true;
4261 : }
4262 : }
4263 128 : if (found) {
4264 19 : continue;
4265 : }
4266 :
4267 : /*
4268 : * append the new "servicePrincipalName" -
4269 : * code derived from ldb_msg_add_value().
4270 : *
4271 : * Open coded to make it clear that we must
4272 : * append to the MOD_REPLACE el created above.
4273 : */
4274 109 : vals = talloc_realloc(ac->msg, el->values,
4275 : struct ldb_val,
4276 : el->num_values + 1);
4277 109 : if (vals == NULL) {
4278 0 : return ldb_module_oom(ac->module);
4279 : }
4280 109 : el->values = vals;
4281 109 : el->values[el->num_values] = data_blob_string_const(new_str);
4282 109 : ++(el->num_values);
4283 : }
4284 : }
4285 :
4286 480 : talloc_free(res);
4287 :
4288 480 : return LDB_SUCCESS;
4289 : }
4290 :
4291 : /* This checks the "fSMORoleOwner" attributes */
4292 1287 : static int samldb_fsmo_role_owner_check(struct samldb_ctx *ac)
4293 : {
4294 1287 : struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
4295 1287 : const char * const no_attrs[] = { NULL };
4296 166 : struct ldb_message_element *el;
4297 166 : struct ldb_message *tmp_msg;
4298 166 : struct ldb_dn *res_dn;
4299 166 : struct ldb_result *res;
4300 166 : int ret;
4301 1453 : ret = dsdb_get_expected_new_values(ac,
4302 1287 : ac->msg,
4303 : "fSMORoleOwner",
4304 : &el,
4305 1287 : ac->req->operation);
4306 1287 : if (ret != LDB_SUCCESS) {
4307 0 : return ret;
4308 : }
4309 :
4310 1287 : if (el == NULL) {
4311 : /* we are not affected */
4312 3 : return LDB_SUCCESS;
4313 : }
4314 1284 : if (el->num_values != 1) {
4315 6 : goto choose_error_code;
4316 : }
4317 :
4318 : /* Create a temporary message for fetching the "fSMORoleOwner" */
4319 1278 : tmp_msg = ldb_msg_new(ac->msg);
4320 1278 : if (tmp_msg == NULL) {
4321 0 : return ldb_module_oom(ac->module);
4322 : }
4323 1278 : ret = ldb_msg_add(tmp_msg, el, 0);
4324 1278 : if (ret != LDB_SUCCESS) {
4325 0 : return ret;
4326 : }
4327 1278 : res_dn = ldb_msg_find_attr_as_dn(ldb, ac, tmp_msg, "fSMORoleOwner");
4328 1278 : talloc_free(tmp_msg);
4329 :
4330 1278 : if (res_dn == NULL) {
4331 0 : ldb_set_errstring(ldb,
4332 : "samldb: 'fSMORoleOwner' attributes have to reference 'nTDSDSA' entries!");
4333 0 : goto choose_error_code;
4334 : }
4335 :
4336 : /* Fetched DN has to reference a "nTDSDSA" entry */
4337 1278 : ret = dsdb_module_search(ac->module, ac, &res, res_dn, LDB_SCOPE_BASE,
4338 : no_attrs,
4339 : DSDB_FLAG_NEXT_MODULE | DSDB_SEARCH_SHOW_DELETED,
4340 : ac->req, "(objectClass=nTDSDSA)");
4341 1278 : if (ret != LDB_SUCCESS) {
4342 0 : return ret;
4343 : }
4344 1278 : if (res->count != 1) {
4345 6 : ldb_set_errstring(ldb,
4346 : "samldb: 'fSMORoleOwner' attributes have to reference 'nTDSDSA' entries!");
4347 6 : return LDB_ERR_UNWILLING_TO_PERFORM;
4348 : }
4349 :
4350 1272 : talloc_free(res);
4351 :
4352 1272 : return LDB_SUCCESS;
4353 :
4354 6 : choose_error_code:
4355 : /* this is just how it is */
4356 6 : if (ac->req->operation == LDB_ADD) {
4357 3 : return LDB_ERR_CONSTRAINT_VIOLATION;
4358 : } else {
4359 3 : return LDB_ERR_UNWILLING_TO_PERFORM;
4360 : }
4361 : }
4362 :
4363 : /*
4364 : * Return zero if the number of zero bits in the address (looking from low to
4365 : * high) is equal to or greater than the length minus the mask. Otherwise it
4366 : * returns -1.
4367 : */
4368 140 : static int check_cidr_zero_bits(uint8_t *address, unsigned int len,
4369 : unsigned int mask)
4370 : {
4371 : /* <address> is an integer in big-endian form, <len> bits long. All
4372 : bits between <mask> and <len> must be zero. */
4373 0 : int i;
4374 0 : unsigned int byte_len;
4375 0 : unsigned int byte_mask;
4376 0 : unsigned int bit_mask;
4377 140 : if (len == 32) {
4378 60 : DBG_INFO("Looking at address %02x%02x%02x%02x, mask %u\n",
4379 : address[0], address[1], address[2], address[3],
4380 : mask);
4381 80 : } else if (len == 128){
4382 80 : DBG_INFO("Looking at address "
4383 : "%02x%02x-%02x%02x-%02x%02x-%02x%02x-"
4384 : "%02x%02x-%02x%02x-%02x%02x-%02x%02x, mask %u\n",
4385 : address[0], address[1], address[2], address[3],
4386 : address[4], address[5], address[6], address[7],
4387 : address[8], address[9], address[10], address[11],
4388 : address[12], address[13], address[14], address[15],
4389 : mask);
4390 : }
4391 :
4392 140 : if (mask > len){
4393 5 : DBG_INFO("mask %u is too big (> %u)\n", mask, len);
4394 5 : return -1;
4395 : }
4396 135 : if (mask == len){
4397 : /* single address subnet.
4398 : * In IPv4 all 255s is invalid by the bitmask != address rule
4399 : * in MS-ADTS. IPv6 does not suffer.
4400 : */
4401 10 : if (len == 32){
4402 5 : if (address[0] == 255 &&
4403 1 : address[1] == 255 &&
4404 1 : address[2] == 255 &&
4405 1 : address[3] == 255){
4406 1 : return -1;
4407 : }
4408 : }
4409 9 : return 0;
4410 : }
4411 :
4412 125 : byte_len = len / 8;
4413 125 : byte_mask = mask / 8;
4414 :
4415 717 : for (i = byte_len - 1; i > byte_mask; i--){
4416 594 : DBG_DEBUG("checking byte %d %02x\n", i, address[i]);
4417 594 : if (address[i] != 0){
4418 2 : return -1;
4419 : }
4420 : }
4421 123 : bit_mask = (1 << (8 - (mask & 7))) - 1;
4422 123 : DBG_DEBUG("checking bitmask %02x & %02x overlap %02x\n", bit_mask, address[byte_mask],
4423 : bit_mask & address[byte_mask]);
4424 123 : if (address[byte_mask] & bit_mask){
4425 15 : return -1;
4426 : }
4427 :
4428 : /* According to MS-ADTS, the mask can't exactly equal the bitmask for
4429 : * IPv4 (but this is fine for v6). That is 255.255.80.0/17 is bad,
4430 : * because the bitmask implied by "/17" is 255.255.80.0.
4431 : *
4432 : * The bit_mask used in the previous check is the complement of what
4433 : * we want here.
4434 : */
4435 108 : if (len == 32 && address[byte_mask] == (uint8_t)~bit_mask){
4436 37 : bool ok = false;
4437 41 : for (i = 0; i < byte_mask; i++){
4438 37 : if (address[i] != 255){
4439 33 : ok = true;
4440 33 : break;
4441 : }
4442 : }
4443 37 : if (ok == false){
4444 4 : return -1;
4445 : }
4446 : }
4447 104 : return 0;
4448 : }
4449 :
4450 :
4451 :
4452 165 : static int check_address_roundtrip(const char *address, int family,
4453 : const uint8_t *address_bytes,
4454 : char *buffer, int buffer_len)
4455 : {
4456 : /*
4457 : * Check that the address is in the canonical RFC5952 format for IPv6,
4458 : * and lacks extra leading zeros for each dotted decimal for IPv4.
4459 : * Handily this is what inet_ntop() gives you.
4460 : */
4461 165 : const char *address_redux = inet_ntop(family, address_bytes,
4462 : buffer, buffer_len);
4463 165 : if (address_redux == NULL){
4464 0 : DBG_INFO("Address round trip %s failed unexpectedly"
4465 : " with errno %d\n", address, errno);
4466 0 : return -1;
4467 : }
4468 165 : if (strcasecmp(address, address_redux) != 0){
4469 25 : DBG_INFO("Address %s round trips to %s; fail!\n",
4470 : address, address_redux);
4471 : /* If the address family is IPv6, and the address is in a
4472 : certain range
4473 :
4474 : */
4475 25 : if (strchr(address_redux, '.') != NULL){
4476 7 : DEBUG(0, ("The IPv6 address '%s' has the misfortune of "
4477 : "lying in a range that was once used for "
4478 : "IPv4 embedding (that is, it might also be "
4479 : "represented as '%s').\n", address,
4480 : address_redux));
4481 : }
4482 25 : return -1;
4483 : }
4484 140 : return 0;
4485 : }
4486 :
4487 :
4488 :
4489 : /*
4490 : * MS-ADTS v20150630 6.1.1.2.2.2.1 Subnet Object, refers to RFC1166 and
4491 : * RFC2373. It specifies something seemingly indistinguishable from an RFC4632
4492 : * CIDR address range without saying so explicitly. Here we follow the CIDR
4493 : * spec.
4494 : *
4495 : * Return 0 on success, -1 on error.
4496 : */
4497 210 : static int verify_cidr(const char *cidr)
4498 : {
4499 210 : char *address = NULL, *slash = NULL;
4500 0 : bool has_colon, has_dot;
4501 0 : int res, ret;
4502 0 : unsigned long mask;
4503 210 : uint8_t *address_bytes = NULL;
4504 210 : char *address_redux = NULL;
4505 0 : unsigned int address_len;
4506 210 : TALLOC_CTX *frame = NULL;
4507 210 : int error = 0;
4508 :
4509 210 : DBG_DEBUG("CIDR is %s\n", cidr);
4510 210 : frame = talloc_stackframe();
4511 210 : address = talloc_strdup(frame, cidr);
4512 210 : if (address == NULL){
4513 0 : goto error;
4514 : }
4515 :
4516 : /* there must be a '/' */
4517 210 : slash = strchr(address, '/');
4518 210 : if (slash == NULL){
4519 2 : goto error;
4520 : }
4521 : /* terminate the address for strchr, inet_pton */
4522 208 : *slash = '\0';
4523 :
4524 208 : mask = smb_strtoul(slash + 1, NULL, 10, &error, SMB_STR_FULL_STR_CONV);
4525 208 : if (mask == 0){
4526 6 : DBG_INFO("Windows does not like the zero mask, "
4527 : "so nor do we: %s\n", cidr);
4528 6 : goto error;
4529 : }
4530 :
4531 202 : if (error != 0){
4532 6 : DBG_INFO("CIDR mask is not a proper integer: %s\n", cidr);
4533 6 : goto error;
4534 : }
4535 :
4536 196 : address_bytes = talloc_size(frame, sizeof(struct in6_addr));
4537 196 : if (address_bytes == NULL){
4538 0 : goto error;
4539 : }
4540 :
4541 196 : address_redux = talloc_size(frame, INET6_ADDRSTRLEN);
4542 196 : if (address_redux == NULL){
4543 0 : goto error;
4544 : }
4545 :
4546 196 : DBG_INFO("found address %s, mask %lu\n", address, mask);
4547 196 : has_colon = (strchr(address, ':') == NULL) ? false : true;
4548 196 : has_dot = (strchr(address, '.') == NULL) ? false : true;
4549 196 : if (has_dot && has_colon){
4550 : /* This seems to be an IPv4 address embedded in IPv6, which is
4551 : icky. We don't support it. */
4552 2 : DBG_INFO("Refusing to consider cidr '%s' with dots and colons\n",
4553 : cidr);
4554 2 : goto error;
4555 194 : } else if (has_colon){ /* looks like IPv6 */
4556 115 : res = inet_pton(AF_INET6, address, address_bytes);
4557 115 : if (res != 1) {
4558 10 : DBG_INFO("Address in %s fails to parse as IPv6\n", cidr);
4559 10 : goto error;
4560 : }
4561 105 : address_len = 128;
4562 105 : if (check_address_roundtrip(address, AF_INET6, address_bytes,
4563 : address_redux, INET6_ADDRSTRLEN)){
4564 25 : goto error;
4565 : }
4566 79 : } else if (has_dot) {
4567 : /* looks like IPv4 */
4568 79 : if (strcmp(address, "0.0.0.0") == 0){
4569 1 : DBG_INFO("Windows does not like the zero IPv4 address, "
4570 : "so nor do we.\n");
4571 1 : goto error;
4572 : }
4573 78 : res = inet_pton(AF_INET, address, address_bytes);
4574 78 : if (res != 1) {
4575 18 : DBG_INFO("Address in %s fails to parse as IPv4\n", cidr);
4576 18 : goto error;
4577 : }
4578 60 : address_len = 32;
4579 :
4580 60 : if (check_address_roundtrip(address, AF_INET, address_bytes,
4581 : address_redux, INET_ADDRSTRLEN)){
4582 0 : goto error;
4583 : }
4584 : } else {
4585 : /* This doesn't look like an IP address at all. */
4586 0 : goto error;
4587 : }
4588 :
4589 140 : ret = check_cidr_zero_bits(address_bytes, address_len, mask);
4590 140 : talloc_free(frame);
4591 140 : return ret;
4592 70 : error:
4593 70 : talloc_free(frame);
4594 70 : return -1;
4595 : }
4596 :
4597 :
4598 210 : static int samldb_verify_subnet(struct samldb_ctx *ac, struct ldb_dn *dn)
4599 : {
4600 210 : struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
4601 210 : const char *cidr = NULL;
4602 210 : const struct ldb_val *rdn_value = NULL;
4603 :
4604 210 : rdn_value = ldb_dn_get_rdn_val(dn);
4605 210 : if (rdn_value == NULL) {
4606 0 : ldb_set_errstring(ldb, "samldb: ldb_dn_get_rdn_val "
4607 : "failed");
4608 0 : return LDB_ERR_UNWILLING_TO_PERFORM;
4609 : }
4610 :
4611 210 : cidr = ldb_dn_escape_value(ac, *rdn_value);
4612 210 : DBG_INFO("looking at cidr '%s'\n", cidr);
4613 210 : if (cidr == NULL) {
4614 0 : ldb_set_errstring(ldb,
4615 : "samldb: adding an empty subnet cidr seems wrong");
4616 0 : return LDB_ERR_UNWILLING_TO_PERFORM;
4617 : }
4618 :
4619 210 : if (verify_cidr(cidr)){
4620 97 : ldb_set_errstring(ldb,
4621 : "samldb: subnet value is invalid");
4622 97 : return LDB_ERR_INVALID_DN_SYNTAX;
4623 : }
4624 :
4625 113 : return LDB_SUCCESS;
4626 : }
4627 :
4628 615040 : static char *refer_if_rodc(struct ldb_context *ldb, struct ldb_request *req,
4629 : struct ldb_dn *dn)
4630 : {
4631 615040 : bool rodc = false;
4632 83756 : struct loadparm_context *lp_ctx;
4633 83756 : char *referral;
4634 83756 : int ret;
4635 83756 : WERROR err;
4636 :
4637 1230080 : if (ldb_request_get_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID) ||
4638 615040 : ldb_request_get_control(req, DSDB_CONTROL_DBCHECK_MODIFY_RO_REPLICA)) {
4639 0 : return NULL;
4640 : }
4641 :
4642 615040 : ret = samdb_rodc(ldb, &rodc);
4643 615040 : if (ret != LDB_SUCCESS) {
4644 19 : DEBUG(4, (__location__ ": unable to tell if we are an RODC\n"));
4645 19 : return NULL;
4646 : }
4647 :
4648 615021 : if (rodc) {
4649 23 : const char *domain = NULL;
4650 0 : struct ldb_dn *fsmo_role_dn;
4651 0 : struct ldb_dn *role_owner_dn;
4652 23 : ldb_set_errstring(ldb, "RODC modify is forbidden!");
4653 23 : lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
4654 : struct loadparm_context);
4655 :
4656 23 : err = dsdb_get_fsmo_role_info(req, ldb, DREPL_PDC_MASTER,
4657 : &fsmo_role_dn, &role_owner_dn);
4658 23 : if (W_ERROR_IS_OK(err)) {
4659 23 : struct ldb_dn *server_dn = ldb_dn_copy(req, role_owner_dn);
4660 23 : if (server_dn != NULL) {
4661 23 : ldb_dn_remove_child_components(server_dn, 1);
4662 :
4663 23 : domain = samdb_dn_to_dnshostname(ldb, req,
4664 : server_dn);
4665 : }
4666 : }
4667 23 : if (domain == NULL) {
4668 0 : domain = lpcfg_dnsdomain(lp_ctx);
4669 : }
4670 23 : referral = talloc_asprintf(req,
4671 : "ldap://%s/%s",
4672 : domain,
4673 : ldb_dn_get_linearized(dn));
4674 23 : return referral;
4675 : }
4676 :
4677 531261 : return NULL;
4678 : }
4679 :
4680 : /*
4681 : * Restrict all access to sensitive attributes.
4682 : *
4683 : * We don't want to even inspect the values, so we can use the same
4684 : * routine for ADD and MODIFY.
4685 : *
4686 : */
4687 :
4688 1131551 : static int samldb_check_sensitive_attributes(struct samldb_ctx *ac)
4689 : {
4690 1131551 : struct ldb_message_element *el = NULL;
4691 1131551 : struct security_token *user_token = NULL;
4692 105121 : int ret;
4693 :
4694 1131551 : if (dsdb_module_am_system(ac->module)) {
4695 224046 : return LDB_SUCCESS;
4696 : }
4697 :
4698 889117 : user_token = acl_user_token(ac->module);
4699 889117 : if (user_token == NULL) {
4700 0 : return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
4701 : }
4702 :
4703 889117 : el = ldb_msg_find_element(ac->msg, "sidHistory");
4704 889117 : if (el) {
4705 : /*
4706 : * sidHistory is restricted to the (not implemented
4707 : * yet in Samba) DsAddSidHistory call (direct LDB access is
4708 : * as SYSTEM so will bypass this).
4709 : *
4710 : * If you want to modify this, say to merge domains,
4711 : * directly modify the sam.ldb as root.
4712 : */
4713 16 : ldb_asprintf_errstring(ldb_module_get_ctx(ac->module),
4714 : "sidHistory "
4715 : "(entry %s) cannot be created "
4716 : "or changed over LDAP!",
4717 16 : ldb_dn_get_linearized(ac->msg->dn));
4718 16 : return LDB_ERR_UNWILLING_TO_PERFORM;
4719 : }
4720 :
4721 889101 : el = ldb_msg_find_element(ac->msg, "msDS-SecondaryKrbTgtNumber");
4722 889101 : if (el) {
4723 0 : struct security_descriptor *domain_sd;
4724 16 : const struct dsdb_class *objectclass = NULL;
4725 : /*
4726 : * msDS-SecondaryKrbTgtNumber allows the creator to
4727 : * become an RODC, this is trusted as an RODC
4728 : * account
4729 : */
4730 16 : ret = samldb_get_domain_secdesc_and_oc(ac, &domain_sd, &objectclass);
4731 16 : if (ret != LDB_SUCCESS) {
4732 8 : return ret;
4733 : }
4734 16 : ret = acl_check_extended_right(ac,
4735 : ac->module,
4736 : ac->req,
4737 : objectclass,
4738 : domain_sd,
4739 : user_token,
4740 : GUID_DRS_DS_INSTALL_REPLICA,
4741 : SEC_ADS_CONTROL_ACCESS,
4742 : NULL);
4743 16 : if (ret != LDB_SUCCESS) {
4744 8 : ldb_asprintf_errstring(ldb_module_get_ctx(ac->module),
4745 : "msDS-SecondaryKrbTgtNumber "
4746 : "(entry %s) cannot be created "
4747 : "or changed without "
4748 : "DS-Install-Replica extended right!",
4749 8 : ldb_dn_get_linearized(ac->msg->dn));
4750 8 : return ret;
4751 : }
4752 : }
4753 :
4754 889093 : el = ldb_msg_find_element(ac->msg, "msDS-AllowedToDelegateTo");
4755 889093 : if (el) {
4756 : /*
4757 : * msDS-AllowedToDelegateTo is incredibly powerful,
4758 : * given that it allows a server to become ANY USER on
4759 : * the target server only listed by SPN so needs to be
4760 : * protected just as the userAccountControl
4761 : * UF_TRUSTED_FOR_DELEGATION is.
4762 : */
4763 :
4764 63 : bool have_priv = security_token_has_privilege(user_token,
4765 : SEC_PRIV_ENABLE_DELEGATION);
4766 63 : if (have_priv == false) {
4767 8 : ldb_asprintf_errstring(ldb_module_get_ctx(ac->module),
4768 : "msDS-AllowedToDelegateTo "
4769 : "(entry %s) cannot be created "
4770 : "or changed without SePrivEnableDelegation!",
4771 8 : ldb_dn_get_linearized(ac->msg->dn));
4772 8 : return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
4773 : }
4774 : }
4775 802352 : return LDB_SUCCESS;
4776 : }
4777 : /* add */
4778 543093 : static int samldb_add(struct ldb_module *module, struct ldb_request *req)
4779 : {
4780 83665 : struct ldb_context *ldb;
4781 83665 : struct samldb_ctx *ac;
4782 83665 : struct ldb_message_element *el;
4783 83665 : int ret;
4784 543093 : char *referral = NULL;
4785 :
4786 543093 : ldb = ldb_module_get_ctx(module);
4787 543093 : ldb_debug(ldb, LDB_DEBUG_TRACE, "samldb_add\n");
4788 :
4789 : /* do not manipulate our control entries */
4790 543093 : if (ldb_dn_is_special(req->op.add.message->dn)) {
4791 538 : return ldb_next_request(module, req);
4792 : }
4793 :
4794 542555 : referral = refer_if_rodc(ldb, req, req->op.add.message->dn);
4795 542555 : if (referral != NULL) {
4796 22 : ret = ldb_module_send_referral(req, referral);
4797 22 : return ret;
4798 : }
4799 :
4800 542533 : el = ldb_msg_find_element(req->op.add.message, "userParameters");
4801 542533 : if (el != NULL && ldb_req_is_untrusted(req)) {
4802 0 : const char *reason = "samldb_add: "
4803 : "setting userParameters is not supported over LDAP, "
4804 : "see https://bugzilla.samba.org/show_bug.cgi?id=8077";
4805 0 : ldb_debug(ldb, LDB_DEBUG_WARNING, "%s", reason);
4806 0 : return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION, reason);
4807 : }
4808 :
4809 542533 : ac = samldb_ctx_init(module, req);
4810 542533 : if (ac == NULL) {
4811 0 : return ldb_operr(ldb);
4812 : }
4813 :
4814 : /* build the new msg */
4815 542533 : ac->msg = ldb_msg_copy_shallow(ac, req->op.add.message);
4816 542533 : if (ac->msg == NULL) {
4817 0 : talloc_free(ac);
4818 0 : ldb_debug(ldb, LDB_DEBUG_FATAL,
4819 : "samldb_add: ldb_msg_copy_shallow failed!\n");
4820 0 : return ldb_operr(ldb);
4821 : }
4822 :
4823 542533 : ret = samldb_check_sensitive_attributes(ac);
4824 542533 : if (ret != LDB_SUCCESS) {
4825 32 : talloc_free(ac);
4826 32 : return ret;
4827 : }
4828 :
4829 542501 : el = ldb_msg_find_element(ac->msg, "fSMORoleOwner");
4830 542501 : if (el != NULL) {
4831 9 : ret = samldb_fsmo_role_owner_check(ac);
4832 9 : if (ret != LDB_SUCCESS) {
4833 6 : return ret;
4834 : }
4835 : }
4836 :
4837 542495 : el = ldb_msg_find_element(ac->msg, "servicePrincipalName");
4838 542495 : if ((el != NULL)) {
4839 : /*
4840 : * We need to check whether the SPN collides with an existing
4841 : * one (anywhere) including via aliases.
4842 : */
4843 1645 : ret = samldb_spn_uniqueness_check(ac, el);
4844 1645 : if (ret != LDB_SUCCESS) {
4845 3 : return ret;
4846 : }
4847 : }
4848 :
4849 542492 : if (samdb_find_attribute(ldb, ac->msg,
4850 : "objectclass", "user") != NULL) {
4851 30109 : ac->type = SAMLDB_TYPE_USER;
4852 :
4853 30109 : ret = samldb_prim_group_trigger(ac);
4854 30109 : if (ret != LDB_SUCCESS) {
4855 26 : return ret;
4856 : }
4857 :
4858 30083 : ret = samldb_objectclass_trigger(ac);
4859 30083 : if (ret != LDB_SUCCESS) {
4860 110 : return ret;
4861 : }
4862 :
4863 29973 : return samldb_fill_object(ac);
4864 : }
4865 :
4866 512383 : if (samdb_find_attribute(ldb, ac->msg,
4867 : "objectclass", "group") != NULL) {
4868 8710 : ac->type = SAMLDB_TYPE_GROUP;
4869 :
4870 8710 : ret = samldb_objectclass_trigger(ac);
4871 8710 : if (ret != LDB_SUCCESS) {
4872 6 : return ret;
4873 : }
4874 :
4875 8704 : return samldb_fill_object(ac);
4876 : }
4877 :
4878 : /* perhaps a foreignSecurityPrincipal? */
4879 503673 : if (samdb_find_attribute(ldb, ac->msg,
4880 : "objectclass",
4881 : "foreignSecurityPrincipal") != NULL) {
4882 3857 : return samldb_fill_foreignSecurityPrincipal_object(ac);
4883 : }
4884 :
4885 499816 : if (samdb_find_attribute(ldb, ac->msg,
4886 : "objectclass", "classSchema") != NULL) {
4887 33722 : ac->type = SAMLDB_TYPE_CLASS;
4888 :
4889 : /* If in provision, these checks are too slow to do */
4890 33722 : if (!ldb_request_get_control(req, DSDB_CONTROL_SKIP_DUPLICATES_CHECK_OID)) {
4891 882 : ret = samldb_schema_governsid_valid_check(ac);
4892 882 : if (ret != LDB_SUCCESS) {
4893 9 : return ret;
4894 : }
4895 : }
4896 :
4897 33713 : ret = samldb_schema_ldapdisplayname_valid_check(ac);
4898 33713 : if (ret != LDB_SUCCESS) {
4899 9 : return ret;
4900 : }
4901 :
4902 33704 : ret = samldb_schema_info_update(ac);
4903 33704 : if (ret != LDB_SUCCESS) {
4904 0 : talloc_free(ac);
4905 0 : return ret;
4906 : }
4907 :
4908 33704 : return samldb_fill_object(ac);
4909 : }
4910 :
4911 466094 : if (samdb_find_attribute(ldb, ac->msg,
4912 : "objectclass", "attributeSchema") != NULL) {
4913 184400 : ac->type = SAMLDB_TYPE_ATTRIBUTE;
4914 :
4915 : /* If in provision, these checks are too slow to do */
4916 184400 : if (!ldb_request_get_control(req, DSDB_CONTROL_SKIP_DUPLICATES_CHECK_OID)) {
4917 1023 : ret = samldb_schema_attributeid_valid_check(ac);
4918 1023 : if (ret != LDB_SUCCESS) {
4919 9 : return ret;
4920 : }
4921 :
4922 1014 : ret = samldb_schema_add_handle_linkid(ac);
4923 1014 : if (ret != LDB_SUCCESS) {
4924 36 : return ret;
4925 : }
4926 :
4927 978 : ret = samldb_schema_add_handle_mapiid(ac);
4928 978 : if (ret != LDB_SUCCESS) {
4929 9 : return ret;
4930 : }
4931 : }
4932 :
4933 184346 : ret = samldb_schema_ldapdisplayname_valid_check(ac);
4934 184346 : if (ret != LDB_SUCCESS) {
4935 18 : return ret;
4936 : }
4937 :
4938 184328 : ret = samldb_schema_info_update(ac);
4939 184328 : if (ret != LDB_SUCCESS) {
4940 0 : talloc_free(ac);
4941 0 : return ret;
4942 : }
4943 :
4944 184328 : return samldb_fill_object(ac);
4945 : }
4946 :
4947 281694 : if (samdb_find_attribute(ldb, ac->msg,
4948 : "objectclass", "subnet") != NULL) {
4949 208 : ret = samldb_verify_subnet(ac, ac->msg->dn);
4950 208 : if (ret != LDB_SUCCESS) {
4951 96 : talloc_free(ac);
4952 96 : return ret;
4953 : }
4954 : /* We are just checking the value is valid, and there are no
4955 : values to fill in. */
4956 : }
4957 :
4958 281598 : talloc_free(ac);
4959 :
4960 : /* nothing matched, go on */
4961 281598 : return ldb_next_request(module, req);
4962 : }
4963 :
4964 : /* modify */
4965 589800 : static int samldb_modify(struct ldb_module *module, struct ldb_request *req)
4966 : {
4967 21582 : struct ldb_context *ldb;
4968 21582 : struct samldb_ctx *ac;
4969 21582 : struct ldb_message_element *el, *el2;
4970 21582 : struct ldb_control *is_undelete;
4971 589800 : bool modified = false;
4972 21582 : int ret;
4973 :
4974 589800 : if (ldb_dn_is_special(req->op.mod.message->dn)) {
4975 : /* do not manipulate our control entries */
4976 715 : return ldb_next_request(module, req);
4977 : }
4978 :
4979 589085 : ldb = ldb_module_get_ctx(module);
4980 :
4981 : /*
4982 : * we are going to need some special handling if in Undelete call.
4983 : * Since tombstone_reanimate module will restore certain attributes,
4984 : * we need to relax checks for: sAMAccountType, primaryGroupID
4985 : */
4986 589085 : is_undelete = ldb_request_get_control(req, DSDB_CONTROL_RESTORE_TOMBSTONE_OID);
4987 :
4988 : /* make sure that "objectSid" is not specified */
4989 589085 : el = ldb_msg_find_element(req->op.mod.message, "objectSid");
4990 589085 : if (el != NULL) {
4991 15 : if (ldb_request_get_control(req, LDB_CONTROL_PROVISION_OID) == NULL) {
4992 15 : ldb_set_errstring(ldb,
4993 : "samldb: objectSid must not be specified!");
4994 15 : return LDB_ERR_UNWILLING_TO_PERFORM;
4995 : }
4996 : }
4997 589070 : if (is_undelete == NULL) {
4998 : /* make sure that "sAMAccountType" is not specified */
4999 588805 : el = ldb_msg_find_element(req->op.mod.message, "sAMAccountType");
5000 588805 : if (el != NULL) {
5001 15 : ldb_set_errstring(ldb,
5002 : "samldb: sAMAccountType must not be specified!");
5003 15 : return LDB_ERR_UNWILLING_TO_PERFORM;
5004 : }
5005 : }
5006 : /* make sure that "isCriticalSystemObject" is not specified */
5007 589055 : el = ldb_msg_find_element(req->op.mod.message, "isCriticalSystemObject");
5008 589055 : if (el != NULL) {
5009 390 : if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID) == NULL) {
5010 1 : ldb_set_errstring(ldb,
5011 : "samldb: isCriticalSystemObject must not be specified!");
5012 1 : return LDB_ERR_UNWILLING_TO_PERFORM;
5013 : }
5014 : }
5015 :
5016 : /* msDS-IntId is not allowed to be modified
5017 : * except when modification comes from replication */
5018 589054 : if (ldb_msg_find_element(req->op.mod.message, "msDS-IntId")) {
5019 36 : if (!ldb_request_get_control(req,
5020 : DSDB_CONTROL_REPLICATED_UPDATE_OID)) {
5021 36 : return LDB_ERR_CONSTRAINT_VIOLATION;
5022 : }
5023 : }
5024 :
5025 589018 : el = ldb_msg_find_element(req->op.mod.message, "userParameters");
5026 589018 : if (el != NULL && ldb_req_is_untrusted(req)) {
5027 0 : const char *reason = "samldb: "
5028 : "setting userParameters is not supported over LDAP, "
5029 : "see https://bugzilla.samba.org/show_bug.cgi?id=8077";
5030 0 : ldb_debug(ldb, LDB_DEBUG_WARNING, "%s", reason);
5031 0 : return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION, reason);
5032 : }
5033 :
5034 589018 : ac = samldb_ctx_init(module, req);
5035 589018 : if (ac == NULL) {
5036 0 : return ldb_operr(ldb);
5037 : }
5038 :
5039 : /* build the new msg */
5040 589018 : ac->msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
5041 589018 : if (ac->msg == NULL) {
5042 0 : talloc_free(ac);
5043 0 : ldb_debug(ldb, LDB_DEBUG_FATAL,
5044 : "samldb_modify: ldb_msg_copy_shallow failed!\n");
5045 0 : return ldb_operr(ldb);
5046 : }
5047 :
5048 589018 : ret = samldb_check_sensitive_attributes(ac);
5049 589018 : if (ret != LDB_SUCCESS) {
5050 0 : talloc_free(ac);
5051 0 : return ret;
5052 : }
5053 :
5054 589018 : if (is_undelete == NULL) {
5055 588753 : el = ldb_msg_find_element(ac->msg, "primaryGroupID");
5056 588753 : if (el != NULL) {
5057 202 : ret = samldb_prim_group_trigger(ac);
5058 202 : if (ret != LDB_SUCCESS) {
5059 14 : return ret;
5060 : }
5061 : }
5062 : }
5063 :
5064 589004 : el = ldb_msg_find_element(ac->msg, "userAccountControl");
5065 589004 : if (el != NULL) {
5066 15984 : modified = true;
5067 15984 : ret = samldb_user_account_control_change(ac);
5068 15984 : if (ret != LDB_SUCCESS) {
5069 181 : return ret;
5070 : }
5071 : }
5072 :
5073 588823 : el = ldb_msg_find_element(ac->msg, "pwdLastSet");
5074 588823 : if (el != NULL) {
5075 321 : modified = true;
5076 321 : ret = samldb_pwd_last_set_change(ac);
5077 321 : if (ret != LDB_SUCCESS) {
5078 6 : return ret;
5079 : }
5080 : }
5081 :
5082 588817 : el = ldb_msg_find_element(ac->msg, "lockoutTime");
5083 588817 : if (el != NULL) {
5084 189 : modified = true;
5085 189 : ret = samldb_lockout_time(ac);
5086 189 : if (ret != LDB_SUCCESS) {
5087 0 : return ret;
5088 : }
5089 : }
5090 :
5091 588817 : el = ldb_msg_find_element(ac->msg, "groupType");
5092 588817 : if (el != NULL) {
5093 146 : modified = true;
5094 146 : ret = samldb_group_type_change(ac);
5095 146 : if (ret != LDB_SUCCESS) {
5096 33 : return ret;
5097 : }
5098 : }
5099 :
5100 588784 : el = ldb_msg_find_element(ac->msg, "sAMAccountName");
5101 588784 : if (el != NULL) {
5102 10 : uint32_t user_account_control;
5103 912 : struct ldb_result *res = NULL;
5104 912 : const char * const attrs[] = { "userAccountControl",
5105 : "objectclass",
5106 : NULL };
5107 922 : ret = dsdb_module_search_dn(ac->module,
5108 : ac,
5109 : &res,
5110 912 : ac->msg->dn,
5111 : attrs,
5112 : DSDB_FLAG_NEXT_MODULE | DSDB_SEARCH_SHOW_DELETED,
5113 : ac->req);
5114 912 : if (ret != LDB_SUCCESS) {
5115 46 : return ret;
5116 : }
5117 10 : user_account_control
5118 912 : = ldb_msg_find_attr_as_uint(res->msgs[0],
5119 : "userAccountControl",
5120 : 0);
5121 :
5122 912 : if ((user_account_control
5123 912 : & UF_TRUST_ACCOUNT_MASK) != 0) {
5124 310 : ac->need_trailing_dollar = true;
5125 :
5126 602 : } else if (samdb_find_attribute(ldb,
5127 602 : res->msgs[0],
5128 : "objectclass",
5129 : "computer")
5130 : != NULL) {
5131 12 : ac->need_trailing_dollar = true;
5132 : }
5133 :
5134 912 : ret = samldb_sam_accountname_valid_check(ac);
5135 912 : if (ret != LDB_SUCCESS) {
5136 46 : return ret;
5137 : }
5138 : }
5139 :
5140 588738 : el = ldb_msg_find_element(ac->msg, "userPrincipalName");
5141 588738 : if (el != NULL) {
5142 424 : ret = samldb_sam_account_upn_clash(ac);
5143 424 : if (ret != LDB_SUCCESS) {
5144 16 : talloc_free(ac);
5145 16 : return ret;
5146 : }
5147 : }
5148 :
5149 588722 : el = ldb_msg_find_element(ac->msg, "ldapDisplayName");
5150 588722 : if (el != NULL) {
5151 29 : ret = samldb_schema_ldapdisplayname_valid_check(ac);
5152 29 : if (ret != LDB_SUCCESS) {
5153 18 : return ret;
5154 : }
5155 : }
5156 :
5157 588704 : el = ldb_msg_find_element(ac->msg, "attributeID");
5158 588704 : if (el != NULL) {
5159 27 : ldb_asprintf_errstring(ldb_module_get_ctx(ac->module),
5160 : "Once set, attributeID values may not be modified");
5161 27 : return LDB_ERR_CONSTRAINT_VIOLATION;
5162 : }
5163 :
5164 588677 : el = ldb_msg_find_element(ac->msg, "governsID");
5165 588677 : if (el != NULL) {
5166 18 : ldb_asprintf_errstring(ldb_module_get_ctx(ac->module),
5167 : "Once set, governsID values may not be modified");
5168 18 : return LDB_ERR_CONSTRAINT_VIOLATION;
5169 : }
5170 :
5171 588659 : el = ldb_msg_find_element(ac->msg, "member");
5172 588659 : if (el != NULL) {
5173 5161 : struct ldb_control *fix_link_sid_ctrl = NULL;
5174 :
5175 5161 : fix_link_sid_ctrl = ldb_request_get_control(ac->req,
5176 : DSDB_CONTROL_DBCHECK_FIX_LINK_DN_SID);
5177 5161 : if (fix_link_sid_ctrl == NULL) {
5178 5159 : ret = samldb_member_check(ac);
5179 5159 : if (ret != LDB_SUCCESS) {
5180 3 : return ret;
5181 : }
5182 : }
5183 : }
5184 :
5185 588656 : el = ldb_msg_find_element(ac->msg, "description");
5186 588656 : if (el != NULL) {
5187 1037 : ret = samldb_description_check(ac, &modified);
5188 1037 : if (ret != LDB_SUCCESS) {
5189 0 : return ret;
5190 : }
5191 : }
5192 :
5193 588656 : el = ldb_msg_find_element(ac->msg, "dNSHostName");
5194 588656 : el2 = ldb_msg_find_element(ac->msg, "sAMAccountName");
5195 588656 : if ((el != NULL) || (el2 != NULL)) {
5196 1611 : modified = true;
5197 : /*
5198 : * samldb_service_principal_names_change() might add SPN
5199 : * changes to the request, so this must come before the SPN
5200 : * uniqueness check below.
5201 : *
5202 : * Note we ALSO have to do the SPN uniqueness check inside
5203 : * samldb_service_principal_names_change(), because it does a
5204 : * subrequest to do requested SPN modifications *before* its
5205 : * automatic ones are added.
5206 : */
5207 1611 : ret = samldb_service_principal_names_change(ac);
5208 1611 : if (ret != LDB_SUCCESS) {
5209 0 : return ret;
5210 : }
5211 : }
5212 :
5213 588656 : el = ldb_msg_find_element(ac->msg, "servicePrincipalName");
5214 588656 : if ((el != NULL)) {
5215 : /*
5216 : * We need to check whether the SPN collides with an existing
5217 : * one (anywhere) including via aliases.
5218 : */
5219 2178 : modified = true;
5220 2178 : ret = samldb_spn_uniqueness_check(ac, el);
5221 2178 : if (ret != LDB_SUCCESS) {
5222 78 : return ret;
5223 : }
5224 : }
5225 :
5226 588578 : el = ldb_msg_find_element(ac->msg, "fSMORoleOwner");
5227 588578 : if (el != NULL) {
5228 1278 : ret = samldb_fsmo_role_owner_check(ac);
5229 1278 : if (ret != LDB_SUCCESS) {
5230 6 : return ret;
5231 : }
5232 : }
5233 :
5234 588572 : if (modified) {
5235 277 : struct ldb_request *child_req;
5236 :
5237 : /* Now perform the real modifications as a child request */
5238 20364 : ret = ldb_build_mod_req(&child_req, ldb, ac,
5239 20087 : ac->msg,
5240 : req->controls,
5241 : req, dsdb_next_callback,
5242 : req);
5243 20087 : LDB_REQ_SET_LOCATION(child_req);
5244 20087 : if (ret != LDB_SUCCESS) {
5245 0 : return ret;
5246 : }
5247 :
5248 20087 : return ldb_next_request(module, child_req);
5249 : }
5250 :
5251 568485 : talloc_free(ac);
5252 :
5253 : /* no change which interests us, go on */
5254 568485 : return ldb_next_request(module, req);
5255 : }
5256 :
5257 : /* delete */
5258 :
5259 72484 : static int samldb_prim_group_users_check(struct samldb_ctx *ac)
5260 : {
5261 159 : struct ldb_context *ldb;
5262 159 : struct dom_sid *sid;
5263 159 : uint32_t rid;
5264 159 : NTSTATUS status;
5265 159 : int ret;
5266 72484 : struct ldb_result *res = NULL;
5267 72484 : struct ldb_result *res_users = NULL;
5268 72484 : const char * const attrs[] = { "objectSid", "isDeleted", NULL };
5269 72484 : const char * const noattrs[] = { NULL };
5270 :
5271 72484 : ldb = ldb_module_get_ctx(ac->module);
5272 :
5273 : /* Finds out the SID/RID of the SAM object */
5274 72484 : ret = dsdb_module_search_dn(ac->module, ac, &res, ac->req->op.del.dn,
5275 : attrs,
5276 : DSDB_FLAG_NEXT_MODULE | DSDB_SEARCH_SHOW_DELETED,
5277 : ac->req);
5278 72484 : if (ret != LDB_SUCCESS) {
5279 0 : return ret;
5280 : }
5281 :
5282 72484 : if (ldb_msg_check_string_attribute(res->msgs[0], "isDeleted", "TRUE")) {
5283 7 : return LDB_SUCCESS;
5284 : }
5285 :
5286 72470 : sid = samdb_result_dom_sid(ac, res->msgs[0], "objectSid");
5287 72470 : if (sid == NULL) {
5288 : /* No SID - it might not be a SAM object - therefore ok */
5289 39835 : return LDB_SUCCESS;
5290 : }
5291 32574 : status = dom_sid_split_rid(ac, sid, NULL, &rid);
5292 32574 : if (!NT_STATUS_IS_OK(status)) {
5293 0 : return ldb_operr(ldb);
5294 : }
5295 32574 : if (rid == 0) {
5296 : /* Special object (security principal?) */
5297 0 : return LDB_SUCCESS;
5298 : }
5299 : /* do not allow deletion of well-known sids */
5300 32592 : if (rid < DSDB_SAMDB_MINIMUM_ALLOWED_RID &&
5301 18 : (ldb_request_get_control(ac->req, LDB_CONTROL_RELAX_OID) == NULL)) {
5302 18 : return LDB_ERR_OTHER;
5303 : }
5304 :
5305 : /* Deny delete requests from groups which are primary ones */
5306 32556 : ret = dsdb_module_search(ac->module, ac, &res_users,
5307 : ldb_get_default_basedn(ldb),
5308 : LDB_SCOPE_SUBTREE, noattrs,
5309 : DSDB_FLAG_NEXT_MODULE,
5310 : ac->req,
5311 : "(&(primaryGroupID=%u)(objectClass=user))", rid);
5312 32556 : if (ret != LDB_SUCCESS) {
5313 0 : return ret;
5314 : }
5315 32556 : if (res_users->count > 0) {
5316 3 : ldb_asprintf_errstring(ldb_module_get_ctx(ac->module),
5317 : "Refusing to delete %s, as it "
5318 : "is still the primaryGroupID "
5319 : "for %u users",
5320 3 : ldb_dn_get_linearized(res->msgs[0]->dn),
5321 3 : res_users->count);
5322 :
5323 : /*
5324 : * Yes, this seems very wrong, but we have a test
5325 : * for this exact error code in sam.py
5326 : */
5327 3 : return LDB_ERR_ENTRY_ALREADY_EXISTS;
5328 : }
5329 :
5330 32462 : return LDB_SUCCESS;
5331 : }
5332 :
5333 72486 : static int samldb_delete(struct ldb_module *module, struct ldb_request *req)
5334 : {
5335 159 : struct samldb_ctx *ac;
5336 72486 : char *referral = NULL;
5337 159 : int ret;
5338 159 : struct ldb_context *ldb;
5339 :
5340 72486 : if (ldb_dn_is_special(req->op.del.dn)) {
5341 : /* do not manipulate our control entries */
5342 1 : return ldb_next_request(module, req);
5343 : }
5344 :
5345 72485 : ldb = ldb_module_get_ctx(module);
5346 :
5347 72485 : referral = refer_if_rodc(ldb, req, req->op.del.dn);
5348 72485 : if (referral != NULL) {
5349 1 : ret = ldb_module_send_referral(req, referral);
5350 1 : return ret;
5351 : }
5352 :
5353 72484 : ac = samldb_ctx_init(module, req);
5354 72484 : if (ac == NULL) {
5355 0 : return ldb_operr(ldb_module_get_ctx(module));
5356 : }
5357 :
5358 72484 : ret = samldb_prim_group_users_check(ac);
5359 72484 : if (ret != LDB_SUCCESS) {
5360 21 : return ret;
5361 : }
5362 :
5363 72463 : talloc_free(ac);
5364 :
5365 72463 : return ldb_next_request(module, req);
5366 : }
5367 :
5368 : /* rename */
5369 :
5370 1479 : static int check_rename_constraints(struct ldb_message *msg,
5371 : struct samldb_ctx *ac,
5372 : struct ldb_dn *olddn, struct ldb_dn *newdn)
5373 : {
5374 1479 : struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
5375 6 : struct ldb_dn *dn1, *dn2, *nc_root;
5376 6 : int32_t systemFlags;
5377 1479 : bool move_op = false;
5378 1479 : bool rename_op = false;
5379 6 : int ret;
5380 :
5381 : /* Skip the checks if old and new DN are the same, or if we have the
5382 : * relax control specified or if the returned objects is already
5383 : * deleted and needs only to be moved for consistency. */
5384 :
5385 1479 : if (ldb_dn_compare(olddn, newdn) == 0) {
5386 6 : return LDB_SUCCESS;
5387 : }
5388 1473 : if (ldb_request_get_control(ac->req, LDB_CONTROL_RELAX_OID) != NULL) {
5389 21 : return LDB_SUCCESS;
5390 : }
5391 :
5392 1452 : if (ldb_msg_find_attr_as_bool(msg, "isDeleted", false)) {
5393 : /*
5394 : * check originating request if we are supposed
5395 : * to "see" this record in first place.
5396 : */
5397 2 : if (ldb_request_get_control(ac->req, LDB_CONTROL_SHOW_DELETED_OID) == NULL) {
5398 1 : return LDB_ERR_NO_SUCH_OBJECT;
5399 : }
5400 1 : return LDB_ERR_UNWILLING_TO_PERFORM;
5401 : }
5402 :
5403 : /* Objects under CN=System */
5404 :
5405 1450 : dn1 = samdb_system_container_dn(ldb, ac);
5406 1450 : if (dn1 == NULL) return ldb_oom(ldb);
5407 :
5408 1451 : if ((ldb_dn_compare_base(dn1, olddn) == 0) &&
5409 1 : (ldb_dn_compare_base(dn1, newdn) != 0)) {
5410 1 : talloc_free(dn1);
5411 1 : ldb_asprintf_errstring(ldb,
5412 : "subtree_rename: Cannot move/rename %s. Objects under CN=System have to stay under it!",
5413 : ldb_dn_get_linearized(olddn));
5414 1 : return LDB_ERR_OTHER;
5415 : }
5416 :
5417 1449 : talloc_free(dn1);
5418 :
5419 : /* LSA objects */
5420 :
5421 2898 : if ((samdb_find_attribute(ldb, msg, "objectClass", "secret") != NULL) ||
5422 1449 : (samdb_find_attribute(ldb, msg, "objectClass", "trustedDomain") != NULL)) {
5423 0 : ldb_asprintf_errstring(ldb,
5424 : "subtree_rename: Cannot move/rename %s. It's an LSA-specific object!",
5425 : ldb_dn_get_linearized(olddn));
5426 0 : return LDB_ERR_UNWILLING_TO_PERFORM;
5427 : }
5428 :
5429 : /* subnet objects */
5430 1449 : if (samdb_find_attribute(ldb, msg, "objectclass", "subnet") != NULL) {
5431 2 : ret = samldb_verify_subnet(ac, newdn);
5432 2 : if (ret != LDB_SUCCESS) {
5433 1 : return ret;
5434 : }
5435 : }
5436 :
5437 : /* systemFlags */
5438 :
5439 1448 : dn1 = ldb_dn_get_parent(ac, olddn);
5440 1448 : if (dn1 == NULL) return ldb_oom(ldb);
5441 1448 : dn2 = ldb_dn_get_parent(ac, newdn);
5442 1448 : if (dn2 == NULL) return ldb_oom(ldb);
5443 :
5444 1448 : if (ldb_dn_compare(dn1, dn2) == 0) {
5445 917 : rename_op = true;
5446 : } else {
5447 525 : move_op = true;
5448 : }
5449 :
5450 1448 : talloc_free(dn1);
5451 1448 : talloc_free(dn2);
5452 :
5453 1448 : systemFlags = ldb_msg_find_attr_as_int(msg, "systemFlags", 0);
5454 :
5455 : /* Fetch name context */
5456 :
5457 1448 : ret = dsdb_find_nc_root(ldb, ac, olddn, &nc_root);
5458 1448 : if (ret != LDB_SUCCESS) {
5459 0 : return ret;
5460 : }
5461 :
5462 1448 : if (ldb_dn_compare(nc_root, ldb_get_schema_basedn(ldb)) == 0) {
5463 8 : if (move_op) {
5464 0 : ldb_asprintf_errstring(ldb,
5465 : "subtree_rename: Cannot move %s within schema partition",
5466 : ldb_dn_get_linearized(olddn));
5467 0 : return LDB_ERR_UNWILLING_TO_PERFORM;
5468 : }
5469 8 : if (rename_op &&
5470 8 : (systemFlags & SYSTEM_FLAG_SCHEMA_BASE_OBJECT) != 0) {
5471 1 : ldb_asprintf_errstring(ldb,
5472 : "subtree_rename: Cannot rename %s within schema partition",
5473 : ldb_dn_get_linearized(olddn));
5474 1 : return LDB_ERR_UNWILLING_TO_PERFORM;
5475 : }
5476 1440 : } else if (ldb_dn_compare(nc_root, ldb_get_config_basedn(ldb)) == 0) {
5477 12 : if (move_op &&
5478 4 : (systemFlags & SYSTEM_FLAG_CONFIG_ALLOW_MOVE) == 0) {
5479 : /* Here we have to do more: control the
5480 : * "ALLOW_LIMITED_MOVE" flag. This means that the
5481 : * grand-grand-parents of two objects have to be equal
5482 : * in order to perform the move (this is used for
5483 : * moving "server" objects in the "sites" container). */
5484 4 : bool limited_move =
5485 4 : systemFlags & SYSTEM_FLAG_CONFIG_ALLOW_LIMITED_MOVE;
5486 :
5487 4 : if (limited_move) {
5488 0 : dn1 = ldb_dn_copy(ac, olddn);
5489 0 : if (dn1 == NULL) return ldb_oom(ldb);
5490 0 : dn2 = ldb_dn_copy(ac, newdn);
5491 0 : if (dn2 == NULL) return ldb_oom(ldb);
5492 :
5493 0 : limited_move &= ldb_dn_remove_child_components(dn1, 3);
5494 0 : limited_move &= ldb_dn_remove_child_components(dn2, 3);
5495 0 : limited_move &= ldb_dn_compare(dn1, dn2) == 0;
5496 :
5497 0 : talloc_free(dn1);
5498 0 : talloc_free(dn2);
5499 : }
5500 :
5501 4 : if (!limited_move
5502 4 : && ldb_request_get_control(ac->req, DSDB_CONTROL_RESTORE_TOMBSTONE_OID) == NULL) {
5503 2 : ldb_asprintf_errstring(ldb,
5504 : "subtree_rename: Cannot move %s to %s in config partition",
5505 : ldb_dn_get_linearized(olddn), ldb_dn_get_linearized(newdn));
5506 2 : return LDB_ERR_UNWILLING_TO_PERFORM;
5507 : }
5508 : }
5509 10 : if (rename_op &&
5510 8 : (systemFlags & SYSTEM_FLAG_CONFIG_ALLOW_RENAME) == 0) {
5511 1 : ldb_asprintf_errstring(ldb,
5512 : "subtree_rename: Cannot rename %s to %s within config partition",
5513 : ldb_dn_get_linearized(olddn), ldb_dn_get_linearized(newdn));
5514 1 : return LDB_ERR_UNWILLING_TO_PERFORM;
5515 : }
5516 1428 : } else if (ldb_dn_compare(nc_root, ldb_get_default_basedn(ldb)) == 0) {
5517 1424 : if (move_op &&
5518 521 : (systemFlags & SYSTEM_FLAG_DOMAIN_DISALLOW_MOVE) != 0) {
5519 1 : ldb_asprintf_errstring(ldb,
5520 : "subtree_rename: Cannot move %s to %s - DISALLOW_MOVE set",
5521 : ldb_dn_get_linearized(olddn), ldb_dn_get_linearized(newdn));
5522 1 : return LDB_ERR_UNWILLING_TO_PERFORM;
5523 : }
5524 1423 : if (rename_op &&
5525 903 : (systemFlags & SYSTEM_FLAG_DOMAIN_DISALLOW_RENAME) != 0) {
5526 1 : ldb_asprintf_errstring(ldb,
5527 : "subtree_rename: Cannot rename %s to %s - DISALLOW_RENAME set",
5528 : ldb_dn_get_linearized(olddn), ldb_dn_get_linearized(newdn));
5529 1 : return LDB_ERR_UNWILLING_TO_PERFORM;
5530 : }
5531 : }
5532 :
5533 1442 : talloc_free(nc_root);
5534 :
5535 1442 : return LDB_SUCCESS;
5536 : }
5537 :
5538 :
5539 2951 : static int samldb_rename_search_base_callback(struct ldb_request *req,
5540 : struct ldb_reply *ares)
5541 : {
5542 15 : struct samldb_ctx *ac;
5543 15 : int ret;
5544 :
5545 2951 : ac = talloc_get_type(req->context, struct samldb_ctx);
5546 :
5547 2951 : if (!ares) {
5548 0 : return ldb_module_done(ac->req, NULL, NULL,
5549 : LDB_ERR_OPERATIONS_ERROR);
5550 : }
5551 2951 : if (ares->error != LDB_SUCCESS) {
5552 0 : return ldb_module_done(ac->req, ares->controls,
5553 : ares->response, ares->error);
5554 : }
5555 :
5556 2951 : switch (ares->type) {
5557 1479 : case LDB_REPLY_ENTRY:
5558 : /*
5559 : * This is the root entry of the originating move
5560 : * respectively rename request. It has been already
5561 : * stored in the list using "subtree_rename_search()".
5562 : * Only this one is subject to constraint checking.
5563 : */
5564 1485 : ret = check_rename_constraints(ares->message, ac,
5565 1473 : ac->req->op.rename.olddn,
5566 1479 : ac->req->op.rename.newdn);
5567 1479 : if (ret != LDB_SUCCESS) {
5568 10 : return ldb_module_done(ac->req, NULL, NULL,
5569 : ret);
5570 : }
5571 1463 : break;
5572 :
5573 0 : case LDB_REPLY_REFERRAL:
5574 : /* ignore */
5575 0 : break;
5576 :
5577 1472 : case LDB_REPLY_DONE:
5578 :
5579 : /*
5580 : * Great, no problem with the rename, so go ahead as
5581 : * if we never were here
5582 : */
5583 1472 : ret = ldb_next_request(ac->module, ac->req);
5584 1472 : talloc_free(ares);
5585 1472 : return ret;
5586 : }
5587 :
5588 1469 : talloc_free(ares);
5589 1469 : return LDB_SUCCESS;
5590 : }
5591 :
5592 :
5593 : /* rename */
5594 1482 : static int samldb_rename(struct ldb_module *module, struct ldb_request *req)
5595 : {
5596 9 : struct ldb_context *ldb;
5597 9 : static const char * const attrs[] = { "objectClass", "systemFlags",
5598 : "isDeleted", NULL };
5599 9 : struct ldb_request *search_req;
5600 9 : struct samldb_ctx *ac;
5601 9 : int ret;
5602 :
5603 1482 : if (ldb_dn_is_special(req->op.rename.olddn)) { /* do not manipulate our control entries */
5604 0 : return ldb_next_request(module, req);
5605 : }
5606 :
5607 1482 : ldb = ldb_module_get_ctx(module);
5608 :
5609 1482 : ac = samldb_ctx_init(module, req);
5610 1482 : if (!ac) {
5611 0 : return ldb_oom(ldb);
5612 : }
5613 :
5614 1482 : ret = ldb_build_search_req(&search_req, ldb, ac,
5615 : req->op.rename.olddn,
5616 : LDB_SCOPE_BASE,
5617 : "(objectClass=*)",
5618 : attrs,
5619 : NULL,
5620 : ac,
5621 : samldb_rename_search_base_callback,
5622 : req);
5623 1482 : LDB_REQ_SET_LOCATION(search_req);
5624 1482 : if (ret != LDB_SUCCESS) {
5625 0 : return ret;
5626 : }
5627 :
5628 1482 : ret = ldb_request_add_control(search_req, LDB_CONTROL_SHOW_RECYCLED_OID,
5629 : true, NULL);
5630 1482 : if (ret != LDB_SUCCESS) {
5631 0 : return ret;
5632 : }
5633 :
5634 1482 : return ldb_next_request(ac->module, search_req);
5635 : }
5636 :
5637 : /* extended */
5638 :
5639 37 : static int samldb_extended_allocate_rid_pool(struct ldb_module *module, struct ldb_request *req)
5640 : {
5641 37 : struct ldb_context *ldb = ldb_module_get_ctx(module);
5642 0 : struct dsdb_fsmo_extended_op *exop;
5643 0 : int ret;
5644 :
5645 37 : exop = talloc_get_type(req->op.extended.data,
5646 : struct dsdb_fsmo_extended_op);
5647 37 : if (!exop) {
5648 0 : ldb_set_errstring(ldb,
5649 : "samldb_extended_allocate_rid_pool: invalid extended data");
5650 0 : return LDB_ERR_PROTOCOL_ERROR;
5651 : }
5652 :
5653 37 : ret = ridalloc_allocate_rid_pool_fsmo(module, exop, req);
5654 37 : if (ret != LDB_SUCCESS) {
5655 0 : return ret;
5656 : }
5657 :
5658 37 : return ldb_module_done(req, NULL, NULL, LDB_SUCCESS);
5659 : }
5660 :
5661 995 : static int samldb_extended_allocate_rid(struct ldb_module *module, struct ldb_request *req)
5662 : {
5663 995 : struct ldb_context *ldb = ldb_module_get_ctx(module);
5664 0 : struct dsdb_extended_allocate_rid *exop;
5665 0 : int ret;
5666 :
5667 995 : exop = talloc_get_type(req->op.extended.data,
5668 : struct dsdb_extended_allocate_rid);
5669 995 : if (!exop) {
5670 0 : ldb_set_errstring(ldb,
5671 : "samldb_extended_allocate_rid: invalid extended data");
5672 0 : return LDB_ERR_PROTOCOL_ERROR;
5673 : }
5674 :
5675 995 : ret = ridalloc_allocate_rid(module, &exop->rid, req);
5676 995 : if (ret != LDB_SUCCESS) {
5677 2 : return ret;
5678 : }
5679 :
5680 993 : return ldb_module_done(req, NULL, NULL, LDB_SUCCESS);
5681 : }
5682 :
5683 39 : static int samldb_extended_create_own_rid_set(struct ldb_module *module, struct ldb_request *req)
5684 : {
5685 39 : struct ldb_context *ldb = ldb_module_get_ctx(module);
5686 0 : int ret;
5687 0 : struct ldb_dn *dn;
5688 :
5689 39 : if (req->op.extended.data != NULL) {
5690 0 : ldb_set_errstring(ldb,
5691 : "samldb_extended_create_own_rid_set: invalid extended data (should be NULL)");
5692 0 : return LDB_ERR_PROTOCOL_ERROR;
5693 : }
5694 :
5695 39 : ret = ridalloc_create_own_rid_set(module, req,
5696 : &dn, req);
5697 39 : if (ret != LDB_SUCCESS) {
5698 1 : return ret;
5699 : }
5700 :
5701 38 : return ldb_module_done(req, NULL, NULL, LDB_SUCCESS);
5702 : }
5703 :
5704 1280937 : static int samldb_extended(struct ldb_module *module, struct ldb_request *req)
5705 : {
5706 1280937 : if (strcmp(req->op.extended.oid, DSDB_EXTENDED_ALLOCATE_RID_POOL) == 0) {
5707 37 : return samldb_extended_allocate_rid_pool(module, req);
5708 : }
5709 :
5710 1280900 : if (strcmp(req->op.extended.oid, DSDB_EXTENDED_ALLOCATE_RID) == 0) {
5711 995 : return samldb_extended_allocate_rid(module, req);
5712 : }
5713 :
5714 1279905 : if (strcmp(req->op.extended.oid, DSDB_EXTENDED_CREATE_OWN_RID_SET) == 0) {
5715 39 : return samldb_extended_create_own_rid_set(module, req);
5716 : }
5717 :
5718 1279866 : return ldb_next_request(module, req);
5719 : }
5720 :
5721 :
5722 : static const struct ldb_module_ops ldb_samldb_module_ops = {
5723 : .name = "samldb",
5724 : .add = samldb_add,
5725 : .modify = samldb_modify,
5726 : .del = samldb_delete,
5727 : .rename = samldb_rename,
5728 : .extended = samldb_extended
5729 : };
5730 :
5731 :
5732 5908 : int ldb_samldb_module_init(const char *version)
5733 : {
5734 5908 : LDB_MODULE_CHECK_VERSION(version);
5735 5908 : return ldb_register_module(&ldb_samldb_module_ops);
5736 : }
|