c7a2f770c043e883656d5891c4ec2b6d4bf1f5f4
[bbaumbach/samba-autobuild/.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 multipe records each giving a single string attribute
237   return the number of matches, or -1 on error
238 */
239 int samdb_search_string_multiple(void *ctx,
240                                  TALLOC_CTX *mem_ctx,
241                                  const char *basedn,
242                                  const char ***strs,
243                                  const char *attr_name,
244                                  const char *format, ...)
245 {
246         va_list ap;
247         int count, i;
248         const char * const attrs[2] = { attr_name, NULL };
249         struct ldb_message **res = NULL;
250
251         va_start(ap, format);
252         count = samdb_search_v(ctx, mem_ctx, basedn, &res, attrs, format, ap);
253         va_end(ap);
254
255         if (count <= 0) {
256                 return count;
257         }
258
259         /* make sure its single valued */
260         for (i=0;i<count;i++) {
261                 if (res[i]->num_elements != 1) {
262                         DEBUG(1,("samdb: search for %s %s not single valued\n", 
263                                  attr_name, format));
264                         samdb_search_free(ctx, mem_ctx, res);
265                         return -1;
266                 }
267         }
268
269         *strs = talloc_array_p(mem_ctx, const char *, count+1);
270         if (! *strs) {
271                 samdb_search_free(ctx, mem_ctx, res);
272                 return -1;
273         }
274
275         for (i=0;i<count;i++) {
276                 (*strs)[i] = samdb_result_string(res[i], attr_name, NULL);
277         }
278         (*strs)[count] = NULL;
279
280         return count;
281 }
282
283 /*
284   pull a uint from a result set. 
285 */
286 uint_t samdb_result_uint(struct ldb_message *msg, const char *attr, uint_t default_value)
287 {
288         return ldb_msg_find_uint(msg, attr, default_value);
289 }
290
291 /*
292   pull a string from a result set. 
293 */
294 const char *samdb_result_string(struct ldb_message *msg, const char *attr, 
295                                 const char *default_value)
296 {
297         return ldb_msg_find_string(msg, attr, default_value);
298 }
299
300 /*
301   pull a rid from a objectSid in a result set. 
302 */
303 uint32 samdb_result_rid_from_sid(TALLOC_CTX *mem_ctx, struct ldb_message *msg, 
304                                  const char *attr, uint32 default_value)
305 {
306         struct dom_sid *sid;
307         const char *sidstr = ldb_msg_find_string(msg, attr, NULL);
308         if (!sidstr) return default_value;
309
310         sid = dom_sid_parse_talloc(mem_ctx, sidstr);
311         if (!sid) return default_value;
312
313         return sid->sub_auths[sid->num_auths-1];
314 }
315
316 /*
317   pull a dom_sid structure from a objectSid in a result set. 
318 */
319 struct dom_sid *samdb_result_dom_sid(TALLOC_CTX *mem_ctx, struct ldb_message *msg, 
320                                      const char *attr)
321 {
322         const char *sidstr = ldb_msg_find_string(msg, attr, NULL);
323         if (!sidstr) return NULL;
324
325         return dom_sid_parse_talloc(mem_ctx, sidstr);
326 }
327
328 /*
329   pull a sid prefix from a objectSid in a result set. 
330   this is used to find the domain sid for a user
331 */
332 const char *samdb_result_sid_prefix(TALLOC_CTX *mem_ctx, struct ldb_message *msg, 
333                                     const char *attr)
334 {
335         struct dom_sid *sid = samdb_result_dom_sid(mem_ctx, msg, attr);
336         if (!sid || sid->num_auths < 1) return NULL;
337
338         sid->num_auths--;
339
340         return dom_sid_string(mem_ctx, sid);
341 }
342
343 /*
344   pull a NTTIME in a result set. 
345 */
346 NTTIME samdb_result_nttime(struct ldb_message *msg, const char *attr, const char *default_value)
347 {
348         const char *str = ldb_msg_find_string(msg, attr, default_value);
349         return nttime_from_string(str);
350 }
351
352 /*
353   pull a double (really a large integer) from a result set. 
354 */
355 double samdb_result_double(struct ldb_message *msg, const char *attr, double default_value)
356 {
357         return ldb_msg_find_double(msg, attr, default_value);
358 }
359
360
361 /*
362   construct the allow_pwd_change field from the PwdLastSet attribute and the 
363   domain password settings
364 */
365 NTTIME samdb_result_allow_pwd_change(void *ctx, TALLOC_CTX *mem_ctx, 
366                                      const char *domain_dn, struct ldb_message *msg, const char *attr)
367 {
368         double attr_time = samdb_result_double(msg, attr, 0);
369         if (attr_time > 0) {
370                 const char *minPwdAge = samdb_search_string(ctx, mem_ctx, NULL, "minPwdAge", 
371                                                             "dn=%s", domain_dn);
372                 if (minPwdAge) {
373                         /* yes, this is a -= not a += as minPwdAge is stored as the negative
374                            of the number of 100-nano-seconds */
375                         attr_time -= strtod(minPwdAge, NULL);
376                 }
377         }
378         return nttime_from_double_nt(attr_time);
379 }
380
381 /*
382   construct the force_pwd_change field from the PwdLastSet attribute and the 
383   domain password settings
384 */
385 NTTIME samdb_result_force_pwd_change(void *ctx, TALLOC_CTX *mem_ctx, 
386                                      const char *domain_dn, struct ldb_message *msg, const char *attr)
387 {
388         double attr_time = samdb_result_double(msg, attr, 0);
389         if (attr_time > 0) {
390                 const char *maxPwdAge = samdb_search_string(ctx, mem_ctx, NULL, "maxPwdAge", 
391                                                             "dn=%s", domain_dn);
392                 if (!maxPwdAge || strcmp(maxPwdAge, "0") == 0) {
393                         attr_time = 0;
394                 } else {
395                         attr_time -= strtod(maxPwdAge, NULL);
396                 }
397         }
398         return nttime_from_double_nt(attr_time);
399 }
400
401 /*
402   pull a samr_Hash structutre from a result set. 
403 */
404 struct samr_Hash samdb_result_hash(struct ldb_message *msg, const char *attr)
405 {
406         struct samr_Hash hash;
407         const struct ldb_val *val = ldb_msg_find_ldb_val(msg, attr);
408         ZERO_STRUCT(hash);
409         if (val) {
410                 memcpy(hash.hash, val->data, MIN(val->length, 16));
411         }
412         return hash;
413 }
414
415 /*
416   pull an array of samr_Hash structutres from a result set. 
417 */
418 uint_t samdb_result_hashes(TALLOC_CTX *mem_ctx, struct ldb_message *msg, 
419                            const char *attr, struct samr_Hash **hashes)
420 {
421         uint_t count = 0;
422         const struct ldb_val *val = ldb_msg_find_ldb_val(msg, attr);
423         int i;
424
425         *hashes = NULL;
426         if (!val) {
427                 return 0;
428         }
429         count = val->length / 16;
430         if (count == 0) {
431                 return 0;
432         }
433
434         *hashes = talloc_array_p(mem_ctx, struct samr_Hash, count);
435         if (! *hashes) {
436                 return 0;
437         }
438
439         for (i=0;i<count;i++) {
440                 memcpy((*hashes)[i].hash, (i*16)+(char *)val->data, 16);
441         }
442
443         return count;
444 }
445
446 NTSTATUS samdb_result_passwords(TALLOC_CTX *mem_ctx, struct ldb_message *msg, 
447                                 uint8 **lm_pwd, uint8 **nt_pwd) 
448 {
449
450         const char *unicodePwd = samdb_result_string(msg, "unicodePwd", NULL);
451         
452         struct samr_Hash *lmPwdHash, *ntPwdHash;
453         if (unicodePwd) {
454                 if (nt_pwd) {
455                         ntPwdHash = talloc_p(mem_ctx, struct samr_Hash);
456                         if (!ntPwdHash) {
457                                 return NT_STATUS_NO_MEMORY;
458                         }
459                         
460                         E_md4hash(unicodePwd, ntPwdHash->hash);
461                         *nt_pwd = ntPwdHash->hash;
462                 }
463
464                 if (lm_pwd) {
465                         BOOL lm_hash_ok;
466                 
467                         lmPwdHash = talloc_p(mem_ctx, struct samr_Hash);
468                         if (!lmPwdHash) {
469                                 return NT_STATUS_NO_MEMORY;
470                         }
471                         
472                         /* compute the new nt and lm hashes */
473                         lm_hash_ok = E_deshash(unicodePwd, lmPwdHash->hash);
474                         
475                         if (lm_hash_ok) {
476                                 *lm_pwd = lmPwdHash->hash;
477                         } else {
478                                 *lm_pwd = NULL;
479                         }
480                 }
481         } else {
482                 if (nt_pwd) {
483                         int num_nt;
484                         num_nt = samdb_result_hashes(mem_ctx, msg, "ntPwdHash", &ntPwdHash);
485                         if (num_nt == 0) {
486                                 nt_pwd = NULL;
487                         } else if (num_nt > 1) {
488                                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
489                         } else {
490                                 *nt_pwd = ntPwdHash[0].hash;
491                         }
492                 }
493                 if (lm_pwd) {
494                         int num_lm;
495                         num_lm = samdb_result_hashes(mem_ctx, msg, "lmPwdHash", &lmPwdHash);
496                         if (num_lm == 0) {
497                                 *lm_pwd = NULL;
498                         } else if (num_lm > 1) {
499                                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
500                         } else {
501                                 *lm_pwd = lmPwdHash[0].hash;
502                         }
503                 }
504                 
505         }
506         return NT_STATUS_OK;
507 }
508
509 /*
510   pull a samr_LogonHours structutre from a result set. 
511 */
512 struct samr_LogonHours samdb_result_logon_hours(TALLOC_CTX *mem_ctx, struct ldb_message *msg, const char *attr)
513 {
514         struct samr_LogonHours hours;
515         const int units_per_week = 168;
516         const struct ldb_val *val = ldb_msg_find_ldb_val(msg, attr);
517         ZERO_STRUCT(hours);
518         hours.bitmap = talloc_array_p(mem_ctx, uint8, units_per_week);
519         if (!hours.bitmap) {
520                 return hours;
521         }
522         hours.units_per_week = units_per_week;
523         memset(hours.bitmap, 0xFF, units_per_week);
524         if (val) {
525                 memcpy(hours.bitmap, val->data, MIN(val->length, units_per_week));
526         }
527         return hours;
528 }
529
530 /*
531   pull a set of account_flags from a result set. 
532 */
533 uint16 samdb_result_acct_flags(struct ldb_message *msg, const char *attr)
534 {
535         uint_t userAccountControl = ldb_msg_find_uint(msg, attr, 0);
536         return samdb_uf2acb(userAccountControl);
537 }
538
539 /*
540   copy from a template record to a message
541 */
542 int samdb_copy_template(void *ctx, TALLOC_CTX *mem_ctx, 
543                         struct ldb_message *msg, const char *expression)
544 {
545         struct ldb_message **res, *t;
546         int ret, i, j;
547         
548
549         /* pull the template record */
550         ret = samdb_search(ctx, mem_ctx, NULL, &res, NULL, expression);
551         if (ret != 1) {
552                 DEBUG(1,("samdb: ERROR: template '%s' matched %d records\n", 
553                          expression, ret));
554                 return -1;
555         }
556         t = res[0];
557
558         for (i=0;i<t->num_elements;i++) {
559                 struct ldb_message_element *el = &t->elements[i];
560                 /* some elements should not be copied from the template */
561                 if (strcasecmp(el->name, "cn") == 0 ||
562                     strcasecmp(el->name, "name") == 0 ||
563                     strcasecmp(el->name, "sAMAccountName") == 0) {
564                         continue;
565                 }
566                 for (j=0;j<el->num_values;j++) {
567                         if (strcasecmp(el->name, "objectClass") == 0 &&
568                             (strcasecmp((char *)el->values[j].data, "Template") == 0 ||
569                              strcasecmp((char *)el->values[j].data, "userTemplate") == 0 ||
570                              strcasecmp((char *)el->values[j].data, "groupTemplate") == 0)) {
571                                 continue;
572                         }
573                         samdb_msg_add_string(ctx, mem_ctx, msg, el->name, 
574                                              (char *)el->values[j].data);
575                 }
576         }
577
578         return 0;
579 }
580
581
582 /*
583   allocate a new id, attempting to do it atomically
584   return 0 on failure, the id on success
585 */
586 static NTSTATUS _samdb_allocate_next_id(void *ctx, TALLOC_CTX *mem_ctx, const char *dn, 
587                                         const char *attr, uint32 *id)
588 {
589         struct samdb_context *sam_ctx = ctx;
590         struct ldb_message msg;
591         int ret;
592         const char *str;
593         struct ldb_val vals[2];
594         struct ldb_message_element els[2];
595
596         str = samdb_search_string(ctx, mem_ctx, NULL, attr, "dn=%s", dn);
597         if (!str) {
598                 DEBUG(1,("id not found at %s %s\n", dn, attr));
599                 return NT_STATUS_OBJECT_NAME_INVALID;
600         }
601
602         *id = strtol(str, NULL, 0);
603         if ((*id)+1 == 0) {
604                 /* out of IDs ! */
605                 return NT_STATUS_INSUFFICIENT_RESOURCES;
606         }
607
608         /* we do a delete and add as a single operation. That prevents
609            a race */
610         ZERO_STRUCT(msg);
611         msg.dn = talloc_strdup(mem_ctx, dn);
612         if (!msg.dn) {
613                 return NT_STATUS_NO_MEMORY;
614         }
615         msg.num_elements = 2;
616         msg.elements = els;
617
618         els[0].num_values = 1;
619         els[0].values = &vals[0];
620         els[0].flags = LDB_FLAG_MOD_DELETE;
621         els[0].name = talloc_strdup(mem_ctx, attr);
622         if (!els[0].name) {
623                 return NT_STATUS_NO_MEMORY;
624         }
625
626         els[1].num_values = 1;
627         els[1].values = &vals[1];
628         els[1].flags = LDB_FLAG_MOD_ADD;
629         els[1].name = els[0].name;
630
631         vals[0].data = talloc_asprintf(mem_ctx, "%u", *id);
632         if (!vals[0].data) {
633                 return NT_STATUS_NO_MEMORY;
634         }
635         vals[0].length = strlen(vals[0].data);
636
637         vals[1].data = talloc_asprintf(mem_ctx, "%u", (*id)+1);
638         if (!vals[1].data) {
639                 return NT_STATUS_NO_MEMORY;
640         }
641         vals[1].length = strlen(vals[1].data);
642
643         ret = ldb_modify(sam_ctx->ldb, &msg);
644         if (ret != 0) {
645                 return NT_STATUS_UNEXPECTED_IO_ERROR;
646         }
647
648         (*id)++;
649
650         return NT_STATUS_OK;
651 }
652
653 /*
654   allocate a new id, attempting to do it atomically
655   return 0 on failure, the id on success
656 */
657 NTSTATUS samdb_allocate_next_id(void *ctx, TALLOC_CTX *mem_ctx, const char *dn, const char *attr,
658                                 uint32 *id)
659 {
660         int tries = 10;
661         NTSTATUS status;
662
663         /* we need to try multiple times to cope with two account
664            creations at the same time */
665         while (tries--) {
666                 status = _samdb_allocate_next_id(ctx, mem_ctx, dn, attr, id);
667                 if (!NT_STATUS_EQUAL(NT_STATUS_UNEXPECTED_IO_ERROR, status)) {
668                         break;
669                 }
670         }
671
672         if (NT_STATUS_EQUAL(NT_STATUS_UNEXPECTED_IO_ERROR, status)) {
673                 DEBUG(1,("Failed to increment id %s at %s\n", attr, dn));
674         }
675
676         return status;
677 }
678
679
680 /*
681   add a string element to a message
682 */
683 int samdb_msg_add_string(void *ctx, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
684                          const char *attr_name, const char *str)
685 {
686         struct samdb_context *sam_ctx = ctx;
687         char *s = talloc_strdup(mem_ctx, str);
688         char *a = talloc_strdup(mem_ctx, attr_name);
689         if (s == NULL || a == NULL) {
690                 return -1;
691         }
692         ldb_set_alloc(sam_ctx->ldb, samdb_alloc, mem_ctx);
693         return ldb_msg_add_string(sam_ctx->ldb, msg, a, s);
694 }
695
696 /*
697   add a delete element operation to a message
698 */
699 int samdb_msg_add_delete(void *ctx, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
700                          const char *attr_name)
701 {
702         struct samdb_context *sam_ctx = ctx;
703         char *a = talloc_strdup(mem_ctx, attr_name);
704         if (a == NULL) {
705                 return -1;
706         }
707         ldb_set_alloc(sam_ctx->ldb, samdb_alloc, mem_ctx);
708         /* we use an empty replace rather than a delete, as it allows for 
709            samdb_replace() to be used everywhere */
710         return ldb_msg_add_empty(sam_ctx->ldb, msg, a, LDB_FLAG_MOD_REPLACE);
711 }
712
713 /*
714   add a uint_t element to a message
715 */
716 int samdb_msg_add_uint(void *ctx, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
717                        const char *attr_name, uint_t v)
718 {
719         const char *s = talloc_asprintf(mem_ctx, "%u", v);
720         return samdb_msg_add_string(ctx, mem_ctx, msg, attr_name, s);
721 }
722
723 /*
724   add a double element to a message (actually a large integer)
725 */
726 int samdb_msg_add_double(void *ctx, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
727                          const char *attr_name, double v)
728 {
729         const char *s = talloc_asprintf(mem_ctx, "%.0f", v);
730         return samdb_msg_add_string(ctx, mem_ctx, msg, attr_name, s);
731 }
732
733 /*
734   add a samr_Hash element to a message
735 */
736 int samdb_msg_add_hash(void *ctx, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
737                        const char *attr_name, struct samr_Hash hash)
738 {
739         struct samdb_context *sam_ctx = ctx;
740         struct ldb_val val;
741         val.data = talloc(mem_ctx, 16);
742         val.length = 16;
743         if (!val.data) {
744                 return -1;
745         }
746         memcpy(val.data, hash.hash, 16);
747         ldb_set_alloc(sam_ctx->ldb, samdb_alloc, mem_ctx);
748         return ldb_msg_add_value(sam_ctx->ldb, msg, attr_name, &val);
749 }
750
751 /*
752   add a samr_Hash array to a message
753 */
754 int samdb_msg_add_hashes(void *ctx, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
755                          const char *attr_name, struct samr_Hash *hashes, uint_t count)
756 {
757         struct samdb_context *sam_ctx = ctx;
758         struct ldb_val val;
759         int i;
760         val.data = talloc(mem_ctx, count*16);
761         val.length = count*16;
762         if (!val.data) {
763                 return -1;
764         }
765         for (i=0;i<count;i++) {
766                 memcpy(i*16 + (char *)val.data, hashes[i].hash, 16);
767         }
768         ldb_set_alloc(sam_ctx->ldb, samdb_alloc, mem_ctx);
769         return ldb_msg_add_value(sam_ctx->ldb, msg, attr_name, &val);
770 }
771
772 /*
773   add a acct_flags element to a message
774 */
775 int samdb_msg_add_acct_flags(void *ctx, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
776                              const char *attr_name, uint32 v)
777 {
778         return samdb_msg_add_uint(ctx, mem_ctx, msg, attr_name, samdb_acb2uf(v));
779 }
780
781 /*
782   add a logon_hours element to a message
783 */
784 int samdb_msg_add_logon_hours(void *ctx, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
785                               const char *attr_name, struct samr_LogonHours hours)
786 {
787         struct samdb_context *sam_ctx = ctx;
788         struct ldb_val val;
789         val.length = hours.units_per_week / 8;
790         val.data = hours.bitmap;
791         ldb_set_alloc(sam_ctx->ldb, samdb_alloc, mem_ctx);
792         return ldb_msg_add_value(sam_ctx->ldb, msg, attr_name, &val);
793 }
794
795 /*
796   set a string element in a message
797 */
798 int samdb_msg_set_string(void *ctx, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
799                          const char *attr_name, const char *str)
800 {
801         struct samdb_context *sam_ctx = ctx;
802         struct ldb_message_element *el;
803
804         ldb_set_alloc(sam_ctx->ldb, samdb_alloc, mem_ctx);
805
806         el = ldb_msg_find_element(msg, attr_name);
807         if (el) {
808                 el->num_values = 0;
809         }
810         return samdb_msg_add_string(ctx, mem_ctx, msg, attr_name, str);
811 }
812
813 /*
814   set a ldaptime element in a message
815 */
816 int samdb_msg_set_ldaptime(void *ctx, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
817                            const char *attr_name, time_t t)
818 {
819         char *str = ldap_timestring(mem_ctx, t);
820         if (!str) {
821                 return -1;
822         }
823         return samdb_msg_set_string(ctx, mem_ctx, msg, attr_name, str);
824 }
825
826 /*
827   add a record
828 */
829 int samdb_add(void *ctx, TALLOC_CTX *mem_ctx, struct ldb_message *msg)
830 {
831         struct samdb_context *sam_ctx = ctx;
832
833         ldb_set_alloc(sam_ctx->ldb, samdb_alloc, mem_ctx);
834         return ldb_add(sam_ctx->ldb, msg);
835 }
836
837 /*
838   delete a record
839 */
840 int samdb_delete(void *ctx, TALLOC_CTX *mem_ctx, const char *dn)
841 {
842         struct samdb_context *sam_ctx = ctx;
843
844         ldb_set_alloc(sam_ctx->ldb, samdb_alloc, mem_ctx);
845         return ldb_delete(sam_ctx->ldb, dn);
846 }
847
848 /*
849   modify a record
850 */
851 int samdb_modify(void *ctx, TALLOC_CTX *mem_ctx, struct ldb_message *msg)
852 {
853         struct samdb_context *sam_ctx = ctx;
854
855         ldb_set_alloc(sam_ctx->ldb, samdb_alloc, mem_ctx);
856         return ldb_modify(sam_ctx->ldb, msg);
857 }
858
859 /*
860   replace elements in a record
861 */
862 int samdb_replace(void *ctx, TALLOC_CTX *mem_ctx, struct ldb_message *msg)
863 {
864         int i;
865
866         /* mark all the message elements as LDB_FLAG_MOD_REPLACE */
867         for (i=0;i<msg->num_elements;i++) {
868                 msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
869         }
870
871         /* modify the samdb record */
872         return samdb_modify(ctx, mem_ctx, msg);
873 }
874