2 Unix SMB/CIFS implementation.
4 interface functions for the sam database
6 Copyright (C) Andrew Tridgell 2004
7 Copyright (C) Volker Lendecke 2004
8 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2006
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 2 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, write to the Free Software
22 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 #include "librpc/gen_ndr/ndr_netlogon.h"
27 #include "librpc/gen_ndr/ndr_misc.h"
28 #include "librpc/gen_ndr/ndr_security.h"
29 #include "lib/ldb/include/ldb.h"
30 #include "lib/ldb/include/ldb_errors.h"
31 #include "libcli/security/security.h"
32 #include "auth/credentials/credentials.h"
33 #include "libcli/auth/proto.h"
34 #include "libcli/ldap/ldap.h"
35 #include "system/time.h"
36 #include "system/filesys.h"
38 #include "dsdb/samdb/samdb.h"
42 connect to the SAM database
43 return an opaque context pointer on success, or NULL on failure
45 struct ldb_context *samdb_connect(TALLOC_CTX *mem_ctx,
46 struct auth_session_info *session_info)
48 struct ldb_context *ldb;
49 ldb = ldb_wrap_connect(mem_ctx, lp_sam_url(), session_info,
58 search the sam for the specified attributes in a specific domain, filter on
59 objectSid being in domain_sid.
61 int samdb_search_domain(struct ldb_context *sam_ldb,
63 const struct ldb_dn *basedn,
64 struct ldb_message ***res,
65 const char * const *attrs,
66 const struct dom_sid *domain_sid,
67 const char *format, ...) _PRINTF_ATTRIBUTE(7,8)
73 count = gendb_search_v(sam_ldb, mem_ctx, basedn,
74 res, attrs, format, ap);
80 struct dom_sid *entry_sid;
82 entry_sid = samdb_result_dom_sid(mem_ctx, (*res)[i], "objectSid");
84 if ((entry_sid == NULL) ||
85 (!dom_sid_in_domain(domain_sid, entry_sid))) {
86 /* Delete that entry from the result set */
87 (*res)[i] = (*res)[count-1];
89 talloc_free(entry_sid);
92 talloc_free(entry_sid);
100 search the sam for a single string attribute in exactly 1 record
102 const char *samdb_search_string_v(struct ldb_context *sam_ldb,
104 const struct ldb_dn *basedn,
105 const char *attr_name,
106 const char *format, va_list ap) _PRINTF_ATTRIBUTE(5,0)
109 const char *attrs[2] = { NULL, NULL };
110 struct ldb_message **res = NULL;
112 attrs[0] = attr_name;
114 count = gendb_search_v(sam_ldb, mem_ctx, basedn, &res, attrs, format, ap);
116 DEBUG(1,("samdb: search for %s %s not single valued (count=%d)\n",
117 attr_name, format, count));
124 return samdb_result_string(res[0], attr_name, NULL);
129 search the sam for a single string attribute in exactly 1 record
131 const char *samdb_search_string(struct ldb_context *sam_ldb,
133 const struct ldb_dn *basedn,
134 const char *attr_name,
135 const char *format, ...) _PRINTF_ATTRIBUTE(5,6)
140 va_start(ap, format);
141 str = samdb_search_string_v(sam_ldb, mem_ctx, basedn, attr_name, format, ap);
147 struct ldb_dn *samdb_search_dn(struct ldb_context *sam_ldb,
149 const struct ldb_dn *basedn,
150 const char *format, ...) _PRINTF_ATTRIBUTE(4,5)
154 struct ldb_message **res = NULL;
157 va_start(ap, format);
158 count = gendb_search_v(sam_ldb, mem_ctx, basedn, &res, NULL, format, ap);
161 if (count != 1) return NULL;
163 ret = talloc_steal(mem_ctx, res[0]->dn);
170 search the sam for a dom_sid attribute in exactly 1 record
172 struct dom_sid *samdb_search_dom_sid(struct ldb_context *sam_ldb,
174 const struct ldb_dn *basedn,
175 const char *attr_name,
176 const char *format, ...) _PRINTF_ATTRIBUTE(5,6)
180 struct ldb_message **res;
181 const char *attrs[2] = { NULL, NULL };
184 attrs[0] = attr_name;
186 va_start(ap, format);
187 count = gendb_search_v(sam_ldb, mem_ctx, basedn, &res, attrs, format, ap);
190 DEBUG(1,("samdb: search for %s %s not single valued (count=%d)\n",
191 attr_name, format, count));
197 sid = samdb_result_dom_sid(mem_ctx, res[0], attr_name);
203 return the count of the number of records in the sam matching the query
205 int samdb_search_count(struct ldb_context *sam_ldb,
207 const struct ldb_dn *basedn,
208 const char *format, ...) _PRINTF_ATTRIBUTE(4,5)
211 struct ldb_message **res;
212 const char * const attrs[] = { NULL };
215 va_start(ap, format);
216 ret = gendb_search_v(sam_ldb, mem_ctx, basedn, &res, attrs, format, ap);
224 search the sam for a single integer attribute in exactly 1 record
226 uint_t samdb_search_uint(struct ldb_context *sam_ldb,
228 uint_t default_value,
229 const struct ldb_dn *basedn,
230 const char *attr_name,
231 const char *format, ...) _PRINTF_ATTRIBUTE(6,7)
235 struct ldb_message **res;
236 const char *attrs[2] = { NULL, NULL };
238 attrs[0] = attr_name;
240 va_start(ap, format);
241 count = gendb_search_v(sam_ldb, mem_ctx, basedn, &res, attrs, format, ap);
245 return default_value;
248 return samdb_result_uint(res[0], attr_name, default_value);
252 search the sam for a single signed 64 bit integer attribute in exactly 1 record
254 int64_t samdb_search_int64(struct ldb_context *sam_ldb,
256 int64_t default_value,
257 const struct ldb_dn *basedn,
258 const char *attr_name,
259 const char *format, ...) _PRINTF_ATTRIBUTE(6,7)
263 struct ldb_message **res;
264 const char *attrs[2] = { NULL, NULL };
266 attrs[0] = attr_name;
268 va_start(ap, format);
269 count = gendb_search_v(sam_ldb, mem_ctx, basedn, &res, attrs, format, ap);
273 return default_value;
276 return samdb_result_int64(res[0], attr_name, default_value);
280 search the sam for multipe records each giving a single string attribute
281 return the number of matches, or -1 on error
283 int samdb_search_string_multiple(struct ldb_context *sam_ldb,
285 const struct ldb_dn *basedn,
287 const char *attr_name,
288 const char *format, ...) _PRINTF_ATTRIBUTE(6,7)
292 const char *attrs[2] = { NULL, NULL };
293 struct ldb_message **res = NULL;
295 attrs[0] = attr_name;
297 va_start(ap, format);
298 count = gendb_search_v(sam_ldb, mem_ctx, basedn, &res, attrs, format, ap);
305 /* make sure its single valued */
306 for (i=0;i<count;i++) {
307 if (res[i]->num_elements != 1) {
308 DEBUG(1,("samdb: search for %s %s not single valued\n",
315 *strs = talloc_array(mem_ctx, const char *, count+1);
321 for (i=0;i<count;i++) {
322 (*strs)[i] = samdb_result_string(res[i], attr_name, NULL);
324 (*strs)[count] = NULL;
330 pull a uint from a result set.
332 uint_t samdb_result_uint(const struct ldb_message *msg, const char *attr, uint_t default_value)
334 return ldb_msg_find_attr_as_uint(msg, attr, default_value);
338 pull a (signed) int64 from a result set.
340 int64_t samdb_result_int64(const struct ldb_message *msg, const char *attr, int64_t default_value)
342 return ldb_msg_find_attr_as_int64(msg, attr, default_value);
346 pull a string from a result set.
348 const char *samdb_result_string(const struct ldb_message *msg, const char *attr,
349 const char *default_value)
351 return ldb_msg_find_attr_as_string(msg, attr, default_value);
354 struct ldb_dn *samdb_result_dn(TALLOC_CTX *mem_ctx, const struct ldb_message *msg,
355 const char *attr, struct ldb_dn *default_value)
357 const char *string = samdb_result_string(msg, attr, NULL);
358 if (string == NULL) return default_value;
359 return ldb_dn_explode(mem_ctx, string);
363 pull a rid from a objectSid in a result set.
365 uint32_t samdb_result_rid_from_sid(TALLOC_CTX *mem_ctx, const struct ldb_message *msg,
366 const char *attr, uint32_t default_value)
371 sid = samdb_result_dom_sid(mem_ctx, msg, attr);
373 return default_value;
375 rid = sid->sub_auths[sid->num_auths-1];
381 pull a dom_sid structure from a objectSid in a result set.
383 struct dom_sid *samdb_result_dom_sid(TALLOC_CTX *mem_ctx, const struct ldb_message *msg,
386 const struct ldb_val *v;
389 v = ldb_msg_find_ldb_val(msg, attr);
393 sid = talloc(mem_ctx, struct dom_sid);
397 status = ndr_pull_struct_blob(v, sid, sid,
398 (ndr_pull_flags_fn_t)ndr_pull_dom_sid);
399 if (!NT_STATUS_IS_OK(status)) {
407 pull a guid structure from a objectGUID in a result set.
409 struct GUID samdb_result_guid(const struct ldb_message *msg, const char *attr)
411 const struct ldb_val *v;
418 v = ldb_msg_find_ldb_val(msg, attr);
421 mem_ctx = talloc_named_const(NULL, 0, "samdb_result_guid");
422 if (!mem_ctx) return guid;
423 status = ndr_pull_struct_blob(v, mem_ctx, &guid,
424 (ndr_pull_flags_fn_t)ndr_pull_GUID);
425 talloc_free(mem_ctx);
426 if (!NT_STATUS_IS_OK(status)) {
434 pull a sid prefix from a objectSid in a result set.
435 this is used to find the domain sid for a user
437 struct dom_sid *samdb_result_sid_prefix(TALLOC_CTX *mem_ctx, const struct ldb_message *msg,
440 struct dom_sid *sid = samdb_result_dom_sid(mem_ctx, msg, attr);
441 if (!sid || sid->num_auths < 1) return NULL;
447 pull a NTTIME in a result set.
449 NTTIME samdb_result_nttime(struct ldb_message *msg, const char *attr, NTTIME default_value)
451 const char *str = ldb_msg_find_attr_as_string(msg, attr, NULL);
452 if (!str) return default_value;
453 return nttime_from_string(str);
457 pull a uint64_t from a result set.
459 uint64_t samdb_result_uint64(struct ldb_message *msg, const char *attr, uint64_t default_value)
461 return ldb_msg_find_attr_as_uint64(msg, attr, default_value);
466 construct the allow_password_change field from the PwdLastSet attribute and the
467 domain password settings
469 NTTIME samdb_result_allow_password_change(struct ldb_context *sam_ldb,
471 const struct ldb_dn *domain_dn,
472 struct ldb_message *msg,
475 uint64_t attr_time = samdb_result_uint64(msg, attr, 0);
478 if (attr_time == 0) {
482 minPwdAge = samdb_search_int64(sam_ldb, mem_ctx, 0, domain_dn, "minPwdAge", NULL);
484 /* yes, this is a -= not a += as minPwdAge is stored as the negative
485 of the number of 100-nano-seconds */
486 attr_time -= minPwdAge;
492 construct the force_password_change field from the PwdLastSet attribute and the
493 domain password settings
495 NTTIME samdb_result_force_password_change(struct ldb_context *sam_ldb,
497 const struct ldb_dn *domain_dn,
498 struct ldb_message *msg)
500 uint64_t attr_time = samdb_result_uint64(msg, "pwdLastSet", 0);
501 uint32_t user_flags = samdb_result_uint64(msg, "userAccountControl", 0);
504 if (user_flags & UF_DONT_EXPIRE_PASSWD) {
505 return 0x7FFFFFFFFFFFFFFFULL;
508 if (attr_time == 0) {
512 maxPwdAge = samdb_search_int64(sam_ldb, mem_ctx, 0, domain_dn, "maxPwdAge", NULL);
513 if (maxPwdAge == 0) {
516 attr_time -= maxPwdAge;
523 pull a samr_Password structutre from a result set.
525 struct samr_Password *samdb_result_hash(TALLOC_CTX *mem_ctx, struct ldb_message *msg, const char *attr)
527 struct samr_Password *hash = NULL;
528 const struct ldb_val *val = ldb_msg_find_ldb_val(msg, attr);
529 if (val && (val->length >= sizeof(hash->hash))) {
530 hash = talloc(mem_ctx, struct samr_Password);
531 memcpy(hash->hash, val->data, MIN(val->length, sizeof(hash->hash)));
537 pull an array of samr_Password structutres from a result set.
539 uint_t samdb_result_hashes(TALLOC_CTX *mem_ctx, struct ldb_message *msg,
540 const char *attr, struct samr_Password **hashes)
543 const struct ldb_val *val = ldb_msg_find_ldb_val(msg, attr);
550 count = val->length / 16;
555 *hashes = talloc_array(mem_ctx, struct samr_Password, count);
560 for (i=0;i<count;i++) {
561 memcpy((*hashes)[i].hash, (i*16)+(char *)val->data, 16);
567 NTSTATUS samdb_result_passwords(TALLOC_CTX *mem_ctx, struct ldb_message *msg,
568 struct samr_Password **lm_pwd, struct samr_Password **nt_pwd)
570 struct samr_Password *lmPwdHash, *ntPwdHash;
573 num_nt = samdb_result_hashes(mem_ctx, msg, "ntPwdHash", &ntPwdHash);
576 } else if (num_nt > 1) {
577 return NT_STATUS_INTERNAL_DB_CORRUPTION;
579 *nt_pwd = &ntPwdHash[0];
584 num_lm = samdb_result_hashes(mem_ctx, msg, "lmPwdHash", &lmPwdHash);
587 } else if (num_lm > 1) {
588 return NT_STATUS_INTERNAL_DB_CORRUPTION;
590 *lm_pwd = &lmPwdHash[0];
597 pull a samr_LogonHours structutre from a result set.
599 struct samr_LogonHours samdb_result_logon_hours(TALLOC_CTX *mem_ctx, struct ldb_message *msg, const char *attr)
601 struct samr_LogonHours hours;
602 const int units_per_week = 168;
603 const struct ldb_val *val = ldb_msg_find_ldb_val(msg, attr);
605 hours.bits = talloc_array(mem_ctx, uint8_t, units_per_week);
609 hours.units_per_week = units_per_week;
610 memset(hours.bits, 0xFF, units_per_week);
612 memcpy(hours.bits, val->data, MIN(val->length, units_per_week));
618 pull a set of account_flags from a result set.
620 uint16_t samdb_result_acct_flags(struct ldb_message *msg, const char *attr)
622 uint_t userAccountControl = ldb_msg_find_attr_as_uint(msg, attr, 0);
623 return samdb_uf2acb(userAccountControl);
627 /* Find an attribute, with a particular value */
628 struct ldb_message_element *samdb_find_attribute(struct ldb_context *ldb,
629 const struct ldb_message *msg,
630 const char *name, const char *value)
633 struct ldb_message_element *el = ldb_msg_find_element(msg, name);
636 v.data = discard_const_p(uint8_t, value);
637 v.length = strlen(value);
643 for (i=0;i<el->num_values;i++) {
644 if (strcasecmp(value, (char *)el->values[i].data) == 0) {
652 int samdb_find_or_add_value(struct ldb_context *ldb, struct ldb_message *msg, const char *name, const char *set_value)
654 if (samdb_find_attribute(ldb, msg, name, set_value) == NULL) {
655 return samdb_msg_add_string(ldb, msg, msg, name, set_value);
660 int samdb_find_or_add_attribute(struct ldb_context *ldb, struct ldb_message *msg, const char *name, const char *set_value)
662 struct ldb_message_element *el;
664 el = ldb_msg_find_element(msg, name);
669 return samdb_msg_add_string(ldb, msg, msg, name, set_value);
674 copy from a template record to a message
676 int samdb_copy_template(struct ldb_context *ldb,
677 struct ldb_message *msg, const char *filter,
678 const char **errstring)
680 struct ldb_result *res;
681 struct ldb_message *t;
683 struct ldb_dn *basedn = ldb_dn_explode(ldb, "cn=Templates");
687 /* pull the template record */
688 ret = ldb_search(ldb, basedn, LDB_SCOPE_SUBTREE, filter, NULL, &res);
690 if (ret != LDB_SUCCESS) {
691 *errstring = talloc_steal(msg, ldb_errstring(ldb));
694 if (res->count != 1) {
695 *errstring = talloc_asprintf(msg, "samdb_copy_template: ERROR: template '%s' matched %d records, expected 1\n", filter,
698 return LDB_ERR_OPERATIONS_ERROR;
702 for (i = 0; i < t->num_elements; i++) {
703 struct ldb_message_element *el = &t->elements[i];
704 /* some elements should not be copied from the template */
705 if (strcasecmp(el->name, "cn") == 0 ||
706 strcasecmp(el->name, "name") == 0 ||
707 strcasecmp(el->name, "sAMAccountName") == 0 ||
708 strcasecmp(el->name, "sAMAccountName") == 0 ||
709 strcasecmp(el->name, "distinguishedName") == 0 ||
710 strcasecmp(el->name, "objectGUID") == 0) {
713 for (j = 0; j < el->num_values; j++) {
714 if (strcasecmp(el->name, "objectClass") == 0) {
715 if (strcasecmp((char *)el->values[j].data, "Template") == 0 ||
716 strcasecmp((char *)el->values[j].data, "userTemplate") == 0 ||
717 strcasecmp((char *)el->values[j].data, "groupTemplate") == 0 ||
718 strcasecmp((char *)el->values[j].data, "foreignSecurityPrincipalTemplate") == 0 ||
719 strcasecmp((char *)el->values[j].data, "aliasTemplate") == 0 ||
720 strcasecmp((char *)el->values[j].data, "trustedDomainTemplate") == 0 ||
721 strcasecmp((char *)el->values[j].data, "secretTemplate") == 0) {
724 ret = samdb_find_or_add_value(ldb, msg, el->name,
725 (char *)el->values[j].data);
727 *errstring = talloc_asprintf(msg, "Adding objectClass %s failed.\n", el->values[j].data);
732 ret = samdb_find_or_add_attribute(ldb, msg, el->name,
733 (char *)el->values[j].data);
735 *errstring = talloc_asprintf(msg, "Adding attribute %s failed.\n", el->name);
750 add a string element to a message
752 int samdb_msg_add_string(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
753 const char *attr_name, const char *str)
755 char *s = talloc_strdup(mem_ctx, str);
756 char *a = talloc_strdup(mem_ctx, attr_name);
757 if (s == NULL || a == NULL) {
758 return LDB_ERR_OPERATIONS_ERROR;
760 return ldb_msg_add_string(msg, a, s);
764 add a dom_sid element to a message
766 int samdb_msg_add_dom_sid(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
767 const char *attr_name, struct dom_sid *sid)
771 status = ndr_push_struct_blob(&v, mem_ctx, sid,
772 (ndr_push_flags_fn_t)ndr_push_dom_sid);
773 if (!NT_STATUS_IS_OK(status)) {
776 return ldb_msg_add_value(msg, attr_name, &v);
781 add a delete element operation to a message
783 int samdb_msg_add_delete(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
784 const char *attr_name)
786 /* we use an empty replace rather than a delete, as it allows for
787 samdb_replace() to be used everywhere */
788 return ldb_msg_add_empty(msg, attr_name, LDB_FLAG_MOD_REPLACE);
792 add a add attribute value to a message
794 int samdb_msg_add_addval(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
795 const char *attr_name, const char *value)
797 struct ldb_message_element *el;
800 a = talloc_strdup(mem_ctx, attr_name);
803 v = talloc_strdup(mem_ctx, value);
806 ret = ldb_msg_add_string(msg, a, v);
809 el = ldb_msg_find_element(msg, a);
812 el->flags = LDB_FLAG_MOD_ADD;
817 add a delete attribute value to a message
819 int samdb_msg_add_delval(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
820 const char *attr_name, const char *value)
822 struct ldb_message_element *el;
825 a = talloc_strdup(mem_ctx, attr_name);
828 v = talloc_strdup(mem_ctx, value);
831 ret = ldb_msg_add_string(msg, a, v);
834 el = ldb_msg_find_element(msg, a);
837 el->flags = LDB_FLAG_MOD_DELETE;
842 add a int element to a message
844 int samdb_msg_add_int(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
845 const char *attr_name, int v)
847 const char *s = talloc_asprintf(mem_ctx, "%d", v);
848 return samdb_msg_add_string(sam_ldb, mem_ctx, msg, attr_name, s);
852 add a uint_t element to a message
854 int samdb_msg_add_uint(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
855 const char *attr_name, uint_t v)
857 const char *s = talloc_asprintf(mem_ctx, "%u", v);
858 return samdb_msg_add_string(sam_ldb, mem_ctx, msg, attr_name, s);
862 add a (signed) int64_t element to a message
864 int samdb_msg_add_int64(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
865 const char *attr_name, int64_t v)
867 const char *s = talloc_asprintf(mem_ctx, "%lld", (long long)v);
868 return samdb_msg_add_string(sam_ldb, mem_ctx, msg, attr_name, s);
872 add a uint64_t element to a message
874 int samdb_msg_add_uint64(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
875 const char *attr_name, uint64_t v)
877 const char *s = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)v);
878 return samdb_msg_add_string(sam_ldb, mem_ctx, msg, attr_name, s);
882 add a samr_Password element to a message
884 int samdb_msg_add_hash(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
885 const char *attr_name, struct samr_Password *hash)
888 val.data = talloc_memdup(mem_ctx, hash->hash, 16);
893 return ldb_msg_add_value(msg, attr_name, &val);
897 add a samr_Password array to a message
899 int samdb_msg_add_hashes(TALLOC_CTX *mem_ctx, struct ldb_message *msg,
900 const char *attr_name, struct samr_Password *hashes, uint_t count)
904 val.data = talloc_array_size(mem_ctx, 16, count);
905 val.length = count*16;
909 for (i=0;i<count;i++) {
910 memcpy(i*16 + (char *)val.data, hashes[i].hash, 16);
912 return ldb_msg_add_value(msg, attr_name, &val);
916 add a acct_flags element to a message
918 int samdb_msg_add_acct_flags(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
919 const char *attr_name, uint32_t v)
921 return samdb_msg_add_uint(sam_ldb, mem_ctx, msg, attr_name, samdb_acb2uf(v));
925 add a logon_hours element to a message
927 int samdb_msg_add_logon_hours(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
928 const char *attr_name, struct samr_LogonHours *hours)
931 val.length = hours->units_per_week / 8;
932 val.data = hours->bits;
933 return ldb_msg_add_value(msg, attr_name, &val);
937 add a general value element to a message
939 int samdb_msg_add_value(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
940 const char *attr_name, const struct ldb_val *val)
942 return ldb_msg_add_value(msg, attr_name, val);
946 sets a general value element to a message
948 int samdb_msg_set_value(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
949 const char *attr_name, const struct ldb_val *val)
951 struct ldb_message_element *el;
953 el = ldb_msg_find_element(msg, attr_name);
957 return ldb_msg_add_value(msg, attr_name, val);
961 set a string element in a message
963 int samdb_msg_set_string(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
964 const char *attr_name, const char *str)
966 struct ldb_message_element *el;
968 el = ldb_msg_find_element(msg, attr_name);
972 return samdb_msg_add_string(sam_ldb, mem_ctx, msg, attr_name, str);
978 int samdb_add(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg)
980 return ldb_add(sam_ldb, msg);
986 int samdb_delete(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, const struct ldb_dn *dn)
988 return ldb_delete(sam_ldb, dn);
994 int samdb_modify(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg)
996 return ldb_modify(sam_ldb, msg);
1000 replace elements in a record
1002 int samdb_replace(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg)
1006 /* mark all the message elements as LDB_FLAG_MOD_REPLACE */
1007 for (i=0;i<msg->num_elements;i++) {
1008 msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
1011 /* modify the samdb record */
1012 return samdb_modify(sam_ldb, mem_ctx, msg);
1016 return a default security descriptor
1018 struct security_descriptor *samdb_default_security_descriptor(TALLOC_CTX *mem_ctx)
1020 struct security_descriptor *sd;
1022 sd = security_descriptor_initialise(mem_ctx);
1027 struct ldb_dn *samdb_base_dn(TALLOC_CTX *mem_ctx)
1029 TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
1030 int server_role = lp_server_role();
1031 const char **split_realm;
1038 if ((server_role == ROLE_DOMAIN_PDC)
1039 || (server_role == ROLE_DOMAIN_BDC)) {
1041 split_realm = str_list_make(tmp_ctx, lp_realm(), ".");
1043 talloc_free(tmp_ctx);
1047 i = str_list_length(split_realm);
1049 for (; i >= 0; i--) {
1050 dn = ldb_dn_build_child(tmp_ctx, "dc", split_realm[i], dn);
1052 talloc_free(tmp_ctx);
1058 return ldb_dn_string_compose(mem_ctx, NULL, "cn=%s", lp_netbios_name());
1063 work out the domain sid for the current open ldb
1065 const struct dom_sid *samdb_domain_sid(struct ldb_context *ldb)
1067 const char *attrs[] = { "rootDomainNamingContext", NULL };
1069 struct ldb_result *res = NULL;
1070 TALLOC_CTX *tmp_ctx;
1071 struct dom_sid *domain_sid;
1072 const char *basedn_s;
1073 struct ldb_dn *basedn;
1075 /* see if we have a cached copy */
1076 domain_sid = ldb_get_opaque(ldb, "cache.domain_sid");
1081 tmp_ctx = talloc_new(ldb);
1082 if (tmp_ctx == NULL) {
1086 basedn = ldb_dn_explode(tmp_ctx, "");
1087 if (basedn == NULL) {
1091 /* find the basedn of the domain from the rootdse */
1092 ret = ldb_search(ldb, basedn, LDB_SCOPE_BASE, NULL, attrs, &res);
1093 talloc_steal(tmp_ctx, res);
1094 if (ret != LDB_SUCCESS || res->count != 1) {
1098 basedn_s = ldb_msg_find_attr_as_string(res->msgs[0], "rootDomainNamingContext", NULL);
1099 if (basedn_s == NULL) {
1103 basedn = ldb_dn_explode(tmp_ctx, basedn_s);
1104 if (basedn == NULL) {
1108 /* find the domain_sid */
1109 domain_sid = samdb_search_dom_sid(ldb, tmp_ctx, basedn,
1110 "objectSid", "objectClass=domainDNS");
1111 if (domain_sid == NULL) {
1115 /* cache the domain_sid in the ldb */
1116 if (ldb_set_opaque(ldb, "cache.domain_sid", domain_sid) != LDB_SUCCESS) {
1120 talloc_steal(ldb, domain_sid);
1121 talloc_free(tmp_ctx);
1126 DEBUG(1,("Failed to find domain_sid for open ldb\n"));
1127 talloc_free(tmp_ctx);
1132 check that a password is sufficiently complex
1134 static BOOL samdb_password_complexity_ok(const char *pass)
1136 return check_password_quality(pass);
1142 set the user password using plaintext, obeying any user or domain
1143 password restrictions
1145 note that this function doesn't actually store the result in the
1146 database, it just fills in the "mod" structure with ldb modify
1147 elements to setup the correct change when samdb_replace() is
1148 called. This allows the caller to combine the change with other
1149 changes (as is needed by some of the set user info levels)
1151 The caller should probably have a transaction wrapping this
1153 _PUBLIC_ NTSTATUS samdb_set_password(struct ldb_context *ctx, TALLOC_CTX *mem_ctx,
1154 const struct ldb_dn *user_dn,
1155 const struct ldb_dn *domain_dn,
1156 struct ldb_message *mod,
1157 const char *new_pass,
1158 struct samr_Password *lmNewHash,
1159 struct samr_Password *ntNewHash,
1162 enum samr_RejectReason *reject_reason,
1163 struct samr_DomInfo1 **_dominfo)
1165 const char * const user_attrs[] = { "userAccountControl", "sambaLMPwdHistory",
1166 "sambaNTPwdHistory",
1167 "lmPwdHash", "ntPwdHash",
1169 "pwdLastSet", NULL };
1170 const char * const domain_attrs[] = { "pwdProperties", "pwdHistoryLength",
1171 "maxPwdAge", "minPwdAge",
1172 "minPwdLength", NULL };
1175 uint_t minPwdLength, pwdProperties, pwdHistoryLength;
1176 uint_t userAccountControl;
1177 struct samr_Password *sambaLMPwdHistory, *sambaNTPwdHistory, *lmPwdHash, *ntPwdHash;
1178 struct samr_Password local_lmNewHash, local_ntNewHash;
1179 int sambaLMPwdHistory_len, sambaNTPwdHistory_len;
1180 struct dom_sid *domain_sid;
1181 struct ldb_message **res;
1183 time_t now = time(NULL);
1187 /* we need to know the time to compute password age */
1188 unix_to_nt_time(&now_nt, now);
1190 /* pull all the user parameters */
1191 count = gendb_search_dn(ctx, mem_ctx, user_dn, &res, user_attrs);
1193 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1195 userAccountControl = samdb_result_uint(res[0], "userAccountControl", 0);
1196 sambaLMPwdHistory_len = samdb_result_hashes(mem_ctx, res[0],
1197 "sambaLMPwdHistory", &sambaLMPwdHistory);
1198 sambaNTPwdHistory_len = samdb_result_hashes(mem_ctx, res[0],
1199 "sambaNTPwdHistory", &sambaNTPwdHistory);
1200 lmPwdHash = samdb_result_hash(mem_ctx, res[0], "lmPwdHash");
1201 ntPwdHash = samdb_result_hash(mem_ctx, res[0], "ntPwdHash");
1202 pwdLastSet = samdb_result_uint64(res[0], "pwdLastSet", 0);
1205 /* pull the domain parameters */
1206 count = gendb_search_dn(ctx, mem_ctx, domain_dn, &res, domain_attrs);
1208 DEBUG(2, ("samdb_set_password: Domain DN %s is invalid, for user %s\n",
1209 ldb_dn_linearize(mem_ctx, domain_dn),
1210 ldb_dn_linearize(mem_ctx, user_dn)));
1211 return NT_STATUS_NO_SUCH_DOMAIN;
1214 /* work out the domain sid, and pull the domain from there */
1215 domain_sid = samdb_result_sid_prefix(mem_ctx, res[0], "objectSid");
1216 if (domain_sid == NULL) {
1217 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1220 count = gendb_search(ctx, mem_ctx, samdb_base_dn(mem_ctx), &res, domain_attrs,
1222 ldap_encode_ndr_dom_sid(mem_ctx, domain_sid));
1224 DEBUG(2, ("samdb_set_password: Could not find domain to match SID: %s, for user %s\n",
1225 dom_sid_string(mem_ctx, domain_sid),
1226 ldb_dn_linearize(mem_ctx, user_dn)));
1227 return NT_STATUS_NO_SUCH_DOMAIN;
1231 pwdProperties = samdb_result_uint(res[0], "pwdProperties", 0);
1232 pwdHistoryLength = samdb_result_uint(res[0], "pwdHistoryLength", 0);
1233 minPwdLength = samdb_result_uint(res[0], "minPwdLength", 0);
1234 minPwdAge = samdb_result_int64(res[0], "minPwdAge", 0);
1237 struct samr_DomInfo1 *dominfo;
1238 /* on failure we need to fill in the reject reasons */
1239 dominfo = talloc(mem_ctx, struct samr_DomInfo1);
1240 if (dominfo == NULL) {
1241 return NT_STATUS_NO_MEMORY;
1243 dominfo->min_password_length = minPwdLength;
1244 dominfo->password_properties = pwdProperties;
1245 dominfo->password_history_length = pwdHistoryLength;
1246 dominfo->max_password_age = minPwdAge;
1247 dominfo->min_password_age = minPwdAge;
1248 *_dominfo = dominfo;
1252 /* check the various password restrictions */
1253 if (restrictions && minPwdLength > strlen_m(new_pass)) {
1254 if (reject_reason) {
1255 *reject_reason = SAMR_REJECT_TOO_SHORT;
1257 return NT_STATUS_PASSWORD_RESTRICTION;
1260 /* possibly check password complexity */
1261 if (restrictions && pwdProperties & DOMAIN_PASSWORD_COMPLEX &&
1262 !samdb_password_complexity_ok(new_pass)) {
1263 if (reject_reason) {
1264 *reject_reason = SAMR_REJECT_COMPLEXITY;
1266 return NT_STATUS_PASSWORD_RESTRICTION;
1269 /* compute the new nt and lm hashes */
1270 if (E_deshash(new_pass, local_lmNewHash.hash)) {
1271 lmNewHash = &local_lmNewHash;
1273 E_md4hash(new_pass, local_ntNewHash.hash);
1274 ntNewHash = &local_ntNewHash;
1277 if (restrictions && user_change) {
1278 /* are all password changes disallowed? */
1279 if (pwdProperties & DOMAIN_REFUSE_PASSWORD_CHANGE) {
1280 if (reject_reason) {
1281 *reject_reason = SAMR_REJECT_OTHER;
1283 return NT_STATUS_PASSWORD_RESTRICTION;
1286 /* can this user change password? */
1287 if (userAccountControl & UF_PASSWD_CANT_CHANGE) {
1288 if (reject_reason) {
1289 *reject_reason = SAMR_REJECT_OTHER;
1291 return NT_STATUS_PASSWORD_RESTRICTION;
1294 /* yes, this is a minus. The ages are in negative 100nsec units! */
1295 if (pwdLastSet - minPwdAge > now_nt) {
1296 if (reject_reason) {
1297 *reject_reason = SAMR_REJECT_OTHER;
1299 return NT_STATUS_PASSWORD_RESTRICTION;
1302 /* check the immediately past password */
1303 if (pwdHistoryLength > 0) {
1304 if (lmNewHash && lmPwdHash && memcmp(lmNewHash->hash, lmPwdHash->hash, 16) == 0) {
1305 if (reject_reason) {
1306 *reject_reason = SAMR_REJECT_COMPLEXITY;
1308 return NT_STATUS_PASSWORD_RESTRICTION;
1310 if (ntNewHash && ntPwdHash && memcmp(ntNewHash->hash, ntPwdHash->hash, 16) == 0) {
1311 if (reject_reason) {
1312 *reject_reason = SAMR_REJECT_COMPLEXITY;
1314 return NT_STATUS_PASSWORD_RESTRICTION;
1318 /* check the password history */
1319 sambaLMPwdHistory_len = MIN(sambaLMPwdHistory_len, pwdHistoryLength);
1320 sambaNTPwdHistory_len = MIN(sambaNTPwdHistory_len, pwdHistoryLength);
1322 for (i=0; lmNewHash && i<sambaLMPwdHistory_len;i++) {
1323 if (memcmp(lmNewHash->hash, sambaLMPwdHistory[i].hash, 16) == 0) {
1324 if (reject_reason) {
1325 *reject_reason = SAMR_REJECT_COMPLEXITY;
1327 return NT_STATUS_PASSWORD_RESTRICTION;
1330 for (i=0; ntNewHash && i<sambaNTPwdHistory_len;i++) {
1331 if (memcmp(ntNewHash->hash, sambaNTPwdHistory[i].hash, 16) == 0) {
1332 if (reject_reason) {
1333 *reject_reason = SAMR_REJECT_COMPLEXITY;
1335 return NT_STATUS_PASSWORD_RESTRICTION;
1340 #define CHECK_RET(x) do { if (x != 0) return NT_STATUS_NO_MEMORY; } while(0)
1342 /* the password is acceptable. Start forming the new fields */
1344 /* if we know the cleartext, then only set it.
1345 * Modules in ldb will set all the appropriate
1347 CHECK_RET(samdb_msg_add_string(ctx, mem_ctx, mod,
1348 "sambaPassword", new_pass));
1350 /* We don't have the cleartext, so delete the old one
1351 * and set what we have of the hashes */
1352 CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "sambaPassword"));
1355 CHECK_RET(samdb_msg_add_hash(ctx, mem_ctx, mod, "lmPwdHash", lmNewHash));
1357 CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "lmPwdHash"));
1361 CHECK_RET(samdb_msg_add_hash(ctx, mem_ctx, mod, "ntPwdHash", ntNewHash));
1363 CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "ntPwdHash"));
1367 return NT_STATUS_OK;
1372 set the user password using plaintext, obeying any user or domain
1373 password restrictions
1375 This wrapper function takes a SID as input, rather than a user DN,
1376 and actually performs the password change
1379 _PUBLIC_ NTSTATUS samdb_set_password_sid(struct ldb_context *ctx, TALLOC_CTX *mem_ctx,
1380 const struct dom_sid *user_sid,
1381 const char *new_pass,
1382 struct samr_Password *lmNewHash,
1383 struct samr_Password *ntNewHash,
1386 enum samr_RejectReason *reject_reason,
1387 struct samr_DomInfo1 **_dominfo)
1390 struct ldb_dn *user_dn;
1391 struct ldb_message *msg;
1394 ret = ldb_transaction_start(ctx);
1396 DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(ctx)));
1397 return NT_STATUS_TRANSACTION_ABORTED;
1400 user_dn = samdb_search_dn(ctx, mem_ctx, samdb_base_dn(mem_ctx),
1401 "(&(objectSid=%s)(objectClass=user))",
1402 ldap_encode_ndr_dom_sid(mem_ctx, user_sid));
1404 ldb_transaction_cancel(ctx);
1405 DEBUG(3, ("samdb_set_password_sid: SID %s not found in samdb, returning NO_SUCH_USER\n",
1406 dom_sid_string(mem_ctx, user_sid)));
1407 return NT_STATUS_NO_SUCH_USER;
1410 msg = ldb_msg_new(mem_ctx);
1412 ldb_transaction_cancel(ctx);
1413 return NT_STATUS_NO_MEMORY;
1416 msg->dn = ldb_dn_copy(msg, user_dn);
1418 ldb_transaction_cancel(ctx);
1419 return NT_STATUS_NO_MEMORY;
1422 nt_status = samdb_set_password(ctx, mem_ctx,
1425 lmNewHash, ntNewHash,
1426 user_change, /* This is a password set, not change */
1427 restrictions, /* run restriction tests */
1428 reject_reason, _dominfo);
1429 if (!NT_STATUS_IS_OK(nt_status)) {
1430 ldb_transaction_cancel(ctx);
1434 /* modify the samdb record */
1435 ret = samdb_replace(ctx, mem_ctx, msg);
1437 ldb_transaction_cancel(ctx);
1438 return NT_STATUS_ACCESS_DENIED;
1441 ret = ldb_transaction_commit(ctx);
1443 DEBUG(0,("Failed to commit transaction to change password on %s: %s\n",
1444 ldb_dn_linearize(mem_ctx, msg->dn),
1445 ldb_errstring(ctx)));
1446 return NT_STATUS_TRANSACTION_ABORTED;
1448 return NT_STATUS_OK;
1451 /****************************************************************************
1452 Create the SID list for this user.
1453 ****************************************************************************/
1454 NTSTATUS security_token_create(TALLOC_CTX *mem_ctx,
1455 struct dom_sid *user_sid,
1456 struct dom_sid *group_sid,
1458 struct dom_sid **groupSIDs,
1459 BOOL is_authenticated,
1460 struct security_token **token)
1462 struct security_token *ptoken;
1466 ptoken = security_token_initialise(mem_ctx);
1467 NT_STATUS_HAVE_NO_MEMORY(ptoken);
1469 ptoken->sids = talloc_array(ptoken, struct dom_sid *, n_groupSIDs + 5);
1470 NT_STATUS_HAVE_NO_MEMORY(ptoken->sids);
1472 ptoken->user_sid = talloc_reference(ptoken, user_sid);
1473 ptoken->group_sid = talloc_reference(ptoken, group_sid);
1474 ptoken->privilege_mask = 0;
1476 ptoken->sids[0] = ptoken->user_sid;
1477 ptoken->sids[1] = ptoken->group_sid;
1480 * Finally add the "standard" SIDs.
1481 * The only difference between guest and "anonymous"
1482 * is the addition of Authenticated_Users.
1484 ptoken->sids[2] = dom_sid_parse_talloc(ptoken->sids, SID_WORLD);
1485 NT_STATUS_HAVE_NO_MEMORY(ptoken->sids[2]);
1486 ptoken->sids[3] = dom_sid_parse_talloc(ptoken->sids, SID_NT_NETWORK);
1487 NT_STATUS_HAVE_NO_MEMORY(ptoken->sids[3]);
1488 ptoken->num_sids = 4;
1490 if (is_authenticated) {
1491 ptoken->sids[4] = dom_sid_parse_talloc(ptoken->sids, SID_NT_AUTHENTICATED_USERS);
1492 NT_STATUS_HAVE_NO_MEMORY(ptoken->sids[4]);
1496 for (i = 0; i < n_groupSIDs; i++) {
1497 size_t check_sid_idx;
1498 for (check_sid_idx = 1;
1499 check_sid_idx < ptoken->num_sids;
1501 if (dom_sid_equal(ptoken->sids[check_sid_idx], groupSIDs[i])) {
1506 if (check_sid_idx == ptoken->num_sids) {
1507 ptoken->sids[ptoken->num_sids++] = talloc_reference(ptoken->sids, groupSIDs[i]);
1511 /* setup the privilege mask for this token */
1512 status = samdb_privilege_setup(ptoken);
1513 if (!NT_STATUS_IS_OK(status)) {
1514 talloc_free(ptoken);
1518 security_token_debug(10, ptoken);
1522 return NT_STATUS_OK;
1526 NTSTATUS samdb_create_foreign_security_principal(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
1527 struct dom_sid *sid, struct ldb_dn **ret_dn)
1529 struct ldb_message *msg;
1530 struct ldb_dn *basedn;
1534 sidstr = dom_sid_string(mem_ctx, sid);
1535 NT_STATUS_HAVE_NO_MEMORY(sidstr);
1537 /* We might have to create a ForeignSecurityPrincipal, even if this user
1538 * is in our own domain */
1540 msg = ldb_msg_new(mem_ctx);
1542 return NT_STATUS_NO_MEMORY;
1545 /* TODO: Hmmm. This feels wrong. How do I find the base dn to
1546 * put the ForeignSecurityPrincipals? d_state->domain_dn does
1547 * not work, this is wrong for the Builtin domain, there's no
1548 * cn=For...,cn=Builtin,dc={BASEDN}. -- vl
1551 basedn = samdb_search_dn(sam_ctx, mem_ctx, samdb_base_dn(mem_ctx),
1552 "(&(objectClass=container)(cn=ForeignSecurityPrincipals))");
1554 if (basedn == NULL) {
1555 DEBUG(0, ("Failed to find DN for "
1556 "ForeignSecurityPrincipal container\n"));
1557 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1560 /* add core elements to the ldb_message for the alias */
1561 msg->dn = ldb_dn_build_child(mem_ctx, "CN", sidstr, basedn);
1562 if (msg->dn == NULL)
1563 return NT_STATUS_NO_MEMORY;
1565 samdb_msg_add_string(sam_ctx, mem_ctx, msg,
1567 "foreignSecurityPrincipal");
1569 /* create the alias */
1570 ret = samdb_add(sam_ctx, mem_ctx, msg);
1572 DEBUG(0,("Failed to create foreignSecurityPrincipal "
1574 ldb_dn_linearize(mem_ctx, msg->dn),
1575 ldb_errstring(sam_ctx)));
1576 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1579 return NT_STATUS_OK;