r890: convert samba4 to use [u]int8_t instead of [u]int8
[samba.git] / source4 / rpc_server / samr / 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
25 struct samdb_context {
26         struct ldb_context *ldb;
27 };
28
29
30 /*
31   this is used to catch debug messages from ldb
32 */
33 void samdb_debug(void *context, enum ldb_debug_level level, const char *fmt, va_list ap)
34 {
35         char *s = NULL;
36         if (DEBUGLEVEL < 4 && level > LDB_DEBUG_WARNING) {
37                 return;
38         }
39         vasprintf(&s, fmt, ap);
40         if (!s) return;
41         DEBUG(level, ("samdb: %s\n", s));
42         free(s);
43 }
44
45 /*
46   connect to the SAM database
47   return an opaque context pointer on success, or NULL on failure
48  */
49 void *samdb_connect(void)
50 {
51         struct samdb_context *ctx;
52         /*
53           the way that unix fcntl locking works forces us to have a
54           static ldb handle here rather than a much more sensible
55           approach of having the ldb handle as part of the
56           samr_Connect() pipe state. Otherwise we would try to open
57           the ldb more than once, and tdb would rightly refuse the
58           second open due to the broken nature of unix locking.
59         */
60         static struct ldb_context *static_sam_db;
61
62         if (static_sam_db == NULL) {
63                 static_sam_db = ldb_connect(lp_sam_url(), 0, NULL);
64                 if (static_sam_db == NULL) {
65                         return NULL;
66                 }
67         }
68
69         ldb_set_debug(static_sam_db, samdb_debug, NULL);
70
71         ctx = malloc_p(struct samdb_context);
72         if (!ctx) {
73                 errno = ENOMEM;
74                 return NULL;
75         }
76
77         ctx->ldb = static_sam_db;
78
79         return ctx;
80 }
81
82 /* close a connection to the sam */
83 void samdb_close(void *ctx)
84 {
85         struct samdb_context *sam_ctx = ctx;
86         /* we don't actually close due to broken posix locking semantics */
87         sam_ctx->ldb = NULL;
88         free(sam_ctx);
89 }
90
91 /*
92   a alloc function for ldb
93 */
94 static void *samdb_alloc(void *context, void *ptr, size_t size)
95 {
96         return talloc_realloc((TALLOC_CTX *)context, ptr, size);
97 }
98
99 /*
100   search the sam for the specified attributes - va_list varient
101 */
102 int samdb_search_v(void *ctx, 
103                    TALLOC_CTX *mem_ctx,
104                    const char *basedn,
105                    struct ldb_message ***res,
106                    const char * const *attrs,
107                    const char *format, 
108                    va_list ap)
109 {
110         struct samdb_context *sam_ctx = ctx;
111         char *expr = NULL;
112         int count;
113
114         vasprintf(&expr, format, ap);
115         if (expr == NULL) {
116                 return -1;
117         }
118
119         ldb_set_alloc(sam_ctx->ldb, samdb_alloc, mem_ctx);
120
121         count = ldb_search(sam_ctx->ldb, basedn, LDB_SCOPE_SUBTREE, expr, attrs, res);
122
123         DEBUG(4,("samdb_search_v: %s %s -> %d\n", basedn?basedn:"NULL", expr, count));
124
125         free(expr);
126
127         return count;
128 }
129                                  
130
131 /*
132   search the sam for the specified attributes - varargs varient
133 */
134 int samdb_search(void *ctx,
135                  TALLOC_CTX *mem_ctx, 
136                  const char *basedn,
137                  struct ldb_message ***res,
138                  const char * const *attrs,
139                  const char *format, ...)
140 {
141         va_list ap;
142         int count;
143
144         va_start(ap, format);
145         count = samdb_search_v(ctx, mem_ctx, basedn, res, attrs, format, ap);
146         va_end(ap);
147
148         return count;
149 }
150
151 /*
152   free up a search result
153 */
154 int samdb_search_free(void *ctx,
155                       TALLOC_CTX *mem_ctx, struct ldb_message **res)
156 {
157         struct samdb_context *sam_ctx = ctx;
158         ldb_set_alloc(sam_ctx->ldb, samdb_alloc, mem_ctx);
159         return ldb_search_free(sam_ctx->ldb, res);
160 }
161
162 /*
163   search the sam for a single string attribute in exactly 1 record
164 */
165 const char *samdb_search_string_v(void *ctx,
166                                   TALLOC_CTX *mem_ctx,
167                                   const char *basedn,
168                                   const char *attr_name,
169                                   const char *format, va_list ap)
170 {
171         int count;
172         const char * const attrs[2] = { attr_name, NULL };
173         struct ldb_message **res = NULL;
174
175         count = samdb_search_v(ctx, mem_ctx, basedn, &res, attrs, format, ap);
176         if (count > 1) {                
177                 DEBUG(1,("samdb: search for %s %s not single valued (count=%d)\n", 
178                          attr_name, format, count));
179         }
180         if (count != 1) {
181                 samdb_search_free(ctx, mem_ctx, res);
182                 return NULL;
183         }
184
185         return samdb_result_string(res[0], attr_name, NULL);
186 }
187                                  
188
189 /*
190   search the sam for a single string attribute in exactly 1 record
191 */
192 const char *samdb_search_string(void *ctx,
193                                 TALLOC_CTX *mem_ctx,
194                                 const char *basedn,
195                                 const char *attr_name,
196                                 const char *format, ...)
197 {
198         va_list ap;
199         const char *str;
200
201         va_start(ap, format);
202         str = samdb_search_string_v(ctx, mem_ctx, basedn, attr_name, format, ap);
203         va_end(ap);
204
205         return str;
206 }
207
208
209 /*
210   search the sam for a single integer attribute in exactly 1 record
211 */
212 uint_t samdb_search_uint(void *ctx,
213                          TALLOC_CTX *mem_ctx,
214                          uint_t default_value,
215                          const char *basedn,
216                          const char *attr_name,
217                          const char *format, ...)
218 {
219         va_list ap;
220         int count;
221         struct ldb_message **res;
222         const char * const attrs[2] = { attr_name, NULL };
223
224         va_start(ap, format);
225         count = samdb_search_v(ctx, mem_ctx, basedn, &res, attrs, format, ap);
226         va_end(ap);
227
228         if (count != 1) {
229                 return default_value;
230         }
231
232         return samdb_result_uint(res[0], attr_name, default_value);
233 }
234
235 /*
236   search the sam for a single signed 64 bit integer attribute in exactly 1 record
237 */
238 int64_t samdb_search_int64(void *ctx,
239                            TALLOC_CTX *mem_ctx,
240                            int64_t default_value,
241                            const char *basedn,
242                            const char *attr_name,
243                            const char *format, ...)
244 {
245         va_list ap;
246         int count;
247         struct ldb_message **res;
248         const char * const attrs[2] = { attr_name, NULL };
249
250         va_start(ap, format);
251         count = samdb_search_v(ctx, mem_ctx, basedn, &res, attrs, format, ap);
252         va_end(ap);
253
254         if (count != 1) {
255                 return default_value;
256         }
257
258         return samdb_result_int64(res[0], attr_name, default_value);
259 }
260
261 /*
262   search the sam for multipe records each giving a single string attribute
263   return the number of matches, or -1 on error
264 */
265 int samdb_search_string_multiple(void *ctx,
266                                  TALLOC_CTX *mem_ctx,
267                                  const char *basedn,
268                                  const char ***strs,
269                                  const char *attr_name,
270                                  const char *format, ...)
271 {
272         va_list ap;
273         int count, i;
274         const char * const attrs[2] = { attr_name, NULL };
275         struct ldb_message **res = NULL;
276
277         va_start(ap, format);
278         count = samdb_search_v(ctx, mem_ctx, basedn, &res, attrs, format, ap);
279         va_end(ap);
280
281         if (count <= 0) {
282                 return count;
283         }
284
285         /* make sure its single valued */
286         for (i=0;i<count;i++) {
287                 if (res[i]->num_elements != 1) {
288                         DEBUG(1,("samdb: search for %s %s not single valued\n", 
289                                  attr_name, format));
290                         samdb_search_free(ctx, mem_ctx, res);
291                         return -1;
292                 }
293         }
294
295         *strs = talloc_array_p(mem_ctx, const char *, count+1);
296         if (! *strs) {
297                 samdb_search_free(ctx, mem_ctx, res);
298                 return -1;
299         }
300
301         for (i=0;i<count;i++) {
302                 (*strs)[i] = samdb_result_string(res[i], attr_name, NULL);
303         }
304         (*strs)[count] = NULL;
305
306         return count;
307 }
308
309 /*
310   pull a uint from a result set. 
311 */
312 uint_t samdb_result_uint(struct ldb_message *msg, const char *attr, uint_t default_value)
313 {
314         return ldb_msg_find_uint(msg, attr, default_value);
315 }
316
317 /*
318   pull a (signed) int64 from a result set. 
319 */
320 int64_t samdb_result_int64(struct ldb_message *msg, const char *attr, int64_t default_value)
321 {
322         return ldb_msg_find_int64(msg, attr, default_value);
323 }
324
325 /*
326   pull a string from a result set. 
327 */
328 const char *samdb_result_string(struct ldb_message *msg, const char *attr, 
329                                 const char *default_value)
330 {
331         return ldb_msg_find_string(msg, attr, default_value);
332 }
333
334 /*
335   pull a rid from a objectSid in a result set. 
336 */
337 uint32_t samdb_result_rid_from_sid(TALLOC_CTX *mem_ctx, struct ldb_message *msg, 
338                                  const char *attr, uint32_t default_value)
339 {
340         struct dom_sid *sid;
341         const char *sidstr = ldb_msg_find_string(msg, attr, NULL);
342         if (!sidstr) return default_value;
343
344         sid = dom_sid_parse_talloc(mem_ctx, sidstr);
345         if (!sid) return default_value;
346
347         return sid->sub_auths[sid->num_auths-1];
348 }
349
350 /*
351   pull a dom_sid structure from a objectSid in a result set. 
352 */
353 struct dom_sid *samdb_result_dom_sid(TALLOC_CTX *mem_ctx, struct ldb_message *msg, 
354                                      const char *attr)
355 {
356         const char *sidstr = ldb_msg_find_string(msg, attr, NULL);
357         if (!sidstr) return NULL;
358
359         return dom_sid_parse_talloc(mem_ctx, sidstr);
360 }
361
362 /*
363   pull a sid prefix from a objectSid in a result set. 
364   this is used to find the domain sid for a user
365 */
366 const char *samdb_result_sid_prefix(TALLOC_CTX *mem_ctx, struct ldb_message *msg, 
367                                     const char *attr)
368 {
369         struct dom_sid *sid = samdb_result_dom_sid(mem_ctx, msg, attr);
370         if (!sid || sid->num_auths < 1) return NULL;
371
372         sid->num_auths--;
373
374         return dom_sid_string(mem_ctx, sid);
375 }
376
377 /*
378   pull a NTTIME in a result set. 
379 */
380 NTTIME samdb_result_nttime(struct ldb_message *msg, const char *attr, const char *default_value)
381 {
382         const char *str = ldb_msg_find_string(msg, attr, default_value);
383         return nttime_from_string(str);
384 }
385
386 /*
387   pull a uint64_t from a result set. 
388 */
389 uint64_t samdb_result_uint64(struct ldb_message *msg, const char *attr, uint64_t default_value)
390 {
391         return ldb_msg_find_uint64(msg, attr, default_value);
392 }
393
394
395 /*
396   construct the allow_pwd_change field from the PwdLastSet attribute and the 
397   domain password settings
398 */
399 NTTIME samdb_result_allow_pwd_change(void *ctx, TALLOC_CTX *mem_ctx, 
400                                      const char *domain_dn, struct ldb_message *msg, const char *attr)
401 {
402         uint64_t attr_time = samdb_result_uint64(msg, attr, 0);
403         int64_t minPwdAge;
404
405         if (attr_time == 0) {
406                 return 0;
407         }
408
409         minPwdAge = samdb_search_int64(ctx, mem_ctx, 0, "minPwdAge", "dn=%s", domain_dn);
410
411         /* yes, this is a -= not a += as minPwdAge is stored as the negative
412            of the number of 100-nano-seconds */
413         attr_time -= minPwdAge;
414
415         return attr_time;
416 }
417
418 /*
419   construct the force_pwd_change field from the PwdLastSet attribute and the 
420   domain password settings
421 */
422 NTTIME samdb_result_force_pwd_change(void *ctx, TALLOC_CTX *mem_ctx, 
423                                      const char *domain_dn, struct ldb_message *msg, const char *attr)
424 {
425         uint64_t attr_time = samdb_result_uint64(msg, attr, 0);
426         int64_t maxPwdAge;
427
428         if (attr_time == 0) {
429                 return 0;
430         }
431
432         maxPwdAge = samdb_search_int64(ctx, mem_ctx, 0, "maxPwdAge", "dn=%s", domain_dn);
433         if (maxPwdAge == 0) {
434                 attr_time = 0;
435         } else {
436                 attr_time -= maxPwdAge;
437         }
438
439         return attr_time;
440 }
441
442 /*
443   pull a samr_Hash structutre from a result set. 
444 */
445 struct samr_Hash samdb_result_hash(struct ldb_message *msg, const char *attr)
446 {
447         struct samr_Hash hash;
448         const struct ldb_val *val = ldb_msg_find_ldb_val(msg, attr);
449         ZERO_STRUCT(hash);
450         if (val) {
451                 memcpy(hash.hash, val->data, MIN(val->length, 16));
452         }
453         return hash;
454 }
455
456 /*
457   pull an array of samr_Hash structutres from a result set. 
458 */
459 uint_t samdb_result_hashes(TALLOC_CTX *mem_ctx, struct ldb_message *msg, 
460                            const char *attr, struct samr_Hash **hashes)
461 {
462         uint_t count = 0;
463         const struct ldb_val *val = ldb_msg_find_ldb_val(msg, attr);
464         int i;
465
466         *hashes = NULL;
467         if (!val) {
468                 return 0;
469         }
470         count = val->length / 16;
471         if (count == 0) {
472                 return 0;
473         }
474
475         *hashes = talloc_array_p(mem_ctx, struct samr_Hash, count);
476         if (! *hashes) {
477                 return 0;
478         }
479
480         for (i=0;i<count;i++) {
481                 memcpy((*hashes)[i].hash, (i*16)+(char *)val->data, 16);
482         }
483
484         return count;
485 }
486
487 NTSTATUS samdb_result_passwords(TALLOC_CTX *mem_ctx, struct ldb_message *msg, 
488                                 uint8_t **lm_pwd, uint8_t **nt_pwd) 
489 {
490
491         const char *unicodePwd = samdb_result_string(msg, "unicodePwd", NULL);
492         
493         struct samr_Hash *lmPwdHash, *ntPwdHash;
494         if (unicodePwd) {
495                 if (nt_pwd) {
496                         ntPwdHash = talloc_p(mem_ctx, struct samr_Hash);
497                         if (!ntPwdHash) {
498                                 return NT_STATUS_NO_MEMORY;
499                         }
500                         
501                         E_md4hash(unicodePwd, ntPwdHash->hash);
502                         *nt_pwd = ntPwdHash->hash;
503                 }
504
505                 if (lm_pwd) {
506                         BOOL lm_hash_ok;
507                 
508                         lmPwdHash = talloc_p(mem_ctx, struct samr_Hash);
509                         if (!lmPwdHash) {
510                                 return NT_STATUS_NO_MEMORY;
511                         }
512                         
513                         /* compute the new nt and lm hashes */
514                         lm_hash_ok = E_deshash(unicodePwd, lmPwdHash->hash);
515                         
516                         if (lm_hash_ok) {
517                                 *lm_pwd = lmPwdHash->hash;
518                         } else {
519                                 *lm_pwd = NULL;
520                         }
521                 }
522         } else {
523                 if (nt_pwd) {
524                         int num_nt;
525                         num_nt = samdb_result_hashes(mem_ctx, msg, "ntPwdHash", &ntPwdHash);
526                         if (num_nt == 0) {
527                                 *nt_pwd = NULL;
528                         } else if (num_nt > 1) {
529                                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
530                         } else {
531                                 *nt_pwd = ntPwdHash[0].hash;
532                         }
533                 }
534                 if (lm_pwd) {
535                         int num_lm;
536                         num_lm = samdb_result_hashes(mem_ctx, msg, "lmPwdHash", &lmPwdHash);
537                         if (num_lm == 0) {
538                                 *lm_pwd = NULL;
539                         } else if (num_lm > 1) {
540                                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
541                         } else {
542                                 *lm_pwd = lmPwdHash[0].hash;
543                         }
544                 }
545                 
546         }
547         return NT_STATUS_OK;
548 }
549
550 /*
551   pull a samr_LogonHours structutre from a result set. 
552 */
553 struct samr_LogonHours samdb_result_logon_hours(TALLOC_CTX *mem_ctx, struct ldb_message *msg, const char *attr)
554 {
555         struct samr_LogonHours hours;
556         const int units_per_week = 168;
557         const struct ldb_val *val = ldb_msg_find_ldb_val(msg, attr);
558         ZERO_STRUCT(hours);
559         hours.bitmap = talloc_array_p(mem_ctx, uint8, units_per_week);
560         if (!hours.bitmap) {
561                 return hours;
562         }
563         hours.units_per_week = units_per_week;
564         memset(hours.bitmap, 0xFF, units_per_week);
565         if (val) {
566                 memcpy(hours.bitmap, val->data, MIN(val->length, units_per_week));
567         }
568         return hours;
569 }
570
571 /*
572   pull a set of account_flags from a result set. 
573 */
574 uint16_t samdb_result_acct_flags(struct ldb_message *msg, const char *attr)
575 {
576         uint_t userAccountControl = ldb_msg_find_uint(msg, attr, 0);
577         return samdb_uf2acb(userAccountControl);
578 }
579
580 /*
581   copy from a template record to a message
582 */
583 int samdb_copy_template(void *ctx, TALLOC_CTX *mem_ctx, 
584                         struct ldb_message *msg, const char *expression)
585 {
586         struct ldb_message **res, *t;
587         int ret, i, j;
588         
589
590         /* pull the template record */
591         ret = samdb_search(ctx, mem_ctx, NULL, &res, NULL, expression);
592         if (ret != 1) {
593                 DEBUG(1,("samdb: ERROR: template '%s' matched %d records\n", 
594                          expression, ret));
595                 return -1;
596         }
597         t = res[0];
598
599         for (i=0;i<t->num_elements;i++) {
600                 struct ldb_message_element *el = &t->elements[i];
601                 /* some elements should not be copied from the template */
602                 if (strcasecmp(el->name, "cn") == 0 ||
603                     strcasecmp(el->name, "name") == 0 ||
604                     strcasecmp(el->name, "sAMAccountName") == 0) {
605                         continue;
606                 }
607                 for (j=0;j<el->num_values;j++) {
608                         if (strcasecmp(el->name, "objectClass") == 0 &&
609                             (strcasecmp((char *)el->values[j].data, "Template") == 0 ||
610                              strcasecmp((char *)el->values[j].data, "userTemplate") == 0 ||
611                              strcasecmp((char *)el->values[j].data, "groupTemplate") == 0)) {
612                                 continue;
613                         }
614                         samdb_msg_add_string(ctx, mem_ctx, msg, el->name, 
615                                              (char *)el->values[j].data);
616                 }
617         }
618
619         return 0;
620 }
621
622
623 /*
624   allocate a new id, attempting to do it atomically
625   return 0 on failure, the id on success
626 */
627 static NTSTATUS _samdb_allocate_next_id(void *ctx, TALLOC_CTX *mem_ctx, const char *dn, 
628                                         const char *attr, uint32_t *id)
629 {
630         struct samdb_context *sam_ctx = ctx;
631         struct ldb_message msg;
632         int ret;
633         const char *str;
634         struct ldb_val vals[2];
635         struct ldb_message_element els[2];
636
637         str = samdb_search_string(ctx, mem_ctx, NULL, attr, "dn=%s", dn);
638         if (!str) {
639                 DEBUG(1,("id not found at %s %s\n", dn, attr));
640                 return NT_STATUS_OBJECT_NAME_INVALID;
641         }
642
643         *id = strtol(str, NULL, 0);
644         if ((*id)+1 == 0) {
645                 /* out of IDs ! */
646                 return NT_STATUS_INSUFFICIENT_RESOURCES;
647         }
648
649         /* we do a delete and add as a single operation. That prevents
650            a race */
651         ZERO_STRUCT(msg);
652         msg.dn = talloc_strdup(mem_ctx, dn);
653         if (!msg.dn) {
654                 return NT_STATUS_NO_MEMORY;
655         }
656         msg.num_elements = 2;
657         msg.elements = els;
658
659         els[0].num_values = 1;
660         els[0].values = &vals[0];
661         els[0].flags = LDB_FLAG_MOD_DELETE;
662         els[0].name = talloc_strdup(mem_ctx, attr);
663         if (!els[0].name) {
664                 return NT_STATUS_NO_MEMORY;
665         }
666
667         els[1].num_values = 1;
668         els[1].values = &vals[1];
669         els[1].flags = LDB_FLAG_MOD_ADD;
670         els[1].name = els[0].name;
671
672         vals[0].data = talloc_asprintf(mem_ctx, "%u", *id);
673         if (!vals[0].data) {
674                 return NT_STATUS_NO_MEMORY;
675         }
676         vals[0].length = strlen(vals[0].data);
677
678         vals[1].data = talloc_asprintf(mem_ctx, "%u", (*id)+1);
679         if (!vals[1].data) {
680                 return NT_STATUS_NO_MEMORY;
681         }
682         vals[1].length = strlen(vals[1].data);
683
684         ret = ldb_modify(sam_ctx->ldb, &msg);
685         if (ret != 0) {
686                 return NT_STATUS_UNEXPECTED_IO_ERROR;
687         }
688
689         (*id)++;
690
691         return NT_STATUS_OK;
692 }
693
694 /*
695   allocate a new id, attempting to do it atomically
696   return 0 on failure, the id on success
697 */
698 NTSTATUS samdb_allocate_next_id(void *ctx, TALLOC_CTX *mem_ctx, const char *dn, const char *attr,
699                                 uint32_t *id)
700 {
701         int tries = 10;
702         NTSTATUS status;
703
704         /* we need to try multiple times to cope with two account
705            creations at the same time */
706         while (tries--) {
707                 status = _samdb_allocate_next_id(ctx, mem_ctx, dn, attr, id);
708                 if (!NT_STATUS_EQUAL(NT_STATUS_UNEXPECTED_IO_ERROR, status)) {
709                         break;
710                 }
711         }
712
713         if (NT_STATUS_EQUAL(NT_STATUS_UNEXPECTED_IO_ERROR, status)) {
714                 DEBUG(1,("Failed to increment id %s at %s\n", attr, dn));
715         }
716
717         return status;
718 }
719
720
721 /*
722   add a string element to a message
723 */
724 int samdb_msg_add_string(void *ctx, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
725                          const char *attr_name, const char *str)
726 {
727         struct samdb_context *sam_ctx = ctx;
728         char *s = talloc_strdup(mem_ctx, str);
729         char *a = talloc_strdup(mem_ctx, attr_name);
730         if (s == NULL || a == NULL) {
731                 return -1;
732         }
733         ldb_set_alloc(sam_ctx->ldb, samdb_alloc, mem_ctx);
734         return ldb_msg_add_string(sam_ctx->ldb, msg, a, s);
735 }
736
737 /*
738   add a delete element operation to a message
739 */
740 int samdb_msg_add_delete(void *ctx, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
741                          const char *attr_name)
742 {
743         struct samdb_context *sam_ctx = ctx;
744         char *a = talloc_strdup(mem_ctx, attr_name);
745         if (a == NULL) {
746                 return -1;
747         }
748         ldb_set_alloc(sam_ctx->ldb, samdb_alloc, mem_ctx);
749         /* we use an empty replace rather than a delete, as it allows for 
750            samdb_replace() to be used everywhere */
751         return ldb_msg_add_empty(sam_ctx->ldb, msg, a, LDB_FLAG_MOD_REPLACE);
752 }
753
754 /*
755   add a uint_t element to a message
756 */
757 int samdb_msg_add_uint(void *ctx, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
758                        const char *attr_name, uint_t v)
759 {
760         const char *s = talloc_asprintf(mem_ctx, "%u", v);
761         return samdb_msg_add_string(ctx, mem_ctx, msg, attr_name, s);
762 }
763
764 /*
765   add a (signed) int64_t element to a message
766 */
767 int samdb_msg_add_int64(void *ctx, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
768                         const char *attr_name, int64_t v)
769 {
770         const char *s = talloc_asprintf(mem_ctx, "%lld", v);
771         return samdb_msg_add_string(ctx, mem_ctx, msg, attr_name, s);
772 }
773
774 /*
775   add a uint64_t element to a message
776 */
777 int samdb_msg_add_uint64(void *ctx, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
778                         const char *attr_name, uint64_t v)
779 {
780         const char *s = talloc_asprintf(mem_ctx, "%llu", v);
781         return samdb_msg_add_string(ctx, mem_ctx, msg, attr_name, s);
782 }
783
784 /*
785   add a samr_Hash element to a message
786 */
787 int samdb_msg_add_hash(void *ctx, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
788                        const char *attr_name, struct samr_Hash hash)
789 {
790         struct samdb_context *sam_ctx = ctx;
791         struct ldb_val val;
792         val.data = talloc(mem_ctx, 16);
793         val.length = 16;
794         if (!val.data) {
795                 return -1;
796         }
797         memcpy(val.data, hash.hash, 16);
798         ldb_set_alloc(sam_ctx->ldb, samdb_alloc, mem_ctx);
799         return ldb_msg_add_value(sam_ctx->ldb, msg, attr_name, &val);
800 }
801
802 /*
803   add a samr_Hash array to a message
804 */
805 int samdb_msg_add_hashes(void *ctx, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
806                          const char *attr_name, struct samr_Hash *hashes, uint_t count)
807 {
808         struct samdb_context *sam_ctx = ctx;
809         struct ldb_val val;
810         int i;
811         val.data = talloc(mem_ctx, count*16);
812         val.length = count*16;
813         if (!val.data) {
814                 return -1;
815         }
816         for (i=0;i<count;i++) {
817                 memcpy(i*16 + (char *)val.data, hashes[i].hash, 16);
818         }
819         ldb_set_alloc(sam_ctx->ldb, samdb_alloc, mem_ctx);
820         return ldb_msg_add_value(sam_ctx->ldb, msg, attr_name, &val);
821 }
822
823 /*
824   add a acct_flags element to a message
825 */
826 int samdb_msg_add_acct_flags(void *ctx, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
827                              const char *attr_name, uint32_t v)
828 {
829         return samdb_msg_add_uint(ctx, mem_ctx, msg, attr_name, samdb_acb2uf(v));
830 }
831
832 /*
833   add a logon_hours element to a message
834 */
835 int samdb_msg_add_logon_hours(void *ctx, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
836                               const char *attr_name, struct samr_LogonHours hours)
837 {
838         struct samdb_context *sam_ctx = ctx;
839         struct ldb_val val;
840         val.length = hours.units_per_week / 8;
841         val.data = hours.bitmap;
842         ldb_set_alloc(sam_ctx->ldb, samdb_alloc, mem_ctx);
843         return ldb_msg_add_value(sam_ctx->ldb, msg, attr_name, &val);
844 }
845
846 /*
847   set a string element in a message
848 */
849 int samdb_msg_set_string(void *ctx, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
850                          const char *attr_name, const char *str)
851 {
852         struct samdb_context *sam_ctx = ctx;
853         struct ldb_message_element *el;
854
855         ldb_set_alloc(sam_ctx->ldb, samdb_alloc, mem_ctx);
856
857         el = ldb_msg_find_element(msg, attr_name);
858         if (el) {
859                 el->num_values = 0;
860         }
861         return samdb_msg_add_string(ctx, mem_ctx, msg, attr_name, str);
862 }
863
864 /*
865   set a ldaptime element in a message
866 */
867 int samdb_msg_set_ldaptime(void *ctx, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
868                            const char *attr_name, time_t t)
869 {
870         char *str = ldap_timestring(mem_ctx, t);
871         if (!str) {
872                 return -1;
873         }
874         return samdb_msg_set_string(ctx, mem_ctx, msg, attr_name, str);
875 }
876
877 /*
878   add a record
879 */
880 int samdb_add(void *ctx, TALLOC_CTX *mem_ctx, struct ldb_message *msg)
881 {
882         struct samdb_context *sam_ctx = ctx;
883
884         ldb_set_alloc(sam_ctx->ldb, samdb_alloc, mem_ctx);
885         return ldb_add(sam_ctx->ldb, msg);
886 }
887
888 /*
889   delete a record
890 */
891 int samdb_delete(void *ctx, TALLOC_CTX *mem_ctx, const char *dn)
892 {
893         struct samdb_context *sam_ctx = ctx;
894
895         ldb_set_alloc(sam_ctx->ldb, samdb_alloc, mem_ctx);
896         return ldb_delete(sam_ctx->ldb, dn);
897 }
898
899 /*
900   modify a record
901 */
902 int samdb_modify(void *ctx, TALLOC_CTX *mem_ctx, struct ldb_message *msg)
903 {
904         struct samdb_context *sam_ctx = ctx;
905
906         ldb_set_alloc(sam_ctx->ldb, samdb_alloc, mem_ctx);
907         return ldb_modify(sam_ctx->ldb, msg);
908 }
909
910 /*
911   replace elements in a record
912 */
913 int samdb_replace(void *ctx, TALLOC_CTX *mem_ctx, struct ldb_message *msg)
914 {
915         int i;
916
917         /* mark all the message elements as LDB_FLAG_MOD_REPLACE */
918         for (i=0;i<msg->num_elements;i++) {
919                 msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
920         }
921
922         /* modify the samdb record */
923         return samdb_modify(ctx, mem_ctx, msg);
924 }
925