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 1206324 : 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 1206324 : ldb = ldb_module_get_ctx(module);
102 :
103 1206324 : ac = talloc_zero(req, struct samldb_ctx);
104 1206324 : if (ac == NULL) {
105 0 : ldb_oom(ldb);
106 0 : return NULL;
107 : }
108 :
109 1206324 : ac->module = module;
110 1206324 : ac->req = req;
111 :
112 1206324 : return ac;
113 : }
114 :
115 366700 : static int samldb_add_step(struct samldb_ctx *ac, samldb_step_fn_t fn)
116 : {
117 47778 : struct samldb_step *step, *stepper;
118 :
119 366700 : step = talloc_zero(ac, struct samldb_step);
120 366700 : if (step == NULL) {
121 0 : return ldb_oom(ldb_module_get_ctx(ac->module));
122 : }
123 :
124 366700 : step->fn = fn;
125 :
126 366700 : if (ac->steps == NULL) {
127 260671 : ac->steps = step;
128 260671 : ac->curstep = step;
129 : } else {
130 106029 : if (ac->curstep == NULL)
131 0 : return ldb_operr(ldb_module_get_ctx(ac->module));
132 139759 : for (stepper = ac->curstep; stepper->next != NULL;
133 33558 : stepper = stepper->next);
134 106029 : stepper->next = step;
135 : }
136 :
137 318922 : return LDB_SUCCESS;
138 : }
139 :
140 260556 : static int samldb_first_step(struct samldb_ctx *ac)
141 : {
142 260556 : if (ac->steps == NULL) {
143 0 : return ldb_operr(ldb_module_get_ctx(ac->module));
144 : }
145 :
146 260556 : ac->curstep = ac->steps;
147 260556 : return ac->curstep->fn(ac);
148 : }
149 :
150 366345 : static int samldb_next_step(struct samldb_ctx *ac)
151 : {
152 366345 : if (ac->curstep->next) {
153 105989 : ac->curstep = ac->curstep->next;
154 105989 : 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 260356 : if (ac->ares) {
160 260356 : return ldb_module_done(ac->req, ac->ares->controls,
161 219735 : ac->ares->response, LDB_SUCCESS);
162 : } else {
163 0 : return ldb_module_done(ac->req, NULL, NULL, LDB_SUCCESS);
164 : }
165 : }
166 :
167 339719 : 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 339719 : struct ldb_message_element *el = NULL;
177 42140 : int ret;
178 :
179 339719 : *value = NULL;
180 :
181 381859 : ret = dsdb_get_expected_new_values(ac,
182 339719 : ac->msg,
183 : attr,
184 : &el,
185 339719 : ac->req->operation);
186 :
187 339719 : if (ret != LDB_SUCCESS) {
188 0 : return ret;
189 : }
190 339719 : if (el == NULL) {
191 : /* we are not affected */
192 25997 : return LDB_SUCCESS;
193 : }
194 :
195 312665 : 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 312663 : } 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 312654 : 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 312654 : *value = (char *)el->values[0].data;
220 :
221 312654 : return LDB_SUCCESS;
222 : }
223 :
224 259729 : 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 259729 : struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
229 259729 : const char * const no_attrs[] = { NULL };
230 259729 : struct ldb_result *res = NULL;
231 259729 : const char *str = NULL;
232 259729 : const char *enc_str = NULL;
233 39972 : int ret;
234 :
235 259729 : ret = samldb_get_single_valued_attr(ldb, ac, attr, &str);
236 259729 : if (ret != LDB_SUCCESS) {
237 11 : return ret;
238 : }
239 259718 : if (str == NULL) {
240 : /* the attribute wasn't found */
241 734 : return LDB_SUCCESS;
242 : }
243 :
244 258984 : enc_str = ldb_binary_encode_string(ac, str);
245 258984 : 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 258984 : 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 257079 : 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 258984 : if (ret != LDB_SUCCESS) {
268 0 : return ret;
269 : }
270 258984 : if (res->count > 1) {
271 0 : return ldb_operr(ldb);
272 258984 : } else if (res->count == 1) {
273 569 : 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 258900 : talloc_free(res);
281 :
282 258900 : return LDB_SUCCESS;
283 : }
284 :
285 :
286 :
287 107167 : 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 107167 : const char * const no_attrs[] = { NULL };
301 107167 : struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
302 107167 : struct ldb_result *res = NULL;
303 2220 : int ret;
304 107167 : char *enc_value = ldb_binary_encode_string(ac, value);
305 107167 : if (enc_value == NULL) {
306 0 : return ldb_module_oom(ac->module);
307 : }
308 107167 : 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 107167 : talloc_free(enc_value);
315 :
316 107167 : if (ret != LDB_SUCCESS) {
317 0 : return ret;
318 107167 : } else if (res->count > 1) {
319 0 : return ldb_operr(ldb);
320 107167 : } else if (res->count == 1) {
321 819 : 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 104925 : return LDB_SUCCESS;
334 : }
335 :
336 39576 : 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 39576 : struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
351 1076 : size_t i;
352 :
353 685227 : for (i = 0; name[i] != '\0'; i++) {
354 644581 : uint8_t c = name[i];
355 644581 : char *p = NULL;
356 644581 : 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 644575 : p = strchr("\"[]:;|=+*?<>/\\,", c);
364 644575 : 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 39570 : 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 39570 : 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 38494 : return LDB_SUCCESS;
387 : }
388 :
389 39995 : static int samldb_sam_account_upn_clash(struct samldb_ctx *ac)
390 : {
391 39995 : struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
392 1084 : int ret;
393 39995 : struct ldb_dn *base_dn = ldb_get_default_basedn(ldb);
394 39995 : TALLOC_CTX *tmp_ctx = NULL;
395 39995 : const char *real_sam = NULL;
396 39995 : const char *real_upn = NULL;
397 39995 : char *implied_sam = NULL;
398 39995 : char *implied_upn = NULL;
399 39995 : const char *realm = NULL;
400 :
401 39995 : ret = samldb_get_single_valued_attr(ldb, ac,
402 : "sAMAccountName",
403 : &real_sam);
404 39995 : if (ret != LDB_SUCCESS) {
405 0 : return ret;
406 : }
407 39995 : ret = samldb_get_single_valued_attr(ldb, ac,
408 : "userPrincipalName",
409 : &real_upn);
410 39995 : if (ret != LDB_SUCCESS) {
411 0 : return ret;
412 : }
413 39995 : if (real_upn == NULL && real_sam == NULL) {
414 : /* Not changing these things, so we're done */
415 0 : return LDB_SUCCESS;
416 : }
417 :
418 39995 : tmp_ctx = talloc_new(ac);
419 39995 : realm = samdb_dn_to_dns_domain(tmp_ctx, base_dn);
420 39995 : if (realm == NULL) {
421 0 : talloc_free(tmp_ctx);
422 0 : return ldb_operr(ldb);
423 : }
424 :
425 39995 : 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 39995 : if (real_sam != NULL) {
467 39576 : implied_upn = talloc_asprintf(tmp_ctx, "%s@%s",
468 : real_sam, realm);
469 39576 : 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 39995 : if (real_sam != NULL) {
480 39576 : ret = samldb_sam_account_upn_clash_sub_search(
481 : ac, tmp_ctx, base_dn, "sAMAccountName",
482 : real_sam, "");
483 :
484 39576 : if (ret != LDB_SUCCESS) {
485 0 : talloc_free(tmp_ctx);
486 0 : return ret;
487 : }
488 39576 : ret = samaccountname_bad_chars_check(ac, real_sam);
489 39576 : if (ret != LDB_SUCCESS) {
490 6 : talloc_free(tmp_ctx);
491 6 : return ret;
492 : }
493 : }
494 39989 : if (implied_upn != NULL) {
495 39570 : ret = samldb_sam_account_upn_clash_sub_search(
496 : ac, tmp_ctx, base_dn, "userPrincipalName", implied_upn,
497 : "(implied by sAMAccountName)");
498 :
499 39570 : if (ret != LDB_SUCCESS) {
500 6 : talloc_free(tmp_ctx);
501 6 : return ret;
502 : }
503 : }
504 39983 : 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 39973 : 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 39967 : talloc_free(tmp_ctx);
525 39967 : return LDB_SUCCESS;
526 : }
527 :
528 :
529 : /* This is run during an add or modify */
530 39600 : static int samldb_sam_accountname_valid_check(struct samldb_ctx *ac)
531 : {
532 39600 : int ret = 0;
533 1076 : bool is_admin;
534 39600 : struct security_token *user_token = NULL;
535 39600 : struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
536 39600 : struct ldb_message_element *el = NULL;
537 :
538 40676 : ret = dsdb_get_expected_new_values(ac,
539 39600 : ac->msg,
540 : "samAccountName",
541 : &el,
542 39600 : ac->req->operation);
543 39600 : if (ret != LDB_SUCCESS) {
544 0 : return ret;
545 : }
546 :
547 39600 : 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 39585 : 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 39585 : if (ret == LDB_ERR_CONSTRAINT_VIOLATION
567 2 : && ac->req->operation == LDB_MODIFY) {
568 1 : ret = LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
569 39584 : } else if (ret == LDB_ERR_OBJECT_CLASS_VIOLATION) {
570 0 : ret = LDB_ERR_CONSTRAINT_VIOLATION;
571 : }
572 39585 : if (ret != LDB_SUCCESS) {
573 14 : return ret;
574 : }
575 :
576 39571 : ret = samldb_sam_account_upn_clash(ac);
577 39571 : if (ret != LDB_SUCCESS) {
578 12 : return ret;
579 : }
580 :
581 39559 : if (!ac->need_trailing_dollar) {
582 34100 : return LDB_SUCCESS;
583 : }
584 :
585 : /* This does not permit a single $ */
586 4479 : 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 4479 : user_token = acl_user_token(ac->module);
595 4479 : if (user_token == NULL) {
596 0 : return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
597 : }
598 :
599 96 : is_admin
600 4479 : = security_token_has_builtin_administrators(user_token);
601 :
602 4479 : if (is_admin) {
603 : /*
604 : * Administrators are allowed to select strange names.
605 : * This is poor practice but not prevented.
606 : */
607 3552 : 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 38687 : static int samldb_check_sAMAccountName(struct samldb_ctx *ac)
987 : {
988 1066 : int ret;
989 :
990 38687 : 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 38687 : ret = samldb_sam_accountname_valid_check(ac);
998 38687 : if (ret != LDB_SUCCESS) {
999 8 : return ret;
1000 : }
1001 :
1002 38679 : return samldb_next_step(ac);
1003 : }
1004 :
1005 :
1006 33591 : 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 33591 : ndr_err = ndr_push_struct_blob(&v, msg, sid,
1014 : (ndr_push_flags_fn_t)ndr_push_dom_sid);
1015 33591 : if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1016 0 : return false;
1017 : }
1018 33591 : return (ldb_msg_add_value(msg, name, &v, NULL) == 0);
1019 : }
1020 :
1021 :
1022 : /* allocate a SID using our RID Set */
1023 33546 : static int samldb_allocate_sid(struct samldb_ctx *ac)
1024 : {
1025 172 : uint32_t rid;
1026 172 : struct dom_sid *sid;
1027 33546 : struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
1028 172 : int ret;
1029 :
1030 33546 : ret = ridalloc_allocate_rid(ac->module, &rid, ac->req);
1031 33546 : if (ret != LDB_SUCCESS) {
1032 0 : return ret;
1033 : }
1034 :
1035 33546 : sid = dom_sid_add_rid(ac, samdb_domain_sid(ldb), rid);
1036 33546 : if (sid == NULL) {
1037 0 : return ldb_module_oom(ac->module);
1038 : }
1039 :
1040 33546 : if ( ! samldb_msg_add_sid(ac->msg, "objectSid", sid)) {
1041 0 : return ldb_operr(ldb);
1042 : }
1043 :
1044 33546 : 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 260560 : 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 260560 : ac = talloc_get_type(req->context, struct samldb_ctx);
1330 260560 : ldb = ldb_module_get_ctx(ac->module);
1331 :
1332 260560 : if (!ares) {
1333 0 : return ldb_module_done(ac->req, NULL, NULL,
1334 : LDB_ERR_OPERATIONS_ERROR);
1335 : }
1336 :
1337 260560 : if (ares->type == LDB_REPLY_REFERRAL) {
1338 0 : return ldb_module_send_referral(ac->req, ares->referral);
1339 : }
1340 :
1341 260560 : if (ares->error != LDB_SUCCESS) {
1342 204 : return ldb_module_done(ac->req, ares->controls,
1343 : ares->response, ares->error);
1344 : }
1345 260356 : 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 260356 : ac->ares = talloc_steal(ac, ares);
1353 :
1354 260356 : ret = samldb_next_step(ac);
1355 260356 : if (ret != LDB_SUCCESS) {
1356 0 : return ldb_module_done(ac->req, NULL, NULL, ret);
1357 : }
1358 219735 : return ret;
1359 : }
1360 :
1361 260548 : 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 260548 : ldb = ldb_module_get_ctx(ac->module);
1368 :
1369 301170 : ret = ldb_build_add_req(&req, ldb, ac,
1370 260548 : ac->msg,
1371 219926 : ac->req->controls,
1372 : ac, samldb_add_entry_callback,
1373 : ac->req);
1374 260548 : LDB_REQ_SET_LOCATION(req);
1375 260548 : if (ret != LDB_SUCCESS) {
1376 0 : return ret;
1377 : }
1378 :
1379 260548 : 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 256719 : static int samldb_fill_object(struct samldb_ctx *ac)
1405 : {
1406 256719 : struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
1407 39962 : int ret;
1408 :
1409 : /* Add information for the different account types */
1410 256719 : switch(ac->type) {
1411 29983 : case SAMLDB_TYPE_USER: {
1412 29983 : struct ldb_control *rodc_control = ldb_request_get_control(ac->req,
1413 : LDB_CONTROL_RODC_DCPROMO_OID);
1414 29983 : 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 29983 : ret = samldb_add_step(ac, samldb_check_sAMAccountName);
1423 29983 : if (ret != LDB_SUCCESS) return ret;
1424 :
1425 29983 : ret = samldb_add_step(ac, samldb_add_entry);
1426 29983 : if (ret != LDB_SUCCESS) return ret;
1427 29762 : 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 256701 : 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 38803 : static int samldb_objectclass_trigger(struct samldb_ctx *ac)
1788 : {
1789 38803 : struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
1790 38803 : 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 38803 : el = ldb_msg_find_element(ac->msg, "sAMAccountType");
1798 38803 : 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 38802 : sid = samdb_result_dom_sid(ac, ac->msg, "objectSid");
1810 43885 : 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 38802 : if ((sid == NULL) && (ac->req->operation == LDB_ADD) && !skip_allocate_sids) {
1819 33661 : ret = samldb_add_step(ac, samldb_allocate_sid);
1820 33661 : if (ret != LDB_SUCCESS) return ret;
1821 : }
1822 :
1823 38802 : switch(ac->type) {
1824 30092 : case SAMLDB_TYPE_USER: {
1825 221 : uint32_t raw_uac;
1826 221 : uint32_t user_account_control;
1827 221 : bool is_computer_objectclass;
1828 30092 : bool uac_generated = false, uac_add_flags = false;
1829 30092 : uint32_t default_user_account_control = UF_NORMAL_ACCOUNT;
1830 : /* Step 1.2: Default values */
1831 30092 : ret = dsdb_user_obj_set_defaults(ldb, ac->msg, ac->req);
1832 30092 : if (ret != LDB_SUCCESS) return ret;
1833 :
1834 221 : is_computer_objectclass
1835 60184 : = (samdb_find_attribute(ldb,
1836 30092 : ac->msg,
1837 : "objectclass",
1838 : "computer")
1839 : != NULL);
1840 :
1841 30092 : if (is_computer_objectclass) {
1842 94 : default_user_account_control
1843 4124 : = 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 30092 : el = ldb_msg_find_element(ac->msg, "userAccountControl");
1850 30092 : 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 30092 : el = ldb_msg_find_element(ac->msg, "userAccountControl");
1862 30092 : SMB_ASSERT(el != NULL);
1863 :
1864 : /* Step 1.3: "userAccountControl" -> "sAMAccountType" mapping */
1865 30092 : user_account_control = ldb_msg_find_attr_as_uint(ac->msg,
1866 : "userAccountControl",
1867 : 0);
1868 30092 : 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 30092 : 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 30092 : if ((user_account_control & UF_LOCKOUT) != 0) {
1886 7 : user_account_control &= ~UF_LOCKOUT;
1887 7 : uac_generated = true;
1888 : }
1889 30092 : if ((user_account_control & UF_PASSWORD_EXPIRED) != 0) {
1890 7 : user_account_control &= ~UF_PASSWORD_EXPIRED;
1891 7 : uac_generated = true;
1892 : }
1893 :
1894 30092 : ret = samldb_check_user_account_control_rules(ac, NULL,
1895 : raw_uac,
1896 : user_account_control,
1897 : 0,
1898 : is_computer_objectclass);
1899 30092 : 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 29983 : if ((user_account_control
1909 29983 : & UF_TRUST_ACCOUNT_MASK) != 0) {
1910 4085 : ac->need_trailing_dollar = true;
1911 : }
1912 :
1913 29983 : if (is_computer_objectclass) {
1914 4082 : ac->need_trailing_dollar = true;
1915 : }
1916 :
1917 : /* add "sAMAccountType" attribute */
1918 29983 : ret = dsdb_user_obj_set_account_type(ldb, ac->msg, user_account_control, NULL);
1919 29983 : if (ret != LDB_SUCCESS) {
1920 0 : return ret;
1921 : }
1922 :
1923 : /* "isCriticalSystemObject" might be set */
1924 29983 : if (user_account_control &
1925 : (UF_SERVER_TRUST_ACCOUNT | UF_PARTIAL_SECRETS_ACCOUNT)) {
1926 1231 : ret = ldb_msg_add_string_flags(ac->msg, "isCriticalSystemObject",
1927 : "TRUE", LDB_FLAG_MOD_REPLACE);
1928 1231 : if (ret != LDB_SUCCESS) {
1929 0 : return ret;
1930 : }
1931 28752 : } 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 29983 : if (!ldb_msg_find_element(ac->msg, "primaryGroupID")) {
1941 199 : uint32_t rid;
1942 :
1943 29855 : ret = dsdb_user_obj_set_primary_group_id(ldb, ac->msg, user_account_control, &rid);
1944 29855 : if (ret != LDB_SUCCESS) {
1945 0 : return ret;
1946 : }
1947 : /*
1948 : * Older AD deployments don't know about the
1949 : * RODC group
1950 : */
1951 29855 : 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 29983 : 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 29762 : 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 37621 : 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 30119 : static int samldb_prim_group_set(struct samldb_ctx *ac)
2077 : {
2078 30119 : struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
2079 221 : uint32_t rid;
2080 :
2081 30119 : rid = ldb_msg_find_attr_as_uint(ac->msg, "primaryGroupID", (uint32_t) -1);
2082 30119 : if (rid == (uint32_t) -1) {
2083 : /* we aren't affected of any primary group set */
2084 29766 : 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 30321 : static int samldb_prim_group_trigger(struct samldb_ctx *ac)
2305 : {
2306 223 : int ret;
2307 :
2308 30321 : if (ac->req->operation == LDB_ADD) {
2309 30119 : ret = samldb_prim_group_set(ac);
2310 : } else {
2311 202 : ret = samldb_prim_group_change(ac);
2312 : }
2313 :
2314 30321 : return ret;
2315 : }
2316 :
2317 46066 : 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 46066 : int ret = 0;
2322 46066 : 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 46066 : } 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 :
2367 188524 : for (i = 0; i < ARRAY_SIZE(map); i++) {
2368 188524 : if (user_account_control & map[i].uac) {
2369 45736 : need_check = true;
2370 45736 : break;
2371 : }
2372 : }
2373 46066 : if (need_check == false) {
2374 0 : return LDB_SUCCESS;
2375 : }
2376 :
2377 368105 : for (i = 0; i < ARRAY_SIZE(map); i++) {
2378 322107 : uint32_t this_uac = user_account_control & map[i].uac;
2379 322107 : if (this_uac != 0) {
2380 47371 : if (map[i].never) {
2381 16 : ret = LDB_ERR_OTHER;
2382 16 : break;
2383 47355 : } else if (map[i].needs != 0) {
2384 439 : if ((map[i].needs & user_account_control) == 0) {
2385 51 : ret = LDB_ERR_OTHER;
2386 51 : break;
2387 : }
2388 46916 : } else if (map[i].not_with != 0) {
2389 46916 : if ((map[i].not_with & user_account_control) != 0) {
2390 1 : ret = LDB_ERR_OTHER;
2391 1 : break;
2392 : }
2393 : }
2394 : }
2395 : }
2396 46066 : if (ret != LDB_SUCCESS) {
2397 68 : switch (ac->req->operation) {
2398 7 : case LDB_ADD:
2399 7 : ldb_asprintf_errstring(ldb_module_get_ctx(ac->module),
2400 : "Failed to add %s: %s",
2401 7 : ldb_dn_get_linearized(ac->msg->dn),
2402 7 : map[i].error_string);
2403 7 : break;
2404 61 : case LDB_MODIFY:
2405 61 : ldb_asprintf_errstring(ldb_module_get_ctx(ac->module),
2406 : "Failed to modify %s: %s",
2407 61 : ldb_dn_get_linearized(ac->msg->dn),
2408 61 : map[i].error_string);
2409 61 : break;
2410 0 : default:
2411 0 : return ldb_module_operr(ac->module);
2412 : }
2413 : }
2414 45736 : return ret;
2415 : }
2416 :
2417 : /*
2418 : * It would be best if these rules apply, always, but for now they
2419 : * apply only to non-admins
2420 : */
2421 45998 : static int samldb_check_user_account_control_objectclass_invariants(
2422 : struct samldb_ctx *ac,
2423 : uint32_t user_account_control,
2424 : uint32_t user_account_control_old,
2425 : bool is_computer_objectclass)
2426 : {
2427 45998 : struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
2428 :
2429 45998 : uint32_t old_ufa = user_account_control_old & UF_ACCOUNT_TYPE_MASK;
2430 45998 : uint32_t new_ufa = user_account_control & UF_ACCOUNT_TYPE_MASK;
2431 :
2432 45998 : uint32_t old_rodc = user_account_control_old & UF_PARTIAL_SECRETS_ACCOUNT;
2433 45998 : uint32_t new_rodc = user_account_control & UF_PARTIAL_SECRETS_ACCOUNT;
2434 :
2435 330 : bool is_admin;
2436 330 : struct security_token *user_token
2437 45998 : = acl_user_token(ac->module);
2438 45998 : if (user_token == NULL) {
2439 0 : return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
2440 : }
2441 :
2442 330 : is_admin
2443 45998 : = security_token_has_builtin_administrators(user_token);
2444 :
2445 :
2446 : /*
2447 : * We want to allow changes to (eg) disable an account
2448 : * that was created wrong, only checking the
2449 : * objectclass if the account type changes.
2450 : */
2451 45998 : if (old_ufa == new_ufa && old_rodc == new_rodc) {
2452 15480 : return LDB_SUCCESS;
2453 : }
2454 :
2455 30409 : switch (new_ufa) {
2456 26010 : case UF_NORMAL_ACCOUNT:
2457 26010 : if (is_computer_objectclass && !is_admin) {
2458 39 : ldb_asprintf_errstring(ldb,
2459 : "%08X: samldb: UF_NORMAL_ACCOUNT "
2460 : "requires objectclass 'user' not 'computer'!",
2461 39 : W_ERROR_V(WERR_DS_MACHINE_ACCOUNT_CREATED_PRENT4));
2462 39 : return LDB_ERR_OBJECT_CLASS_VIOLATION;
2463 : }
2464 25844 : break;
2465 :
2466 91 : case UF_INTERDOMAIN_TRUST_ACCOUNT:
2467 91 : if (is_computer_objectclass) {
2468 8 : ldb_asprintf_errstring(ldb,
2469 : "%08X: samldb: UF_INTERDOMAIN_TRUST_ACCOUNT "
2470 : "requires objectclass 'user' not 'computer'!",
2471 8 : W_ERROR_V(WERR_DS_MACHINE_ACCOUNT_CREATED_PRENT4));
2472 8 : return LDB_ERR_OBJECT_CLASS_VIOLATION;
2473 : }
2474 83 : break;
2475 :
2476 3137 : case UF_WORKSTATION_TRUST_ACCOUNT:
2477 3137 : if (!is_computer_objectclass) {
2478 : /*
2479 : * Modify of a user account account into a
2480 : * workstation without objectclass computer
2481 : * as an admin is still permitted, but not
2482 : * to make an RODC
2483 : */
2484 70 : if (is_admin
2485 47 : && ac->req->operation == LDB_MODIFY
2486 25 : && new_rodc == 0) {
2487 22 : break;
2488 : }
2489 48 : ldb_asprintf_errstring(ldb,
2490 : "%08X: samldb: UF_WORKSTATION_TRUST_ACCOUNT "
2491 : "requires objectclass 'computer'!",
2492 48 : W_ERROR_V(WERR_DS_MACHINE_ACCOUNT_CREATED_PRENT4));
2493 48 : return LDB_ERR_OBJECT_CLASS_VIOLATION;
2494 : }
2495 3031 : break;
2496 :
2497 1171 : case UF_SERVER_TRUST_ACCOUNT:
2498 1171 : if (!is_computer_objectclass) {
2499 38 : ldb_asprintf_errstring(ldb,
2500 : "%08X: samldb: UF_SERVER_TRUST_ACCOUNT "
2501 : "requires objectclass 'computer'!",
2502 38 : W_ERROR_V(WERR_DS_MACHINE_ACCOUNT_CREATED_PRENT4));
2503 38 : return LDB_ERR_OBJECT_CLASS_VIOLATION;
2504 : }
2505 1075 : break;
2506 :
2507 0 : default:
2508 0 : ldb_asprintf_errstring(ldb,
2509 : "%08X: samldb: invalid userAccountControl[0x%08X]",
2510 0 : W_ERROR_V(WERR_INVALID_PARAMETER),
2511 : user_account_control);
2512 0 : return LDB_ERR_OTHER;
2513 : }
2514 30055 : return LDB_SUCCESS;
2515 : }
2516 :
2517 43801 : static int samldb_get_domain_secdesc_and_oc(struct samldb_ctx *ac,
2518 : struct security_descriptor **domain_sd,
2519 : const struct dsdb_class **objectclass)
2520 : {
2521 43801 : const char * const sd_attrs[] = {"ntSecurityDescriptor", "objectClass", NULL};
2522 233 : struct ldb_result *res;
2523 43801 : struct ldb_dn *domain_dn = ldb_get_default_basedn(ldb_module_get_ctx(ac->module));
2524 43801 : const struct dsdb_schema *schema = NULL;
2525 43801 : struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
2526 43801 : int ret = dsdb_module_search_dn(ac->module, ac, &res,
2527 : domain_dn,
2528 : sd_attrs,
2529 : DSDB_FLAG_NEXT_MODULE | DSDB_SEARCH_SHOW_DELETED,
2530 : ac->req);
2531 43801 : if (ret != LDB_SUCCESS) {
2532 0 : return ret;
2533 : }
2534 43801 : if (res->count != 1) {
2535 0 : return ldb_module_operr(ac->module);
2536 : }
2537 :
2538 43801 : schema = dsdb_get_schema(ldb, ac->req);
2539 43801 : if (!schema) {
2540 0 : return ldb_module_operr(ac->module);;
2541 : }
2542 43801 : *objectclass = dsdb_get_structural_oc_from_msg(schema, res->msgs[0]);
2543 43801 : return dsdb_get_sd_from_ldb_message(ldb_module_get_ctx(ac->module),
2544 43801 : ac, res->msgs[0], domain_sd);
2545 :
2546 : }
2547 :
2548 : /**
2549 : * Validate that the restriction in point 5 of MS-SAMR 3.1.1.8.10 userAccountControl is honoured
2550 : *
2551 : */
2552 45865 : static int samldb_check_user_account_control_acl(struct samldb_ctx *ac,
2553 : struct dom_sid *sid,
2554 : uint32_t user_account_control,
2555 : uint32_t user_account_control_old)
2556 : {
2557 330 : size_t i;
2558 45865 : int ret = 0;
2559 45865 : bool need_acl_check = false;
2560 330 : struct security_token *user_token;
2561 330 : struct security_descriptor *domain_sd;
2562 45865 : const struct dsdb_class *objectclass = NULL;
2563 330 : static const struct uac_to_guid {
2564 : uint32_t uac;
2565 : uint32_t priv_to_change_from;
2566 : const char *oid;
2567 : const char *guid;
2568 : enum sec_privilege privilege;
2569 : bool delete_is_privileged;
2570 : bool admin_required;
2571 : const char *error_string;
2572 : } map[] = {
2573 : {
2574 : .uac = UF_PASSWD_NOTREQD,
2575 : .guid = GUID_DRS_UPDATE_PASSWORD_NOT_REQUIRED_BIT,
2576 : .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"
2577 : },
2578 : {
2579 : .uac = UF_DONT_EXPIRE_PASSWD,
2580 : .guid = GUID_DRS_UNEXPIRE_PASSWORD,
2581 : .error_string = "Adding the UF_DONT_EXPIRE_PASSWD bit in userAccountControl requires the Unexpire-Password right that was not given on the Domain object"
2582 : },
2583 : {
2584 : .uac = UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED,
2585 : .guid = GUID_DRS_ENABLE_PER_USER_REVERSIBLY_ENCRYPTED_PASSWORD,
2586 : .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"
2587 : },
2588 : {
2589 : .uac = UF_SERVER_TRUST_ACCOUNT,
2590 : .guid = GUID_DRS_DS_INSTALL_REPLICA,
2591 : .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"
2592 : },
2593 : {
2594 : .uac = UF_PARTIAL_SECRETS_ACCOUNT,
2595 : .guid = GUID_DRS_DS_INSTALL_REPLICA,
2596 : .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"
2597 : },
2598 : {
2599 : .uac = UF_WORKSTATION_TRUST_ACCOUNT,
2600 : .priv_to_change_from = UF_NORMAL_ACCOUNT,
2601 : .error_string = "Swapping UF_NORMAL_ACCOUNT to UF_WORKSTATION_TRUST_ACCOUNT requires the user to be a member of the domain admins group"
2602 : },
2603 : {
2604 : .uac = UF_NORMAL_ACCOUNT,
2605 : .priv_to_change_from = UF_WORKSTATION_TRUST_ACCOUNT,
2606 : .error_string = "Swapping UF_WORKSTATION_TRUST_ACCOUNT to UF_NORMAL_ACCOUNT requires the user to be a member of the domain admins group"
2607 : },
2608 : {
2609 : .uac = UF_INTERDOMAIN_TRUST_ACCOUNT,
2610 : .oid = DSDB_CONTROL_PERMIT_INTERDOMAIN_TRUST_UAC_OID,
2611 : .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",
2612 : .delete_is_privileged = true
2613 : },
2614 : {
2615 : .uac = UF_TRUSTED_FOR_DELEGATION,
2616 : .privilege = SEC_PRIV_ENABLE_DELEGATION,
2617 : .delete_is_privileged = true,
2618 : .error_string = "Updating the UF_TRUSTED_FOR_DELEGATION bit in userAccountControl is not permitted without the SeEnableDelegationPrivilege"
2619 : },
2620 : {
2621 : .uac = UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION,
2622 : .privilege = SEC_PRIV_ENABLE_DELEGATION,
2623 : .delete_is_privileged = true,
2624 : .error_string = "Updating the UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION bit in userAccountControl is not permitted without the SeEnableDelegationPrivilege"
2625 : }
2626 :
2627 : };
2628 :
2629 45865 : if (dsdb_module_am_system(ac->module)) {
2630 2041 : return LDB_SUCCESS;
2631 : }
2632 :
2633 287023 : for (i = 0; i < ARRAY_SIZE(map); i++) {
2634 287023 : if (user_account_control & map[i].uac) {
2635 43494 : need_acl_check = true;
2636 43494 : break;
2637 : }
2638 : }
2639 43727 : if (need_acl_check == false) {
2640 0 : return LDB_SUCCESS;
2641 : }
2642 :
2643 43727 : user_token = acl_user_token(ac->module);
2644 43727 : if (user_token == NULL) {
2645 0 : return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
2646 : }
2647 :
2648 43727 : ret = samldb_get_domain_secdesc_and_oc(ac, &domain_sd, &objectclass);
2649 43727 : if (ret != LDB_SUCCESS) {
2650 0 : return ret;
2651 : }
2652 :
2653 480649 : for (i = 0; i < ARRAY_SIZE(map); i++) {
2654 437005 : uint32_t this_uac_new = user_account_control & map[i].uac;
2655 437005 : uint32_t this_uac_old = user_account_control_old & map[i].uac;
2656 437005 : if (this_uac_new != this_uac_old) {
2657 45532 : if (this_uac_old != 0) {
2658 13985 : if (map[i].delete_is_privileged == false) {
2659 13971 : continue;
2660 : }
2661 : }
2662 31561 : if (map[i].oid) {
2663 75 : struct ldb_control *control = ldb_request_get_control(ac->req, map[i].oid);
2664 75 : if (control == NULL) {
2665 8 : ret = LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
2666 : }
2667 31486 : } else if (map[i].privilege != SEC_PRIV_INVALID) {
2668 795 : bool have_priv = security_token_has_privilege(user_token,
2669 773 : map[i].privilege);
2670 795 : if (have_priv == false) {
2671 32 : ret = LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
2672 : }
2673 30691 : } else if (map[i].priv_to_change_from & user_account_control_old) {
2674 120 : bool is_admin = security_token_has_builtin_administrators(user_token);
2675 120 : if (is_admin == false) {
2676 7 : ret = LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
2677 : }
2678 30571 : } else if (map[i].guid) {
2679 2954 : ret = acl_check_extended_right(ac,
2680 : ac->module,
2681 : ac->req,
2682 : objectclass,
2683 : domain_sd,
2684 : user_token,
2685 2780 : map[i].guid,
2686 : SEC_ADS_CONTROL_ACCESS,
2687 : sid);
2688 : } else {
2689 27514 : ret = LDB_SUCCESS;
2690 : }
2691 31458 : if (ret != LDB_SUCCESS) {
2692 83 : break;
2693 : }
2694 : }
2695 : }
2696 43727 : if (ret == LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS) {
2697 83 : switch (ac->req->operation) {
2698 33 : case LDB_ADD:
2699 33 : ldb_asprintf_errstring(ldb_module_get_ctx(ac->module),
2700 : "Failed to add %s: %s",
2701 33 : ldb_dn_get_linearized(ac->msg->dn),
2702 33 : map[i].error_string);
2703 33 : break;
2704 50 : case LDB_MODIFY:
2705 50 : ldb_asprintf_errstring(ldb_module_get_ctx(ac->module),
2706 : "Failed to modify %s: %s",
2707 50 : ldb_dn_get_linearized(ac->msg->dn),
2708 50 : map[i].error_string);
2709 50 : break;
2710 0 : default:
2711 0 : return ldb_module_operr(ac->module);
2712 : }
2713 83 : if (map[i].guid) {
2714 0 : struct ldb_dn *domain_dn
2715 36 : = ldb_get_default_basedn(ldb_module_get_ctx(ac->module));
2716 36 : dsdb_acl_debug(domain_sd, acl_user_token(ac->module),
2717 : domain_dn,
2718 : true,
2719 : 10);
2720 : }
2721 : }
2722 43494 : return ret;
2723 : }
2724 :
2725 46066 : static int samldb_check_user_account_control_rules(struct samldb_ctx *ac,
2726 : struct dom_sid *sid,
2727 : uint32_t req_uac,
2728 : uint32_t user_account_control,
2729 : uint32_t user_account_control_old,
2730 : bool is_computer_objectclass)
2731 : {
2732 330 : int ret;
2733 46066 : struct dsdb_control_password_user_account_control *uac = NULL;
2734 :
2735 46066 : ret = samldb_check_user_account_control_invariants(ac, user_account_control);
2736 46066 : if (ret != LDB_SUCCESS) {
2737 68 : return ret;
2738 : }
2739 45998 : ret = samldb_check_user_account_control_objectclass_invariants(ac,
2740 : user_account_control,
2741 : user_account_control_old,
2742 : is_computer_objectclass);
2743 45998 : if (ret != LDB_SUCCESS) {
2744 133 : return ret;
2745 : }
2746 :
2747 45865 : ret = samldb_check_user_account_control_acl(ac, sid, user_account_control, user_account_control_old);
2748 45865 : if (ret != LDB_SUCCESS) {
2749 83 : return ret;
2750 : }
2751 :
2752 45782 : uac = talloc_zero(ac->req,
2753 : struct dsdb_control_password_user_account_control);
2754 45782 : if (uac == NULL) {
2755 0 : return ldb_module_oom(ac->module);
2756 : }
2757 :
2758 45782 : uac->req_flags = req_uac;
2759 45782 : uac->old_flags = user_account_control_old;
2760 45782 : uac->new_flags = user_account_control;
2761 :
2762 45782 : ret = ldb_request_add_control(ac->req,
2763 : DSDB_CONTROL_PASSWORD_USER_ACCOUNT_CONTROL_OID,
2764 : false, uac);
2765 45782 : if (ret != LDB_SUCCESS) {
2766 0 : return ret;
2767 : }
2768 :
2769 45452 : return ret;
2770 : }
2771 :
2772 :
2773 : /**
2774 : * This function is called on LDB modify operations. It performs some additions/
2775 : * replaces on the current LDB message when "userAccountControl" changes.
2776 : */
2777 15980 : static int samldb_user_account_control_change(struct samldb_ctx *ac)
2778 : {
2779 15980 : struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
2780 109 : uint32_t old_uac;
2781 109 : uint32_t new_uac;
2782 109 : uint32_t raw_uac;
2783 109 : uint32_t old_ufa;
2784 109 : uint32_t new_ufa;
2785 109 : uint32_t old_uac_computed;
2786 109 : uint32_t clear_uac;
2787 109 : uint32_t old_atype;
2788 109 : uint32_t new_atype;
2789 109 : uint32_t old_pgrid;
2790 109 : uint32_t new_pgrid;
2791 109 : NTTIME old_lockoutTime;
2792 109 : struct ldb_message_element *el;
2793 109 : struct ldb_val *val;
2794 109 : struct ldb_val computer_val;
2795 109 : struct ldb_message *tmp_msg;
2796 109 : struct dom_sid *sid;
2797 109 : int ret;
2798 109 : struct ldb_result *res;
2799 15980 : const char * const attrs[] = {
2800 : "objectClass",
2801 : "isCriticalSystemObject",
2802 : "userAccountControl",
2803 : "msDS-User-Account-Control-Computed",
2804 : "lockoutTime",
2805 : "objectSid",
2806 : NULL
2807 : };
2808 15980 : bool is_computer_objectclass = false;
2809 15980 : bool old_is_critical = false;
2810 15980 : bool new_is_critical = false;
2811 :
2812 16089 : ret = dsdb_get_expected_new_values(ac,
2813 15980 : ac->msg,
2814 : "userAccountControl",
2815 : &el,
2816 15980 : ac->req->operation);
2817 15980 : if (ret != LDB_SUCCESS) {
2818 0 : return ret;
2819 : }
2820 :
2821 15980 : if (el == NULL || el->num_values == 0) {
2822 6 : ldb_asprintf_errstring(ldb,
2823 : "%08X: samldb: 'userAccountControl' can't be deleted!",
2824 6 : W_ERROR_V(WERR_DS_ILLEGAL_MOD_OPERATION));
2825 6 : return LDB_ERR_UNWILLING_TO_PERFORM;
2826 : }
2827 :
2828 : /* Create a temporary message for fetching the "userAccountControl" */
2829 15974 : tmp_msg = ldb_msg_new(ac->msg);
2830 15974 : if (tmp_msg == NULL) {
2831 0 : return ldb_module_oom(ac->module);
2832 : }
2833 15974 : ret = ldb_msg_add(tmp_msg, el, 0);
2834 15974 : if (ret != LDB_SUCCESS) {
2835 0 : return ret;
2836 : }
2837 15974 : raw_uac = ldb_msg_find_attr_as_uint(tmp_msg,
2838 : "userAccountControl",
2839 : 0);
2840 15974 : talloc_free(tmp_msg);
2841 : /*
2842 : * UF_LOCKOUT, UF_PASSWD_CANT_CHANGE and UF_PASSWORD_EXPIRED
2843 : * are only generated and not stored. We ignore them almost
2844 : * completely, along with unknown bits and UF_SCRIPT.
2845 : *
2846 : * The only exception is ACB_AUTOLOCK, which features in
2847 : * clear_acb when the bit is cleared in this modify operation.
2848 : *
2849 : * MS-SAMR 2.2.1.13 UF_FLAG Codes states that some bits are
2850 : * ignored by clients and servers
2851 : */
2852 15974 : new_uac = raw_uac & UF_SETTABLE_BITS;
2853 :
2854 : /* Fetch the old "userAccountControl" and "objectClass" */
2855 15974 : ret = dsdb_module_search_dn(ac->module, ac, &res, ac->msg->dn, attrs,
2856 : DSDB_FLAG_NEXT_MODULE, ac->req);
2857 15974 : if (ret != LDB_SUCCESS) {
2858 0 : return ret;
2859 : }
2860 15974 : old_uac = ldb_msg_find_attr_as_uint(res->msgs[0], "userAccountControl", 0);
2861 15974 : if (old_uac == 0) {
2862 0 : return ldb_operr(ldb);
2863 : }
2864 15974 : old_uac_computed = ldb_msg_find_attr_as_uint(res->msgs[0],
2865 : "msDS-User-Account-Control-Computed", 0);
2866 15974 : old_lockoutTime = ldb_msg_find_attr_as_int64(res->msgs[0],
2867 : "lockoutTime", 0);
2868 15974 : old_is_critical = ldb_msg_find_attr_as_bool(res->msgs[0],
2869 : "isCriticalSystemObject", 0);
2870 : /*
2871 : * When we do not have objectclass "computer" we cannot
2872 : * switch to a workstation or (RO)DC
2873 : */
2874 15974 : el = ldb_msg_find_element(res->msgs[0], "objectClass");
2875 15974 : if (el == NULL) {
2876 0 : return ldb_operr(ldb);
2877 : }
2878 15974 : computer_val = data_blob_string_const("computer");
2879 15974 : val = ldb_msg_find_val(el, &computer_val);
2880 15974 : if (val != NULL) {
2881 1533 : is_computer_objectclass = true;
2882 : }
2883 :
2884 15974 : old_ufa = old_uac & UF_ACCOUNT_TYPE_MASK;
2885 15974 : old_atype = ds_uf2atype(old_ufa);
2886 15974 : old_pgrid = ds_uf2prim_group_rid(old_uac);
2887 :
2888 15974 : new_ufa = new_uac & UF_ACCOUNT_TYPE_MASK;
2889 15974 : if (new_ufa == 0) {
2890 : /*
2891 : * "userAccountControl" = 0 or missing one of the
2892 : * types means "UF_NORMAL_ACCOUNT". See MS-SAMR
2893 : * 3.1.1.8.10 point 8
2894 : */
2895 267 : new_ufa = UF_NORMAL_ACCOUNT;
2896 267 : new_uac |= new_ufa;
2897 : }
2898 15974 : sid = samdb_result_dom_sid(res, res->msgs[0], "objectSid");
2899 15974 : if (sid == NULL) {
2900 0 : return ldb_module_operr(ac->module);
2901 : }
2902 :
2903 15974 : ret = samldb_check_user_account_control_rules(ac, sid,
2904 : raw_uac,
2905 : new_uac,
2906 : old_uac,
2907 : is_computer_objectclass);
2908 15974 : if (ret != LDB_SUCCESS) {
2909 175 : return ret;
2910 : }
2911 :
2912 15799 : new_atype = ds_uf2atype(new_ufa);
2913 15799 : new_pgrid = ds_uf2prim_group_rid(new_uac);
2914 :
2915 15799 : clear_uac = (old_uac | old_uac_computed) & ~raw_uac;
2916 :
2917 15799 : switch (new_ufa) {
2918 14454 : case UF_NORMAL_ACCOUNT:
2919 14454 : new_is_critical = old_is_critical;
2920 14454 : break;
2921 :
2922 0 : case UF_INTERDOMAIN_TRUST_ACCOUNT:
2923 0 : new_is_critical = true;
2924 0 : break;
2925 :
2926 706 : case UF_WORKSTATION_TRUST_ACCOUNT:
2927 706 : new_is_critical = false;
2928 706 : if (new_uac & UF_PARTIAL_SECRETS_ACCOUNT) {
2929 205 : new_is_critical = true;
2930 : }
2931 670 : break;
2932 :
2933 602 : case UF_SERVER_TRUST_ACCOUNT:
2934 602 : new_is_critical = true;
2935 602 : break;
2936 :
2937 0 : default:
2938 0 : ldb_asprintf_errstring(ldb,
2939 : "%08X: samldb: invalid userAccountControl[0x%08X]",
2940 0 : W_ERROR_V(WERR_INVALID_PARAMETER), raw_uac);
2941 0 : return LDB_ERR_OTHER;
2942 : }
2943 :
2944 15799 : if (old_atype != new_atype) {
2945 142 : ret = samdb_msg_append_uint(ldb, ac->msg, ac->msg,
2946 : "sAMAccountType", new_atype,
2947 : LDB_FLAG_MOD_REPLACE);
2948 142 : if (ret != LDB_SUCCESS) {
2949 0 : return ret;
2950 : }
2951 : }
2952 :
2953 : /* As per MS-SAMR 3.1.1.8.10 these flags have not to be set */
2954 15799 : if ((clear_uac & UF_LOCKOUT) && (old_lockoutTime != 0)) {
2955 : /* "lockoutTime" reset as per MS-SAMR 3.1.1.8.10 */
2956 10 : ldb_msg_remove_attr(ac->msg, "lockoutTime");
2957 10 : ret = samdb_msg_append_uint64(ldb, ac->msg, ac->msg, "lockoutTime",
2958 : (NTTIME)0, LDB_FLAG_MOD_REPLACE);
2959 10 : if (ret != LDB_SUCCESS) {
2960 0 : return ret;
2961 : }
2962 : }
2963 :
2964 : /*
2965 : * "isCriticalSystemObject" might be set/changed
2966 : *
2967 : * Even a change from UF_NORMAL_ACCOUNT (implicitly FALSE) to
2968 : * UF_WORKSTATION_TRUST_ACCOUNT (actually FALSE) triggers
2969 : * creating the attribute.
2970 : */
2971 15799 : if (old_is_critical != new_is_critical || old_atype != new_atype) {
2972 221 : ret = ldb_msg_append_string(ac->msg, "isCriticalSystemObject",
2973 : new_is_critical ? "TRUE": "FALSE",
2974 : LDB_FLAG_MOD_REPLACE);
2975 221 : if (ret != LDB_SUCCESS) {
2976 0 : return ret;
2977 : }
2978 : }
2979 :
2980 15799 : if (!ldb_msg_find_element(ac->msg, "primaryGroupID") &&
2981 : (old_pgrid != new_pgrid)) {
2982 : /* Older AD deployments don't know about the RODC group */
2983 224 : if (new_pgrid == DOMAIN_RID_READONLY_DCS) {
2984 21 : ret = samldb_prim_group_tester(ac, new_pgrid);
2985 21 : if (ret != LDB_SUCCESS) {
2986 0 : return ret;
2987 : }
2988 : }
2989 :
2990 224 : ret = samdb_msg_append_uint(ldb, ac->msg, ac->msg,
2991 : "primaryGroupID", new_pgrid,
2992 : LDB_FLAG_MOD_REPLACE);
2993 224 : if (ret != LDB_SUCCESS) {
2994 0 : return ret;
2995 : }
2996 : }
2997 :
2998 : /* Propagate eventual "userAccountControl" attribute changes */
2999 15799 : if (old_uac != new_uac) {
3000 15196 : char *tempstr = talloc_asprintf(ac->msg, "%d",
3001 : new_uac);
3002 15196 : if (tempstr == NULL) {
3003 0 : return ldb_module_oom(ac->module);
3004 : }
3005 :
3006 15196 : ret = ldb_msg_add_empty(ac->msg,
3007 : "userAccountControl",
3008 : LDB_FLAG_MOD_REPLACE,
3009 : &el);
3010 15196 : el->values = talloc(ac->msg, struct ldb_val);
3011 15196 : el->num_values = 1;
3012 15196 : el->values[0].data = (uint8_t *) tempstr;
3013 15196 : el->values[0].length = strlen(tempstr);
3014 : } else {
3015 603 : ldb_msg_remove_attr(ac->msg, "userAccountControl");
3016 : }
3017 :
3018 15690 : return LDB_SUCCESS;
3019 : }
3020 :
3021 59 : static int samldb_check_pwd_last_set_acl(struct samldb_ctx *ac,
3022 : struct dom_sid *sid)
3023 : {
3024 59 : struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
3025 59 : int ret = 0;
3026 59 : struct security_token *user_token = NULL;
3027 59 : struct security_descriptor *domain_sd = NULL;
3028 59 : struct ldb_dn *domain_dn = ldb_get_default_basedn(ldb_module_get_ctx(ac->module));
3029 59 : const char *operation = "";
3030 59 : const struct dsdb_class *objectclass = NULL;
3031 :
3032 59 : if (dsdb_module_am_system(ac->module)) {
3033 1 : return LDB_SUCCESS;
3034 : }
3035 :
3036 58 : switch (ac->req->operation) {
3037 0 : case LDB_ADD:
3038 0 : operation = "add";
3039 0 : break;
3040 58 : case LDB_MODIFY:
3041 58 : operation = "modify";
3042 58 : break;
3043 0 : default:
3044 0 : return ldb_module_operr(ac->module);
3045 : }
3046 :
3047 58 : user_token = acl_user_token(ac->module);
3048 58 : if (user_token == NULL) {
3049 0 : return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
3050 : }
3051 :
3052 58 : ret = samldb_get_domain_secdesc_and_oc(ac, &domain_sd, &objectclass);
3053 58 : if (ret != LDB_SUCCESS) {
3054 0 : return ret;
3055 : }
3056 58 : ret = acl_check_extended_right(ac,
3057 : ac->module,
3058 : ac->req,
3059 : objectclass,
3060 : domain_sd,
3061 : user_token,
3062 : GUID_DRS_UNEXPIRE_PASSWORD,
3063 : SEC_ADS_CONTROL_ACCESS,
3064 : sid);
3065 58 : if (ret != LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS) {
3066 58 : return ret;
3067 : }
3068 :
3069 0 : ldb_debug_set(ldb, LDB_DEBUG_WARNING,
3070 : "Failed to %s %s: "
3071 : "Setting pwdLastSet to -1 requires the "
3072 : "Unexpire-Password right that was not given "
3073 : "on the Domain object",
3074 : operation,
3075 0 : ldb_dn_get_linearized(ac->msg->dn));
3076 0 : dsdb_acl_debug(domain_sd, user_token,
3077 : domain_dn, true, 10);
3078 :
3079 0 : return ret;
3080 : }
3081 :
3082 : /**
3083 : * This function is called on LDB modify operations. It performs some additions/
3084 : * replaces on the current LDB message when "pwdLastSet" changes.
3085 : */
3086 321 : static int samldb_pwd_last_set_change(struct samldb_ctx *ac)
3087 : {
3088 321 : struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
3089 321 : NTTIME last_set = 0;
3090 321 : struct ldb_message_element *el = NULL;
3091 321 : struct ldb_message *tmp_msg = NULL;
3092 321 : struct dom_sid *self_sid = NULL;
3093 11 : int ret;
3094 321 : struct ldb_result *res = NULL;
3095 321 : const char * const attrs[] = {
3096 : "objectSid",
3097 : NULL
3098 : };
3099 :
3100 332 : ret = dsdb_get_expected_new_values(ac,
3101 321 : ac->msg,
3102 : "pwdLastSet",
3103 : &el,
3104 321 : ac->req->operation);
3105 321 : if (ret != LDB_SUCCESS) {
3106 0 : return ret;
3107 : }
3108 :
3109 321 : if (el == NULL || el->num_values == 0) {
3110 6 : ldb_asprintf_errstring(ldb,
3111 : "%08X: samldb: 'pwdLastSet' can't be deleted!",
3112 6 : W_ERROR_V(WERR_DS_ILLEGAL_MOD_OPERATION));
3113 6 : return LDB_ERR_UNWILLING_TO_PERFORM;
3114 : }
3115 :
3116 : /* Create a temporary message for fetching the "userAccountControl" */
3117 315 : tmp_msg = ldb_msg_new(ac->msg);
3118 315 : if (tmp_msg == NULL) {
3119 0 : return ldb_module_oom(ac->module);
3120 : }
3121 315 : ret = ldb_msg_add(tmp_msg, el, 0);
3122 315 : if (ret != LDB_SUCCESS) {
3123 0 : return ret;
3124 : }
3125 315 : last_set = samdb_result_nttime(tmp_msg, "pwdLastSet", 0);
3126 315 : talloc_free(tmp_msg);
3127 :
3128 : /*
3129 : * Setting -1 (0xFFFFFFFFFFFFFFFF) requires the Unexpire-Password right
3130 : */
3131 315 : if (last_set != UINT64_MAX) {
3132 245 : return LDB_SUCCESS;
3133 : }
3134 :
3135 : /* Fetch the "objectSid" */
3136 59 : ret = dsdb_module_search_dn(ac->module, ac, &res, ac->msg->dn, attrs,
3137 : DSDB_FLAG_NEXT_MODULE, ac->req);
3138 59 : if (ret != LDB_SUCCESS) {
3139 0 : return ret;
3140 : }
3141 59 : self_sid = samdb_result_dom_sid(res, res->msgs[0], "objectSid");
3142 59 : if (self_sid == NULL) {
3143 0 : return ldb_module_operr(ac->module);
3144 : }
3145 :
3146 59 : ret = samldb_check_pwd_last_set_acl(ac, self_sid);
3147 59 : if (ret != LDB_SUCCESS) {
3148 0 : return ret;
3149 : }
3150 :
3151 59 : return LDB_SUCCESS;
3152 : }
3153 :
3154 189 : static int samldb_lockout_time(struct samldb_ctx *ac)
3155 : {
3156 189 : struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
3157 0 : NTTIME lockoutTime;
3158 0 : struct ldb_message_element *el;
3159 0 : struct ldb_message *tmp_msg;
3160 0 : int ret;
3161 :
3162 189 : ret = dsdb_get_expected_new_values(ac,
3163 189 : ac->msg,
3164 : "lockoutTime",
3165 : &el,
3166 189 : ac->req->operation);
3167 189 : if (ret != LDB_SUCCESS) {
3168 0 : return ret;
3169 : }
3170 :
3171 189 : if (el == NULL || el->num_values == 0) {
3172 0 : ldb_asprintf_errstring(ldb,
3173 : "%08X: samldb: 'lockoutTime' can't be deleted!",
3174 0 : W_ERROR_V(WERR_DS_ILLEGAL_MOD_OPERATION));
3175 0 : return LDB_ERR_UNWILLING_TO_PERFORM;
3176 : }
3177 :
3178 : /* Create a temporary message for fetching the "lockoutTime" */
3179 189 : tmp_msg = ldb_msg_new(ac->msg);
3180 189 : if (tmp_msg == NULL) {
3181 0 : return ldb_module_oom(ac->module);
3182 : }
3183 189 : ret = ldb_msg_add(tmp_msg, el, 0);
3184 189 : if (ret != LDB_SUCCESS) {
3185 0 : return ret;
3186 : }
3187 189 : lockoutTime = ldb_msg_find_attr_as_int64(tmp_msg,
3188 : "lockoutTime",
3189 : 0);
3190 189 : talloc_free(tmp_msg);
3191 :
3192 189 : if (lockoutTime != 0) {
3193 63 : return LDB_SUCCESS;
3194 : }
3195 :
3196 : /* lockoutTime == 0 resets badPwdCount */
3197 126 : ldb_msg_remove_attr(ac->msg, "badPwdCount");
3198 126 : ret = samdb_msg_append_int(ldb, ac->msg, ac->msg,
3199 : "badPwdCount", 0,
3200 : LDB_FLAG_MOD_REPLACE);
3201 126 : if (ret != LDB_SUCCESS) {
3202 0 : return ret;
3203 : }
3204 :
3205 126 : return LDB_SUCCESS;
3206 : }
3207 :
3208 146 : static int samldb_group_type_change(struct samldb_ctx *ac)
3209 : {
3210 146 : struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
3211 0 : uint32_t group_type, old_group_type, account_type;
3212 0 : struct ldb_message_element *el;
3213 0 : struct ldb_message *tmp_msg;
3214 0 : int ret;
3215 0 : struct ldb_result *res;
3216 146 : const char * const attrs[] = { "groupType", NULL };
3217 :
3218 146 : ret = dsdb_get_expected_new_values(ac,
3219 146 : ac->msg,
3220 : "groupType",
3221 : &el,
3222 146 : ac->req->operation);
3223 146 : if (ret != LDB_SUCCESS) {
3224 0 : return ret;
3225 : }
3226 :
3227 146 : if (el == NULL) {
3228 : /* we are not affected */
3229 3 : return LDB_SUCCESS;
3230 : }
3231 :
3232 : /* Create a temporary message for fetching the "groupType" */
3233 143 : tmp_msg = ldb_msg_new(ac->msg);
3234 143 : if (tmp_msg == NULL) {
3235 0 : return ldb_module_oom(ac->module);
3236 : }
3237 143 : ret = ldb_msg_add(tmp_msg, el, 0);
3238 143 : if (ret != LDB_SUCCESS) {
3239 0 : return ret;
3240 : }
3241 143 : group_type = ldb_msg_find_attr_as_uint(tmp_msg, "groupType", 0);
3242 143 : talloc_free(tmp_msg);
3243 :
3244 143 : ret = dsdb_module_search_dn(ac->module, ac, &res, ac->msg->dn, attrs,
3245 : DSDB_FLAG_NEXT_MODULE |
3246 : DSDB_SEARCH_SHOW_DELETED, ac->req);
3247 143 : if (ret != LDB_SUCCESS) {
3248 0 : return ret;
3249 : }
3250 143 : old_group_type = ldb_msg_find_attr_as_uint(res->msgs[0], "groupType", 0);
3251 143 : if (old_group_type == 0) {
3252 0 : return ldb_operr(ldb);
3253 : }
3254 :
3255 : /* Group type switching isn't so easy as it seems: We can only
3256 : * change in this directions: global <-> universal <-> local
3257 : * On each step also the group type itself
3258 : * (security/distribution) is variable. */
3259 :
3260 143 : if (ldb_request_get_control(ac->req, LDB_CONTROL_PROVISION_OID) == NULL) {
3261 143 : switch (group_type) {
3262 41 : case GTYPE_SECURITY_GLOBAL_GROUP:
3263 : case GTYPE_DISTRIBUTION_GLOBAL_GROUP:
3264 : /* change to "universal" allowed */
3265 41 : if ((old_group_type == GTYPE_SECURITY_DOMAIN_LOCAL_GROUP) ||
3266 0 : (old_group_type == GTYPE_DISTRIBUTION_DOMAIN_LOCAL_GROUP)) {
3267 9 : ldb_set_errstring(ldb,
3268 : "samldb: Change from security/distribution local group forbidden!");
3269 9 : return LDB_ERR_UNWILLING_TO_PERFORM;
3270 : }
3271 32 : break;
3272 :
3273 43 : case GTYPE_SECURITY_UNIVERSAL_GROUP:
3274 : case GTYPE_DISTRIBUTION_UNIVERSAL_GROUP:
3275 : /* each change allowed */
3276 43 : break;
3277 44 : case GTYPE_SECURITY_DOMAIN_LOCAL_GROUP:
3278 : case GTYPE_DISTRIBUTION_DOMAIN_LOCAL_GROUP:
3279 : /* change to "universal" allowed */
3280 44 : if ((old_group_type == GTYPE_SECURITY_GLOBAL_GROUP) ||
3281 0 : (old_group_type == GTYPE_DISTRIBUTION_GLOBAL_GROUP)) {
3282 9 : ldb_set_errstring(ldb,
3283 : "samldb: Change from security/distribution global group forbidden!");
3284 9 : return LDB_ERR_UNWILLING_TO_PERFORM;
3285 : }
3286 35 : break;
3287 :
3288 15 : case GTYPE_SECURITY_BUILTIN_LOCAL_GROUP:
3289 : default:
3290 : /* we don't allow this "groupType" values */
3291 15 : return LDB_ERR_UNWILLING_TO_PERFORM;
3292 0 : break;
3293 : }
3294 : }
3295 :
3296 110 : account_type = ds_gtype2atype(group_type);
3297 110 : if (account_type == 0) {
3298 0 : ldb_set_errstring(ldb, "samldb: Unrecognized account type!");
3299 0 : return LDB_ERR_UNWILLING_TO_PERFORM;
3300 : }
3301 110 : ret = samdb_msg_append_uint(ldb, ac->msg, ac->msg, "sAMAccountType",
3302 : account_type, LDB_FLAG_MOD_REPLACE);
3303 110 : if (ret != LDB_SUCCESS) {
3304 0 : return ret;
3305 : }
3306 :
3307 110 : return LDB_SUCCESS;
3308 : }
3309 :
3310 5159 : static int samldb_member_check(struct samldb_ctx *ac)
3311 : {
3312 5159 : const char * const attrs[] = { "objectSid", NULL };
3313 5159 : struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
3314 39 : struct ldb_message_element *el;
3315 39 : struct ldb_dn *member_dn;
3316 39 : struct dom_sid *sid;
3317 39 : struct ldb_result *res;
3318 39 : struct dom_sid *group_sid;
3319 39 : unsigned int i, j;
3320 39 : int ret;
3321 :
3322 : /* Fetch information from the existing object */
3323 :
3324 5159 : ret = dsdb_module_search(ac->module, ac, &res, ac->msg->dn, LDB_SCOPE_BASE, attrs,
3325 : DSDB_FLAG_NEXT_MODULE | DSDB_SEARCH_SHOW_DELETED, ac->req, NULL);
3326 5159 : if (ret != LDB_SUCCESS) {
3327 0 : return ret;
3328 : }
3329 5159 : if (res->count != 1) {
3330 0 : return ldb_operr(ldb);
3331 : }
3332 :
3333 5159 : group_sid = samdb_result_dom_sid(res, res->msgs[0], "objectSid");
3334 5159 : if (group_sid == NULL) {
3335 0 : return ldb_operr(ldb);
3336 : }
3337 :
3338 : /* We've to walk over all modification entries and consider the "member"
3339 : * ones. */
3340 14397 : for (i = 0; i < ac->msg->num_elements; i++) {
3341 9241 : if (ldb_attr_cmp(ac->msg->elements[i].name, "member") != 0) {
3342 0 : continue;
3343 : }
3344 :
3345 9202 : el = &ac->msg->elements[i];
3346 18816 : for (j = 0; j < el->num_values; j++) {
3347 40 : struct ldb_result *group_res;
3348 9578 : const char *group_attrs[] = { "primaryGroupID" , NULL };
3349 40 : uint32_t prim_group_rid;
3350 :
3351 9578 : if (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_DELETE) {
3352 : /* Deletes will be handled in
3353 : * repl_meta_data, and deletes not
3354 : * matching a member will return
3355 : * LDB_ERR_UNWILLING_TO_PERFORM
3356 : * there */
3357 1026 : continue;
3358 : }
3359 :
3360 8991 : member_dn = ldb_dn_from_ldb_val(ac, ldb,
3361 8982 : &el->values[j]);
3362 8982 : if (!ldb_dn_validate(member_dn)) {
3363 3 : return ldb_operr(ldb);
3364 : }
3365 :
3366 : /* Denies to add "member"s to groups which are primary
3367 : * ones for them - in this case return
3368 : * ERR_ENTRY_ALREADY_EXISTS. */
3369 :
3370 8982 : ret = dsdb_module_search_dn(ac->module, ac, &group_res,
3371 : member_dn, group_attrs,
3372 : DSDB_FLAG_NEXT_MODULE, ac->req);
3373 8982 : if (ret == LDB_ERR_NO_SUCH_OBJECT) {
3374 : /* member DN doesn't exist yet */
3375 0 : continue;
3376 : }
3377 8982 : if (ret != LDB_SUCCESS) {
3378 0 : return ret;
3379 : }
3380 8982 : prim_group_rid = ldb_msg_find_attr_as_uint(group_res->msgs[0], "primaryGroupID", (uint32_t)-1);
3381 8982 : if (prim_group_rid == (uint32_t) -1) {
3382 : /* the member hasn't to be a user account ->
3383 : * therefore no check needed in this case. */
3384 430 : continue;
3385 : }
3386 :
3387 8552 : sid = dom_sid_add_rid(ac, samdb_domain_sid(ldb),
3388 : prim_group_rid);
3389 8552 : if (sid == NULL) {
3390 0 : return ldb_operr(ldb);
3391 : }
3392 :
3393 8552 : if (dom_sid_equal(group_sid, sid)) {
3394 3 : ldb_asprintf_errstring(ldb,
3395 : "samldb: member %s already set via primaryGroupID %u",
3396 : ldb_dn_get_linearized(member_dn), prim_group_rid);
3397 3 : return LDB_ERR_ENTRY_ALREADY_EXISTS;
3398 : }
3399 : }
3400 : }
3401 :
3402 5156 : talloc_free(res);
3403 :
3404 5156 : return LDB_SUCCESS;
3405 : }
3406 :
3407 : /* SAM objects have special rules regarding the "description" attribute on
3408 : * modify operations. */
3409 1029 : static int samldb_description_check(struct samldb_ctx *ac, bool *modified)
3410 : {
3411 1029 : struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
3412 1029 : const char * const attrs[] = { "objectClass", "description", NULL };
3413 123 : struct ldb_result *res;
3414 123 : unsigned int i;
3415 123 : int ret;
3416 :
3417 : /* Fetch information from the existing object */
3418 1029 : ret = dsdb_module_search(ac->module, ac, &res, ac->msg->dn, LDB_SCOPE_BASE, attrs,
3419 : DSDB_FLAG_NEXT_MODULE | DSDB_SEARCH_SHOW_DELETED, ac->req,
3420 : "(|(objectclass=user)(objectclass=group)(objectclass=samDomain)(objectclass=samServer))");
3421 1029 : if (ret != LDB_SUCCESS) {
3422 : /* don't treat it specially ... let normal error codes
3423 : happen from other places */
3424 0 : ldb_reset_err_string(ldb);
3425 0 : return LDB_SUCCESS;
3426 : }
3427 1029 : if (res->count == 0) {
3428 : /* we didn't match the filter */
3429 315 : talloc_free(res);
3430 315 : return LDB_SUCCESS;
3431 : }
3432 :
3433 : /* We've to walk over all modification entries and consider the
3434 : * "description" ones. */
3435 2520 : for (i = 0; i < ac->msg->num_elements; i++) {
3436 1806 : if (ldb_attr_cmp(ac->msg->elements[i].name, "description") == 0) {
3437 717 : ac->msg->elements[i].flags |= LDB_FLAG_INTERNAL_FORCE_SINGLE_VALUE_CHECK;
3438 717 : *modified = true;
3439 : }
3440 : }
3441 :
3442 714 : talloc_free(res);
3443 :
3444 714 : return LDB_SUCCESS;
3445 : }
3446 :
3447 : #define SPN_ALIAS_NONE 0
3448 : #define SPN_ALIAS_LINK 1
3449 : #define SPN_ALIAS_TARGET 2
3450 :
3451 5838 : static int find_spn_aliases(struct ldb_context *ldb,
3452 : TALLOC_CTX *mem_ctx,
3453 : const char *service_class,
3454 : char ***aliases,
3455 : size_t *n_aliases,
3456 : int *direction)
3457 : {
3458 : /*
3459 : * If you change the way this works, you should also look at changing
3460 : * LDB_lookup_spn_alias() in source4/dsdb/samdb/cracknames.c, which
3461 : * does some of the same work.
3462 : *
3463 : * In particular, note that sPNMappings are resolved on a first come,
3464 : * first served basis. For example, if we have
3465 : *
3466 : * host=ldap,cifs
3467 : * foo=ldap
3468 : * cifs=host,alerter
3469 : *
3470 : * then 'ldap', 'cifs', and 'host' will resolve to 'host', and
3471 : * 'alerter' will resolve to 'cifs'.
3472 : *
3473 : * If this resolution method is made more complicated, then the
3474 : * cracknames function should also be changed.
3475 : */
3476 272 : size_t i, j;
3477 272 : int ret;
3478 272 : bool ok;
3479 5838 : struct ldb_result *res = NULL;
3480 5838 : struct ldb_message_element *spnmappings = NULL;
3481 5838 : TALLOC_CTX *tmp_ctx = NULL;
3482 5838 : struct ldb_dn *service_dn = NULL;
3483 :
3484 5838 : const char *attrs[] = {
3485 : "sPNMappings",
3486 : NULL
3487 : };
3488 :
3489 5838 : *direction = SPN_ALIAS_NONE;
3490 :
3491 5838 : tmp_ctx = talloc_new(mem_ctx);
3492 5838 : if (tmp_ctx == NULL) {
3493 0 : return ldb_oom(ldb);
3494 : }
3495 :
3496 5838 : service_dn = ldb_dn_new(
3497 : tmp_ctx, ldb,
3498 : "CN=Directory Service,CN=Windows NT,CN=Services");
3499 5838 : if (service_dn == NULL) {
3500 0 : talloc_free(tmp_ctx);
3501 0 : return ldb_oom(ldb);
3502 : }
3503 :
3504 5838 : ok = ldb_dn_add_base(service_dn, ldb_get_config_basedn(ldb));
3505 5838 : if (! ok) {
3506 0 : talloc_free(tmp_ctx);
3507 0 : return LDB_ERR_OPERATIONS_ERROR;
3508 : }
3509 :
3510 5838 : ret = ldb_search(ldb, tmp_ctx, &res, service_dn, LDB_SCOPE_BASE,
3511 : attrs, "(objectClass=nTDSService)");
3512 :
3513 5838 : if (ret != LDB_SUCCESS || res->count != 1) {
3514 0 : DBG_WARNING("sPNMappings not found.\n");
3515 0 : talloc_free(tmp_ctx);
3516 0 : return ret;
3517 : }
3518 :
3519 5838 : spnmappings = ldb_msg_find_element(res->msgs[0], "sPNMappings");
3520 5838 : if (spnmappings == NULL || spnmappings->num_values == 0) {
3521 0 : DBG_WARNING("no sPNMappings attribute\n");
3522 0 : talloc_free(tmp_ctx);
3523 0 : return LDB_ERR_NO_SUCH_OBJECT;
3524 : }
3525 5838 : *n_aliases = 0;
3526 :
3527 7445 : for (i = 0; i < spnmappings->num_values; i++) {
3528 5838 : char *p = NULL;
3529 6110 : char *mapping = talloc_strndup(
3530 : tmp_ctx,
3531 5838 : (char *)spnmappings->values[i].data,
3532 5838 : spnmappings->values[i].length);
3533 5838 : if (mapping == NULL) {
3534 0 : talloc_free(tmp_ctx);
3535 0 : return ldb_oom(ldb);
3536 : }
3537 :
3538 5838 : p = strchr(mapping, '=');
3539 5838 : if (p == NULL) {
3540 0 : talloc_free(tmp_ctx);
3541 0 : return LDB_ERR_ALIAS_PROBLEM;
3542 : }
3543 5838 : p[0] = '\0';
3544 5838 : p++;
3545 :
3546 5838 : if (strcasecmp(mapping, service_class) == 0) {
3547 : /*
3548 : * We need to return the reverse aliases for this one.
3549 : *
3550 : * typically, this means the service_class is "host"
3551 : * and the mapping is "host=alerter,appmgmt,cisvc,..",
3552 : * so we get "alerter", "appmgmt", etc in the list of
3553 : * aliases.
3554 : */
3555 :
3556 : /* There is one more field than there are commas */
3557 3716 : size_t n = 1;
3558 :
3559 1560240 : for (j = 0; p[j] != '\0'; j++) {
3560 1556300 : if (p[j] == ',') {
3561 204880 : n++;
3562 204880 : p[j] = '\0';
3563 : }
3564 : }
3565 3940 : *aliases = talloc_array(mem_ctx, char*, n);
3566 3940 : if (*aliases == NULL) {
3567 0 : talloc_free(tmp_ctx);
3568 0 : return ldb_oom(ldb);
3569 : }
3570 3940 : *n_aliases = n;
3571 3940 : talloc_steal(mem_ctx, mapping);
3572 212984 : for (j = 0; j < n; j++) {
3573 208820 : (*aliases)[j] = p;
3574 208820 : p += strlen(p) + 1;
3575 : }
3576 3940 : talloc_free(tmp_ctx);
3577 3940 : *direction = SPN_ALIAS_LINK;
3578 3940 : return LDB_SUCCESS;
3579 : }
3580 : /*
3581 : * We need to look along the list to see if service_class is
3582 : * there; if so, we return a list of one item (probably "host").
3583 : */
3584 2460 : do {
3585 96539 : char *str = p;
3586 96539 : p = strchr(p, ',');
3587 96539 : if (p != NULL) {
3588 94932 : p[0] = '\0';
3589 94932 : p++;
3590 : }
3591 96539 : if (strcasecmp(str, service_class) == 0) {
3592 291 : *aliases = talloc_array(mem_ctx, char*, 1);
3593 291 : if (*aliases == NULL) {
3594 0 : talloc_free(tmp_ctx);
3595 0 : return ldb_oom(ldb);
3596 : }
3597 291 : *n_aliases = 1;
3598 291 : (*aliases)[0] = mapping;
3599 291 : talloc_steal(mem_ctx, mapping);
3600 291 : talloc_free(tmp_ctx);
3601 291 : *direction = SPN_ALIAS_TARGET;
3602 291 : return LDB_SUCCESS;
3603 : }
3604 96248 : } while (p != NULL);
3605 : }
3606 1607 : DBG_INFO("no sPNMappings alias for '%s'\n", service_class);
3607 1607 : talloc_free(tmp_ctx);
3608 1607 : *aliases = NULL;
3609 1607 : *n_aliases = 0;
3610 1607 : return LDB_SUCCESS;
3611 : }
3612 :
3613 :
3614 215069 : static int get_spn_dn(struct ldb_context *ldb,
3615 : TALLOC_CTX *tmp_ctx,
3616 : const char *candidate,
3617 : struct ldb_dn **dn)
3618 : {
3619 12150 : int ret;
3620 215069 : const char *empty_attrs[] = { NULL };
3621 215069 : struct ldb_message *msg = NULL;
3622 215069 : struct ldb_dn *base_dn = ldb_get_default_basedn(ldb);
3623 :
3624 215069 : const char *enc_candidate = NULL;
3625 :
3626 215069 : *dn = NULL;
3627 :
3628 215069 : enc_candidate = ldb_binary_encode_string(tmp_ctx, candidate);
3629 215069 : if (enc_candidate == NULL) {
3630 0 : return ldb_operr(ldb);
3631 : }
3632 :
3633 215069 : ret = dsdb_search_one(ldb,
3634 : tmp_ctx,
3635 : &msg,
3636 : base_dn,
3637 : LDB_SCOPE_SUBTREE,
3638 : empty_attrs,
3639 : 0,
3640 : "(servicePrincipalName=%s)",
3641 : enc_candidate);
3642 215069 : if (ret != LDB_SUCCESS) {
3643 202315 : return ret;
3644 : }
3645 608 : *dn = msg->dn;
3646 608 : return LDB_SUCCESS;
3647 : }
3648 :
3649 :
3650 68 : static int check_spn_write_rights(struct ldb_context *ldb,
3651 : TALLOC_CTX *mem_ctx,
3652 : const char *spn,
3653 : struct ldb_dn *dn)
3654 : {
3655 2 : int ret;
3656 68 : struct ldb_message *msg = NULL;
3657 68 : struct ldb_message_element *del_el = NULL;
3658 68 : struct ldb_message_element *add_el = NULL;
3659 68 : struct ldb_val val = {
3660 : .data = discard_const_p(uint8_t, spn),
3661 68 : .length = strlen(spn)
3662 : };
3663 :
3664 68 : msg = ldb_msg_new(mem_ctx);
3665 68 : if (msg == NULL) {
3666 0 : return ldb_oom(ldb);
3667 : }
3668 68 : msg->dn = dn;
3669 :
3670 68 : ret = ldb_msg_add_empty(msg,
3671 : "servicePrincipalName",
3672 : LDB_FLAG_MOD_DELETE,
3673 : &del_el);
3674 68 : if (ret != LDB_SUCCESS) {
3675 0 : talloc_free(msg);
3676 0 : return ret;
3677 : }
3678 :
3679 68 : del_el->values = talloc_array(msg->elements, struct ldb_val, 1);
3680 68 : if (del_el->values == NULL) {
3681 0 : talloc_free(msg);
3682 0 : return ret;
3683 : }
3684 :
3685 68 : del_el->values[0] = val;
3686 68 : del_el->num_values = 1;
3687 :
3688 68 : ret = ldb_msg_add_empty(msg,
3689 : "servicePrincipalName",
3690 : LDB_FLAG_MOD_ADD,
3691 : &add_el);
3692 68 : if (ret != LDB_SUCCESS) {
3693 0 : talloc_free(msg);
3694 0 : return ret;
3695 : }
3696 :
3697 68 : add_el->values = talloc_array(msg->elements, struct ldb_val, 1);
3698 68 : if (add_el->values == NULL) {
3699 0 : talloc_free(msg);
3700 0 : return ret;
3701 : }
3702 :
3703 68 : add_el->values[0] = val;
3704 68 : add_el->num_values = 1;
3705 :
3706 68 : ret = ldb_modify(ldb, msg);
3707 68 : if (ret == LDB_ERR_NO_SUCH_ATTRIBUTE) {
3708 0 : DBG_ERR("hmm I think we're OK, but not sure\n");
3709 68 : } else if (ret != LDB_SUCCESS) {
3710 14 : DBG_ERR("SPN write rights check failed with %d\n", ret);
3711 14 : talloc_free(msg);
3712 14 : return ret;
3713 : }
3714 54 : talloc_free(msg);
3715 54 : return LDB_SUCCESS;
3716 : }
3717 :
3718 :
3719 5838 : static int check_spn_alias_collision(struct ldb_context *ldb,
3720 : TALLOC_CTX *mem_ctx,
3721 : const char *spn,
3722 : struct ldb_dn *target_dn)
3723 : {
3724 272 : int ret;
3725 5838 : char *service_class = NULL;
3726 5838 : char *spn_tail = NULL;
3727 5838 : char *p = NULL;
3728 5838 : char **aliases = NULL;
3729 5838 : size_t n_aliases = 0;
3730 272 : size_t i, len;
3731 5838 : TALLOC_CTX *tmp_ctx = NULL;
3732 5838 : const char *target_dnstr = ldb_dn_get_linearized(target_dn);
3733 272 : int link_direction;
3734 :
3735 5838 : tmp_ctx = talloc_new(mem_ctx);
3736 5838 : if (tmp_ctx == NULL) {
3737 0 : return ldb_oom(ldb);
3738 : }
3739 :
3740 : /*
3741 : * "dns/example.com/xxx" gives
3742 : * service_class = "dns"
3743 : * spn_tail = "example.com/xxx"
3744 : */
3745 5838 : p = strchr(spn, '/');
3746 5838 : if (p == NULL) {
3747 : /* bad SPN */
3748 0 : talloc_free(tmp_ctx);
3749 0 : return ldb_error(ldb,
3750 : LDB_ERR_OPERATIONS_ERROR,
3751 : "malformed servicePrincipalName");
3752 : }
3753 5838 : len = p - spn;
3754 :
3755 5838 : service_class = talloc_strndup(tmp_ctx, spn, len);
3756 5838 : if (service_class == NULL) {
3757 0 : talloc_free(tmp_ctx);
3758 0 : return ldb_oom(ldb);
3759 : }
3760 5838 : spn_tail = p + 1;
3761 :
3762 5838 : ret = find_spn_aliases(ldb,
3763 : tmp_ctx,
3764 : service_class,
3765 : &aliases,
3766 : &n_aliases,
3767 : &link_direction);
3768 5838 : if (ret != LDB_SUCCESS) {
3769 0 : talloc_free(tmp_ctx);
3770 0 : return ret;
3771 : }
3772 :
3773 : /*
3774 : * we have the list of aliases, and now we need to combined them with
3775 : * spn_tail and see if we can find the SPN.
3776 : */
3777 214555 : for (i = 0; i < n_aliases; i++) {
3778 208759 : struct ldb_dn *colliding_dn = NULL;
3779 208759 : const char *colliding_dnstr = NULL;
3780 :
3781 220635 : char *candidate = talloc_asprintf(tmp_ctx,
3782 : "%s/%s",
3783 208759 : aliases[i],
3784 : spn_tail);
3785 208759 : if (candidate == NULL) {
3786 0 : talloc_free(tmp_ctx);
3787 42 : return ldb_oom(ldb);
3788 : }
3789 :
3790 208759 : ret = get_spn_dn(ldb, tmp_ctx, candidate, &colliding_dn);
3791 208759 : if (ret == LDB_ERR_NO_SUCH_OBJECT) {
3792 208623 : DBG_DEBUG("SPN alias '%s' not found (good)\n",
3793 : candidate);
3794 208623 : talloc_free(candidate);
3795 208623 : continue;
3796 : }
3797 136 : if (ret != LDB_SUCCESS) {
3798 0 : DBG_ERR("SPN '%s' search error %d\n", candidate, ret);
3799 0 : talloc_free(tmp_ctx);
3800 0 : return ret;
3801 : }
3802 :
3803 136 : target_dnstr = ldb_dn_get_linearized(target_dn);
3804 : /*
3805 : * We have found an existing SPN that matches the alias. That
3806 : * is OK only if it is on the object we are trying to add to,
3807 : * or if the SPN on the other side is a more generic alias for
3808 : * this one and we also have rights to modify it.
3809 : *
3810 : * That is, we can put "host/X" and "cifs/X" on the same
3811 : * object, but not on different objects, unless we put the
3812 : * host/X on first, and could also change that object when we
3813 : * add cifs/X. It is forbidden to add the objects in the other
3814 : * order.
3815 : *
3816 : * The rationale for this is that adding "cifs/X" effectively
3817 : * changes "host/X" by diverting traffic. If "host/X" can be
3818 : * added after "cifs/X", a sneaky person could get "cifs/X" in
3819 : * first, making "host/X" have less effect than intended.
3820 : *
3821 : * Note: we also can't have "host/X" and "Host/X" on the same
3822 : * object, but that is not relevant here.
3823 : */
3824 :
3825 136 : ret = ldb_dn_compare(colliding_dn, target_dn);
3826 136 : if (ret != 0) {
3827 96 : colliding_dnstr = ldb_dn_get_linearized(colliding_dn);
3828 96 : DBG_ERR("trying to add SPN '%s' on '%s' when '%s' is "
3829 : "on '%s'\n",
3830 : spn,
3831 : target_dnstr,
3832 : candidate,
3833 : colliding_dnstr);
3834 :
3835 96 : if (link_direction == SPN_ALIAS_LINK) {
3836 : /* we don't allow host/X if there is a
3837 : * cifs/X */
3838 28 : talloc_free(tmp_ctx);
3839 28 : return LDB_ERR_CONSTRAINT_VIOLATION;
3840 : }
3841 68 : ret = check_spn_write_rights(ldb,
3842 : tmp_ctx,
3843 : candidate,
3844 : colliding_dn);
3845 68 : if (ret != LDB_SUCCESS) {
3846 14 : DBG_ERR("SPN '%s' is on '%s' so '%s' can't be "
3847 : "added to '%s'\n",
3848 : candidate,
3849 : colliding_dnstr,
3850 : spn,
3851 : target_dnstr);
3852 14 : talloc_free(tmp_ctx);
3853 14 : ldb_asprintf_errstring(ldb,
3854 : "samldb: spn[%s] would cause a conflict",
3855 : spn);
3856 14 : return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
3857 : }
3858 : } else {
3859 40 : DBG_INFO("SPNs '%s' and '%s' alias both on '%s'\n",
3860 : candidate, spn, target_dnstr);
3861 : }
3862 94 : talloc_free(candidate);
3863 : }
3864 :
3865 5796 : talloc_free(tmp_ctx);
3866 5796 : return LDB_SUCCESS;
3867 : }
3868 :
3869 6310 : static int check_spn_direct_collision(struct ldb_context *ldb,
3870 : TALLOC_CTX *mem_ctx,
3871 : const char *spn,
3872 : struct ldb_dn *target_dn)
3873 : {
3874 274 : int ret;
3875 6310 : TALLOC_CTX *tmp_ctx = NULL;
3876 6310 : struct ldb_dn *colliding_dn = NULL;
3877 6310 : const char *target_dnstr = NULL;
3878 6310 : const char *colliding_dnstr = NULL;
3879 :
3880 6310 : tmp_ctx = talloc_new(mem_ctx);
3881 6310 : if (tmp_ctx == NULL) {
3882 0 : return ldb_oom(ldb);
3883 : }
3884 :
3885 6310 : ret = get_spn_dn(ldb, tmp_ctx, spn, &colliding_dn);
3886 6310 : if (ret == LDB_ERR_NO_SUCH_OBJECT) {
3887 5838 : DBG_DEBUG("SPN '%s' not found (good)\n", spn);
3888 5838 : talloc_free(tmp_ctx);
3889 5838 : return LDB_SUCCESS;
3890 : }
3891 472 : if (ret != LDB_SUCCESS) {
3892 0 : DBG_ERR("SPN '%s' search error %d\n", spn, ret);
3893 0 : talloc_free(tmp_ctx);
3894 0 : if (ret == LDB_ERR_COMPARE_TRUE) {
3895 : /*
3896 : * COMPARE_TRUE has special meaning here and we don't
3897 : * want to return it by mistake.
3898 : */
3899 0 : ret = LDB_ERR_OPERATIONS_ERROR;
3900 : }
3901 0 : return ret;
3902 : }
3903 : /*
3904 : * We have found this exact SPN. This is mostly harmless (depend on
3905 : * ADD vs REPLACE) when the spn is being put on the object that
3906 : * already has, so we let it through to succeed or fail as some other
3907 : * module sees fit.
3908 : */
3909 472 : target_dnstr = ldb_dn_get_linearized(target_dn);
3910 472 : ret = ldb_dn_compare(colliding_dn, target_dn);
3911 472 : if (ret != 0) {
3912 23 : colliding_dnstr = ldb_dn_get_linearized(colliding_dn);
3913 23 : DBG_ERR("SPN '%s' is on '%s' so it can't be "
3914 : "added to '%s'\n",
3915 : spn,
3916 : colliding_dnstr,
3917 : target_dnstr);
3918 23 : ldb_asprintf_errstring(ldb,
3919 : "samldb: spn[%s] would cause a conflict",
3920 : spn);
3921 23 : talloc_free(tmp_ctx);
3922 23 : return LDB_ERR_CONSTRAINT_VIOLATION;
3923 : }
3924 :
3925 449 : DBG_INFO("SPN '%s' is already on '%s'\n",
3926 : spn, target_dnstr);
3927 449 : talloc_free(tmp_ctx);
3928 449 : return LDB_ERR_COMPARE_TRUE;
3929 : }
3930 :
3931 :
3932 6326 : static int count_spn_components(struct ldb_val val)
3933 : {
3934 : /*
3935 : * a 3 part servicePrincipalName has two slashes, like
3936 : * ldap/example.com/DomainDNSZones.example.com.
3937 : *
3938 : * In krb5_parse_name_flags() we don't count "\/" as a slash (i.e.
3939 : * escaped by a backslash), but this is not the behaviour of Windows
3940 : * on setting a servicePrincipalName -- slashes are counted regardless
3941 : * of backslashes.
3942 : *
3943 : * Accordingly, here we ignore backslashes. This will reject
3944 : * multi-slash SPNs that krb5_parse_name_flags() would accept, and
3945 : * allow ones in the form "a\/b" that it won't parse.
3946 : */
3947 274 : size_t i;
3948 6326 : int slashes = 0;
3949 209760 : for (i = 0; i < val.length; i++) {
3950 203442 : char c = val.data[i];
3951 203442 : if (c == '/') {
3952 7691 : slashes++;
3953 7691 : if (slashes == 3) {
3954 : /* at this point we don't care */
3955 8 : return 4;
3956 : }
3957 : }
3958 : }
3959 6318 : return slashes + 1;
3960 : }
3961 :
3962 :
3963 : /* Check that "servicePrincipalName" changes do not introduce a collision
3964 : * globally. */
3965 3823 : static int samldb_spn_uniqueness_check(struct samldb_ctx *ac,
3966 : struct ldb_message_element *spn_el)
3967 : {
3968 3823 : struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
3969 130 : int ret;
3970 3823 : const char *spn = NULL;
3971 130 : size_t i;
3972 3823 : TALLOC_CTX *tmp_ctx = talloc_new(ac->msg);
3973 3823 : if (tmp_ctx == NULL) {
3974 0 : return ldb_oom(ldb);
3975 : }
3976 :
3977 10068 : for (i = 0; i < spn_el->num_values; i++) {
3978 274 : int n_components;
3979 6326 : spn = (char *)spn_el->values[i].data;
3980 :
3981 6326 : n_components = count_spn_components(spn_el->values[i]);
3982 6326 : if (n_components > 3 || n_components < 2) {
3983 16 : ldb_asprintf_errstring(ldb,
3984 : "samldb: spn[%s] invalid with %u components",
3985 : spn, n_components);
3986 16 : talloc_free(tmp_ctx);
3987 16 : return LDB_ERR_CONSTRAINT_VIOLATION;
3988 : }
3989 :
3990 6584 : ret = check_spn_direct_collision(ldb,
3991 : tmp_ctx,
3992 : spn,
3993 6310 : ac->msg->dn);
3994 6310 : if (ret == LDB_ERR_COMPARE_TRUE) {
3995 449 : DBG_INFO("SPN %s re-added to the same object\n", spn);
3996 449 : continue;
3997 : }
3998 5861 : if (ret != LDB_SUCCESS) {
3999 23 : DBG_ERR("SPN %s failed direct uniqueness check\n", spn);
4000 23 : talloc_free(tmp_ctx);
4001 23 : return ret;
4002 : }
4003 :
4004 6110 : ret = check_spn_alias_collision(ldb,
4005 : tmp_ctx,
4006 : spn,
4007 5838 : ac->msg->dn);
4008 :
4009 5838 : if (ret == LDB_ERR_NO_SUCH_OBJECT) {
4010 : /* we have no sPNMappings, hence no aliases */
4011 0 : break;
4012 : }
4013 5838 : if (ret != LDB_SUCCESS) {
4014 42 : DBG_ERR("SPN %s failed alias uniqueness check\n", spn);
4015 42 : talloc_free(tmp_ctx);
4016 42 : return ret;
4017 : }
4018 5796 : DBG_INFO("SPN %s seems to be unique\n", spn);
4019 : }
4020 :
4021 3742 : talloc_free(tmp_ctx);
4022 3742 : return LDB_SUCCESS;
4023 : }
4024 :
4025 :
4026 :
4027 : /* This trigger adapts the "servicePrincipalName" attributes if the
4028 : * "dNSHostName" and/or "sAMAccountName" attribute change(s) */
4029 1612 : static int samldb_service_principal_names_change(struct samldb_ctx *ac)
4030 : {
4031 1612 : struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
4032 1612 : struct ldb_message_element *el = NULL, *el2 = NULL;
4033 84 : struct ldb_message *msg;
4034 1612 : const char * const attrs[] = { "servicePrincipalName", NULL };
4035 84 : struct ldb_result *res;
4036 1612 : const char *dns_hostname = NULL, *old_dns_hostname = NULL,
4037 1612 : *sam_accountname = NULL, *old_sam_accountname = NULL;
4038 84 : unsigned int i, j;
4039 84 : int ret;
4040 :
4041 1696 : ret = dsdb_get_expected_new_values(ac,
4042 1612 : ac->msg,
4043 : "dNSHostName",
4044 : &el,
4045 1612 : ac->req->operation);
4046 1612 : if (ret != LDB_SUCCESS) {
4047 0 : return ret;
4048 : }
4049 1696 : ret = dsdb_get_expected_new_values(ac,
4050 1612 : ac->msg,
4051 : "sAMAccountName",
4052 : &el2,
4053 1612 : ac->req->operation);
4054 1612 : if (ret != LDB_SUCCESS) {
4055 0 : return ret;
4056 : }
4057 1612 : if ((el == NULL) && (el2 == NULL)) {
4058 : /* we are not affected */
4059 3 : return LDB_SUCCESS;
4060 : }
4061 :
4062 : /* Create a temporary message for fetching the "dNSHostName" */
4063 1609 : if (el != NULL) {
4064 764 : const char *dns_attrs[] = { "dNSHostName", NULL };
4065 764 : msg = ldb_msg_new(ac->msg);
4066 764 : if (msg == NULL) {
4067 0 : return ldb_module_oom(ac->module);
4068 : }
4069 764 : ret = ldb_msg_add(msg, el, 0);
4070 764 : if (ret != LDB_SUCCESS) {
4071 0 : return ret;
4072 : }
4073 764 : dns_hostname = talloc_strdup(ac,
4074 : ldb_msg_find_attr_as_string(msg, "dNSHostName", NULL));
4075 764 : if (dns_hostname == NULL) {
4076 0 : return ldb_module_oom(ac->module);
4077 : }
4078 :
4079 764 : talloc_free(msg);
4080 :
4081 764 : ret = dsdb_module_search_dn(ac->module, ac, &res, ac->msg->dn,
4082 : dns_attrs, DSDB_FLAG_NEXT_MODULE, ac->req);
4083 764 : if (ret == LDB_SUCCESS) {
4084 764 : old_dns_hostname = ldb_msg_find_attr_as_string(res->msgs[0], "dNSHostName", NULL);
4085 : }
4086 : }
4087 :
4088 : /* Create a temporary message for fetching the "sAMAccountName" */
4089 1609 : if (el2 != NULL) {
4090 867 : char *tempstr, *tempstr2 = NULL;
4091 867 : const char *acct_attrs[] = { "sAMAccountName", NULL };
4092 :
4093 867 : msg = ldb_msg_new(ac->msg);
4094 867 : if (msg == NULL) {
4095 0 : return ldb_module_oom(ac->module);
4096 : }
4097 867 : ret = ldb_msg_add(msg, el2, 0);
4098 867 : if (ret != LDB_SUCCESS) {
4099 0 : return ret;
4100 : }
4101 867 : tempstr = talloc_strdup(ac,
4102 : ldb_msg_find_attr_as_string(msg, "sAMAccountName", NULL));
4103 867 : talloc_free(msg);
4104 :
4105 867 : ret = dsdb_module_search_dn(ac->module, ac, &res, ac->msg->dn, acct_attrs,
4106 : DSDB_FLAG_NEXT_MODULE, ac->req);
4107 867 : if (ret == LDB_SUCCESS) {
4108 867 : tempstr2 = talloc_strdup(ac,
4109 867 : ldb_msg_find_attr_as_string(res->msgs[0],
4110 : "sAMAccountName", NULL));
4111 : }
4112 :
4113 :
4114 : /* The "sAMAccountName" needs some additional trimming: we need
4115 : * to remove the trailing "$"s if they exist. */
4116 867 : if ((tempstr != NULL) && (tempstr[0] != '\0') &&
4117 867 : (tempstr[strlen(tempstr) - 1] == '$')) {
4118 184 : tempstr[strlen(tempstr) - 1] = '\0';
4119 : }
4120 867 : if ((tempstr2 != NULL) && (tempstr2[0] != '\0') &&
4121 867 : (tempstr2[strlen(tempstr2) - 1] == '$')) {
4122 231 : tempstr2[strlen(tempstr2) - 1] = '\0';
4123 : }
4124 867 : sam_accountname = tempstr;
4125 867 : old_sam_accountname = tempstr2;
4126 : }
4127 :
4128 1609 : if (old_dns_hostname == NULL) {
4129 : /* we cannot change when the old name is unknown */
4130 1493 : dns_hostname = NULL;
4131 : }
4132 1725 : if ((old_dns_hostname != NULL) && (dns_hostname != NULL) &&
4133 116 : (strcasecmp_m(old_dns_hostname, dns_hostname) == 0)) {
4134 : /* The "dNSHostName" didn't change */
4135 51 : dns_hostname = NULL;
4136 : }
4137 :
4138 1609 : if (old_sam_accountname == NULL) {
4139 : /* we cannot change when the old name is unknown */
4140 742 : sam_accountname = NULL;
4141 : }
4142 2476 : if ((old_sam_accountname != NULL) && (sam_accountname != NULL) &&
4143 867 : (strcasecmp_m(old_sam_accountname, sam_accountname) == 0)) {
4144 : /* The "sAMAccountName" didn't change */
4145 444 : sam_accountname = NULL;
4146 : }
4147 :
4148 1609 : if ((dns_hostname == NULL) && (sam_accountname == NULL)) {
4149 : /* Well, there are information missing (old name(s)) or the
4150 : * names didn't change. We've nothing to do and can exit here */
4151 1051 : return LDB_SUCCESS;
4152 : }
4153 :
4154 : /*
4155 : * Potential "servicePrincipalName" changes in the same request have
4156 : * to be handled before the update (Windows behaviour).
4157 : *
4158 : * We extract the SPN changes into a new message and run it through
4159 : * the stack from this module, so that it subjects them to the SPN
4160 : * checks we have here.
4161 : */
4162 480 : el = ldb_msg_find_element(ac->msg, "servicePrincipalName");
4163 480 : if (el != NULL) {
4164 34 : msg = ldb_msg_new(ac->msg);
4165 34 : if (msg == NULL) {
4166 0 : return ldb_module_oom(ac->module);
4167 : }
4168 34 : msg->dn = ac->msg->dn;
4169 :
4170 0 : do {
4171 34 : ret = ldb_msg_add(msg, el, el->flags);
4172 34 : if (ret != LDB_SUCCESS) {
4173 0 : return ret;
4174 : }
4175 :
4176 34 : ldb_msg_remove_element(ac->msg, el);
4177 :
4178 34 : el = ldb_msg_find_element(ac->msg,
4179 : "servicePrincipalName");
4180 34 : } while (el != NULL);
4181 :
4182 34 : ret = dsdb_module_modify(ac->module, msg,
4183 : DSDB_FLAG_OWN_MODULE, ac->req);
4184 34 : if (ret != LDB_SUCCESS) {
4185 0 : return ret;
4186 : }
4187 34 : talloc_free(msg);
4188 : }
4189 :
4190 : /* Fetch the "servicePrincipalName"s if any */
4191 480 : ret = dsdb_module_search(ac->module, ac, &res, ac->msg->dn, LDB_SCOPE_BASE, attrs,
4192 : DSDB_FLAG_NEXT_MODULE, ac->req, NULL);
4193 480 : if (ret != LDB_SUCCESS) {
4194 0 : return ret;
4195 : }
4196 480 : if ((res->count != 1) || (res->msgs[0]->num_elements > 1)) {
4197 0 : return ldb_operr(ldb);
4198 : }
4199 :
4200 480 : if (res->msgs[0]->num_elements == 1) {
4201 : /*
4202 : * Yes, we do have "servicePrincipalName"s. First we update them
4203 : * locally, that means we do always substitute the current
4204 : * "dNSHostName" with the new one and/or "sAMAccountName"
4205 : * without "$" with the new one and then we append the
4206 : * modified "servicePrincipalName"s as a message element
4207 : * replace to the modification request (Windows behaviour). We
4208 : * need also to make sure that the values remain case-
4209 : * insensitively unique.
4210 : */
4211 :
4212 71 : ret = ldb_msg_add_empty(ac->msg, "servicePrincipalName",
4213 : LDB_FLAG_MOD_REPLACE, &el);
4214 71 : if (ret != LDB_SUCCESS) {
4215 0 : return ret;
4216 : }
4217 :
4218 199 : for (i = 0; i < res->msgs[0]->elements[0].num_values; i++) {
4219 6 : char *old_str, *new_str;
4220 128 : char *pos = NULL;
4221 6 : const char *tok;
4222 6 : struct ldb_val *vals;
4223 128 : bool found = false;
4224 :
4225 128 : old_str = (char *)
4226 128 : res->msgs[0]->elements[0].values[i].data;
4227 :
4228 128 : new_str = talloc_strdup(ac->msg,
4229 128 : strtok_r(old_str, "/", &pos));
4230 128 : if (new_str == NULL) {
4231 0 : return ldb_module_oom(ac->module);
4232 : }
4233 :
4234 262 : while ((tok = strtok_r(NULL, "/", &pos)) != NULL) {
4235 247 : if ((dns_hostname != NULL) &&
4236 113 : (strcasecmp_m(tok, old_dns_hostname) == 0)) {
4237 57 : tok = dns_hostname;
4238 : }
4239 175 : if ((sam_accountname != NULL) &&
4240 41 : (strcasecmp_m(tok, old_sam_accountname) == 0)) {
4241 17 : tok = sam_accountname;
4242 : }
4243 :
4244 134 : new_str = talloc_asprintf(ac->msg, "%s/%s",
4245 : new_str, tok);
4246 134 : if (new_str == NULL) {
4247 0 : return ldb_module_oom(ac->module);
4248 : }
4249 : }
4250 :
4251 : /* Uniqueness check */
4252 208 : for (j = 0; (!found) && (j < el->num_values); j++) {
4253 80 : if (strcasecmp_m((char *)el->values[j].data,
4254 : new_str) == 0) {
4255 19 : found = true;
4256 : }
4257 : }
4258 128 : if (found) {
4259 19 : continue;
4260 : }
4261 :
4262 : /*
4263 : * append the new "servicePrincipalName" -
4264 : * code derived from ldb_msg_add_value().
4265 : *
4266 : * Open coded to make it clear that we must
4267 : * append to the MOD_REPLACE el created above.
4268 : */
4269 109 : vals = talloc_realloc(ac->msg, el->values,
4270 : struct ldb_val,
4271 : el->num_values + 1);
4272 109 : if (vals == NULL) {
4273 0 : return ldb_module_oom(ac->module);
4274 : }
4275 109 : el->values = vals;
4276 109 : el->values[el->num_values] = data_blob_string_const(new_str);
4277 109 : ++(el->num_values);
4278 : }
4279 : }
4280 :
4281 480 : talloc_free(res);
4282 :
4283 480 : return LDB_SUCCESS;
4284 : }
4285 :
4286 : /* This checks the "fSMORoleOwner" attributes */
4287 1287 : static int samldb_fsmo_role_owner_check(struct samldb_ctx *ac)
4288 : {
4289 1287 : struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
4290 1287 : const char * const no_attrs[] = { NULL };
4291 166 : struct ldb_message_element *el;
4292 166 : struct ldb_message *tmp_msg;
4293 166 : struct ldb_dn *res_dn;
4294 166 : struct ldb_result *res;
4295 166 : int ret;
4296 1453 : ret = dsdb_get_expected_new_values(ac,
4297 1287 : ac->msg,
4298 : "fSMORoleOwner",
4299 : &el,
4300 1287 : ac->req->operation);
4301 1287 : if (ret != LDB_SUCCESS) {
4302 0 : return ret;
4303 : }
4304 :
4305 1287 : if (el == NULL) {
4306 : /* we are not affected */
4307 3 : return LDB_SUCCESS;
4308 : }
4309 1284 : if (el->num_values != 1) {
4310 6 : goto choose_error_code;
4311 : }
4312 :
4313 : /* Create a temporary message for fetching the "fSMORoleOwner" */
4314 1278 : tmp_msg = ldb_msg_new(ac->msg);
4315 1278 : if (tmp_msg == NULL) {
4316 0 : return ldb_module_oom(ac->module);
4317 : }
4318 1278 : ret = ldb_msg_add(tmp_msg, el, 0);
4319 1278 : if (ret != LDB_SUCCESS) {
4320 0 : return ret;
4321 : }
4322 1278 : res_dn = ldb_msg_find_attr_as_dn(ldb, ac, tmp_msg, "fSMORoleOwner");
4323 1278 : talloc_free(tmp_msg);
4324 :
4325 1278 : if (res_dn == NULL) {
4326 0 : ldb_set_errstring(ldb,
4327 : "samldb: 'fSMORoleOwner' attributes have to reference 'nTDSDSA' entries!");
4328 0 : goto choose_error_code;
4329 : }
4330 :
4331 : /* Fetched DN has to reference a "nTDSDSA" entry */
4332 1278 : ret = dsdb_module_search(ac->module, ac, &res, res_dn, LDB_SCOPE_BASE,
4333 : no_attrs,
4334 : DSDB_FLAG_NEXT_MODULE | DSDB_SEARCH_SHOW_DELETED,
4335 : ac->req, "(objectClass=nTDSDSA)");
4336 1278 : if (ret != LDB_SUCCESS) {
4337 0 : return ret;
4338 : }
4339 1278 : if (res->count != 1) {
4340 6 : ldb_set_errstring(ldb,
4341 : "samldb: 'fSMORoleOwner' attributes have to reference 'nTDSDSA' entries!");
4342 6 : return LDB_ERR_UNWILLING_TO_PERFORM;
4343 : }
4344 :
4345 1272 : talloc_free(res);
4346 :
4347 1272 : return LDB_SUCCESS;
4348 :
4349 6 : choose_error_code:
4350 : /* this is just how it is */
4351 6 : if (ac->req->operation == LDB_ADD) {
4352 3 : return LDB_ERR_CONSTRAINT_VIOLATION;
4353 : } else {
4354 3 : return LDB_ERR_UNWILLING_TO_PERFORM;
4355 : }
4356 : }
4357 :
4358 : /*
4359 : * Return zero if the number of zero bits in the address (looking from low to
4360 : * high) is equal to or greater than the length minus the mask. Otherwise it
4361 : * returns -1.
4362 : */
4363 140 : static int check_cidr_zero_bits(uint8_t *address, unsigned int len,
4364 : unsigned int mask)
4365 : {
4366 : /* <address> is an integer in big-endian form, <len> bits long. All
4367 : bits between <mask> and <len> must be zero. */
4368 0 : int i;
4369 0 : unsigned int byte_len;
4370 0 : unsigned int byte_mask;
4371 0 : unsigned int bit_mask;
4372 140 : if (len == 32) {
4373 60 : DBG_INFO("Looking at address %02x%02x%02x%02x, mask %u\n",
4374 : address[0], address[1], address[2], address[3],
4375 : mask);
4376 80 : } else if (len == 128){
4377 80 : DBG_INFO("Looking at address "
4378 : "%02x%02x-%02x%02x-%02x%02x-%02x%02x-"
4379 : "%02x%02x-%02x%02x-%02x%02x-%02x%02x, mask %u\n",
4380 : address[0], address[1], address[2], address[3],
4381 : address[4], address[5], address[6], address[7],
4382 : address[8], address[9], address[10], address[11],
4383 : address[12], address[13], address[14], address[15],
4384 : mask);
4385 : }
4386 :
4387 140 : if (mask > len){
4388 5 : DBG_INFO("mask %u is too big (> %u)\n", mask, len);
4389 5 : return -1;
4390 : }
4391 135 : if (mask == len){
4392 : /* single address subnet.
4393 : * In IPv4 all 255s is invalid by the bitmask != address rule
4394 : * in MS-ADTS. IPv6 does not suffer.
4395 : */
4396 10 : if (len == 32){
4397 5 : if (address[0] == 255 &&
4398 1 : address[1] == 255 &&
4399 1 : address[2] == 255 &&
4400 1 : address[3] == 255){
4401 1 : return -1;
4402 : }
4403 : }
4404 9 : return 0;
4405 : }
4406 :
4407 125 : byte_len = len / 8;
4408 125 : byte_mask = mask / 8;
4409 :
4410 717 : for (i = byte_len - 1; i > byte_mask; i--){
4411 594 : DBG_DEBUG("checking byte %d %02x\n", i, address[i]);
4412 594 : if (address[i] != 0){
4413 2 : return -1;
4414 : }
4415 : }
4416 123 : bit_mask = (1 << (8 - (mask & 7))) - 1;
4417 123 : DBG_DEBUG("checking bitmask %02x & %02x overlap %02x\n", bit_mask, address[byte_mask],
4418 : bit_mask & address[byte_mask]);
4419 123 : if (address[byte_mask] & bit_mask){
4420 15 : return -1;
4421 : }
4422 :
4423 : /* According to MS-ADTS, the mask can't exactly equal the bitmask for
4424 : * IPv4 (but this is fine for v6). That is 255.255.80.0/17 is bad,
4425 : * because the bitmask implied by "/17" is 255.255.80.0.
4426 : *
4427 : * The bit_mask used in the previous check is the complement of what
4428 : * we want here.
4429 : */
4430 108 : if (len == 32 && address[byte_mask] == (uint8_t)~bit_mask){
4431 37 : bool ok = false;
4432 41 : for (i = 0; i < byte_mask; i++){
4433 37 : if (address[i] != 255){
4434 33 : ok = true;
4435 33 : break;
4436 : }
4437 : }
4438 37 : if (ok == false){
4439 4 : return -1;
4440 : }
4441 : }
4442 104 : return 0;
4443 : }
4444 :
4445 :
4446 :
4447 165 : static int check_address_roundtrip(const char *address, int family,
4448 : const uint8_t *address_bytes,
4449 : char *buffer, int buffer_len)
4450 : {
4451 : /*
4452 : * Check that the address is in the canonical RFC5952 format for IPv6,
4453 : * and lacks extra leading zeros for each dotted decimal for IPv4.
4454 : * Handily this is what inet_ntop() gives you.
4455 : */
4456 165 : const char *address_redux = inet_ntop(family, address_bytes,
4457 : buffer, buffer_len);
4458 165 : if (address_redux == NULL){
4459 0 : DBG_INFO("Address round trip %s failed unexpectedly"
4460 : " with errno %d\n", address, errno);
4461 0 : return -1;
4462 : }
4463 165 : if (strcasecmp(address, address_redux) != 0){
4464 25 : DBG_INFO("Address %s round trips to %s; fail!\n",
4465 : address, address_redux);
4466 : /* If the address family is IPv6, and the address is in a
4467 : certain range
4468 :
4469 : */
4470 25 : if (strchr(address_redux, '.') != NULL){
4471 7 : DEBUG(0, ("The IPv6 address '%s' has the misfortune of "
4472 : "lying in a range that was once used for "
4473 : "IPv4 embedding (that is, it might also be "
4474 : "represented as '%s').\n", address,
4475 : address_redux));
4476 : }
4477 25 : return -1;
4478 : }
4479 140 : return 0;
4480 : }
4481 :
4482 :
4483 :
4484 : /*
4485 : * MS-ADTS v20150630 6.1.1.2.2.2.1 Subnet Object, refers to RFC1166 and
4486 : * RFC2373. It specifies something seemingly indistinguishable from an RFC4632
4487 : * CIDR address range without saying so explicitly. Here we follow the CIDR
4488 : * spec.
4489 : *
4490 : * Return 0 on success, -1 on error.
4491 : */
4492 210 : static int verify_cidr(const char *cidr)
4493 : {
4494 210 : char *address = NULL, *slash = NULL;
4495 0 : bool has_colon, has_dot;
4496 0 : int res, ret;
4497 0 : unsigned long mask;
4498 210 : uint8_t *address_bytes = NULL;
4499 210 : char *address_redux = NULL;
4500 0 : unsigned int address_len;
4501 210 : TALLOC_CTX *frame = NULL;
4502 210 : int error = 0;
4503 :
4504 210 : DBG_DEBUG("CIDR is %s\n", cidr);
4505 210 : frame = talloc_stackframe();
4506 210 : address = talloc_strdup(frame, cidr);
4507 210 : if (address == NULL){
4508 0 : goto error;
4509 : }
4510 :
4511 : /* there must be a '/' */
4512 210 : slash = strchr(address, '/');
4513 210 : if (slash == NULL){
4514 2 : goto error;
4515 : }
4516 : /* terminate the address for strchr, inet_pton */
4517 208 : *slash = '\0';
4518 :
4519 208 : mask = smb_strtoul(slash + 1, NULL, 10, &error, SMB_STR_FULL_STR_CONV);
4520 208 : if (mask == 0){
4521 6 : DBG_INFO("Windows does not like the zero mask, "
4522 : "so nor do we: %s\n", cidr);
4523 6 : goto error;
4524 : }
4525 :
4526 202 : if (error != 0){
4527 6 : DBG_INFO("CIDR mask is not a proper integer: %s\n", cidr);
4528 6 : goto error;
4529 : }
4530 :
4531 196 : address_bytes = talloc_size(frame, sizeof(struct in6_addr));
4532 196 : if (address_bytes == NULL){
4533 0 : goto error;
4534 : }
4535 :
4536 196 : address_redux = talloc_size(frame, INET6_ADDRSTRLEN);
4537 196 : if (address_redux == NULL){
4538 0 : goto error;
4539 : }
4540 :
4541 196 : DBG_INFO("found address %s, mask %lu\n", address, mask);
4542 196 : has_colon = (strchr(address, ':') == NULL) ? false : true;
4543 196 : has_dot = (strchr(address, '.') == NULL) ? false : true;
4544 196 : if (has_dot && has_colon){
4545 : /* This seems to be an IPv4 address embedded in IPv6, which is
4546 : icky. We don't support it. */
4547 2 : DBG_INFO("Refusing to consider cidr '%s' with dots and colons\n",
4548 : cidr);
4549 2 : goto error;
4550 194 : } else if (has_colon){ /* looks like IPv6 */
4551 115 : res = inet_pton(AF_INET6, address, address_bytes);
4552 115 : if (res != 1) {
4553 10 : DBG_INFO("Address in %s fails to parse as IPv6\n", cidr);
4554 10 : goto error;
4555 : }
4556 105 : address_len = 128;
4557 105 : if (check_address_roundtrip(address, AF_INET6, address_bytes,
4558 : address_redux, INET6_ADDRSTRLEN)){
4559 25 : goto error;
4560 : }
4561 79 : } else if (has_dot) {
4562 : /* looks like IPv4 */
4563 79 : if (strcmp(address, "0.0.0.0") == 0){
4564 1 : DBG_INFO("Windows does not like the zero IPv4 address, "
4565 : "so nor do we.\n");
4566 1 : goto error;
4567 : }
4568 78 : res = inet_pton(AF_INET, address, address_bytes);
4569 78 : if (res != 1) {
4570 18 : DBG_INFO("Address in %s fails to parse as IPv4\n", cidr);
4571 18 : goto error;
4572 : }
4573 60 : address_len = 32;
4574 :
4575 60 : if (check_address_roundtrip(address, AF_INET, address_bytes,
4576 : address_redux, INET_ADDRSTRLEN)){
4577 0 : goto error;
4578 : }
4579 : } else {
4580 : /* This doesn't look like an IP address at all. */
4581 0 : goto error;
4582 : }
4583 :
4584 140 : ret = check_cidr_zero_bits(address_bytes, address_len, mask);
4585 140 : talloc_free(frame);
4586 140 : return ret;
4587 70 : error:
4588 70 : talloc_free(frame);
4589 70 : return -1;
4590 : }
4591 :
4592 :
4593 210 : static int samldb_verify_subnet(struct samldb_ctx *ac, struct ldb_dn *dn)
4594 : {
4595 210 : struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
4596 210 : const char *cidr = NULL;
4597 210 : const struct ldb_val *rdn_value = NULL;
4598 :
4599 210 : rdn_value = ldb_dn_get_rdn_val(dn);
4600 210 : if (rdn_value == NULL) {
4601 0 : ldb_set_errstring(ldb, "samldb: ldb_dn_get_rdn_val "
4602 : "failed");
4603 0 : return LDB_ERR_UNWILLING_TO_PERFORM;
4604 : }
4605 :
4606 210 : cidr = ldb_dn_escape_value(ac, *rdn_value);
4607 210 : DBG_INFO("looking at cidr '%s'\n", cidr);
4608 210 : if (cidr == NULL) {
4609 0 : ldb_set_errstring(ldb,
4610 : "samldb: adding an empty subnet cidr seems wrong");
4611 0 : return LDB_ERR_UNWILLING_TO_PERFORM;
4612 : }
4613 :
4614 210 : if (verify_cidr(cidr)){
4615 97 : ldb_set_errstring(ldb,
4616 : "samldb: subnet value is invalid");
4617 97 : return LDB_ERR_INVALID_DN_SYNTAX;
4618 : }
4619 :
4620 113 : return LDB_SUCCESS;
4621 : }
4622 :
4623 615071 : static char *refer_if_rodc(struct ldb_context *ldb, struct ldb_request *req,
4624 : struct ldb_dn *dn)
4625 : {
4626 615071 : bool rodc = false;
4627 83756 : struct loadparm_context *lp_ctx;
4628 83756 : char *referral;
4629 83756 : int ret;
4630 83756 : WERROR err;
4631 :
4632 1230142 : if (ldb_request_get_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID) ||
4633 615071 : ldb_request_get_control(req, DSDB_CONTROL_DBCHECK_MODIFY_RO_REPLICA)) {
4634 0 : return NULL;
4635 : }
4636 :
4637 615071 : ret = samdb_rodc(ldb, &rodc);
4638 615071 : if (ret != LDB_SUCCESS) {
4639 19 : DEBUG(4, (__location__ ": unable to tell if we are an RODC\n"));
4640 19 : return NULL;
4641 : }
4642 :
4643 615052 : if (rodc) {
4644 23 : const char *domain = NULL;
4645 0 : struct ldb_dn *fsmo_role_dn;
4646 0 : struct ldb_dn *role_owner_dn;
4647 23 : ldb_set_errstring(ldb, "RODC modify is forbidden!");
4648 23 : lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
4649 : struct loadparm_context);
4650 :
4651 23 : err = dsdb_get_fsmo_role_info(req, ldb, DREPL_PDC_MASTER,
4652 : &fsmo_role_dn, &role_owner_dn);
4653 23 : if (W_ERROR_IS_OK(err)) {
4654 23 : struct ldb_dn *server_dn = ldb_dn_copy(req, role_owner_dn);
4655 23 : if (server_dn != NULL) {
4656 23 : ldb_dn_remove_child_components(server_dn, 1);
4657 :
4658 23 : domain = samdb_dn_to_dnshostname(ldb, req,
4659 : server_dn);
4660 : }
4661 : }
4662 23 : if (domain == NULL) {
4663 0 : domain = lpcfg_dnsdomain(lp_ctx);
4664 : }
4665 23 : referral = talloc_asprintf(req,
4666 : "ldap://%s/%s",
4667 : domain,
4668 : ldb_dn_get_linearized(dn));
4669 23 : return referral;
4670 : }
4671 :
4672 531292 : return NULL;
4673 : }
4674 :
4675 : /*
4676 : * Restrict all access to sensitive attributes.
4677 : *
4678 : * We don't want to even inspect the values, so we can use the same
4679 : * routine for ADD and MODIFY.
4680 : *
4681 : */
4682 :
4683 1132346 : static int samldb_check_sensitive_attributes(struct samldb_ctx *ac)
4684 : {
4685 1132346 : struct ldb_message_element *el = NULL;
4686 1132346 : struct security_token *user_token = NULL;
4687 105121 : int ret;
4688 :
4689 1132346 : if (dsdb_module_am_system(ac->module)) {
4690 224808 : return LDB_SUCCESS;
4691 : }
4692 :
4693 889150 : user_token = acl_user_token(ac->module);
4694 889150 : if (user_token == NULL) {
4695 0 : return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
4696 : }
4697 :
4698 889150 : el = ldb_msg_find_element(ac->msg, "sidHistory");
4699 889150 : if (el) {
4700 : /*
4701 : * sidHistory is restricted to the (not implemented
4702 : * yet in Samba) DsAddSidHistory call (direct LDB access is
4703 : * as SYSTEM so will bypass this).
4704 : *
4705 : * If you want to modify this, say to merge domains,
4706 : * directly modify the sam.ldb as root.
4707 : */
4708 16 : ldb_asprintf_errstring(ldb_module_get_ctx(ac->module),
4709 : "sidHistory "
4710 : "(entry %s) cannot be created "
4711 : "or changed over LDAP!",
4712 16 : ldb_dn_get_linearized(ac->msg->dn));
4713 16 : return LDB_ERR_UNWILLING_TO_PERFORM;
4714 : }
4715 :
4716 889134 : el = ldb_msg_find_element(ac->msg, "msDS-SecondaryKrbTgtNumber");
4717 889134 : if (el) {
4718 0 : struct security_descriptor *domain_sd;
4719 16 : const struct dsdb_class *objectclass = NULL;
4720 : /*
4721 : * msDS-SecondaryKrbTgtNumber allows the creator to
4722 : * become an RODC, this is trusted as an RODC
4723 : * account
4724 : */
4725 16 : ret = samldb_get_domain_secdesc_and_oc(ac, &domain_sd, &objectclass);
4726 16 : if (ret != LDB_SUCCESS) {
4727 8 : return ret;
4728 : }
4729 16 : ret = acl_check_extended_right(ac,
4730 : ac->module,
4731 : ac->req,
4732 : objectclass,
4733 : domain_sd,
4734 : user_token,
4735 : GUID_DRS_DS_INSTALL_REPLICA,
4736 : SEC_ADS_CONTROL_ACCESS,
4737 : NULL);
4738 16 : if (ret != LDB_SUCCESS) {
4739 8 : ldb_asprintf_errstring(ldb_module_get_ctx(ac->module),
4740 : "msDS-SecondaryKrbTgtNumber "
4741 : "(entry %s) cannot be created "
4742 : "or changed without "
4743 : "DS-Install-Replica extended right!",
4744 8 : ldb_dn_get_linearized(ac->msg->dn));
4745 8 : return ret;
4746 : }
4747 : }
4748 :
4749 889126 : el = ldb_msg_find_element(ac->msg, "msDS-AllowedToDelegateTo");
4750 889126 : if (el) {
4751 : /*
4752 : * msDS-AllowedToDelegateTo is incredibly powerful,
4753 : * given that it allows a server to become ANY USER on
4754 : * the target server only listed by SPN so needs to be
4755 : * protected just as the userAccountControl
4756 : * UF_TRUSTED_FOR_DELEGATION is.
4757 : */
4758 :
4759 63 : bool have_priv = security_token_has_privilege(user_token,
4760 : SEC_PRIV_ENABLE_DELEGATION);
4761 63 : if (have_priv == false) {
4762 8 : ldb_asprintf_errstring(ldb_module_get_ctx(ac->module),
4763 : "msDS-AllowedToDelegateTo "
4764 : "(entry %s) cannot be created "
4765 : "or changed without SePrivEnableDelegation!",
4766 8 : ldb_dn_get_linearized(ac->msg->dn));
4767 8 : return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
4768 : }
4769 : }
4770 802385 : return LDB_SUCCESS;
4771 : }
4772 : /* add */
4773 543112 : static int samldb_add(struct ldb_module *module, struct ldb_request *req)
4774 : {
4775 83665 : struct ldb_context *ldb;
4776 83665 : struct samldb_ctx *ac;
4777 83665 : struct ldb_message_element *el;
4778 83665 : int ret;
4779 543112 : char *referral = NULL;
4780 :
4781 543112 : ldb = ldb_module_get_ctx(module);
4782 543112 : ldb_debug(ldb, LDB_DEBUG_TRACE, "samldb_add\n");
4783 :
4784 : /* do not manipulate our control entries */
4785 543112 : if (ldb_dn_is_special(req->op.add.message->dn)) {
4786 538 : return ldb_next_request(module, req);
4787 : }
4788 :
4789 542574 : referral = refer_if_rodc(ldb, req, req->op.add.message->dn);
4790 542574 : if (referral != NULL) {
4791 22 : ret = ldb_module_send_referral(req, referral);
4792 22 : return ret;
4793 : }
4794 :
4795 542552 : el = ldb_msg_find_element(req->op.add.message, "userParameters");
4796 542552 : if (el != NULL && ldb_req_is_untrusted(req)) {
4797 0 : const char *reason = "samldb_add: "
4798 : "setting userParameters is not supported over LDAP, "
4799 : "see https://bugzilla.samba.org/show_bug.cgi?id=8077";
4800 0 : ldb_debug(ldb, LDB_DEBUG_WARNING, "%s", reason);
4801 0 : return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION, reason);
4802 : }
4803 :
4804 542552 : ac = samldb_ctx_init(module, req);
4805 542552 : if (ac == NULL) {
4806 0 : return ldb_operr(ldb);
4807 : }
4808 :
4809 : /* build the new msg */
4810 542552 : ac->msg = ldb_msg_copy_shallow(ac, req->op.add.message);
4811 542552 : if (ac->msg == NULL) {
4812 0 : talloc_free(ac);
4813 0 : ldb_debug(ldb, LDB_DEBUG_FATAL,
4814 : "samldb_add: ldb_msg_copy_shallow failed!\n");
4815 0 : return ldb_operr(ldb);
4816 : }
4817 :
4818 542552 : ret = samldb_check_sensitive_attributes(ac);
4819 542552 : if (ret != LDB_SUCCESS) {
4820 32 : talloc_free(ac);
4821 32 : return ret;
4822 : }
4823 :
4824 542520 : el = ldb_msg_find_element(ac->msg, "fSMORoleOwner");
4825 542520 : if (el != NULL) {
4826 9 : ret = samldb_fsmo_role_owner_check(ac);
4827 9 : if (ret != LDB_SUCCESS) {
4828 6 : return ret;
4829 : }
4830 : }
4831 :
4832 542514 : el = ldb_msg_find_element(ac->msg, "servicePrincipalName");
4833 542514 : if ((el != NULL)) {
4834 : /*
4835 : * We need to check whether the SPN collides with an existing
4836 : * one (anywhere) including via aliases.
4837 : */
4838 1645 : ret = samldb_spn_uniqueness_check(ac, el);
4839 1645 : if (ret != LDB_SUCCESS) {
4840 3 : return ret;
4841 : }
4842 : }
4843 :
4844 542511 : if (samdb_find_attribute(ldb, ac->msg,
4845 : "objectclass", "user") != NULL) {
4846 30119 : ac->type = SAMLDB_TYPE_USER;
4847 :
4848 30119 : ret = samldb_prim_group_trigger(ac);
4849 30119 : if (ret != LDB_SUCCESS) {
4850 26 : return ret;
4851 : }
4852 :
4853 30093 : ret = samldb_objectclass_trigger(ac);
4854 30093 : if (ret != LDB_SUCCESS) {
4855 110 : return ret;
4856 : }
4857 :
4858 29983 : return samldb_fill_object(ac);
4859 : }
4860 :
4861 512392 : if (samdb_find_attribute(ldb, ac->msg,
4862 : "objectclass", "group") != NULL) {
4863 8710 : ac->type = SAMLDB_TYPE_GROUP;
4864 :
4865 8710 : ret = samldb_objectclass_trigger(ac);
4866 8710 : if (ret != LDB_SUCCESS) {
4867 6 : return ret;
4868 : }
4869 :
4870 8704 : return samldb_fill_object(ac);
4871 : }
4872 :
4873 : /* perhaps a foreignSecurityPrincipal? */
4874 503682 : if (samdb_find_attribute(ldb, ac->msg,
4875 : "objectclass",
4876 : "foreignSecurityPrincipal") != NULL) {
4877 3857 : return samldb_fill_foreignSecurityPrincipal_object(ac);
4878 : }
4879 :
4880 499825 : if (samdb_find_attribute(ldb, ac->msg,
4881 : "objectclass", "classSchema") != NULL) {
4882 33722 : ac->type = SAMLDB_TYPE_CLASS;
4883 :
4884 : /* If in provision, these checks are too slow to do */
4885 33722 : if (!ldb_request_get_control(req, DSDB_CONTROL_SKIP_DUPLICATES_CHECK_OID)) {
4886 882 : ret = samldb_schema_governsid_valid_check(ac);
4887 882 : if (ret != LDB_SUCCESS) {
4888 9 : return ret;
4889 : }
4890 : }
4891 :
4892 33713 : ret = samldb_schema_ldapdisplayname_valid_check(ac);
4893 33713 : if (ret != LDB_SUCCESS) {
4894 9 : return ret;
4895 : }
4896 :
4897 33704 : ret = samldb_schema_info_update(ac);
4898 33704 : if (ret != LDB_SUCCESS) {
4899 0 : talloc_free(ac);
4900 0 : return ret;
4901 : }
4902 :
4903 33704 : return samldb_fill_object(ac);
4904 : }
4905 :
4906 466103 : if (samdb_find_attribute(ldb, ac->msg,
4907 : "objectclass", "attributeSchema") != NULL) {
4908 184400 : ac->type = SAMLDB_TYPE_ATTRIBUTE;
4909 :
4910 : /* If in provision, these checks are too slow to do */
4911 184400 : if (!ldb_request_get_control(req, DSDB_CONTROL_SKIP_DUPLICATES_CHECK_OID)) {
4912 1023 : ret = samldb_schema_attributeid_valid_check(ac);
4913 1023 : if (ret != LDB_SUCCESS) {
4914 9 : return ret;
4915 : }
4916 :
4917 1014 : ret = samldb_schema_add_handle_linkid(ac);
4918 1014 : if (ret != LDB_SUCCESS) {
4919 36 : return ret;
4920 : }
4921 :
4922 978 : ret = samldb_schema_add_handle_mapiid(ac);
4923 978 : if (ret != LDB_SUCCESS) {
4924 9 : return ret;
4925 : }
4926 : }
4927 :
4928 184346 : ret = samldb_schema_ldapdisplayname_valid_check(ac);
4929 184346 : if (ret != LDB_SUCCESS) {
4930 18 : return ret;
4931 : }
4932 :
4933 184328 : ret = samldb_schema_info_update(ac);
4934 184328 : if (ret != LDB_SUCCESS) {
4935 0 : talloc_free(ac);
4936 0 : return ret;
4937 : }
4938 :
4939 184328 : return samldb_fill_object(ac);
4940 : }
4941 :
4942 281703 : if (samdb_find_attribute(ldb, ac->msg,
4943 : "objectclass", "subnet") != NULL) {
4944 208 : ret = samldb_verify_subnet(ac, ac->msg->dn);
4945 208 : if (ret != LDB_SUCCESS) {
4946 96 : talloc_free(ac);
4947 96 : return ret;
4948 : }
4949 : /* We are just checking the value is valid, and there are no
4950 : values to fill in. */
4951 : }
4952 :
4953 281607 : talloc_free(ac);
4954 :
4955 : /* nothing matched, go on */
4956 281607 : return ldb_next_request(module, req);
4957 : }
4958 :
4959 : /* modify */
4960 590576 : static int samldb_modify(struct ldb_module *module, struct ldb_request *req)
4961 : {
4962 21582 : struct ldb_context *ldb;
4963 21582 : struct samldb_ctx *ac;
4964 21582 : struct ldb_message_element *el, *el2;
4965 21582 : struct ldb_control *is_undelete;
4966 590576 : bool modified = false;
4967 21582 : int ret;
4968 :
4969 590576 : if (ldb_dn_is_special(req->op.mod.message->dn)) {
4970 : /* do not manipulate our control entries */
4971 715 : return ldb_next_request(module, req);
4972 : }
4973 :
4974 589861 : ldb = ldb_module_get_ctx(module);
4975 :
4976 : /*
4977 : * we are going to need some special handling if in Undelete call.
4978 : * Since tombstone_reanimate module will restore certain attributes,
4979 : * we need to relax checks for: sAMAccountType, primaryGroupID
4980 : */
4981 589861 : is_undelete = ldb_request_get_control(req, DSDB_CONTROL_RESTORE_TOMBSTONE_OID);
4982 :
4983 : /* make sure that "objectSid" is not specified */
4984 589861 : el = ldb_msg_find_element(req->op.mod.message, "objectSid");
4985 589861 : if (el != NULL) {
4986 15 : if (ldb_request_get_control(req, LDB_CONTROL_PROVISION_OID) == NULL) {
4987 15 : ldb_set_errstring(ldb,
4988 : "samldb: objectSid must not be specified!");
4989 15 : return LDB_ERR_UNWILLING_TO_PERFORM;
4990 : }
4991 : }
4992 589846 : if (is_undelete == NULL) {
4993 : /* make sure that "sAMAccountType" is not specified */
4994 589581 : el = ldb_msg_find_element(req->op.mod.message, "sAMAccountType");
4995 589581 : if (el != NULL) {
4996 15 : ldb_set_errstring(ldb,
4997 : "samldb: sAMAccountType must not be specified!");
4998 15 : return LDB_ERR_UNWILLING_TO_PERFORM;
4999 : }
5000 : }
5001 : /* make sure that "isCriticalSystemObject" is not specified */
5002 589831 : el = ldb_msg_find_element(req->op.mod.message, "isCriticalSystemObject");
5003 589831 : if (el != NULL) {
5004 390 : if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID) == NULL) {
5005 1 : ldb_set_errstring(ldb,
5006 : "samldb: isCriticalSystemObject must not be specified!");
5007 1 : return LDB_ERR_UNWILLING_TO_PERFORM;
5008 : }
5009 : }
5010 :
5011 : /* msDS-IntId is not allowed to be modified
5012 : * except when modification comes from replication */
5013 589830 : if (ldb_msg_find_element(req->op.mod.message, "msDS-IntId")) {
5014 36 : if (!ldb_request_get_control(req,
5015 : DSDB_CONTROL_REPLICATED_UPDATE_OID)) {
5016 36 : return LDB_ERR_CONSTRAINT_VIOLATION;
5017 : }
5018 : }
5019 :
5020 589794 : el = ldb_msg_find_element(req->op.mod.message, "userParameters");
5021 589794 : if (el != NULL && ldb_req_is_untrusted(req)) {
5022 0 : const char *reason = "samldb: "
5023 : "setting userParameters is not supported over LDAP, "
5024 : "see https://bugzilla.samba.org/show_bug.cgi?id=8077";
5025 0 : ldb_debug(ldb, LDB_DEBUG_WARNING, "%s", reason);
5026 0 : return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION, reason);
5027 : }
5028 :
5029 589794 : ac = samldb_ctx_init(module, req);
5030 589794 : if (ac == NULL) {
5031 0 : return ldb_operr(ldb);
5032 : }
5033 :
5034 : /* build the new msg */
5035 589794 : ac->msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
5036 589794 : if (ac->msg == NULL) {
5037 0 : talloc_free(ac);
5038 0 : ldb_debug(ldb, LDB_DEBUG_FATAL,
5039 : "samldb_modify: ldb_msg_copy_shallow failed!\n");
5040 0 : return ldb_operr(ldb);
5041 : }
5042 :
5043 589794 : ret = samldb_check_sensitive_attributes(ac);
5044 589794 : if (ret != LDB_SUCCESS) {
5045 0 : talloc_free(ac);
5046 0 : return ret;
5047 : }
5048 :
5049 589794 : if (is_undelete == NULL) {
5050 589529 : el = ldb_msg_find_element(ac->msg, "primaryGroupID");
5051 589529 : if (el != NULL) {
5052 202 : ret = samldb_prim_group_trigger(ac);
5053 202 : if (ret != LDB_SUCCESS) {
5054 14 : return ret;
5055 : }
5056 : }
5057 : }
5058 :
5059 589780 : el = ldb_msg_find_element(ac->msg, "userAccountControl");
5060 589780 : if (el != NULL) {
5061 15980 : modified = true;
5062 15980 : ret = samldb_user_account_control_change(ac);
5063 15980 : if (ret != LDB_SUCCESS) {
5064 181 : return ret;
5065 : }
5066 : }
5067 :
5068 589599 : el = ldb_msg_find_element(ac->msg, "pwdLastSet");
5069 589599 : if (el != NULL) {
5070 321 : modified = true;
5071 321 : ret = samldb_pwd_last_set_change(ac);
5072 321 : if (ret != LDB_SUCCESS) {
5073 6 : return ret;
5074 : }
5075 : }
5076 :
5077 589593 : el = ldb_msg_find_element(ac->msg, "lockoutTime");
5078 589593 : if (el != NULL) {
5079 189 : modified = true;
5080 189 : ret = samldb_lockout_time(ac);
5081 189 : if (ret != LDB_SUCCESS) {
5082 0 : return ret;
5083 : }
5084 : }
5085 :
5086 589593 : el = ldb_msg_find_element(ac->msg, "groupType");
5087 589593 : if (el != NULL) {
5088 146 : modified = true;
5089 146 : ret = samldb_group_type_change(ac);
5090 146 : if (ret != LDB_SUCCESS) {
5091 33 : return ret;
5092 : }
5093 : }
5094 :
5095 589560 : el = ldb_msg_find_element(ac->msg, "sAMAccountName");
5096 589560 : if (el != NULL) {
5097 10 : uint32_t user_account_control;
5098 913 : struct ldb_result *res = NULL;
5099 913 : const char * const attrs[] = { "userAccountControl",
5100 : "objectclass",
5101 : NULL };
5102 923 : ret = dsdb_module_search_dn(ac->module,
5103 : ac,
5104 : &res,
5105 913 : ac->msg->dn,
5106 : attrs,
5107 : DSDB_FLAG_NEXT_MODULE | DSDB_SEARCH_SHOW_DELETED,
5108 : ac->req);
5109 913 : if (ret != LDB_SUCCESS) {
5110 46 : return ret;
5111 : }
5112 10 : user_account_control
5113 913 : = ldb_msg_find_attr_as_uint(res->msgs[0],
5114 : "userAccountControl",
5115 : 0);
5116 :
5117 913 : if ((user_account_control
5118 913 : & UF_TRUST_ACCOUNT_MASK) != 0) {
5119 310 : ac->need_trailing_dollar = true;
5120 :
5121 603 : } else if (samdb_find_attribute(ldb,
5122 603 : res->msgs[0],
5123 : "objectclass",
5124 : "computer")
5125 : != NULL) {
5126 12 : ac->need_trailing_dollar = true;
5127 : }
5128 :
5129 913 : ret = samldb_sam_accountname_valid_check(ac);
5130 913 : if (ret != LDB_SUCCESS) {
5131 46 : return ret;
5132 : }
5133 : }
5134 :
5135 589514 : el = ldb_msg_find_element(ac->msg, "userPrincipalName");
5136 589514 : if (el != NULL) {
5137 424 : ret = samldb_sam_account_upn_clash(ac);
5138 424 : if (ret != LDB_SUCCESS) {
5139 16 : talloc_free(ac);
5140 16 : return ret;
5141 : }
5142 : }
5143 :
5144 589498 : el = ldb_msg_find_element(ac->msg, "ldapDisplayName");
5145 589498 : if (el != NULL) {
5146 29 : ret = samldb_schema_ldapdisplayname_valid_check(ac);
5147 29 : if (ret != LDB_SUCCESS) {
5148 18 : return ret;
5149 : }
5150 : }
5151 :
5152 589480 : el = ldb_msg_find_element(ac->msg, "attributeID");
5153 589480 : if (el != NULL) {
5154 27 : ldb_asprintf_errstring(ldb_module_get_ctx(ac->module),
5155 : "Once set, attributeID values may not be modified");
5156 27 : return LDB_ERR_CONSTRAINT_VIOLATION;
5157 : }
5158 :
5159 589453 : el = ldb_msg_find_element(ac->msg, "governsID");
5160 589453 : if (el != NULL) {
5161 18 : ldb_asprintf_errstring(ldb_module_get_ctx(ac->module),
5162 : "Once set, governsID values may not be modified");
5163 18 : return LDB_ERR_CONSTRAINT_VIOLATION;
5164 : }
5165 :
5166 589435 : el = ldb_msg_find_element(ac->msg, "member");
5167 589435 : if (el != NULL) {
5168 5161 : struct ldb_control *fix_link_sid_ctrl = NULL;
5169 :
5170 5161 : fix_link_sid_ctrl = ldb_request_get_control(ac->req,
5171 : DSDB_CONTROL_DBCHECK_FIX_LINK_DN_SID);
5172 5161 : if (fix_link_sid_ctrl == NULL) {
5173 5159 : ret = samldb_member_check(ac);
5174 5159 : if (ret != LDB_SUCCESS) {
5175 3 : return ret;
5176 : }
5177 : }
5178 : }
5179 :
5180 589432 : el = ldb_msg_find_element(ac->msg, "description");
5181 589432 : if (el != NULL) {
5182 1029 : ret = samldb_description_check(ac, &modified);
5183 1029 : if (ret != LDB_SUCCESS) {
5184 0 : return ret;
5185 : }
5186 : }
5187 :
5188 589432 : el = ldb_msg_find_element(ac->msg, "dNSHostName");
5189 589432 : el2 = ldb_msg_find_element(ac->msg, "sAMAccountName");
5190 589432 : if ((el != NULL) || (el2 != NULL)) {
5191 1612 : modified = true;
5192 : /*
5193 : * samldb_service_principal_names_change() might add SPN
5194 : * changes to the request, so this must come before the SPN
5195 : * uniqueness check below.
5196 : *
5197 : * Note we ALSO have to do the SPN uniqueness check inside
5198 : * samldb_service_principal_names_change(), because it does a
5199 : * subrequest to do requested SPN modifications *before* its
5200 : * automatic ones are added.
5201 : */
5202 1612 : ret = samldb_service_principal_names_change(ac);
5203 1612 : if (ret != LDB_SUCCESS) {
5204 0 : return ret;
5205 : }
5206 : }
5207 :
5208 589432 : el = ldb_msg_find_element(ac->msg, "servicePrincipalName");
5209 589432 : if ((el != NULL)) {
5210 : /*
5211 : * We need to check whether the SPN collides with an existing
5212 : * one (anywhere) including via aliases.
5213 : */
5214 2178 : modified = true;
5215 2178 : ret = samldb_spn_uniqueness_check(ac, el);
5216 2178 : if (ret != LDB_SUCCESS) {
5217 78 : return ret;
5218 : }
5219 : }
5220 :
5221 589354 : el = ldb_msg_find_element(ac->msg, "fSMORoleOwner");
5222 589354 : if (el != NULL) {
5223 1278 : ret = samldb_fsmo_role_owner_check(ac);
5224 1278 : if (ret != LDB_SUCCESS) {
5225 6 : return ret;
5226 : }
5227 : }
5228 :
5229 589348 : if (modified) {
5230 277 : struct ldb_request *child_req;
5231 :
5232 : /* Now perform the real modifications as a child request */
5233 20353 : ret = ldb_build_mod_req(&child_req, ldb, ac,
5234 20076 : ac->msg,
5235 : req->controls,
5236 : req, dsdb_next_callback,
5237 : req);
5238 20076 : LDB_REQ_SET_LOCATION(child_req);
5239 20076 : if (ret != LDB_SUCCESS) {
5240 0 : return ret;
5241 : }
5242 :
5243 20076 : return ldb_next_request(module, child_req);
5244 : }
5245 :
5246 569272 : talloc_free(ac);
5247 :
5248 : /* no change which interests us, go on */
5249 569272 : return ldb_next_request(module, req);
5250 : }
5251 :
5252 : /* delete */
5253 :
5254 72496 : static int samldb_prim_group_users_check(struct samldb_ctx *ac)
5255 : {
5256 159 : struct ldb_context *ldb;
5257 159 : struct dom_sid *sid;
5258 159 : uint32_t rid;
5259 159 : NTSTATUS status;
5260 159 : int ret;
5261 72496 : struct ldb_result *res = NULL;
5262 72496 : struct ldb_result *res_users = NULL;
5263 72496 : const char * const attrs[] = { "objectSid", "isDeleted", NULL };
5264 72496 : const char * const noattrs[] = { NULL };
5265 :
5266 72496 : ldb = ldb_module_get_ctx(ac->module);
5267 :
5268 : /* Finds out the SID/RID of the SAM object */
5269 72496 : ret = dsdb_module_search_dn(ac->module, ac, &res, ac->req->op.del.dn,
5270 : attrs,
5271 : DSDB_FLAG_NEXT_MODULE | DSDB_SEARCH_SHOW_DELETED,
5272 : ac->req);
5273 72496 : if (ret != LDB_SUCCESS) {
5274 0 : return ret;
5275 : }
5276 :
5277 72496 : if (ldb_msg_check_string_attribute(res->msgs[0], "isDeleted", "TRUE")) {
5278 7 : return LDB_SUCCESS;
5279 : }
5280 :
5281 72482 : sid = samdb_result_dom_sid(ac, res->msgs[0], "objectSid");
5282 72482 : if (sid == NULL) {
5283 : /* No SID - it might not be a SAM object - therefore ok */
5284 39840 : return LDB_SUCCESS;
5285 : }
5286 32581 : status = dom_sid_split_rid(ac, sid, NULL, &rid);
5287 32581 : if (!NT_STATUS_IS_OK(status)) {
5288 0 : return ldb_operr(ldb);
5289 : }
5290 32581 : if (rid == 0) {
5291 : /* Special object (security principal?) */
5292 0 : return LDB_SUCCESS;
5293 : }
5294 : /* do not allow deletion of well-known sids */
5295 32599 : if (rid < DSDB_SAMDB_MINIMUM_ALLOWED_RID &&
5296 18 : (ldb_request_get_control(ac->req, LDB_CONTROL_RELAX_OID) == NULL)) {
5297 18 : return LDB_ERR_OTHER;
5298 : }
5299 :
5300 : /* Deny delete requests from groups which are primary ones */
5301 32563 : ret = dsdb_module_search(ac->module, ac, &res_users,
5302 : ldb_get_default_basedn(ldb),
5303 : LDB_SCOPE_SUBTREE, noattrs,
5304 : DSDB_FLAG_NEXT_MODULE,
5305 : ac->req,
5306 : "(&(primaryGroupID=%u)(objectClass=user))", rid);
5307 32563 : if (ret != LDB_SUCCESS) {
5308 0 : return ret;
5309 : }
5310 32563 : if (res_users->count > 0) {
5311 3 : ldb_asprintf_errstring(ldb_module_get_ctx(ac->module),
5312 : "Refusing to delete %s, as it "
5313 : "is still the primaryGroupID "
5314 : "for %u users",
5315 3 : ldb_dn_get_linearized(res->msgs[0]->dn),
5316 3 : res_users->count);
5317 :
5318 : /*
5319 : * Yes, this seems very wrong, but we have a test
5320 : * for this exact error code in sam.py
5321 : */
5322 3 : return LDB_ERR_ENTRY_ALREADY_EXISTS;
5323 : }
5324 :
5325 32469 : return LDB_SUCCESS;
5326 : }
5327 :
5328 72498 : static int samldb_delete(struct ldb_module *module, struct ldb_request *req)
5329 : {
5330 159 : struct samldb_ctx *ac;
5331 72498 : char *referral = NULL;
5332 159 : int ret;
5333 159 : struct ldb_context *ldb;
5334 :
5335 72498 : if (ldb_dn_is_special(req->op.del.dn)) {
5336 : /* do not manipulate our control entries */
5337 1 : return ldb_next_request(module, req);
5338 : }
5339 :
5340 72497 : ldb = ldb_module_get_ctx(module);
5341 :
5342 72497 : referral = refer_if_rodc(ldb, req, req->op.del.dn);
5343 72497 : if (referral != NULL) {
5344 1 : ret = ldb_module_send_referral(req, referral);
5345 1 : return ret;
5346 : }
5347 :
5348 72496 : ac = samldb_ctx_init(module, req);
5349 72496 : if (ac == NULL) {
5350 0 : return ldb_operr(ldb_module_get_ctx(module));
5351 : }
5352 :
5353 72496 : ret = samldb_prim_group_users_check(ac);
5354 72496 : if (ret != LDB_SUCCESS) {
5355 21 : return ret;
5356 : }
5357 :
5358 72475 : talloc_free(ac);
5359 :
5360 72475 : return ldb_next_request(module, req);
5361 : }
5362 :
5363 : /* rename */
5364 :
5365 1479 : static int check_rename_constraints(struct ldb_message *msg,
5366 : struct samldb_ctx *ac,
5367 : struct ldb_dn *olddn, struct ldb_dn *newdn)
5368 : {
5369 1479 : struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
5370 6 : struct ldb_dn *dn1, *dn2, *nc_root;
5371 6 : int32_t systemFlags;
5372 1479 : bool move_op = false;
5373 1479 : bool rename_op = false;
5374 6 : int ret;
5375 :
5376 : /* Skip the checks if old and new DN are the same, or if we have the
5377 : * relax control specified or if the returned objects is already
5378 : * deleted and needs only to be moved for consistency. */
5379 :
5380 1479 : if (ldb_dn_compare(olddn, newdn) == 0) {
5381 6 : return LDB_SUCCESS;
5382 : }
5383 1473 : if (ldb_request_get_control(ac->req, LDB_CONTROL_RELAX_OID) != NULL) {
5384 21 : return LDB_SUCCESS;
5385 : }
5386 :
5387 1452 : if (ldb_msg_find_attr_as_bool(msg, "isDeleted", false)) {
5388 : /*
5389 : * check originating request if we are supposed
5390 : * to "see" this record in first place.
5391 : */
5392 2 : if (ldb_request_get_control(ac->req, LDB_CONTROL_SHOW_DELETED_OID) == NULL) {
5393 1 : return LDB_ERR_NO_SUCH_OBJECT;
5394 : }
5395 1 : return LDB_ERR_UNWILLING_TO_PERFORM;
5396 : }
5397 :
5398 : /* Objects under CN=System */
5399 :
5400 1450 : dn1 = samdb_system_container_dn(ldb, ac);
5401 1450 : if (dn1 == NULL) return ldb_oom(ldb);
5402 :
5403 1451 : if ((ldb_dn_compare_base(dn1, olddn) == 0) &&
5404 1 : (ldb_dn_compare_base(dn1, newdn) != 0)) {
5405 1 : talloc_free(dn1);
5406 1 : ldb_asprintf_errstring(ldb,
5407 : "subtree_rename: Cannot move/rename %s. Objects under CN=System have to stay under it!",
5408 : ldb_dn_get_linearized(olddn));
5409 1 : return LDB_ERR_OTHER;
5410 : }
5411 :
5412 1449 : talloc_free(dn1);
5413 :
5414 : /* LSA objects */
5415 :
5416 2898 : if ((samdb_find_attribute(ldb, msg, "objectClass", "secret") != NULL) ||
5417 1449 : (samdb_find_attribute(ldb, msg, "objectClass", "trustedDomain") != NULL)) {
5418 0 : ldb_asprintf_errstring(ldb,
5419 : "subtree_rename: Cannot move/rename %s. It's an LSA-specific object!",
5420 : ldb_dn_get_linearized(olddn));
5421 0 : return LDB_ERR_UNWILLING_TO_PERFORM;
5422 : }
5423 :
5424 : /* subnet objects */
5425 1449 : if (samdb_find_attribute(ldb, msg, "objectclass", "subnet") != NULL) {
5426 2 : ret = samldb_verify_subnet(ac, newdn);
5427 2 : if (ret != LDB_SUCCESS) {
5428 1 : return ret;
5429 : }
5430 : }
5431 :
5432 : /* systemFlags */
5433 :
5434 1448 : dn1 = ldb_dn_get_parent(ac, olddn);
5435 1448 : if (dn1 == NULL) return ldb_oom(ldb);
5436 1448 : dn2 = ldb_dn_get_parent(ac, newdn);
5437 1448 : if (dn2 == NULL) return ldb_oom(ldb);
5438 :
5439 1448 : if (ldb_dn_compare(dn1, dn2) == 0) {
5440 917 : rename_op = true;
5441 : } else {
5442 525 : move_op = true;
5443 : }
5444 :
5445 1448 : talloc_free(dn1);
5446 1448 : talloc_free(dn2);
5447 :
5448 1448 : systemFlags = ldb_msg_find_attr_as_int(msg, "systemFlags", 0);
5449 :
5450 : /* Fetch name context */
5451 :
5452 1448 : ret = dsdb_find_nc_root(ldb, ac, olddn, &nc_root);
5453 1448 : if (ret != LDB_SUCCESS) {
5454 0 : return ret;
5455 : }
5456 :
5457 1448 : if (ldb_dn_compare(nc_root, ldb_get_schema_basedn(ldb)) == 0) {
5458 8 : if (move_op) {
5459 0 : ldb_asprintf_errstring(ldb,
5460 : "subtree_rename: Cannot move %s within schema partition",
5461 : ldb_dn_get_linearized(olddn));
5462 0 : return LDB_ERR_UNWILLING_TO_PERFORM;
5463 : }
5464 8 : if (rename_op &&
5465 8 : (systemFlags & SYSTEM_FLAG_SCHEMA_BASE_OBJECT) != 0) {
5466 1 : ldb_asprintf_errstring(ldb,
5467 : "subtree_rename: Cannot rename %s within schema partition",
5468 : ldb_dn_get_linearized(olddn));
5469 1 : return LDB_ERR_UNWILLING_TO_PERFORM;
5470 : }
5471 1440 : } else if (ldb_dn_compare(nc_root, ldb_get_config_basedn(ldb)) == 0) {
5472 12 : if (move_op &&
5473 4 : (systemFlags & SYSTEM_FLAG_CONFIG_ALLOW_MOVE) == 0) {
5474 : /* Here we have to do more: control the
5475 : * "ALLOW_LIMITED_MOVE" flag. This means that the
5476 : * grand-grand-parents of two objects have to be equal
5477 : * in order to perform the move (this is used for
5478 : * moving "server" objects in the "sites" container). */
5479 4 : bool limited_move =
5480 4 : systemFlags & SYSTEM_FLAG_CONFIG_ALLOW_LIMITED_MOVE;
5481 :
5482 4 : if (limited_move) {
5483 0 : dn1 = ldb_dn_copy(ac, olddn);
5484 0 : if (dn1 == NULL) return ldb_oom(ldb);
5485 0 : dn2 = ldb_dn_copy(ac, newdn);
5486 0 : if (dn2 == NULL) return ldb_oom(ldb);
5487 :
5488 0 : limited_move &= ldb_dn_remove_child_components(dn1, 3);
5489 0 : limited_move &= ldb_dn_remove_child_components(dn2, 3);
5490 0 : limited_move &= ldb_dn_compare(dn1, dn2) == 0;
5491 :
5492 0 : talloc_free(dn1);
5493 0 : talloc_free(dn2);
5494 : }
5495 :
5496 4 : if (!limited_move
5497 4 : && ldb_request_get_control(ac->req, DSDB_CONTROL_RESTORE_TOMBSTONE_OID) == NULL) {
5498 2 : ldb_asprintf_errstring(ldb,
5499 : "subtree_rename: Cannot move %s to %s in config partition",
5500 : ldb_dn_get_linearized(olddn), ldb_dn_get_linearized(newdn));
5501 2 : return LDB_ERR_UNWILLING_TO_PERFORM;
5502 : }
5503 : }
5504 10 : if (rename_op &&
5505 8 : (systemFlags & SYSTEM_FLAG_CONFIG_ALLOW_RENAME) == 0) {
5506 1 : ldb_asprintf_errstring(ldb,
5507 : "subtree_rename: Cannot rename %s to %s within config partition",
5508 : ldb_dn_get_linearized(olddn), ldb_dn_get_linearized(newdn));
5509 1 : return LDB_ERR_UNWILLING_TO_PERFORM;
5510 : }
5511 1428 : } else if (ldb_dn_compare(nc_root, ldb_get_default_basedn(ldb)) == 0) {
5512 1424 : if (move_op &&
5513 521 : (systemFlags & SYSTEM_FLAG_DOMAIN_DISALLOW_MOVE) != 0) {
5514 1 : ldb_asprintf_errstring(ldb,
5515 : "subtree_rename: Cannot move %s to %s - DISALLOW_MOVE set",
5516 : ldb_dn_get_linearized(olddn), ldb_dn_get_linearized(newdn));
5517 1 : return LDB_ERR_UNWILLING_TO_PERFORM;
5518 : }
5519 1423 : if (rename_op &&
5520 903 : (systemFlags & SYSTEM_FLAG_DOMAIN_DISALLOW_RENAME) != 0) {
5521 1 : ldb_asprintf_errstring(ldb,
5522 : "subtree_rename: Cannot rename %s to %s - DISALLOW_RENAME set",
5523 : ldb_dn_get_linearized(olddn), ldb_dn_get_linearized(newdn));
5524 1 : return LDB_ERR_UNWILLING_TO_PERFORM;
5525 : }
5526 : }
5527 :
5528 1442 : talloc_free(nc_root);
5529 :
5530 1442 : return LDB_SUCCESS;
5531 : }
5532 :
5533 :
5534 2951 : static int samldb_rename_search_base_callback(struct ldb_request *req,
5535 : struct ldb_reply *ares)
5536 : {
5537 15 : struct samldb_ctx *ac;
5538 15 : int ret;
5539 :
5540 2951 : ac = talloc_get_type(req->context, struct samldb_ctx);
5541 :
5542 2951 : if (!ares) {
5543 0 : return ldb_module_done(ac->req, NULL, NULL,
5544 : LDB_ERR_OPERATIONS_ERROR);
5545 : }
5546 2951 : if (ares->error != LDB_SUCCESS) {
5547 0 : return ldb_module_done(ac->req, ares->controls,
5548 : ares->response, ares->error);
5549 : }
5550 :
5551 2951 : switch (ares->type) {
5552 1479 : case LDB_REPLY_ENTRY:
5553 : /*
5554 : * This is the root entry of the originating move
5555 : * respectively rename request. It has been already
5556 : * stored in the list using "subtree_rename_search()".
5557 : * Only this one is subject to constraint checking.
5558 : */
5559 1485 : ret = check_rename_constraints(ares->message, ac,
5560 1473 : ac->req->op.rename.olddn,
5561 1479 : ac->req->op.rename.newdn);
5562 1479 : if (ret != LDB_SUCCESS) {
5563 10 : return ldb_module_done(ac->req, NULL, NULL,
5564 : ret);
5565 : }
5566 1463 : break;
5567 :
5568 0 : case LDB_REPLY_REFERRAL:
5569 : /* ignore */
5570 0 : break;
5571 :
5572 1472 : case LDB_REPLY_DONE:
5573 :
5574 : /*
5575 : * Great, no problem with the rename, so go ahead as
5576 : * if we never were here
5577 : */
5578 1472 : ret = ldb_next_request(ac->module, ac->req);
5579 1472 : talloc_free(ares);
5580 1472 : return ret;
5581 : }
5582 :
5583 1469 : talloc_free(ares);
5584 1469 : return LDB_SUCCESS;
5585 : }
5586 :
5587 :
5588 : /* rename */
5589 1482 : static int samldb_rename(struct ldb_module *module, struct ldb_request *req)
5590 : {
5591 9 : struct ldb_context *ldb;
5592 9 : static const char * const attrs[] = { "objectClass", "systemFlags",
5593 : "isDeleted", NULL };
5594 9 : struct ldb_request *search_req;
5595 9 : struct samldb_ctx *ac;
5596 9 : int ret;
5597 :
5598 1482 : if (ldb_dn_is_special(req->op.rename.olddn)) { /* do not manipulate our control entries */
5599 0 : return ldb_next_request(module, req);
5600 : }
5601 :
5602 1482 : ldb = ldb_module_get_ctx(module);
5603 :
5604 1482 : ac = samldb_ctx_init(module, req);
5605 1482 : if (!ac) {
5606 0 : return ldb_oom(ldb);
5607 : }
5608 :
5609 1482 : ret = ldb_build_search_req(&search_req, ldb, ac,
5610 : req->op.rename.olddn,
5611 : LDB_SCOPE_BASE,
5612 : "(objectClass=*)",
5613 : attrs,
5614 : NULL,
5615 : ac,
5616 : samldb_rename_search_base_callback,
5617 : req);
5618 1482 : LDB_REQ_SET_LOCATION(search_req);
5619 1482 : if (ret != LDB_SUCCESS) {
5620 0 : return ret;
5621 : }
5622 :
5623 1482 : ret = ldb_request_add_control(search_req, LDB_CONTROL_SHOW_RECYCLED_OID,
5624 : true, NULL);
5625 1482 : if (ret != LDB_SUCCESS) {
5626 0 : return ret;
5627 : }
5628 :
5629 1482 : return ldb_next_request(ac->module, search_req);
5630 : }
5631 :
5632 : /* extended */
5633 :
5634 37 : static int samldb_extended_allocate_rid_pool(struct ldb_module *module, struct ldb_request *req)
5635 : {
5636 37 : struct ldb_context *ldb = ldb_module_get_ctx(module);
5637 0 : struct dsdb_fsmo_extended_op *exop;
5638 0 : int ret;
5639 :
5640 37 : exop = talloc_get_type(req->op.extended.data,
5641 : struct dsdb_fsmo_extended_op);
5642 37 : if (!exop) {
5643 0 : ldb_set_errstring(ldb,
5644 : "samldb_extended_allocate_rid_pool: invalid extended data");
5645 0 : return LDB_ERR_PROTOCOL_ERROR;
5646 : }
5647 :
5648 37 : ret = ridalloc_allocate_rid_pool_fsmo(module, exop, req);
5649 37 : if (ret != LDB_SUCCESS) {
5650 0 : return ret;
5651 : }
5652 :
5653 37 : return ldb_module_done(req, NULL, NULL, LDB_SUCCESS);
5654 : }
5655 :
5656 995 : static int samldb_extended_allocate_rid(struct ldb_module *module, struct ldb_request *req)
5657 : {
5658 995 : struct ldb_context *ldb = ldb_module_get_ctx(module);
5659 0 : struct dsdb_extended_allocate_rid *exop;
5660 0 : int ret;
5661 :
5662 995 : exop = talloc_get_type(req->op.extended.data,
5663 : struct dsdb_extended_allocate_rid);
5664 995 : if (!exop) {
5665 0 : ldb_set_errstring(ldb,
5666 : "samldb_extended_allocate_rid: invalid extended data");
5667 0 : return LDB_ERR_PROTOCOL_ERROR;
5668 : }
5669 :
5670 995 : ret = ridalloc_allocate_rid(module, &exop->rid, req);
5671 995 : if (ret != LDB_SUCCESS) {
5672 2 : return ret;
5673 : }
5674 :
5675 993 : return ldb_module_done(req, NULL, NULL, LDB_SUCCESS);
5676 : }
5677 :
5678 39 : static int samldb_extended_create_own_rid_set(struct ldb_module *module, struct ldb_request *req)
5679 : {
5680 39 : struct ldb_context *ldb = ldb_module_get_ctx(module);
5681 0 : int ret;
5682 0 : struct ldb_dn *dn;
5683 :
5684 39 : if (req->op.extended.data != NULL) {
5685 0 : ldb_set_errstring(ldb,
5686 : "samldb_extended_create_own_rid_set: invalid extended data (should be NULL)");
5687 0 : return LDB_ERR_PROTOCOL_ERROR;
5688 : }
5689 :
5690 39 : ret = ridalloc_create_own_rid_set(module, req,
5691 : &dn, req);
5692 39 : if (ret != LDB_SUCCESS) {
5693 1 : return ret;
5694 : }
5695 :
5696 38 : return ldb_module_done(req, NULL, NULL, LDB_SUCCESS);
5697 : }
5698 :
5699 1306605 : static int samldb_extended(struct ldb_module *module, struct ldb_request *req)
5700 : {
5701 1306605 : if (strcmp(req->op.extended.oid, DSDB_EXTENDED_ALLOCATE_RID_POOL) == 0) {
5702 37 : return samldb_extended_allocate_rid_pool(module, req);
5703 : }
5704 :
5705 1306568 : if (strcmp(req->op.extended.oid, DSDB_EXTENDED_ALLOCATE_RID) == 0) {
5706 995 : return samldb_extended_allocate_rid(module, req);
5707 : }
5708 :
5709 1305573 : if (strcmp(req->op.extended.oid, DSDB_EXTENDED_CREATE_OWN_RID_SET) == 0) {
5710 39 : return samldb_extended_create_own_rid_set(module, req);
5711 : }
5712 :
5713 1305534 : return ldb_next_request(module, req);
5714 : }
5715 :
5716 :
5717 : static const struct ldb_module_ops ldb_samldb_module_ops = {
5718 : .name = "samldb",
5719 : .add = samldb_add,
5720 : .modify = samldb_modify,
5721 : .del = samldb_delete,
5722 : .rename = samldb_rename,
5723 : .extended = samldb_extended
5724 : };
5725 :
5726 :
5727 5950 : int ldb_samldb_module_init(const char *version)
5728 : {
5729 5950 : LDB_MODULE_CHECK_VERSION(version);
5730 5950 : return ldb_register_module(&ldb_samldb_module_ops);
5731 : }
|