2 Unix SMB/CIFS implementation.
4 interface functions for the sam database
6 Copyright (C) Andrew Tridgell 2004
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 #include "librpc/gen_ndr/ndr_netlogon.h"
25 #include "lib/ldb/include/ldb.h"
26 #include "system/time.h"
27 #include "system/filesys.h"
31 connect to the SAM database
32 return an opaque context pointer on success, or NULL on failure
34 struct ldb_context *samdb_connect(TALLOC_CTX *mem_ctx)
36 return ldb_wrap_connect(mem_ctx, lp_sam_url(), 0, NULL);
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,
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)
55 count = gendb_search_v(sam_ldb, mem_ctx, basedn,
56 res, attrs, format, ap);
62 struct dom_sid *entry_sid;
64 entry_sid = samdb_result_dom_sid(mem_ctx, (*res)[i],
67 if ((entry_sid == NULL) ||
68 (!dom_sid_in_domain(domain_sid, entry_sid))) {
70 /* Delete that entry from the result set */
71 (*res)[i] = (*res)[count-1];
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,
87 const char *attr_name,
88 const char *format, va_list ap) _PRINTF_ATTRIBUTE(5,0)
91 const char * const attrs[2] = { attr_name, NULL };
92 struct ldb_message **res = NULL;
94 count = gendb_search_v(sam_ldb, mem_ctx, basedn, &res, attrs, format, ap);
96 DEBUG(1,("samdb: search for %s %s not single valued (count=%d)\n",
97 attr_name, format, count));
104 return samdb_result_string(res[0], attr_name, NULL);
109 search the sam for a single string attribute in exactly 1 record
111 const char *samdb_search_string(struct ldb_context *sam_ldb,
114 const char *attr_name,
115 const char *format, ...) _PRINTF_ATTRIBUTE(5,6)
120 va_start(ap, format);
121 str = samdb_search_string_v(sam_ldb, mem_ctx, basedn, attr_name, format, ap);
128 return the count of the number of records in the sam matching the query
130 int samdb_search_count(struct ldb_context *sam_ldb,
133 const char *format, ...) _PRINTF_ATTRIBUTE(4,5)
136 struct ldb_message **res;
137 const char * const attrs[] = { NULL };
140 va_start(ap, format);
141 ret = gendb_search_v(sam_ldb, mem_ctx, basedn, &res, attrs, format, ap);
149 search the sam for a single integer attribute in exactly 1 record
151 uint_t samdb_search_uint(struct ldb_context *sam_ldb,
153 uint_t default_value,
155 const char *attr_name,
156 const char *format, ...) _PRINTF_ATTRIBUTE(6,7)
160 struct ldb_message **res;
161 const char * const attrs[2] = { attr_name, NULL };
163 va_start(ap, format);
164 count = gendb_search_v(sam_ldb, mem_ctx, basedn, &res, attrs, format, ap);
168 return default_value;
171 return samdb_result_uint(res[0], attr_name, default_value);
175 search the sam for a single signed 64 bit integer attribute in exactly 1 record
177 int64_t samdb_search_int64(struct ldb_context *sam_ldb,
179 int64_t default_value,
181 const char *attr_name,
182 const char *format, ...) _PRINTF_ATTRIBUTE(6,7)
186 struct ldb_message **res;
187 const char * const attrs[2] = { attr_name, NULL };
189 va_start(ap, format);
190 count = gendb_search_v(sam_ldb, mem_ctx, basedn, &res, attrs, format, ap);
194 return default_value;
197 return samdb_result_int64(res[0], attr_name, default_value);
201 search the sam for multipe records each giving a single string attribute
202 return the number of matches, or -1 on error
204 int samdb_search_string_multiple(struct ldb_context *sam_ldb,
208 const char *attr_name,
209 const char *format, ...) _PRINTF_ATTRIBUTE(6,7)
213 const char * const attrs[2] = { attr_name, NULL };
214 struct ldb_message **res = NULL;
216 va_start(ap, format);
217 count = gendb_search_v(sam_ldb, mem_ctx, basedn, &res, attrs, format, ap);
224 /* make sure its single valued */
225 for (i=0;i<count;i++) {
226 if (res[i]->num_elements != 1) {
227 DEBUG(1,("samdb: search for %s %s not single valued\n",
234 *strs = talloc_array(mem_ctx, const char *, count+1);
240 for (i=0;i<count;i++) {
241 (*strs)[i] = samdb_result_string(res[i], attr_name, NULL);
243 (*strs)[count] = NULL;
249 pull a uint from a result set.
251 uint_t samdb_result_uint(struct ldb_message *msg, const char *attr, uint_t default_value)
253 return ldb_msg_find_uint(msg, attr, default_value);
257 pull a (signed) int64 from a result set.
259 int64_t samdb_result_int64(struct ldb_message *msg, const char *attr, int64_t default_value)
261 return ldb_msg_find_int64(msg, attr, default_value);
265 pull a string from a result set.
267 const char *samdb_result_string(struct ldb_message *msg, const char *attr,
268 const char *default_value)
270 return ldb_msg_find_string(msg, attr, default_value);
274 pull a rid from a objectSid in a result set.
276 uint32_t samdb_result_rid_from_sid(TALLOC_CTX *mem_ctx, struct ldb_message *msg,
277 const char *attr, uint32_t default_value)
280 const char *sidstr = ldb_msg_find_string(msg, attr, NULL);
281 if (!sidstr) return default_value;
283 sid = dom_sid_parse_talloc(mem_ctx, sidstr);
284 if (!sid) return default_value;
286 return sid->sub_auths[sid->num_auths-1];
290 pull a dom_sid structure from a objectSid in a result set.
292 struct dom_sid *samdb_result_dom_sid(TALLOC_CTX *mem_ctx, struct ldb_message *msg,
295 const char *sidstr = ldb_msg_find_string(msg, attr, NULL);
296 if (!sidstr) return NULL;
298 return dom_sid_parse_talloc(mem_ctx, sidstr);
302 pull a guid structure from a objectGUID in a result set.
304 struct GUID samdb_result_guid(struct ldb_message *msg, const char *attr)
308 const char *guidstr = ldb_msg_find_string(msg, attr, NULL);
312 if (!guidstr) return guid;
314 status = GUID_from_string(guidstr, &guid);
315 if (!NT_STATUS_IS_OK(status)) {
324 pull a sid prefix from a objectSid in a result set.
325 this is used to find the domain sid for a user
327 const char *samdb_result_sid_prefix(TALLOC_CTX *mem_ctx, struct ldb_message *msg,
330 struct dom_sid *sid = samdb_result_dom_sid(mem_ctx, msg, attr);
331 if (!sid || sid->num_auths < 1) return NULL;
335 return dom_sid_string(mem_ctx, sid);
339 pull a NTTIME in a result set.
341 NTTIME samdb_result_nttime(struct ldb_message *msg, const char *attr, const char *default_value)
343 const char *str = ldb_msg_find_string(msg, attr, default_value);
344 return nttime_from_string(str);
348 pull a uint64_t from a result set.
350 uint64_t samdb_result_uint64(struct ldb_message *msg, const char *attr, uint64_t default_value)
352 return ldb_msg_find_uint64(msg, attr, default_value);
357 construct the allow_password_change field from the PwdLastSet attribute and the
358 domain password settings
360 NTTIME samdb_result_allow_password_change(struct ldb_context *sam_ldb,
362 const char *domain_dn,
363 struct ldb_message *msg,
366 uint64_t attr_time = samdb_result_uint64(msg, attr, 0);
369 if (attr_time == 0) {
373 minPwdAge = samdb_search_int64(sam_ldb, mem_ctx, 0,
374 domain_dn, "minPwdAge", NULL);
376 /* yes, this is a -= not a += as minPwdAge is stored as the negative
377 of the number of 100-nano-seconds */
378 attr_time -= minPwdAge;
384 construct the force_password_change field from the PwdLastSet attribute and the
385 domain password settings
387 NTTIME samdb_result_force_password_change(struct ldb_context *sam_ldb,
389 const char *domain_dn,
390 struct ldb_message *msg,
393 uint64_t attr_time = samdb_result_uint64(msg, attr, 0);
396 if (attr_time == 0) {
400 maxPwdAge = samdb_search_int64(sam_ldb, mem_ctx, 0, domain_dn, "maxPwdAge", NULL);
401 if (maxPwdAge == 0) {
404 attr_time -= maxPwdAge;
411 pull a samr_Password structutre from a result set.
413 struct samr_Password samdb_result_hash(struct ldb_message *msg, const char *attr)
415 struct samr_Password hash;
416 const struct ldb_val *val = ldb_msg_find_ldb_val(msg, attr);
419 memcpy(hash.hash, val->data, MIN(val->length, sizeof(hash.hash)));
425 pull an array of samr_Password structutres from a result set.
427 uint_t samdb_result_hashes(TALLOC_CTX *mem_ctx, struct ldb_message *msg,
428 const char *attr, struct samr_Password **hashes)
431 const struct ldb_val *val = ldb_msg_find_ldb_val(msg, attr);
438 count = val->length / 16;
443 *hashes = talloc_array(mem_ctx, struct samr_Password, count);
448 for (i=0;i<count;i++) {
449 memcpy((*hashes)[i].hash, (i*16)+(char *)val->data, 16);
455 NTSTATUS samdb_result_passwords(TALLOC_CTX *mem_ctx, struct ldb_message *msg,
456 struct samr_Password **lm_pwd, struct samr_Password **nt_pwd)
459 const char *unicodePwd = samdb_result_string(msg, "unicodePwd", NULL);
461 struct samr_Password *lmPwdHash, *ntPwdHash;
464 ntPwdHash = talloc(mem_ctx, struct samr_Password);
466 return NT_STATUS_NO_MEMORY;
469 E_md4hash(unicodePwd, ntPwdHash->hash);
476 lmPwdHash = talloc(mem_ctx, struct samr_Password);
478 return NT_STATUS_NO_MEMORY;
481 /* compute the new nt and lm hashes */
482 lm_hash_ok = E_deshash(unicodePwd, lmPwdHash->hash);
493 num_nt = samdb_result_hashes(mem_ctx, msg, "ntPwdHash", &ntPwdHash);
496 } else if (num_nt > 1) {
497 return NT_STATUS_INTERNAL_DB_CORRUPTION;
499 *nt_pwd = &ntPwdHash[0];
504 num_lm = samdb_result_hashes(mem_ctx, msg, "lmPwdHash", &lmPwdHash);
507 } else if (num_lm > 1) {
508 return NT_STATUS_INTERNAL_DB_CORRUPTION;
510 *lm_pwd = &lmPwdHash[0];
519 pull a samr_LogonHours structutre from a result set.
521 struct samr_LogonHours samdb_result_logon_hours(TALLOC_CTX *mem_ctx, struct ldb_message *msg, const char *attr)
523 struct samr_LogonHours hours;
524 const int units_per_week = 168;
525 const struct ldb_val *val = ldb_msg_find_ldb_val(msg, attr);
527 hours.bits = talloc_array(mem_ctx, uint8_t, units_per_week);
531 hours.units_per_week = units_per_week;
532 memset(hours.bits, 0xFF, units_per_week);
534 memcpy(hours.bits, val->data, MIN(val->length, units_per_week));
540 pull a set of account_flags from a result set.
542 uint16_t samdb_result_acct_flags(struct ldb_message *msg, const char *attr)
544 uint_t userAccountControl = ldb_msg_find_uint(msg, attr, 0);
545 return samdb_uf2acb(userAccountControl);
549 copy from a template record to a message
551 int samdb_copy_template(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx,
552 struct ldb_message *msg, const char *expression)
554 struct ldb_message **res, *t;
558 /* pull the template record */
559 ret = gendb_search(sam_ldb, mem_ctx, NULL, &res, NULL, "%s", expression);
561 DEBUG(1,("samdb: ERROR: template '%s' matched %d records\n",
567 for (i=0;i<t->num_elements;i++) {
568 struct ldb_message_element *el = &t->elements[i];
569 /* some elements should not be copied from the template */
570 if (strcasecmp(el->name, "cn") == 0 ||
571 strcasecmp(el->name, "name") == 0 ||
572 strcasecmp(el->name, "sAMAccountName") == 0) {
575 for (j=0;j<el->num_values;j++) {
576 if (strcasecmp(el->name, "objectClass") == 0 &&
577 (strcasecmp((char *)el->values[j].data, "Template") == 0 ||
578 strcasecmp((char *)el->values[j].data, "userTemplate") == 0 ||
579 strcasecmp((char *)el->values[j].data, "groupTemplate") == 0 ||
580 strcasecmp((char *)el->values[j].data, "foreignSecurityTemplate") == 0 ||
581 strcasecmp((char *)el->values[j].data, "aliasTemplate") == 0 ||
582 strcasecmp((char *)el->values[j].data, "trustedDomainTemplate") == 0 ||
583 strcasecmp((char *)el->values[j].data, "secretTemplate") == 0)) {
586 samdb_msg_add_string(sam_ldb, mem_ctx, msg, el->name,
587 (char *)el->values[j].data);
596 allocate a new id, attempting to do it atomically
597 return 0 on failure, the id on success
599 static NTSTATUS _samdb_allocate_next_id(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, const char *dn,
600 const char *attr, uint32_t *id)
602 struct ldb_message msg;
605 struct ldb_val vals[2];
606 struct ldb_message_element els[2];
608 str = samdb_search_string(sam_ldb, mem_ctx, dn, attr, NULL);
610 DEBUG(1,("id not found at %s %s\n", dn, attr));
611 return NT_STATUS_OBJECT_NAME_INVALID;
614 *id = strtol(str, NULL, 0);
617 return NT_STATUS_INSUFFICIENT_RESOURCES;
620 /* we do a delete and add as a single operation. That prevents
623 msg.dn = talloc_strdup(mem_ctx, dn);
625 return NT_STATUS_NO_MEMORY;
627 msg.num_elements = 2;
630 els[0].num_values = 1;
631 els[0].values = &vals[0];
632 els[0].flags = LDB_FLAG_MOD_DELETE;
633 els[0].name = talloc_strdup(mem_ctx, attr);
635 return NT_STATUS_NO_MEMORY;
638 els[1].num_values = 1;
639 els[1].values = &vals[1];
640 els[1].flags = LDB_FLAG_MOD_ADD;
641 els[1].name = els[0].name;
643 vals[0].data = talloc_asprintf(mem_ctx, "%u", *id);
645 return NT_STATUS_NO_MEMORY;
647 vals[0].length = strlen(vals[0].data);
649 vals[1].data = talloc_asprintf(mem_ctx, "%u", (*id)+1);
651 return NT_STATUS_NO_MEMORY;
653 vals[1].length = strlen(vals[1].data);
655 ret = ldb_modify(sam_ldb, &msg);
657 return NT_STATUS_UNEXPECTED_IO_ERROR;
666 allocate a new id, attempting to do it atomically
667 return 0 on failure, the id on success
669 NTSTATUS samdb_allocate_next_id(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, const char *dn, const char *attr,
675 /* we need to try multiple times to cope with two account
676 creations at the same time */
678 status = _samdb_allocate_next_id(sam_ldb, mem_ctx, dn, attr, id);
679 if (!NT_STATUS_EQUAL(NT_STATUS_UNEXPECTED_IO_ERROR, status)) {
684 if (NT_STATUS_EQUAL(NT_STATUS_UNEXPECTED_IO_ERROR, status)) {
685 DEBUG(1,("Failed to increment id %s at %s\n", attr, dn));
693 add a string element to a message
695 int samdb_msg_add_string(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
696 const char *attr_name, const char *str)
698 char *s = talloc_strdup(mem_ctx, str);
699 char *a = talloc_strdup(mem_ctx, attr_name);
700 if (s == NULL || a == NULL) {
703 return ldb_msg_add_string(sam_ldb, msg, a, s);
707 add a delete element operation to a message
709 int samdb_msg_add_delete(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
710 const char *attr_name)
712 char *a = talloc_strdup(mem_ctx, attr_name);
716 /* we use an empty replace rather than a delete, as it allows for
717 samdb_replace() to be used everywhere */
718 return ldb_msg_add_empty(sam_ldb, msg, a, LDB_FLAG_MOD_REPLACE);
722 add a add attribute value to a message
724 int samdb_msg_add_addval(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
725 const char *attr_name, const char *value)
727 struct ldb_message_element *el;
730 a = talloc_strdup(mem_ctx, attr_name);
733 v = talloc_strdup(mem_ctx, value);
736 ret = ldb_msg_add_string(sam_ldb, msg, a, v);
739 el = ldb_msg_find_element(msg, a);
742 el->flags = LDB_FLAG_MOD_ADD;
747 add a delete attribute value to a message
749 int samdb_msg_add_delval(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;
755 a = talloc_strdup(mem_ctx, attr_name);
758 v = talloc_strdup(mem_ctx, value);
761 ret = ldb_msg_add_string(sam_ldb, msg, a, v);
764 el = ldb_msg_find_element(msg, a);
767 el->flags = LDB_FLAG_MOD_DELETE;
772 add a uint_t element to a message
774 int samdb_msg_add_uint(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
775 const char *attr_name, uint_t v)
777 const char *s = talloc_asprintf(mem_ctx, "%u", v);
778 return samdb_msg_add_string(sam_ldb, mem_ctx, msg, attr_name, s);
782 add a (signed) int64_t element to a message
784 int samdb_msg_add_int64(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
785 const char *attr_name, int64_t v)
787 const char *s = talloc_asprintf(mem_ctx, "%lld", v);
788 return samdb_msg_add_string(sam_ldb, mem_ctx, msg, attr_name, s);
792 add a uint64_t element to a message
794 int samdb_msg_add_uint64(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
795 const char *attr_name, uint64_t v)
797 const char *s = talloc_asprintf(mem_ctx, "%llu", v);
798 return samdb_msg_add_string(sam_ldb, mem_ctx, msg, attr_name, s);
802 add a samr_Password element to a message
804 int samdb_msg_add_hash(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
805 const char *attr_name, struct samr_Password *hash)
808 val.data = talloc_memdup(mem_ctx, hash->hash, 16);
813 return ldb_msg_add_value(sam_ldb, msg, attr_name, &val);
817 add a samr_Password array to a message
819 int samdb_msg_add_hashes(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
820 const char *attr_name, struct samr_Password *hashes, uint_t count)
824 val.data = talloc_array_size(mem_ctx, 16, count);
825 val.length = count*16;
829 for (i=0;i<count;i++) {
830 memcpy(i*16 + (char *)val.data, hashes[i].hash, 16);
832 return ldb_msg_add_value(sam_ldb, msg, attr_name, &val);
836 add a acct_flags element to a message
838 int samdb_msg_add_acct_flags(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
839 const char *attr_name, uint32_t v)
841 return samdb_msg_add_uint(sam_ldb, mem_ctx, msg, attr_name, samdb_acb2uf(v));
845 add a logon_hours element to a message
847 int samdb_msg_add_logon_hours(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
848 const char *attr_name, struct samr_LogonHours *hours)
851 val.length = hours->units_per_week / 8;
852 val.data = hours->bits;
853 return ldb_msg_add_value(sam_ldb, msg, attr_name, &val);
857 add a general value element to a message
859 int samdb_msg_add_value(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
860 const char *attr_name, const struct ldb_val *val)
862 return ldb_msg_add_value(sam_ldb, msg, attr_name, val);
866 sets a general value element to a message
868 int samdb_msg_set_value(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
869 const char *attr_name, const struct ldb_val *val)
871 struct ldb_message_element *el;
873 el = ldb_msg_find_element(msg, attr_name);
877 return ldb_msg_add_value(sam_ldb, msg, attr_name, val);
881 set a string element in a message
883 int samdb_msg_set_string(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
884 const char *attr_name, const char *str)
886 struct ldb_message_element *el;
888 el = ldb_msg_find_element(msg, attr_name);
892 return samdb_msg_add_string(sam_ldb, mem_ctx, msg, attr_name, str);
896 set a ldaptime element in a message
898 int samdb_msg_set_ldaptime(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
899 const char *attr_name, time_t t)
901 char *str = ldap_timestring(mem_ctx, t);
905 return samdb_msg_set_string(sam_ldb, mem_ctx, msg, attr_name, str);
911 int samdb_add(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg)
915 time_t now = time(NULL);
917 guid = GUID_random();
918 guidstr = GUID_string(mem_ctx, &guid);
923 samdb_msg_add_string(sam_ldb, mem_ctx, msg, "objectGUID", guidstr);
924 samdb_msg_set_ldaptime(sam_ldb, mem_ctx, msg, "whenCreated", now);
925 return ldb_add(sam_ldb, msg);
931 int samdb_delete(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, const char *dn)
933 return ldb_delete(sam_ldb, dn);
939 int samdb_modify(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg)
941 return ldb_modify(sam_ldb, msg);
945 replace elements in a record
947 int samdb_replace(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg)
951 /* mark all the message elements as LDB_FLAG_MOD_REPLACE */
952 for (i=0;i<msg->num_elements;i++) {
953 msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
956 /* modify the samdb record */
957 return samdb_modify(sam_ldb, mem_ctx, msg);
961 return a default security descriptor
963 struct security_descriptor *samdb_default_security_descriptor(TALLOC_CTX *mem_ctx)
965 struct security_descriptor *sd;
967 sd = security_descriptor_initialise(mem_ctx);