r7582: Better way to have a fast path searching for a specific DN.
[jelmer/samba4-debian.git] / source / dsdb / samdb / samdb.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    interface functions for the sam database
5
6    Copyright (C) Andrew Tridgell 2004
7    
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.
12    
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.
17    
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.
21 */
22
23 #include "includes.h"
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"
28 #include "db_wrap.h"
29
30 /*
31   connect to the SAM database
32   return an opaque context pointer on success, or NULL on failure
33  */
34 struct ldb_context *samdb_connect(TALLOC_CTX *mem_ctx)
35 {
36         return ldb_wrap_connect(mem_ctx, lp_sam_url(), 0, NULL);
37 }
38
39 /*
40   search the sam for the specified attributes in a specific domain, filter on
41   objectSid being in domain_sid.
42 */
43 int samdb_search_domain(struct ldb_context *sam_ldb,
44                         TALLOC_CTX *mem_ctx, 
45                         const char *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)
50 {
51         va_list ap;
52         int i, count;
53
54         va_start(ap, format);
55         count = gendb_search_v(sam_ldb, mem_ctx, basedn, res, attrs,
56                                format, ap);
57         va_end(ap);
58
59         i=0;
60
61         while (i<count) {
62                 struct dom_sid *entry_sid;
63
64                 entry_sid = samdb_result_dom_sid(mem_ctx, (*res)[i],
65                                                  "objectSid");
66
67                 if ((entry_sid == NULL) ||
68                     (!dom_sid_in_domain(domain_sid, entry_sid))) {
69
70                         /* Delete that entry from the result set */
71                         (*res)[i] = (*res)[count-1];
72                         count -= 1;
73                         continue;
74                 }
75                 i += 1;
76         }
77
78         return count;
79 }
80
81 /*
82   search the sam for a single string attribute in exactly 1 record
83 */
84 const char *samdb_search_string_v(struct ldb_context *sam_ldb,
85                                   TALLOC_CTX *mem_ctx,
86                                   const char *basedn,
87                                   const char *attr_name,
88                                   const char *format, va_list ap) _PRINTF_ATTRIBUTE(5,0)
89 {
90         int count;
91         const char * const attrs[2] = { attr_name, NULL };
92         struct ldb_message **res = NULL;
93
94         count = gendb_search_v(sam_ldb, mem_ctx, basedn, &res, attrs, format, ap);
95         if (count > 1) {                
96                 DEBUG(1,("samdb: search for %s %s not single valued (count=%d)\n", 
97                          attr_name, format, count));
98         }
99         if (count != 1) {
100                 talloc_free(res);
101                 return NULL;
102         }
103
104         return samdb_result_string(res[0], attr_name, NULL);
105 }
106                                  
107
108 /*
109   search the sam for a single string attribute in exactly 1 record
110 */
111 const char *samdb_search_string(struct ldb_context *sam_ldb,
112                                 TALLOC_CTX *mem_ctx,
113                                 const char *basedn,
114                                 const char *attr_name,
115                                 const char *format, ...) _PRINTF_ATTRIBUTE(5,6)
116 {
117         va_list ap;
118         const char *str;
119
120         va_start(ap, format);
121         str = samdb_search_string_v(sam_ldb, mem_ctx, basedn, attr_name, format, ap);
122         va_end(ap);
123
124         return str;
125 }
126
127 /*
128   return the count of the number of records in the sam matching the query
129 */
130 int samdb_search_count(struct ldb_context *sam_ldb,
131                        TALLOC_CTX *mem_ctx,
132                        const char *basedn,
133                        const char *format, ...) _PRINTF_ATTRIBUTE(4,5)
134 {
135         va_list ap;
136         struct ldb_message **res;
137         const char * const attrs[] = { NULL };
138         int ret;
139
140         va_start(ap, format);
141         ret = gendb_search_v(sam_ldb, mem_ctx, basedn, &res, attrs, format, ap);
142         va_end(ap);
143
144         return ret;
145 }
146
147
148 /*
149   search the sam for a single integer attribute in exactly 1 record
150 */
151 uint_t samdb_search_uint(struct ldb_context *sam_ldb,
152                          TALLOC_CTX *mem_ctx,
153                          uint_t default_value,
154                          const char *basedn,
155                          const char *attr_name,
156                          const char *format, ...) _PRINTF_ATTRIBUTE(6,7)
157 {
158         va_list ap;
159         int count;
160         struct ldb_message **res;
161         const char * const attrs[2] = { attr_name, NULL };
162
163         va_start(ap, format);
164         count = gendb_search_v(sam_ldb, mem_ctx, basedn, &res, attrs, format, ap);
165         va_end(ap);
166
167         if (count != 1) {
168                 return default_value;
169         }
170
171         return samdb_result_uint(res[0], attr_name, default_value);
172 }
173
174 /*
175   search the sam for a single signed 64 bit integer attribute in exactly 1 record
176 */
177 int64_t samdb_search_int64(struct ldb_context *sam_ldb,
178                            TALLOC_CTX *mem_ctx,
179                            int64_t default_value,
180                            const char *basedn,
181                            const char *attr_name,
182                            const char *format, ...) _PRINTF_ATTRIBUTE(6,7)
183 {
184         va_list ap;
185         int count;
186         struct ldb_message **res;
187         const char * const attrs[2] = { attr_name, NULL };
188
189         va_start(ap, format);
190         count = gendb_search_v(sam_ldb, mem_ctx, basedn, &res, attrs, format, ap);
191         va_end(ap);
192
193         if (count != 1) {
194                 return default_value;
195         }
196
197         return samdb_result_int64(res[0], attr_name, default_value);
198 }
199
200 /*
201   search the sam for multipe records each giving a single string attribute
202   return the number of matches, or -1 on error
203 */
204 int samdb_search_string_multiple(struct ldb_context *sam_ldb,
205                                  TALLOC_CTX *mem_ctx,
206                                  const char *basedn,
207                                  const char ***strs,
208                                  const char *attr_name,
209                                  const char *format, ...) _PRINTF_ATTRIBUTE(6,7)
210 {
211         va_list ap;
212         int count, i;
213         const char * const attrs[2] = { attr_name, NULL };
214         struct ldb_message **res = NULL;
215
216         va_start(ap, format);
217         count = gendb_search_v(sam_ldb, mem_ctx, basedn, &res, attrs, format, ap);
218         va_end(ap);
219
220         if (count <= 0) {
221                 return count;
222         }
223
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", 
228                                  attr_name, format));
229                         talloc_free(res);
230                         return -1;
231                 }
232         }
233
234         *strs = talloc_array(mem_ctx, const char *, count+1);
235         if (! *strs) {
236                 talloc_free(res);
237                 return -1;
238         }
239
240         for (i=0;i<count;i++) {
241                 (*strs)[i] = samdb_result_string(res[i], attr_name, NULL);
242         }
243         (*strs)[count] = NULL;
244
245         return count;
246 }
247
248 /*
249   pull a uint from a result set. 
250 */
251 uint_t samdb_result_uint(struct ldb_message *msg, const char *attr, uint_t default_value)
252 {
253         return ldb_msg_find_uint(msg, attr, default_value);
254 }
255
256 /*
257   pull a (signed) int64 from a result set. 
258 */
259 int64_t samdb_result_int64(struct ldb_message *msg, const char *attr, int64_t default_value)
260 {
261         return ldb_msg_find_int64(msg, attr, default_value);
262 }
263
264 /*
265   pull a string from a result set. 
266 */
267 const char *samdb_result_string(struct ldb_message *msg, const char *attr, 
268                                 const char *default_value)
269 {
270         return ldb_msg_find_string(msg, attr, default_value);
271 }
272
273 /*
274   pull a rid from a objectSid in a result set. 
275 */
276 uint32_t samdb_result_rid_from_sid(TALLOC_CTX *mem_ctx, struct ldb_message *msg, 
277                                  const char *attr, uint32_t default_value)
278 {
279         struct dom_sid *sid;
280         const char *sidstr = ldb_msg_find_string(msg, attr, NULL);
281         if (!sidstr) return default_value;
282
283         sid = dom_sid_parse_talloc(mem_ctx, sidstr);
284         if (!sid) return default_value;
285
286         return sid->sub_auths[sid->num_auths-1];
287 }
288
289 /*
290   pull a dom_sid structure from a objectSid in a result set. 
291 */
292 struct dom_sid *samdb_result_dom_sid(TALLOC_CTX *mem_ctx, struct ldb_message *msg, 
293                                      const char *attr)
294 {
295         const char *sidstr = ldb_msg_find_string(msg, attr, NULL);
296         if (!sidstr) return NULL;
297
298         return dom_sid_parse_talloc(mem_ctx, sidstr);
299 }
300
301 /*
302   pull a guid structure from a objectGUID in a result set. 
303 */
304 struct GUID samdb_result_guid(struct ldb_message *msg, const char *attr)
305 {
306         NTSTATUS status;
307         struct GUID guid;
308         const char *guidstr = ldb_msg_find_string(msg, attr, NULL);
309
310         ZERO_STRUCT(guid);
311
312         if (!guidstr) return guid;
313
314         status = GUID_from_string(guidstr, &guid);
315         if (!NT_STATUS_IS_OK(status)) {
316                 ZERO_STRUCT(guid);
317                 return guid;
318         }
319
320         return guid;
321 }
322
323 /*
324   pull a sid prefix from a objectSid in a result set. 
325   this is used to find the domain sid for a user
326 */
327 const char *samdb_result_sid_prefix(TALLOC_CTX *mem_ctx, struct ldb_message *msg, 
328                                     const char *attr)
329 {
330         struct dom_sid *sid = samdb_result_dom_sid(mem_ctx, msg, attr);
331         if (!sid || sid->num_auths < 1) return NULL;
332
333         sid->num_auths--;
334
335         return dom_sid_string(mem_ctx, sid);
336 }
337
338 /*
339   pull a NTTIME in a result set. 
340 */
341 NTTIME samdb_result_nttime(struct ldb_message *msg, const char *attr, const char *default_value)
342 {
343         const char *str = ldb_msg_find_string(msg, attr, default_value);
344         return nttime_from_string(str);
345 }
346
347 /*
348   pull a uint64_t from a result set. 
349 */
350 uint64_t samdb_result_uint64(struct ldb_message *msg, const char *attr, uint64_t default_value)
351 {
352         return ldb_msg_find_uint64(msg, attr, default_value);
353 }
354
355
356 /*
357   construct the allow_password_change field from the PwdLastSet attribute and the 
358   domain password settings
359 */
360 NTTIME samdb_result_allow_password_change(struct ldb_context *sam_ldb, 
361                                           TALLOC_CTX *mem_ctx, 
362                                           const char *domain_dn, 
363                                           struct ldb_message *msg, 
364                                           const char *attr)
365 {
366         uint64_t attr_time = samdb_result_uint64(msg, attr, 0);
367         int64_t minPwdAge;
368
369         if (attr_time == 0) {
370                 return 0;
371         }
372
373         minPwdAge = samdb_search_int64(sam_ldb, mem_ctx, 0,
374                                        domain_dn, "minPwdAge", NULL);
375
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;
379
380         return attr_time;
381 }
382
383 /*
384   construct the force_password_change field from the PwdLastSet attribute and the 
385   domain password settings
386 */
387 NTTIME samdb_result_force_password_change(struct ldb_context *sam_ldb, 
388                                           TALLOC_CTX *mem_ctx, 
389                                           const char *domain_dn, 
390                                           struct ldb_message *msg, 
391                                           const char *attr)
392 {
393         uint64_t attr_time = samdb_result_uint64(msg, attr, 0);
394         int64_t maxPwdAge;
395
396         if (attr_time == 0) {
397                 return 0;
398         }
399
400         maxPwdAge = samdb_search_int64(sam_ldb, mem_ctx, 0, domain_dn, "maxPwdAge", NULL);
401         if (maxPwdAge == 0) {
402                 return 0;
403         } else {
404                 attr_time -= maxPwdAge;
405         }
406
407         return attr_time;
408 }
409
410 /*
411   pull a samr_Password structutre from a result set. 
412 */
413 struct samr_Password samdb_result_hash(struct ldb_message *msg, const char *attr)
414 {
415         struct samr_Password hash;
416         const struct ldb_val *val = ldb_msg_find_ldb_val(msg, attr);
417         ZERO_STRUCT(hash);
418         if (val) {
419                 memcpy(hash.hash, val->data, MIN(val->length, sizeof(hash.hash)));
420         }
421         return hash;
422 }
423
424 /*
425   pull an array of samr_Password structutres from a result set. 
426 */
427 uint_t samdb_result_hashes(TALLOC_CTX *mem_ctx, struct ldb_message *msg, 
428                            const char *attr, struct samr_Password **hashes)
429 {
430         uint_t count = 0;
431         const struct ldb_val *val = ldb_msg_find_ldb_val(msg, attr);
432         int i;
433
434         *hashes = NULL;
435         if (!val) {
436                 return 0;
437         }
438         count = val->length / 16;
439         if (count == 0) {
440                 return 0;
441         }
442
443         *hashes = talloc_array(mem_ctx, struct samr_Password, count);
444         if (! *hashes) {
445                 return 0;
446         }
447
448         for (i=0;i<count;i++) {
449                 memcpy((*hashes)[i].hash, (i*16)+(char *)val->data, 16);
450         }
451
452         return count;
453 }
454
455 NTSTATUS samdb_result_passwords(TALLOC_CTX *mem_ctx, struct ldb_message *msg, 
456                                 struct samr_Password **lm_pwd, struct samr_Password **nt_pwd) 
457 {
458
459         const char *unicodePwd = samdb_result_string(msg, "unicodePwd", NULL);
460         
461         struct samr_Password *lmPwdHash, *ntPwdHash;
462         if (unicodePwd) {
463                 if (nt_pwd) {
464                         ntPwdHash = talloc(mem_ctx, struct samr_Password);
465                         if (!ntPwdHash) {
466                                 return NT_STATUS_NO_MEMORY;
467                         }
468                         
469                         E_md4hash(unicodePwd, ntPwdHash->hash);
470                         *nt_pwd = ntPwdHash;
471                 }
472
473                 if (lm_pwd) {
474                         BOOL lm_hash_ok;
475                 
476                         lmPwdHash = talloc(mem_ctx, struct samr_Password);
477                         if (!lmPwdHash) {
478                                 return NT_STATUS_NO_MEMORY;
479                         }
480                         
481                         /* compute the new nt and lm hashes */
482                         lm_hash_ok = E_deshash(unicodePwd, lmPwdHash->hash);
483                         
484                         if (lm_hash_ok) {
485                                 *lm_pwd = lmPwdHash;
486                         } else {
487                                 *lm_pwd = NULL;
488                         }
489                 }
490         } else {
491                 if (nt_pwd) {
492                         int num_nt;
493                         num_nt = samdb_result_hashes(mem_ctx, msg, "ntPwdHash", &ntPwdHash);
494                         if (num_nt == 0) {
495                                 *nt_pwd = NULL;
496                         } else if (num_nt > 1) {
497                                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
498                         } else {
499                                 *nt_pwd = &ntPwdHash[0];
500                         }
501                 }
502                 if (lm_pwd) {
503                         int num_lm;
504                         num_lm = samdb_result_hashes(mem_ctx, msg, "lmPwdHash", &lmPwdHash);
505                         if (num_lm == 0) {
506                                 *lm_pwd = NULL;
507                         } else if (num_lm > 1) {
508                                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
509                         } else {
510                                 *lm_pwd = &lmPwdHash[0];
511                         }
512                 }
513                 
514         }
515         return NT_STATUS_OK;
516 }
517
518 /*
519   pull a samr_LogonHours structutre from a result set. 
520 */
521 struct samr_LogonHours samdb_result_logon_hours(TALLOC_CTX *mem_ctx, struct ldb_message *msg, const char *attr)
522 {
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);
526         ZERO_STRUCT(hours);
527         hours.bits = talloc_array(mem_ctx, uint8_t, units_per_week);
528         if (!hours.bits) {
529                 return hours;
530         }
531         hours.units_per_week = units_per_week;
532         memset(hours.bits, 0xFF, units_per_week);
533         if (val) {
534                 memcpy(hours.bits, val->data, MIN(val->length, units_per_week));
535         }
536         return hours;
537 }
538
539 /*
540   pull a set of account_flags from a result set. 
541 */
542 uint16_t samdb_result_acct_flags(struct ldb_message *msg, const char *attr)
543 {
544         uint_t userAccountControl = ldb_msg_find_uint(msg, attr, 0);
545         return samdb_uf2acb(userAccountControl);
546 }
547
548 /*
549   copy from a template record to a message
550 */
551 int samdb_copy_template(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, 
552                         struct ldb_message *msg, const char *expression)
553 {
554         struct ldb_message **res, *t;
555         int ret, i, j;
556         
557
558         /* pull the template record */
559         ret = gendb_search(sam_ldb, mem_ctx, NULL, &res, NULL, "%s", expression);
560         if (ret != 1) {
561                 DEBUG(1,("samdb: ERROR: template '%s' matched %d records\n", 
562                          expression, ret));
563                 return -1;
564         }
565         t = res[0];
566
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) {
573                         continue;
574                 }
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)) {
584                                 continue;
585                         }
586                         samdb_msg_add_string(sam_ldb, mem_ctx, msg, el->name, 
587                                              (char *)el->values[j].data);
588                 }
589         }
590
591         return 0;
592 }
593
594
595 /*
596   allocate a new id, attempting to do it atomically
597   return 0 on failure, the id on success
598 */
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)
601 {
602         struct ldb_message msg;
603         int ret;
604         const char *str;
605         struct ldb_val vals[2];
606         struct ldb_message_element els[2];
607
608         str = samdb_search_string(sam_ldb, mem_ctx, dn, attr, NULL);
609         if (!str) {
610                 DEBUG(1,("id not found at %s %s\n", dn, attr));
611                 return NT_STATUS_OBJECT_NAME_INVALID;
612         }
613
614         *id = strtol(str, NULL, 0);
615         if ((*id)+1 == 0) {
616                 /* out of IDs ! */
617                 return NT_STATUS_INSUFFICIENT_RESOURCES;
618         }
619
620         /* we do a delete and add as a single operation. That prevents
621            a race */
622         ZERO_STRUCT(msg);
623         msg.dn = talloc_strdup(mem_ctx, dn);
624         if (!msg.dn) {
625                 return NT_STATUS_NO_MEMORY;
626         }
627         msg.num_elements = 2;
628         msg.elements = els;
629
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);
634         if (!els[0].name) {
635                 return NT_STATUS_NO_MEMORY;
636         }
637
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;
642
643         vals[0].data = talloc_asprintf(mem_ctx, "%u", *id);
644         if (!vals[0].data) {
645                 return NT_STATUS_NO_MEMORY;
646         }
647         vals[0].length = strlen(vals[0].data);
648
649         vals[1].data = talloc_asprintf(mem_ctx, "%u", (*id)+1);
650         if (!vals[1].data) {
651                 return NT_STATUS_NO_MEMORY;
652         }
653         vals[1].length = strlen(vals[1].data);
654
655         ret = ldb_modify(sam_ldb, &msg);
656         if (ret != 0) {
657                 return NT_STATUS_UNEXPECTED_IO_ERROR;
658         }
659
660         (*id)++;
661
662         return NT_STATUS_OK;
663 }
664
665 /*
666   allocate a new id, attempting to do it atomically
667   return 0 on failure, the id on success
668 */
669 NTSTATUS samdb_allocate_next_id(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, const char *dn, const char *attr,
670                                 uint32_t *id)
671 {
672         int tries = 10;
673         NTSTATUS status;
674
675         /* we need to try multiple times to cope with two account
676            creations at the same time */
677         while (tries--) {
678                 status = _samdb_allocate_next_id(sam_ldb, mem_ctx, dn, attr, id);
679                 if (!NT_STATUS_EQUAL(NT_STATUS_UNEXPECTED_IO_ERROR, status)) {
680                         break;
681                 }
682         }
683
684         if (NT_STATUS_EQUAL(NT_STATUS_UNEXPECTED_IO_ERROR, status)) {
685                 DEBUG(1,("Failed to increment id %s at %s\n", attr, dn));
686         }
687
688         return status;
689 }
690
691
692 /*
693   add a string element to a message
694 */
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)
697 {
698         char *s = talloc_strdup(mem_ctx, str);
699         char *a = talloc_strdup(mem_ctx, attr_name);
700         if (s == NULL || a == NULL) {
701                 return -1;
702         }
703         return ldb_msg_add_string(sam_ldb, msg, a, s);
704 }
705
706 /*
707   add a delete element operation to a message
708 */
709 int samdb_msg_add_delete(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
710                          const char *attr_name)
711 {
712         char *a = talloc_strdup(mem_ctx, attr_name);
713         if (a == NULL) {
714                 return -1;
715         }
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);
719 }
720
721 /*
722   add a add attribute value to a message
723 */
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)
726 {
727         struct ldb_message_element *el;
728         char *a, *v;
729         int ret;
730         a = talloc_strdup(mem_ctx, attr_name);
731         if (a == NULL)
732                 return -1;
733         v = talloc_strdup(mem_ctx, value);
734         if (v == NULL)
735                 return -1;
736         ret = ldb_msg_add_string(sam_ldb, msg, a, v);
737         if (ret != 0)
738                 return ret;
739         el = ldb_msg_find_element(msg, a);
740         if (el == NULL)
741                 return -1;
742         el->flags = LDB_FLAG_MOD_ADD;
743         return 0;
744 }
745
746 /*
747   add a delete attribute value to a message
748 */
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)
751 {
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(sam_ldb, 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_DELETE;
768         return 0;
769 }
770
771 /*
772   add a uint_t element to a message
773 */
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)
776 {
777         const char *s = talloc_asprintf(mem_ctx, "%u", v);
778         return samdb_msg_add_string(sam_ldb, mem_ctx, msg, attr_name, s);
779 }
780
781 /*
782   add a (signed) int64_t element to a message
783 */
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)
786 {
787         const char *s = talloc_asprintf(mem_ctx, "%lld", v);
788         return samdb_msg_add_string(sam_ldb, mem_ctx, msg, attr_name, s);
789 }
790
791 /*
792   add a uint64_t element to a message
793 */
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)
796 {
797         const char *s = talloc_asprintf(mem_ctx, "%llu", v);
798         return samdb_msg_add_string(sam_ldb, mem_ctx, msg, attr_name, s);
799 }
800
801 /*
802   add a samr_Password element to a message
803 */
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)
806 {
807         struct ldb_val val;
808         val.data = talloc_memdup(mem_ctx, hash->hash, 16);
809         if (!val.data) {
810                 return -1;
811         }
812         val.length = 16;
813         return ldb_msg_add_value(sam_ldb, msg, attr_name, &val);
814 }
815
816 /*
817   add a samr_Password array to a message
818 */
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)
821 {
822         struct ldb_val val;
823         int i;
824         val.data = talloc_array_size(mem_ctx, 16, count);
825         val.length = count*16;
826         if (!val.data) {
827                 return -1;
828         }
829         for (i=0;i<count;i++) {
830                 memcpy(i*16 + (char *)val.data, hashes[i].hash, 16);
831         }
832         return ldb_msg_add_value(sam_ldb, msg, attr_name, &val);
833 }
834
835 /*
836   add a acct_flags element to a message
837 */
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)
840 {
841         return samdb_msg_add_uint(sam_ldb, mem_ctx, msg, attr_name, samdb_acb2uf(v));
842 }
843
844 /*
845   add a logon_hours element to a message
846 */
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)
849 {
850         struct ldb_val val;
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);
854 }
855
856 /*
857   add a general value element to a message
858 */
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)
861 {
862         return ldb_msg_add_value(sam_ldb, msg, attr_name, val);
863 }
864
865 /*
866   sets a general value element to a message
867 */
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)
870 {
871         struct ldb_message_element *el;
872
873         el = ldb_msg_find_element(msg, attr_name);
874         if (el) {
875                 el->num_values = 0;
876         }
877         return ldb_msg_add_value(sam_ldb, msg, attr_name, val);
878 }
879
880 /*
881   set a string element in a message
882 */
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)
885 {
886         struct ldb_message_element *el;
887
888         el = ldb_msg_find_element(msg, attr_name);
889         if (el) {
890                 el->num_values = 0;
891         }
892         return samdb_msg_add_string(sam_ldb, mem_ctx, msg, attr_name, str);
893 }
894
895 /*
896   set a ldaptime element in a message
897 */
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)
900 {
901         char *str = ldap_timestring(mem_ctx, t);
902         if (!str) {
903                 return -1;
904         }
905         return samdb_msg_set_string(sam_ldb, mem_ctx, msg, attr_name, str);
906 }
907
908 /*
909   add a record
910 */
911 int samdb_add(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg)
912 {
913         struct GUID guid;
914         const char *guidstr;
915         time_t now = time(NULL);
916         /* a new GUID */
917         guid = GUID_random();
918         guidstr = GUID_string(mem_ctx, &guid);
919         if (!guidstr) {
920                 return -1;
921         }
922
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         samdb_msg_set_ldaptime(sam_ldb, mem_ctx, msg, "whenChanged", now);
926         return ldb_add(sam_ldb, msg);
927 }
928
929 /*
930   delete a record
931 */
932 int samdb_delete(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, const char *dn)
933 {
934         return ldb_delete(sam_ldb, dn);
935 }
936
937 /*
938   modify a record
939 */
940 int samdb_modify(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg)
941 {
942         time_t now = time(NULL);
943         samdb_msg_set_ldaptime(sam_ldb, mem_ctx, msg, "whenChanged", now);
944         return ldb_modify(sam_ldb, msg);
945 }
946
947 /*
948   replace elements in a record
949 */
950 int samdb_replace(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg)
951 {
952         int i;
953
954         /* mark all the message elements as LDB_FLAG_MOD_REPLACE */
955         for (i=0;i<msg->num_elements;i++) {
956                 msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
957         }
958
959         /* modify the samdb record */
960         return samdb_modify(sam_ldb, mem_ctx, msg);
961 }
962
963 /*
964   return a default security descriptor
965 */
966 struct security_descriptor *samdb_default_security_descriptor(TALLOC_CTX *mem_ctx)
967 {
968         struct security_descriptor *sd;
969
970         sd = security_descriptor_initialise(mem_ctx);
971
972         return sd;
973 }