Merge ldb_search() and ldb_search_exp_fmt() into a simgle function.
[Samba.git] / source4 / dsdb / common / util.c
blob2b74d722a18bbccbdfaa247ec3ce2821cb47c794
1 /*
2 Unix SMB/CIFS implementation.
3 Samba utility functions
5 Copyright (C) Andrew Tridgell 2004
6 Copyright (C) Volker Lendecke 2004
7 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2006
8 Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 3 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program. If not, see <http://www.gnu.org/licenses/>.
24 #include "includes.h"
25 #include "events.h"
26 #include "ldb.h"
27 #include "ldb_errors.h"
28 #include "lib/util/util_ldb.h"
29 #include "dsdb/samdb/samdb.h"
30 #include "libcli/security/security.h"
31 #include "librpc/gen_ndr/ndr_security.h"
32 #include "librpc/gen_ndr/ndr_misc.h"
33 #include "dsdb/common/flags.h"
34 #include "dsdb/common/proto.h"
35 #include "libcli/ldap/ldap_ndr.h"
36 #include "param/param.h"
37 #include "libcli/auth/libcli_auth.h"
40 search the sam for the specified attributes in a specific domain, filter on
41 objectSid being in domain_sid.
43 int samdb_search_domain(struct ldb_context *sam_ldb,
44 TALLOC_CTX *mem_ctx,
45 struct ldb_dn *basedn,
46 struct ldb_message ***res,
47 const char * const *attrs,
48 const struct dom_sid *domain_sid,
49 const char *format, ...) _PRINTF_ATTRIBUTE(7,8)
51 va_list ap;
52 int i, count;
54 va_start(ap, format);
55 count = gendb_search_v(sam_ldb, mem_ctx, basedn,
56 res, attrs, format, ap);
57 va_end(ap);
59 i=0;
61 while (i<count) {
62 struct dom_sid *entry_sid;
64 entry_sid = samdb_result_dom_sid(mem_ctx, (*res)[i], "objectSid");
66 if ((entry_sid == NULL) ||
67 (!dom_sid_in_domain(domain_sid, entry_sid))) {
68 /* Delete that entry from the result set */
69 (*res)[i] = (*res)[count-1];
70 count -= 1;
71 talloc_free(entry_sid);
72 continue;
74 talloc_free(entry_sid);
75 i += 1;
78 return count;
82 search the sam for a single string attribute in exactly 1 record
84 const char *samdb_search_string_v(struct ldb_context *sam_ldb,
85 TALLOC_CTX *mem_ctx,
86 struct ldb_dn *basedn,
87 const char *attr_name,
88 const char *format, va_list ap) _PRINTF_ATTRIBUTE(5,0)
90 int count;
91 const char *attrs[2] = { NULL, NULL };
92 struct ldb_message **res = NULL;
94 attrs[0] = attr_name;
96 count = gendb_search_v(sam_ldb, mem_ctx, basedn, &res, attrs, format, ap);
97 if (count > 1) {
98 DEBUG(1,("samdb: search for %s %s not single valued (count=%d)\n",
99 attr_name, format, count));
101 if (count != 1) {
102 talloc_free(res);
103 return NULL;
106 return samdb_result_string(res[0], attr_name, NULL);
111 search the sam for a single string attribute in exactly 1 record
113 const char *samdb_search_string(struct ldb_context *sam_ldb,
114 TALLOC_CTX *mem_ctx,
115 struct ldb_dn *basedn,
116 const char *attr_name,
117 const char *format, ...) _PRINTF_ATTRIBUTE(5,6)
119 va_list ap;
120 const char *str;
122 va_start(ap, format);
123 str = samdb_search_string_v(sam_ldb, mem_ctx, basedn, attr_name, format, ap);
124 va_end(ap);
126 return str;
129 struct ldb_dn *samdb_search_dn(struct ldb_context *sam_ldb,
130 TALLOC_CTX *mem_ctx,
131 struct ldb_dn *basedn,
132 const char *format, ...) _PRINTF_ATTRIBUTE(4,5)
134 va_list ap;
135 struct ldb_dn *ret;
136 struct ldb_message **res = NULL;
137 int count;
139 va_start(ap, format);
140 count = gendb_search_v(sam_ldb, mem_ctx, basedn, &res, NULL, format, ap);
141 va_end(ap);
143 if (count != 1) return NULL;
145 ret = talloc_steal(mem_ctx, res[0]->dn);
146 talloc_free(res);
148 return ret;
152 search the sam for a dom_sid attribute in exactly 1 record
154 struct dom_sid *samdb_search_dom_sid(struct ldb_context *sam_ldb,
155 TALLOC_CTX *mem_ctx,
156 struct ldb_dn *basedn,
157 const char *attr_name,
158 const char *format, ...) _PRINTF_ATTRIBUTE(5,6)
160 va_list ap;
161 int count;
162 struct ldb_message **res;
163 const char *attrs[2] = { NULL, NULL };
164 struct dom_sid *sid;
166 attrs[0] = attr_name;
168 va_start(ap, format);
169 count = gendb_search_v(sam_ldb, mem_ctx, basedn, &res, attrs, format, ap);
170 va_end(ap);
171 if (count > 1) {
172 DEBUG(1,("samdb: search for %s %s not single valued (count=%d)\n",
173 attr_name, format, count));
175 if (count != 1) {
176 talloc_free(res);
177 return NULL;
179 sid = samdb_result_dom_sid(mem_ctx, res[0], attr_name);
180 talloc_free(res);
181 return sid;
185 return the count of the number of records in the sam matching the query
187 int samdb_search_count(struct ldb_context *sam_ldb,
188 TALLOC_CTX *mem_ctx,
189 struct ldb_dn *basedn,
190 const char *format, ...) _PRINTF_ATTRIBUTE(4,5)
192 va_list ap;
193 struct ldb_message **res;
194 const char * const attrs[] = { NULL };
195 int ret;
197 va_start(ap, format);
198 ret = gendb_search_v(sam_ldb, mem_ctx, basedn, &res, attrs, format, ap);
199 va_end(ap);
201 return ret;
206 search the sam for a single integer attribute in exactly 1 record
208 uint_t samdb_search_uint(struct ldb_context *sam_ldb,
209 TALLOC_CTX *mem_ctx,
210 uint_t default_value,
211 struct ldb_dn *basedn,
212 const char *attr_name,
213 const char *format, ...) _PRINTF_ATTRIBUTE(6,7)
215 va_list ap;
216 int count;
217 struct ldb_message **res;
218 const char *attrs[2] = { NULL, NULL };
220 attrs[0] = attr_name;
222 va_start(ap, format);
223 count = gendb_search_v(sam_ldb, mem_ctx, basedn, &res, attrs, format, ap);
224 va_end(ap);
226 if (count != 1) {
227 return default_value;
230 return samdb_result_uint(res[0], attr_name, default_value);
234 search the sam for a single signed 64 bit integer attribute in exactly 1 record
236 int64_t samdb_search_int64(struct ldb_context *sam_ldb,
237 TALLOC_CTX *mem_ctx,
238 int64_t default_value,
239 struct ldb_dn *basedn,
240 const char *attr_name,
241 const char *format, ...) _PRINTF_ATTRIBUTE(6,7)
243 va_list ap;
244 int count;
245 struct ldb_message **res;
246 const char *attrs[2] = { NULL, NULL };
248 attrs[0] = attr_name;
250 va_start(ap, format);
251 count = gendb_search_v(sam_ldb, mem_ctx, basedn, &res, attrs, format, ap);
252 va_end(ap);
254 if (count != 1) {
255 return default_value;
258 return samdb_result_int64(res[0], attr_name, default_value);
262 search the sam for multipe records each giving a single string attribute
263 return the number of matches, or -1 on error
265 int samdb_search_string_multiple(struct ldb_context *sam_ldb,
266 TALLOC_CTX *mem_ctx,
267 struct ldb_dn *basedn,
268 const char ***strs,
269 const char *attr_name,
270 const char *format, ...) _PRINTF_ATTRIBUTE(6,7)
272 va_list ap;
273 int count, i;
274 const char *attrs[2] = { NULL, NULL };
275 struct ldb_message **res = NULL;
277 attrs[0] = attr_name;
279 va_start(ap, format);
280 count = gendb_search_v(sam_ldb, mem_ctx, basedn, &res, attrs, format, ap);
281 va_end(ap);
283 if (count <= 0) {
284 return count;
287 /* make sure its single valued */
288 for (i=0;i<count;i++) {
289 if (res[i]->num_elements != 1) {
290 DEBUG(1,("samdb: search for %s %s not single valued\n",
291 attr_name, format));
292 talloc_free(res);
293 return -1;
297 *strs = talloc_array(mem_ctx, const char *, count+1);
298 if (! *strs) {
299 talloc_free(res);
300 return -1;
303 for (i=0;i<count;i++) {
304 (*strs)[i] = samdb_result_string(res[i], attr_name, NULL);
306 (*strs)[count] = NULL;
308 return count;
312 pull a uint from a result set.
314 uint_t samdb_result_uint(const struct ldb_message *msg, const char *attr, uint_t default_value)
316 return ldb_msg_find_attr_as_uint(msg, attr, default_value);
320 pull a (signed) int64 from a result set.
322 int64_t samdb_result_int64(const struct ldb_message *msg, const char *attr, int64_t default_value)
324 return ldb_msg_find_attr_as_int64(msg, attr, default_value);
328 pull a string from a result set.
330 const char *samdb_result_string(const struct ldb_message *msg, const char *attr,
331 const char *default_value)
333 return ldb_msg_find_attr_as_string(msg, attr, default_value);
336 struct ldb_dn *samdb_result_dn(struct ldb_context *ldb, TALLOC_CTX *mem_ctx, const struct ldb_message *msg,
337 const char *attr, struct ldb_dn *default_value)
339 struct ldb_dn *ret_dn = ldb_msg_find_attr_as_dn(ldb, mem_ctx, msg, attr);
340 if (!ret_dn) {
341 return default_value;
343 return ret_dn;
347 pull a rid from a objectSid in a result set.
349 uint32_t samdb_result_rid_from_sid(TALLOC_CTX *mem_ctx, const struct ldb_message *msg,
350 const char *attr, uint32_t default_value)
352 struct dom_sid *sid;
353 uint32_t rid;
355 sid = samdb_result_dom_sid(mem_ctx, msg, attr);
356 if (sid == NULL) {
357 return default_value;
359 rid = sid->sub_auths[sid->num_auths-1];
360 talloc_free(sid);
361 return rid;
365 pull a dom_sid structure from a objectSid in a result set.
367 struct dom_sid *samdb_result_dom_sid(TALLOC_CTX *mem_ctx, const struct ldb_message *msg,
368 const char *attr)
370 const struct ldb_val *v;
371 struct dom_sid *sid;
372 enum ndr_err_code ndr_err;
373 v = ldb_msg_find_ldb_val(msg, attr);
374 if (v == NULL) {
375 return NULL;
377 sid = talloc(mem_ctx, struct dom_sid);
378 if (sid == NULL) {
379 return NULL;
381 ndr_err = ndr_pull_struct_blob(v, sid, NULL, sid,
382 (ndr_pull_flags_fn_t)ndr_pull_dom_sid);
383 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
384 talloc_free(sid);
385 return NULL;
387 return sid;
391 pull a guid structure from a objectGUID in a result set.
393 struct GUID samdb_result_guid(const struct ldb_message *msg, const char *attr)
395 const struct ldb_val *v;
396 enum ndr_err_code ndr_err;
397 struct GUID guid;
398 TALLOC_CTX *mem_ctx;
400 ZERO_STRUCT(guid);
402 v = ldb_msg_find_ldb_val(msg, attr);
403 if (!v) return guid;
405 mem_ctx = talloc_named_const(NULL, 0, "samdb_result_guid");
406 if (!mem_ctx) return guid;
407 ndr_err = ndr_pull_struct_blob(v, mem_ctx, NULL, &guid,
408 (ndr_pull_flags_fn_t)ndr_pull_GUID);
409 talloc_free(mem_ctx);
410 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
411 return guid;
414 return guid;
418 pull a sid prefix from a objectSid in a result set.
419 this is used to find the domain sid for a user
421 struct dom_sid *samdb_result_sid_prefix(TALLOC_CTX *mem_ctx, const struct ldb_message *msg,
422 const char *attr)
424 struct dom_sid *sid = samdb_result_dom_sid(mem_ctx, msg, attr);
425 if (!sid || sid->num_auths < 1) return NULL;
426 sid->num_auths--;
427 return sid;
431 pull a NTTIME in a result set.
433 NTTIME samdb_result_nttime(struct ldb_message *msg, const char *attr, NTTIME default_value)
435 return ldb_msg_find_attr_as_uint64(msg, attr, default_value);
439 * Windows uses both 0 and 9223372036854775807 (0x7FFFFFFFFFFFFFFFULL) to
440 * indicate an account doesn't expire.
442 * When Windows initially creates an account, it sets
443 * accountExpires = 9223372036854775807 (0x7FFFFFFFFFFFFFFF). However,
444 * when changing from an account having a specific expiration date to
445 * that account never expiring, it sets accountExpires = 0.
447 * Consolidate that logic here to allow clearer logic for account expiry in
448 * the rest of the code.
450 NTTIME samdb_result_account_expires(struct ldb_message *msg)
452 NTTIME ret = ldb_msg_find_attr_as_uint64(msg, "accountExpires",
455 if (ret == 0)
456 ret = 0x7FFFFFFFFFFFFFFFULL;
458 return ret;
462 pull a uint64_t from a result set.
464 uint64_t samdb_result_uint64(struct ldb_message *msg, const char *attr, uint64_t default_value)
466 return ldb_msg_find_attr_as_uint64(msg, attr, default_value);
471 construct the allow_password_change field from the PwdLastSet attribute and the
472 domain password settings
474 NTTIME samdb_result_allow_password_change(struct ldb_context *sam_ldb,
475 TALLOC_CTX *mem_ctx,
476 struct ldb_dn *domain_dn,
477 struct ldb_message *msg,
478 const char *attr)
480 uint64_t attr_time = samdb_result_uint64(msg, attr, 0);
481 int64_t minPwdAge;
483 if (attr_time == 0) {
484 return 0;
487 minPwdAge = samdb_search_int64(sam_ldb, mem_ctx, 0, domain_dn, "minPwdAge", NULL);
489 /* yes, this is a -= not a += as minPwdAge is stored as the negative
490 of the number of 100-nano-seconds */
491 attr_time -= minPwdAge;
493 return attr_time;
497 construct the force_password_change field from the PwdLastSet
498 attribute, the userAccountControl and the domain password settings
500 NTTIME samdb_result_force_password_change(struct ldb_context *sam_ldb,
501 TALLOC_CTX *mem_ctx,
502 struct ldb_dn *domain_dn,
503 struct ldb_message *msg)
505 uint64_t attr_time = samdb_result_uint64(msg, "pwdLastSet", 0);
506 uint32_t userAccountControl = samdb_result_uint64(msg, "userAccountControl", 0);
507 int64_t maxPwdAge;
509 /* Machine accounts don't expire, and there is a flag for 'no expiry' */
510 if (!(userAccountControl & UF_NORMAL_ACCOUNT)
511 || (userAccountControl & UF_DONT_EXPIRE_PASSWD)) {
512 return 0x7FFFFFFFFFFFFFFFULL;
515 if (attr_time == 0) {
516 return 0;
519 maxPwdAge = samdb_search_int64(sam_ldb, mem_ctx, 0, domain_dn, "maxPwdAge", NULL);
520 if (maxPwdAge == 0) {
521 return 0x7FFFFFFFFFFFFFFFULL;
522 } else {
523 attr_time -= maxPwdAge;
526 return attr_time;
530 pull a samr_Password structutre from a result set.
532 struct samr_Password *samdb_result_hash(TALLOC_CTX *mem_ctx, struct ldb_message *msg, const char *attr)
534 struct samr_Password *hash = NULL;
535 const struct ldb_val *val = ldb_msg_find_ldb_val(msg, attr);
536 if (val && (val->length >= sizeof(hash->hash))) {
537 hash = talloc(mem_ctx, struct samr_Password);
538 memcpy(hash->hash, val->data, MIN(val->length, sizeof(hash->hash)));
540 return hash;
544 pull an array of samr_Password structutres from a result set.
546 uint_t samdb_result_hashes(TALLOC_CTX *mem_ctx, struct ldb_message *msg,
547 const char *attr, struct samr_Password **hashes)
549 uint_t count = 0;
550 const struct ldb_val *val = ldb_msg_find_ldb_val(msg, attr);
551 int i;
553 *hashes = NULL;
554 if (!val) {
555 return 0;
557 count = val->length / 16;
558 if (count == 0) {
559 return 0;
562 *hashes = talloc_array(mem_ctx, struct samr_Password, count);
563 if (! *hashes) {
564 return 0;
567 for (i=0;i<count;i++) {
568 memcpy((*hashes)[i].hash, (i*16)+(char *)val->data, 16);
571 return count;
574 NTSTATUS samdb_result_passwords(TALLOC_CTX *mem_ctx, struct ldb_message *msg,
575 struct samr_Password **lm_pwd, struct samr_Password **nt_pwd)
577 struct samr_Password *lmPwdHash, *ntPwdHash;
578 if (nt_pwd) {
579 int num_nt;
580 num_nt = samdb_result_hashes(mem_ctx, msg, "unicodePwd", &ntPwdHash);
581 if (num_nt == 0) {
582 *nt_pwd = NULL;
583 } else if (num_nt > 1) {
584 return NT_STATUS_INTERNAL_DB_CORRUPTION;
585 } else {
586 *nt_pwd = &ntPwdHash[0];
589 if (lm_pwd) {
590 int num_lm;
591 num_lm = samdb_result_hashes(mem_ctx, msg, "dBCSPwd", &lmPwdHash);
592 if (num_lm == 0) {
593 *lm_pwd = NULL;
594 } else if (num_lm > 1) {
595 return NT_STATUS_INTERNAL_DB_CORRUPTION;
596 } else {
597 *lm_pwd = &lmPwdHash[0];
600 return NT_STATUS_OK;
604 pull a samr_LogonHours structutre from a result set.
606 struct samr_LogonHours samdb_result_logon_hours(TALLOC_CTX *mem_ctx, struct ldb_message *msg, const char *attr)
608 struct samr_LogonHours hours;
609 const int units_per_week = 168;
610 const struct ldb_val *val = ldb_msg_find_ldb_val(msg, attr);
611 ZERO_STRUCT(hours);
612 hours.bits = talloc_array(mem_ctx, uint8_t, units_per_week);
613 if (!hours.bits) {
614 return hours;
616 hours.units_per_week = units_per_week;
617 memset(hours.bits, 0xFF, units_per_week);
618 if (val) {
619 memcpy(hours.bits, val->data, MIN(val->length, units_per_week));
621 return hours;
625 pull a set of account_flags from a result set.
627 This requires that the attributes:
628 pwdLastSet
629 userAccountControl
630 be included in 'msg'
632 uint32_t samdb_result_acct_flags(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
633 struct ldb_message *msg, struct ldb_dn *domain_dn)
635 uint32_t userAccountControl = ldb_msg_find_attr_as_uint(msg, "userAccountControl", 0);
636 uint32_t acct_flags = samdb_uf2acb(userAccountControl);
637 NTTIME must_change_time;
638 NTTIME now;
640 must_change_time = samdb_result_force_password_change(sam_ctx, mem_ctx,
641 domain_dn, msg);
643 /* Test account expire time */
644 unix_to_nt_time(&now, time(NULL));
645 /* check for expired password */
646 if (must_change_time < now) {
647 acct_flags |= ACB_PW_EXPIRED;
649 return acct_flags;
653 /* Find an attribute, with a particular value */
655 /* The current callers of this function expect a very specific
656 * behaviour: In particular, objectClass subclass equivilance is not
657 * wanted. This means that we should not lookup the schema for the
658 * comparison function */
659 struct ldb_message_element *samdb_find_attribute(struct ldb_context *ldb,
660 const struct ldb_message *msg,
661 const char *name, const char *value)
663 int i;
664 struct ldb_message_element *el = ldb_msg_find_element(msg, name);
666 if (!el) {
667 return NULL;
670 for (i=0;i<el->num_values;i++) {
671 if (ldb_attr_cmp(value, (char *)el->values[i].data) == 0) {
672 return el;
676 return NULL;
679 int samdb_find_or_add_value(struct ldb_context *ldb, struct ldb_message *msg, const char *name, const char *set_value)
681 if (samdb_find_attribute(ldb, msg, name, set_value) == NULL) {
682 return samdb_msg_add_string(ldb, msg, msg, name, set_value);
684 return LDB_SUCCESS;
687 int samdb_find_or_add_attribute(struct ldb_context *ldb, struct ldb_message *msg, const char *name, const char *set_value)
689 struct ldb_message_element *el;
691 el = ldb_msg_find_element(msg, name);
692 if (el) {
693 return LDB_SUCCESS;
696 return samdb_msg_add_string(ldb, msg, msg, name, set_value);
702 add a string element to a message
704 int samdb_msg_add_string(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
705 const char *attr_name, const char *str)
707 char *s = talloc_strdup(mem_ctx, str);
708 char *a = talloc_strdup(mem_ctx, attr_name);
709 if (s == NULL || a == NULL) {
710 return LDB_ERR_OPERATIONS_ERROR;
712 return ldb_msg_add_string(msg, a, s);
716 add a dom_sid element to a message
718 int samdb_msg_add_dom_sid(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
719 const char *attr_name, struct dom_sid *sid)
721 struct ldb_val v;
722 enum ndr_err_code ndr_err;
724 ndr_err = ndr_push_struct_blob(&v, mem_ctx,
725 lp_iconv_convenience(ldb_get_opaque(sam_ldb, "loadparm")),
726 sid,
727 (ndr_push_flags_fn_t)ndr_push_dom_sid);
728 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
729 return -1;
731 return ldb_msg_add_value(msg, attr_name, &v, NULL);
736 add a delete element operation to a message
738 int samdb_msg_add_delete(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
739 const char *attr_name)
741 /* we use an empty replace rather than a delete, as it allows for
742 samdb_replace() to be used everywhere */
743 return ldb_msg_add_empty(msg, attr_name, LDB_FLAG_MOD_REPLACE, NULL);
747 add a add attribute value to a message
749 int samdb_msg_add_addval(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
750 const char *attr_name, const char *value)
752 struct ldb_message_element *el;
753 char *a, *v;
754 int ret;
755 a = talloc_strdup(mem_ctx, attr_name);
756 if (a == NULL)
757 return -1;
758 v = talloc_strdup(mem_ctx, value);
759 if (v == NULL)
760 return -1;
761 ret = ldb_msg_add_string(msg, a, v);
762 if (ret != 0)
763 return ret;
764 el = ldb_msg_find_element(msg, a);
765 if (el == NULL)
766 return -1;
767 el->flags = LDB_FLAG_MOD_ADD;
768 return 0;
772 add a delete attribute value to a message
774 int samdb_msg_add_delval(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
775 const char *attr_name, const char *value)
777 struct ldb_message_element *el;
778 char *a, *v;
779 int ret;
780 a = talloc_strdup(mem_ctx, attr_name);
781 if (a == NULL)
782 return -1;
783 v = talloc_strdup(mem_ctx, value);
784 if (v == NULL)
785 return -1;
786 ret = ldb_msg_add_string(msg, a, v);
787 if (ret != 0)
788 return ret;
789 el = ldb_msg_find_element(msg, a);
790 if (el == NULL)
791 return -1;
792 el->flags = LDB_FLAG_MOD_DELETE;
793 return 0;
797 add a int element to a message
799 int samdb_msg_add_int(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
800 const char *attr_name, int v)
802 const char *s = talloc_asprintf(mem_ctx, "%d", v);
803 return samdb_msg_add_string(sam_ldb, mem_ctx, msg, attr_name, s);
807 add a uint_t element to a message
809 int samdb_msg_add_uint(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
810 const char *attr_name, uint_t v)
812 const char *s = talloc_asprintf(mem_ctx, "%u", v);
813 return samdb_msg_add_string(sam_ldb, mem_ctx, msg, attr_name, s);
817 add a (signed) int64_t element to a message
819 int samdb_msg_add_int64(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
820 const char *attr_name, int64_t v)
822 const char *s = talloc_asprintf(mem_ctx, "%lld", (long long)v);
823 return samdb_msg_add_string(sam_ldb, mem_ctx, msg, attr_name, s);
827 add a uint64_t element to a message
829 int samdb_msg_add_uint64(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
830 const char *attr_name, uint64_t v)
832 const char *s = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)v);
833 return samdb_msg_add_string(sam_ldb, mem_ctx, msg, attr_name, s);
837 add a samr_Password element to a message
839 int samdb_msg_add_hash(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
840 const char *attr_name, struct samr_Password *hash)
842 struct ldb_val val;
843 val.data = talloc_memdup(mem_ctx, hash->hash, 16);
844 if (!val.data) {
845 return -1;
847 val.length = 16;
848 return ldb_msg_add_value(msg, attr_name, &val, NULL);
852 add a samr_Password array to a message
854 int samdb_msg_add_hashes(TALLOC_CTX *mem_ctx, struct ldb_message *msg,
855 const char *attr_name, struct samr_Password *hashes, uint_t count)
857 struct ldb_val val;
858 int i;
859 val.data = talloc_array_size(mem_ctx, 16, count);
860 val.length = count*16;
861 if (!val.data) {
862 return -1;
864 for (i=0;i<count;i++) {
865 memcpy(i*16 + (char *)val.data, hashes[i].hash, 16);
867 return ldb_msg_add_value(msg, attr_name, &val, NULL);
871 add a acct_flags element to a message
873 int samdb_msg_add_acct_flags(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
874 const char *attr_name, uint32_t v)
876 return samdb_msg_add_uint(sam_ldb, mem_ctx, msg, attr_name, samdb_acb2uf(v));
880 add a logon_hours element to a message
882 int samdb_msg_add_logon_hours(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
883 const char *attr_name, struct samr_LogonHours *hours)
885 struct ldb_val val;
886 val.length = hours->units_per_week / 8;
887 val.data = hours->bits;
888 return ldb_msg_add_value(msg, attr_name, &val, NULL);
892 add a general value element to a message
894 int samdb_msg_add_value(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
895 const char *attr_name, const struct ldb_val *val)
897 return ldb_msg_add_value(msg, attr_name, val, NULL);
901 sets a general value element to a message
903 int samdb_msg_set_value(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
904 const char *attr_name, const struct ldb_val *val)
906 struct ldb_message_element *el;
908 el = ldb_msg_find_element(msg, attr_name);
909 if (el) {
910 el->num_values = 0;
912 return ldb_msg_add_value(msg, attr_name, val, NULL);
916 set a string element in a message
918 int samdb_msg_set_string(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
919 const char *attr_name, const char *str)
921 struct ldb_message_element *el;
923 el = ldb_msg_find_element(msg, attr_name);
924 if (el) {
925 el->num_values = 0;
927 return samdb_msg_add_string(sam_ldb, mem_ctx, msg, attr_name, str);
931 replace elements in a record
933 int samdb_replace(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg)
935 int i;
937 /* mark all the message elements as LDB_FLAG_MOD_REPLACE */
938 for (i=0;i<msg->num_elements;i++) {
939 msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
942 /* modify the samdb record */
943 return ldb_modify(sam_ldb, msg);
947 return a default security descriptor
949 struct security_descriptor *samdb_default_security_descriptor(TALLOC_CTX *mem_ctx)
951 struct security_descriptor *sd;
953 sd = security_descriptor_initialise(mem_ctx);
955 return sd;
958 struct ldb_dn *samdb_base_dn(struct ldb_context *sam_ctx)
960 return ldb_get_default_basedn(sam_ctx);
963 struct ldb_dn *samdb_config_dn(struct ldb_context *sam_ctx)
965 return ldb_get_config_basedn(sam_ctx);
968 struct ldb_dn *samdb_schema_dn(struct ldb_context *sam_ctx)
970 return ldb_get_schema_basedn(sam_ctx);
973 struct ldb_dn *samdb_root_dn(struct ldb_context *sam_ctx)
975 return ldb_get_root_basedn(sam_ctx);
978 struct ldb_dn *samdb_partitions_dn(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx)
980 struct ldb_dn *new_dn;
982 new_dn = ldb_dn_copy(mem_ctx, samdb_config_dn(sam_ctx));
983 if ( ! ldb_dn_add_child_fmt(new_dn, "CN=Partitions")) {
984 talloc_free(new_dn);
985 return NULL;
987 return new_dn;
990 struct ldb_dn *samdb_sites_dn(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx)
992 struct ldb_dn *new_dn;
994 new_dn = ldb_dn_copy(mem_ctx, samdb_config_dn(sam_ctx));
995 if ( ! ldb_dn_add_child_fmt(new_dn, "CN=Sites")) {
996 talloc_free(new_dn);
997 return NULL;
999 return new_dn;
1003 work out the domain sid for the current open ldb
1005 const struct dom_sid *samdb_domain_sid(struct ldb_context *ldb)
1007 TALLOC_CTX *tmp_ctx;
1008 const struct dom_sid *domain_sid;
1009 const char *attrs[] = {
1010 "objectSid",
1011 NULL
1013 struct ldb_result *res;
1014 int ret;
1016 /* see if we have a cached copy */
1017 domain_sid = (struct dom_sid *)ldb_get_opaque(ldb, "cache.domain_sid");
1018 if (domain_sid) {
1019 return domain_sid;
1022 tmp_ctx = talloc_new(ldb);
1023 if (tmp_ctx == NULL) {
1024 goto failed;
1027 ret = ldb_search(ldb, tmp_ctx, &res, ldb_get_default_basedn(ldb), LDB_SCOPE_BASE, attrs, "objectSid=*");
1029 if (ret != LDB_SUCCESS) {
1030 goto failed;
1033 if (res->count != 1) {
1034 goto failed;
1037 domain_sid = samdb_result_dom_sid(tmp_ctx, res->msgs[0], "objectSid");
1038 if (domain_sid == NULL) {
1039 goto failed;
1042 /* cache the domain_sid in the ldb */
1043 if (ldb_set_opaque(ldb, "cache.domain_sid", discard_const_p(struct dom_sid, domain_sid)) != LDB_SUCCESS) {
1044 goto failed;
1047 talloc_steal(ldb, domain_sid);
1048 talloc_free(tmp_ctx);
1050 return domain_sid;
1052 failed:
1053 DEBUG(1,("Failed to find domain_sid for open ldb\n"));
1054 talloc_free(tmp_ctx);
1055 return NULL;
1058 bool samdb_set_domain_sid(struct ldb_context *ldb, const struct dom_sid *dom_sid_in)
1060 TALLOC_CTX *tmp_ctx;
1061 struct dom_sid *dom_sid_new;
1062 struct dom_sid *dom_sid_old;
1064 /* see if we have a cached copy */
1065 dom_sid_old = talloc_get_type(ldb_get_opaque(ldb,
1066 "cache.domain_sid"), struct dom_sid);
1068 tmp_ctx = talloc_new(ldb);
1069 if (tmp_ctx == NULL) {
1070 goto failed;
1073 dom_sid_new = dom_sid_dup(tmp_ctx, dom_sid_in);
1074 if (!dom_sid_new) {
1075 goto failed;
1078 /* cache the domain_sid in the ldb */
1079 if (ldb_set_opaque(ldb, "cache.domain_sid", dom_sid_new) != LDB_SUCCESS) {
1080 goto failed;
1083 talloc_steal(ldb, dom_sid_new);
1084 talloc_free(tmp_ctx);
1085 talloc_free(dom_sid_old);
1087 return true;
1089 failed:
1090 DEBUG(1,("Failed to set our own cached domain SID in the ldb!\n"));
1091 talloc_free(tmp_ctx);
1092 return false;
1095 /* Obtain the short name of the flexible single master operator
1096 * (FSMO), such as the PDC Emulator */
1097 const char *samdb_result_fsmo_name(struct ldb_context *ldb, TALLOC_CTX *mem_ctx, const struct ldb_message *msg,
1098 const char *attr)
1100 /* Format is cn=NTDS Settings,cn=<NETBIOS name of FSMO>,.... */
1101 struct ldb_dn *fsmo_dn = ldb_msg_find_attr_as_dn(ldb, mem_ctx, msg, attr);
1102 const struct ldb_val *val = ldb_dn_get_component_val(fsmo_dn, 1);
1103 const char *name = ldb_dn_get_component_name(fsmo_dn, 1);
1105 if (!name || (ldb_attr_cmp(name, "cn") != 0)) {
1106 /* Ensure this matches the format. This gives us a
1107 * bit more confidence that a 'cn' value will be a
1108 * ascii string */
1109 return NULL;
1111 if (val) {
1112 return (char *)val->data;
1114 return NULL;
1118 work out the ntds settings dn for the current open ldb
1120 struct ldb_dn *samdb_ntds_settings_dn(struct ldb_context *ldb)
1122 TALLOC_CTX *tmp_ctx;
1123 const char *root_attrs[] = { "dsServiceName", NULL };
1124 int ret;
1125 struct ldb_result *root_res;
1126 struct ldb_dn *settings_dn;
1128 /* see if we have a cached copy */
1129 settings_dn = (struct ldb_dn *)ldb_get_opaque(ldb, "cache.settings_dn");
1130 if (settings_dn) {
1131 return settings_dn;
1134 tmp_ctx = talloc_new(ldb);
1135 if (tmp_ctx == NULL) {
1136 goto failed;
1140 ret = ldb_search(ldb, tmp_ctx, &root_res, ldb_dn_new(tmp_ctx, ldb, ""), LDB_SCOPE_BASE, root_attrs, NULL);
1141 if (ret) {
1142 DEBUG(1,("Searching for dsServiceName in rootDSE failed: %s\n",
1143 ldb_errstring(ldb)));
1144 goto failed;
1147 if (root_res->count != 1) {
1148 goto failed;
1151 settings_dn = ldb_msg_find_attr_as_dn(ldb, tmp_ctx, root_res->msgs[0], "dsServiceName");
1153 /* cache the domain_sid in the ldb */
1154 if (ldb_set_opaque(ldb, "cache.settings_dn", settings_dn) != LDB_SUCCESS) {
1155 goto failed;
1158 talloc_steal(ldb, settings_dn);
1159 talloc_free(tmp_ctx);
1161 return settings_dn;
1163 failed:
1164 DEBUG(1,("Failed to find our own NTDS Settings DN in the ldb!\n"));
1165 talloc_free(tmp_ctx);
1166 return NULL;
1170 work out the ntds settings invocationId for the current open ldb
1172 const struct GUID *samdb_ntds_invocation_id(struct ldb_context *ldb)
1174 TALLOC_CTX *tmp_ctx;
1175 const char *attrs[] = { "invocationId", NULL };
1176 int ret;
1177 struct ldb_result *res;
1178 struct GUID *invocation_id;
1180 /* see if we have a cached copy */
1181 invocation_id = (struct GUID *)ldb_get_opaque(ldb, "cache.invocation_id");
1182 if (invocation_id) {
1183 return invocation_id;
1186 tmp_ctx = talloc_new(ldb);
1187 if (tmp_ctx == NULL) {
1188 goto failed;
1191 ret = ldb_search(ldb, tmp_ctx, &res, samdb_ntds_settings_dn(ldb), LDB_SCOPE_BASE, attrs, NULL);
1192 if (ret) {
1193 goto failed;
1196 if (res->count != 1) {
1197 goto failed;
1200 invocation_id = talloc(tmp_ctx, struct GUID);
1201 if (!invocation_id) {
1202 goto failed;
1205 *invocation_id = samdb_result_guid(res->msgs[0], "invocationId");
1207 /* cache the domain_sid in the ldb */
1208 if (ldb_set_opaque(ldb, "cache.invocation_id", invocation_id) != LDB_SUCCESS) {
1209 goto failed;
1212 talloc_steal(ldb, invocation_id);
1213 talloc_free(tmp_ctx);
1215 return invocation_id;
1217 failed:
1218 DEBUG(1,("Failed to find our own NTDS Settings invocationId in the ldb!\n"));
1219 talloc_free(tmp_ctx);
1220 return NULL;
1223 bool samdb_set_ntds_invocation_id(struct ldb_context *ldb, const struct GUID *invocation_id_in)
1225 TALLOC_CTX *tmp_ctx;
1226 struct GUID *invocation_id_new;
1227 struct GUID *invocation_id_old;
1229 /* see if we have a cached copy */
1230 invocation_id_old = (struct GUID *)ldb_get_opaque(ldb,
1231 "cache.invocation_id");
1233 tmp_ctx = talloc_new(ldb);
1234 if (tmp_ctx == NULL) {
1235 goto failed;
1238 invocation_id_new = talloc(tmp_ctx, struct GUID);
1239 if (!invocation_id_new) {
1240 goto failed;
1243 *invocation_id_new = *invocation_id_in;
1245 /* cache the domain_sid in the ldb */
1246 if (ldb_set_opaque(ldb, "cache.invocation_id", invocation_id_new) != LDB_SUCCESS) {
1247 goto failed;
1250 talloc_steal(ldb, invocation_id_new);
1251 talloc_free(tmp_ctx);
1252 talloc_free(invocation_id_old);
1254 return true;
1256 failed:
1257 DEBUG(1,("Failed to set our own cached invocationId in the ldb!\n"));
1258 talloc_free(tmp_ctx);
1259 return false;
1263 work out the ntds settings objectGUID for the current open ldb
1265 const struct GUID *samdb_ntds_objectGUID(struct ldb_context *ldb)
1267 TALLOC_CTX *tmp_ctx;
1268 const char *attrs[] = { "objectGUID", NULL };
1269 int ret;
1270 struct ldb_result *res;
1271 struct GUID *ntds_guid;
1273 /* see if we have a cached copy */
1274 ntds_guid = (struct GUID *)ldb_get_opaque(ldb, "cache.ntds_guid");
1275 if (ntds_guid) {
1276 return ntds_guid;
1279 tmp_ctx = talloc_new(ldb);
1280 if (tmp_ctx == NULL) {
1281 goto failed;
1284 ret = ldb_search(ldb, tmp_ctx, &res, samdb_ntds_settings_dn(ldb), LDB_SCOPE_BASE, attrs, NULL);
1285 if (ret) {
1286 goto failed;
1289 if (res->count != 1) {
1290 goto failed;
1293 ntds_guid = talloc(tmp_ctx, struct GUID);
1294 if (!ntds_guid) {
1295 goto failed;
1298 *ntds_guid = samdb_result_guid(res->msgs[0], "objectGUID");
1300 /* cache the domain_sid in the ldb */
1301 if (ldb_set_opaque(ldb, "cache.ntds_guid", ntds_guid) != LDB_SUCCESS) {
1302 goto failed;
1305 talloc_steal(ldb, ntds_guid);
1306 talloc_free(tmp_ctx);
1308 return ntds_guid;
1310 failed:
1311 DEBUG(1,("Failed to find our own NTDS Settings objectGUID in the ldb!\n"));
1312 talloc_free(tmp_ctx);
1313 return NULL;
1316 bool samdb_set_ntds_objectGUID(struct ldb_context *ldb, const struct GUID *ntds_guid_in)
1318 TALLOC_CTX *tmp_ctx;
1319 struct GUID *ntds_guid_new;
1320 struct GUID *ntds_guid_old;
1322 /* see if we have a cached copy */
1323 ntds_guid_old = (struct GUID *)ldb_get_opaque(ldb, "cache.ntds_guid");
1325 tmp_ctx = talloc_new(ldb);
1326 if (tmp_ctx == NULL) {
1327 goto failed;
1330 ntds_guid_new = talloc(tmp_ctx, struct GUID);
1331 if (!ntds_guid_new) {
1332 goto failed;
1335 *ntds_guid_new = *ntds_guid_in;
1337 /* cache the domain_sid in the ldb */
1338 if (ldb_set_opaque(ldb, "cache.ntds_guid", ntds_guid_new) != LDB_SUCCESS) {
1339 goto failed;
1342 talloc_steal(ldb, ntds_guid_new);
1343 talloc_free(tmp_ctx);
1344 talloc_free(ntds_guid_old);
1346 return true;
1348 failed:
1349 DEBUG(1,("Failed to set our own cached invocationId in the ldb!\n"));
1350 talloc_free(tmp_ctx);
1351 return false;
1355 work out the server dn for the current open ldb
1357 struct ldb_dn *samdb_server_dn(struct ldb_context *ldb, TALLOC_CTX *mem_ctx)
1359 return ldb_dn_get_parent(mem_ctx, samdb_ntds_settings_dn(ldb));
1363 work out the server dn for the current open ldb
1365 struct ldb_dn *samdb_server_site_dn(struct ldb_context *ldb, TALLOC_CTX *mem_ctx)
1367 struct ldb_dn *server_dn;
1368 struct ldb_dn *server_site_dn;
1370 server_dn = samdb_server_dn(ldb, mem_ctx);
1371 if (!server_dn) return NULL;
1373 server_site_dn = ldb_dn_get_parent(mem_ctx, server_dn);
1375 talloc_free(server_dn);
1376 return server_site_dn;
1380 work out if we are the PDC for the domain of the current open ldb
1382 bool samdb_is_pdc(struct ldb_context *ldb)
1384 const char *dom_attrs[] = { "fSMORoleOwner", NULL };
1385 int ret;
1386 struct ldb_result *dom_res;
1387 TALLOC_CTX *tmp_ctx;
1388 bool is_pdc;
1389 struct ldb_dn *pdc;
1391 tmp_ctx = talloc_new(ldb);
1392 if (tmp_ctx == NULL) {
1393 DEBUG(1, ("talloc_new failed in samdb_is_pdc"));
1394 return false;
1397 ret = ldb_search(ldb, tmp_ctx, &dom_res, ldb_get_default_basedn(ldb), LDB_SCOPE_BASE, dom_attrs, NULL);
1398 if (ret) {
1399 DEBUG(1,("Searching for fSMORoleOwner in %s failed: %s\n",
1400 ldb_dn_get_linearized(ldb_get_default_basedn(ldb)),
1401 ldb_errstring(ldb)));
1402 goto failed;
1404 if (dom_res->count != 1) {
1405 goto failed;
1408 pdc = ldb_msg_find_attr_as_dn(ldb, tmp_ctx, dom_res->msgs[0], "fSMORoleOwner");
1410 if (ldb_dn_compare(samdb_ntds_settings_dn(ldb), pdc) == 0) {
1411 is_pdc = true;
1412 } else {
1413 is_pdc = false;
1416 talloc_free(tmp_ctx);
1418 return is_pdc;
1420 failed:
1421 DEBUG(1,("Failed to find if we are the PDC for this ldb\n"));
1422 talloc_free(tmp_ctx);
1423 return false;
1427 work out if we are a Global Catalog server for the domain of the current open ldb
1429 bool samdb_is_gc(struct ldb_context *ldb)
1431 const char *attrs[] = { "options", NULL };
1432 int ret, options;
1433 struct ldb_result *res;
1434 TALLOC_CTX *tmp_ctx;
1436 tmp_ctx = talloc_new(ldb);
1437 if (tmp_ctx == NULL) {
1438 DEBUG(1, ("talloc_new failed in samdb_is_pdc"));
1439 return false;
1442 /* Query cn=ntds settings,.... */
1443 ret = ldb_search(ldb, tmp_ctx, &res, samdb_ntds_settings_dn(ldb), LDB_SCOPE_BASE, attrs, NULL);
1444 if (ret) {
1445 talloc_free(tmp_ctx);
1446 return false;
1448 if (res->count != 1) {
1449 talloc_free(tmp_ctx);
1450 return false;
1453 options = ldb_msg_find_attr_as_int(res->msgs[0], "options", 0);
1454 talloc_free(tmp_ctx);
1456 /* if options attribute has the 0x00000001 flag set, then enable the global catlog */
1457 if (options & 0x000000001) {
1458 return true;
1460 return false;
1463 /* Find a domain object in the parents of a particular DN. */
1464 int samdb_search_for_parent_domain(struct ldb_context *ldb, TALLOC_CTX *mem_ctx, struct ldb_dn *dn,
1465 struct ldb_dn **parent_dn, const char **errstring)
1467 TALLOC_CTX *local_ctx;
1468 struct ldb_dn *sdn = dn;
1469 struct ldb_result *res = NULL;
1470 int ret = 0;
1471 const char *attrs[] = { NULL };
1473 local_ctx = talloc_new(mem_ctx);
1474 if (local_ctx == NULL) return LDB_ERR_OPERATIONS_ERROR;
1476 while ((sdn = ldb_dn_get_parent(local_ctx, sdn))) {
1477 ret = ldb_search(ldb, local_ctx, &res, sdn, LDB_SCOPE_BASE, attrs,
1478 "(|(|(objectClass=domain)(objectClass=builtinDomain))(objectClass=samba4LocalDomain))");
1479 if (ret == LDB_SUCCESS) {
1480 if (res->count == 1) {
1481 break;
1483 } else {
1484 break;
1488 if (ret != LDB_SUCCESS) {
1489 *errstring = talloc_asprintf(mem_ctx, "Error searching for parent domain of %s, failed searching for %s: %s",
1490 ldb_dn_get_linearized(dn),
1491 ldb_dn_get_linearized(sdn),
1492 ldb_errstring(ldb));
1493 talloc_free(local_ctx);
1494 return ret;
1496 if (res->count != 1) {
1497 *errstring = talloc_asprintf(mem_ctx, "Invalid dn (%s), not child of a domain object",
1498 ldb_dn_get_linearized(dn));
1499 talloc_free(local_ctx);
1500 return LDB_ERR_CONSTRAINT_VIOLATION;
1503 *parent_dn = talloc_steal(mem_ctx, res->msgs[0]->dn);
1504 talloc_free(local_ctx);
1505 return ret;
1509 check that a password is sufficiently complex
1511 static bool samdb_password_complexity_ok(const char *pass)
1513 return check_password_quality(pass);
1519 set the user password using plaintext, obeying any user or domain
1520 password restrictions
1522 note that this function doesn't actually store the result in the
1523 database, it just fills in the "mod" structure with ldb modify
1524 elements to setup the correct change when samdb_replace() is
1525 called. This allows the caller to combine the change with other
1526 changes (as is needed by some of the set user info levels)
1528 The caller should probably have a transaction wrapping this
1530 NTSTATUS samdb_set_password(struct ldb_context *ctx, TALLOC_CTX *mem_ctx,
1531 struct ldb_dn *user_dn,
1532 struct ldb_dn *domain_dn,
1533 struct ldb_message *mod,
1534 const char *new_pass,
1535 struct samr_Password *lmNewHash,
1536 struct samr_Password *ntNewHash,
1537 bool user_change,
1538 enum samr_RejectReason *reject_reason,
1539 struct samr_DomInfo1 **_dominfo)
1541 const char * const user_attrs[] = { "userAccountControl", "lmPwdHistory",
1542 "ntPwdHistory",
1543 "dBCSPwd", "unicodePwd",
1544 "objectSid",
1545 "pwdLastSet", NULL };
1546 const char * const domain_attrs[] = { "pwdProperties", "pwdHistoryLength",
1547 "maxPwdAge", "minPwdAge",
1548 "minPwdLength", NULL };
1549 NTTIME pwdLastSet;
1550 int64_t minPwdAge;
1551 uint_t minPwdLength, pwdProperties, pwdHistoryLength;
1552 uint_t userAccountControl;
1553 struct samr_Password *sambaLMPwdHistory, *sambaNTPwdHistory, *lmPwdHash, *ntPwdHash;
1554 struct samr_Password local_lmNewHash, local_ntNewHash;
1555 int sambaLMPwdHistory_len, sambaNTPwdHistory_len;
1556 struct dom_sid *domain_sid;
1557 struct ldb_message **res;
1558 bool restrictions;
1559 int count;
1560 time_t now = time(NULL);
1561 NTTIME now_nt;
1562 int i;
1564 /* we need to know the time to compute password age */
1565 unix_to_nt_time(&now_nt, now);
1567 /* pull all the user parameters */
1568 count = gendb_search_dn(ctx, mem_ctx, user_dn, &res, user_attrs);
1569 if (count != 1) {
1570 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1572 userAccountControl = samdb_result_uint(res[0], "userAccountControl", 0);
1573 sambaLMPwdHistory_len = samdb_result_hashes(mem_ctx, res[0],
1574 "lmPwdHistory", &sambaLMPwdHistory);
1575 sambaNTPwdHistory_len = samdb_result_hashes(mem_ctx, res[0],
1576 "ntPwdHistory", &sambaNTPwdHistory);
1577 lmPwdHash = samdb_result_hash(mem_ctx, res[0], "dBCSPwd");
1578 ntPwdHash = samdb_result_hash(mem_ctx, res[0], "unicodePwd");
1579 pwdLastSet = samdb_result_uint64(res[0], "pwdLastSet", 0);
1581 /* Only non-trust accounts have restrictions (possibly this
1582 * test is the wrong way around, but I like to be restrictive
1583 * if possible */
1584 restrictions = !(userAccountControl & (UF_INTERDOMAIN_TRUST_ACCOUNT
1585 |UF_WORKSTATION_TRUST_ACCOUNT
1586 |UF_SERVER_TRUST_ACCOUNT));
1588 if (domain_dn) {
1589 /* pull the domain parameters */
1590 count = gendb_search_dn(ctx, mem_ctx, domain_dn, &res, domain_attrs);
1591 if (count != 1) {
1592 DEBUG(2, ("samdb_set_password: Domain DN %s is invalid, for user %s\n",
1593 ldb_dn_get_linearized(domain_dn),
1594 ldb_dn_get_linearized(user_dn)));
1595 return NT_STATUS_NO_SUCH_DOMAIN;
1597 } else {
1598 /* work out the domain sid, and pull the domain from there */
1599 domain_sid = samdb_result_sid_prefix(mem_ctx, res[0], "objectSid");
1600 if (domain_sid == NULL) {
1601 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1604 count = gendb_search(ctx, mem_ctx, NULL, &res, domain_attrs,
1605 "(objectSid=%s)",
1606 ldap_encode_ndr_dom_sid(mem_ctx, domain_sid));
1607 if (count != 1) {
1608 DEBUG(2, ("samdb_set_password: Could not find domain to match SID: %s, for user %s\n",
1609 dom_sid_string(mem_ctx, domain_sid),
1610 ldb_dn_get_linearized(user_dn)));
1611 return NT_STATUS_NO_SUCH_DOMAIN;
1615 pwdProperties = samdb_result_uint(res[0], "pwdProperties", 0);
1616 pwdHistoryLength = samdb_result_uint(res[0], "pwdHistoryLength", 0);
1617 minPwdLength = samdb_result_uint(res[0], "minPwdLength", 0);
1618 minPwdAge = samdb_result_int64(res[0], "minPwdAge", 0);
1620 if (_dominfo) {
1621 struct samr_DomInfo1 *dominfo;
1622 /* on failure we need to fill in the reject reasons */
1623 dominfo = talloc(mem_ctx, struct samr_DomInfo1);
1624 if (dominfo == NULL) {
1625 return NT_STATUS_NO_MEMORY;
1627 dominfo->min_password_length = minPwdLength;
1628 dominfo->password_properties = pwdProperties;
1629 dominfo->password_history_length = pwdHistoryLength;
1630 dominfo->max_password_age = minPwdAge;
1631 dominfo->min_password_age = minPwdAge;
1632 *_dominfo = dominfo;
1635 if (restrictions && new_pass) {
1637 /* check the various password restrictions */
1638 if (restrictions && minPwdLength > strlen_m(new_pass)) {
1639 if (reject_reason) {
1640 *reject_reason = SAMR_REJECT_TOO_SHORT;
1642 return NT_STATUS_PASSWORD_RESTRICTION;
1645 /* possibly check password complexity */
1646 if (restrictions && pwdProperties & DOMAIN_PASSWORD_COMPLEX &&
1647 !samdb_password_complexity_ok(new_pass)) {
1648 if (reject_reason) {
1649 *reject_reason = SAMR_REJECT_COMPLEXITY;
1651 return NT_STATUS_PASSWORD_RESTRICTION;
1654 /* compute the new nt and lm hashes */
1655 if (E_deshash(new_pass, local_lmNewHash.hash)) {
1656 lmNewHash = &local_lmNewHash;
1658 if (!E_md4hash(new_pass, local_ntNewHash.hash)) {
1659 /* If we can't convert this password to UCS2, then we should not accept it */
1660 if (reject_reason) {
1661 *reject_reason = SAMR_REJECT_OTHER;
1663 return NT_STATUS_PASSWORD_RESTRICTION;
1665 ntNewHash = &local_ntNewHash;
1668 if (user_change) {
1669 /* are all password changes disallowed? */
1670 if (pwdProperties & DOMAIN_REFUSE_PASSWORD_CHANGE) {
1671 if (reject_reason) {
1672 *reject_reason = SAMR_REJECT_OTHER;
1674 return NT_STATUS_PASSWORD_RESTRICTION;
1677 /* can this user change password? */
1678 if (userAccountControl & UF_PASSWD_CANT_CHANGE) {
1679 if (reject_reason) {
1680 *reject_reason = SAMR_REJECT_OTHER;
1682 return NT_STATUS_PASSWORD_RESTRICTION;
1685 /* yes, this is a minus. The ages are in negative 100nsec units! */
1686 if (pwdLastSet - minPwdAge > now_nt) {
1687 if (reject_reason) {
1688 *reject_reason = SAMR_REJECT_OTHER;
1690 return NT_STATUS_PASSWORD_RESTRICTION;
1693 /* check the immediately past password */
1694 if (pwdHistoryLength > 0) {
1695 if (lmNewHash && lmPwdHash && memcmp(lmNewHash->hash, lmPwdHash->hash, 16) == 0) {
1696 if (reject_reason) {
1697 *reject_reason = SAMR_REJECT_IN_HISTORY;
1699 return NT_STATUS_PASSWORD_RESTRICTION;
1701 if (ntNewHash && ntPwdHash && memcmp(ntNewHash->hash, ntPwdHash->hash, 16) == 0) {
1702 if (reject_reason) {
1703 *reject_reason = SAMR_REJECT_IN_HISTORY;
1705 return NT_STATUS_PASSWORD_RESTRICTION;
1709 /* check the password history */
1710 sambaLMPwdHistory_len = MIN(sambaLMPwdHistory_len, pwdHistoryLength);
1711 sambaNTPwdHistory_len = MIN(sambaNTPwdHistory_len, pwdHistoryLength);
1713 for (i=0; lmNewHash && i<sambaLMPwdHistory_len;i++) {
1714 if (memcmp(lmNewHash->hash, sambaLMPwdHistory[i].hash, 16) == 0) {
1715 if (reject_reason) {
1716 *reject_reason = SAMR_REJECT_IN_HISTORY;
1718 return NT_STATUS_PASSWORD_RESTRICTION;
1721 for (i=0; ntNewHash && i<sambaNTPwdHistory_len;i++) {
1722 if (memcmp(ntNewHash->hash, sambaNTPwdHistory[i].hash, 16) == 0) {
1723 if (reject_reason) {
1724 *reject_reason = SAMR_REJECT_IN_HISTORY;
1726 return NT_STATUS_PASSWORD_RESTRICTION;
1731 #define CHECK_RET(x) do { if (x != 0) return NT_STATUS_NO_MEMORY; } while(0)
1733 /* the password is acceptable. Start forming the new fields */
1734 if (new_pass) {
1735 /* if we know the cleartext, then only set it.
1736 * Modules in ldb will set all the appropriate
1737 * hashes */
1738 CHECK_RET(samdb_msg_add_string(ctx, mem_ctx, mod,
1739 "userPassword", new_pass));
1740 } else {
1741 /* We don't have the cleartext, so delete the old one
1742 * and set what we have of the hashes */
1743 CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "userPassword"));
1745 if (lmNewHash) {
1746 CHECK_RET(samdb_msg_add_hash(ctx, mem_ctx, mod, "dBCSPwd", lmNewHash));
1747 } else {
1748 CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "dBCSPwd"));
1751 if (ntNewHash) {
1752 CHECK_RET(samdb_msg_add_hash(ctx, mem_ctx, mod, "unicodePwd", ntNewHash));
1753 } else {
1754 CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "unicodePwd"));
1758 return NT_STATUS_OK;
1763 set the user password using plaintext, obeying any user or domain
1764 password restrictions
1766 This wrapper function takes a SID as input, rather than a user DN,
1767 and actually performs the password change
1770 NTSTATUS samdb_set_password_sid(struct ldb_context *ctx, TALLOC_CTX *mem_ctx,
1771 const struct dom_sid *user_sid,
1772 const char *new_pass,
1773 struct samr_Password *lmNewHash,
1774 struct samr_Password *ntNewHash,
1775 bool user_change,
1776 enum samr_RejectReason *reject_reason,
1777 struct samr_DomInfo1 **_dominfo)
1779 NTSTATUS nt_status;
1780 struct ldb_dn *user_dn;
1781 struct ldb_message *msg;
1782 int ret;
1784 ret = ldb_transaction_start(ctx);
1785 if (ret) {
1786 DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(ctx)));
1787 return NT_STATUS_TRANSACTION_ABORTED;
1790 user_dn = samdb_search_dn(ctx, mem_ctx, NULL,
1791 "(&(objectSid=%s)(objectClass=user))",
1792 ldap_encode_ndr_dom_sid(mem_ctx, user_sid));
1793 if (!user_dn) {
1794 ldb_transaction_cancel(ctx);
1795 DEBUG(3, ("samdb_set_password_sid: SID %s not found in samdb, returning NO_SUCH_USER\n",
1796 dom_sid_string(mem_ctx, user_sid)));
1797 return NT_STATUS_NO_SUCH_USER;
1800 msg = ldb_msg_new(mem_ctx);
1801 if (msg == NULL) {
1802 ldb_transaction_cancel(ctx);
1803 return NT_STATUS_NO_MEMORY;
1806 msg->dn = ldb_dn_copy(msg, user_dn);
1807 if (!msg->dn) {
1808 ldb_transaction_cancel(ctx);
1809 return NT_STATUS_NO_MEMORY;
1812 nt_status = samdb_set_password(ctx, mem_ctx,
1813 user_dn, NULL,
1814 msg, new_pass,
1815 lmNewHash, ntNewHash,
1816 user_change, /* This is a password set, not change */
1817 reject_reason, _dominfo);
1818 if (!NT_STATUS_IS_OK(nt_status)) {
1819 ldb_transaction_cancel(ctx);
1820 return nt_status;
1823 /* modify the samdb record */
1824 ret = samdb_replace(ctx, mem_ctx, msg);
1825 if (ret != 0) {
1826 ldb_transaction_cancel(ctx);
1827 return NT_STATUS_ACCESS_DENIED;
1830 ret = ldb_transaction_commit(ctx);
1831 if (ret != 0) {
1832 DEBUG(0,("Failed to commit transaction to change password on %s: %s\n",
1833 ldb_dn_get_linearized(msg->dn),
1834 ldb_errstring(ctx)));
1835 return NT_STATUS_TRANSACTION_ABORTED;
1837 return NT_STATUS_OK;
1842 NTSTATUS samdb_create_foreign_security_principal(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
1843 struct dom_sid *sid, struct ldb_dn **ret_dn)
1845 struct ldb_message *msg;
1846 struct ldb_dn *basedn;
1847 const char *sidstr;
1848 int ret;
1850 sidstr = dom_sid_string(mem_ctx, sid);
1851 NT_STATUS_HAVE_NO_MEMORY(sidstr);
1853 /* We might have to create a ForeignSecurityPrincipal, even if this user
1854 * is in our own domain */
1856 msg = ldb_msg_new(mem_ctx);
1857 if (msg == NULL) {
1858 return NT_STATUS_NO_MEMORY;
1861 /* TODO: Hmmm. This feels wrong. How do I find the base dn to
1862 * put the ForeignSecurityPrincipals? d_state->domain_dn does
1863 * not work, this is wrong for the Builtin domain, there's no
1864 * cn=For...,cn=Builtin,dc={BASEDN}. -- vl
1867 basedn = samdb_search_dn(sam_ctx, mem_ctx, NULL,
1868 "(&(objectClass=container)(cn=ForeignSecurityPrincipals))");
1870 if (basedn == NULL) {
1871 DEBUG(0, ("Failed to find DN for "
1872 "ForeignSecurityPrincipal container\n"));
1873 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1876 /* add core elements to the ldb_message for the alias */
1877 msg->dn = ldb_dn_copy(mem_ctx, basedn);
1878 if ( ! ldb_dn_add_child_fmt(msg->dn, "CN=%s", sidstr))
1879 return NT_STATUS_NO_MEMORY;
1881 samdb_msg_add_string(sam_ctx, mem_ctx, msg,
1882 "objectClass",
1883 "foreignSecurityPrincipal");
1885 /* create the alias */
1886 ret = ldb_add(sam_ctx, msg);
1887 if (ret != 0) {
1888 DEBUG(0,("Failed to create foreignSecurityPrincipal "
1889 "record %s: %s\n",
1890 ldb_dn_get_linearized(msg->dn),
1891 ldb_errstring(sam_ctx)));
1892 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1894 *ret_dn = msg->dn;
1895 return NT_STATUS_OK;
1900 Find the DN of a domain, assuming it to be a dotted.dns name
1903 struct ldb_dn *samdb_dns_domain_to_dn(struct ldb_context *ldb, TALLOC_CTX *mem_ctx, const char *dns_domain)
1905 int i;
1906 TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
1907 const char *binary_encoded;
1908 const char **split_realm;
1909 struct ldb_dn *dn;
1911 if (!tmp_ctx) {
1912 return NULL;
1915 split_realm = str_list_make(tmp_ctx, dns_domain, ".");
1916 if (!split_realm) {
1917 talloc_free(tmp_ctx);
1918 return NULL;
1920 dn = ldb_dn_new(mem_ctx, ldb, NULL);
1921 for (i=0; split_realm[i]; i++) {
1922 binary_encoded = ldb_binary_encode_string(tmp_ctx, split_realm[i]);
1923 if (!ldb_dn_add_base_fmt(dn, "dc=%s", binary_encoded)) {
1924 DEBUG(2, ("Failed to add dc=%s element to DN %s\n",
1925 binary_encoded, ldb_dn_get_linearized(dn)));
1926 talloc_free(tmp_ctx);
1927 return NULL;
1930 if (!ldb_dn_validate(dn)) {
1931 DEBUG(2, ("Failed to validated DN %s\n",
1932 ldb_dn_get_linearized(dn)));
1933 return NULL;
1935 return dn;
1938 Find the DN of a domain, be it the netbios or DNS name
1941 struct ldb_dn *samdb_domain_to_dn(struct ldb_context *ldb, TALLOC_CTX *mem_ctx,
1942 const char *domain_name)
1944 const char * const domain_ref_attrs[] = {
1945 "ncName", NULL
1947 const char * const domain_ref2_attrs[] = {
1948 NULL
1950 struct ldb_result *res_domain_ref;
1951 char *escaped_domain = ldb_binary_encode_string(mem_ctx, domain_name);
1952 /* find the domain's DN */
1953 int ret_domain = ldb_search(ldb, mem_ctx,
1954 &res_domain_ref,
1955 samdb_partitions_dn(ldb, mem_ctx),
1956 LDB_SCOPE_ONELEVEL,
1957 domain_ref_attrs,
1958 "(&(nETBIOSName=%s)(objectclass=crossRef))",
1959 escaped_domain);
1960 if (ret_domain != 0) {
1961 return NULL;
1964 if (res_domain_ref->count == 0) {
1965 ret_domain = ldb_search(ldb, mem_ctx,
1966 &res_domain_ref,
1967 samdb_dns_domain_to_dn(ldb, mem_ctx, domain_name),
1968 LDB_SCOPE_BASE,
1969 domain_ref2_attrs,
1970 "(objectclass=domain)");
1971 if (ret_domain != 0) {
1972 return NULL;
1975 if (res_domain_ref->count == 1) {
1976 return res_domain_ref->msgs[0]->dn;
1978 return NULL;
1981 if (res_domain_ref->count > 1) {
1982 DEBUG(0,("Found %d records matching domain [%s]\n",
1983 ret_domain, domain_name));
1984 return NULL;
1987 return samdb_result_dn(ldb, mem_ctx, res_domain_ref->msgs[0], "nCName", NULL);