r12227: I realised that I wasn't yet seeing authenticated LDAP for the ldb
[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 "librpc/gen_ndr/ndr_misc.h"
26 #include "lib/ldb/include/ldb.h"
27 #include "lib/ldb/include/ldb_errors.h"
28 #include "system/time.h"
29 #include "system/filesys.h"
30 #include "db_wrap.h"
31
32 /*
33   connect to the SAM database
34   return an opaque context pointer on success, or NULL on failure
35  */
36 struct ldb_context *samdb_connect(TALLOC_CTX *mem_ctx, 
37                                   struct auth_session_info *session_info)
38 {
39         struct ldb_context *ldb;
40         ldb = ldb_wrap_connect(mem_ctx, lp_sam_url(), session_info,
41                                NULL, 0, NULL);
42         if (!ldb) {
43                 return NULL;
44         }
45         return ldb;
46 }
47
48 /*
49   search the sam for the specified attributes in a specific domain, filter on
50   objectSid being in domain_sid.
51 */
52 int samdb_search_domain(struct ldb_context *sam_ldb,
53                         TALLOC_CTX *mem_ctx, 
54                         const struct ldb_dn *basedn,
55                         struct ldb_message ***res,
56                         const char * const *attrs,
57                         const struct dom_sid *domain_sid,
58                         const char *format, ...)  _PRINTF_ATTRIBUTE(7,8)
59 {
60         va_list ap;
61         int i, count;
62
63         va_start(ap, format);
64         count = gendb_search_v(sam_ldb, mem_ctx, basedn,
65                                res, attrs, format, ap);
66         va_end(ap);
67
68         i=0;
69
70         while (i<count) {
71                 struct dom_sid *entry_sid;
72
73                 entry_sid = samdb_result_dom_sid(mem_ctx, (*res)[i], "objectSid");
74
75                 if ((entry_sid == NULL) ||
76                     (!dom_sid_in_domain(domain_sid, entry_sid))) {
77                         /* Delete that entry from the result set */
78                         (*res)[i] = (*res)[count-1];
79                         count -= 1;
80                         talloc_free(entry_sid);
81                         continue;
82                 }
83                 talloc_free(entry_sid);
84                 i += 1;
85         }
86
87         return count;
88 }
89
90 /*
91   search the sam for a single string attribute in exactly 1 record
92 */
93 const char *samdb_search_string_v(struct ldb_context *sam_ldb,
94                                   TALLOC_CTX *mem_ctx,
95                                   const struct ldb_dn *basedn,
96                                   const char *attr_name,
97                                   const char *format, va_list ap) _PRINTF_ATTRIBUTE(5,0)
98 {
99         int count;
100         const char *attrs[2] = { NULL, NULL };
101         struct ldb_message **res = NULL;
102
103         attrs[0] = attr_name;
104
105         count = gendb_search_v(sam_ldb, mem_ctx, basedn, &res, attrs, format, ap);
106         if (count > 1) {                
107                 DEBUG(1,("samdb: search for %s %s not single valued (count=%d)\n", 
108                          attr_name, format, count));
109         }
110         if (count != 1) {
111                 talloc_free(res);
112                 return NULL;
113         }
114
115         return samdb_result_string(res[0], attr_name, NULL);
116 }
117                                  
118
119 /*
120   search the sam for a single string attribute in exactly 1 record
121 */
122 const char *samdb_search_string(struct ldb_context *sam_ldb,
123                                 TALLOC_CTX *mem_ctx,
124                                 const struct ldb_dn *basedn,
125                                 const char *attr_name,
126                                 const char *format, ...) _PRINTF_ATTRIBUTE(5,6)
127 {
128         va_list ap;
129         const char *str;
130
131         va_start(ap, format);
132         str = samdb_search_string_v(sam_ldb, mem_ctx, basedn, attr_name, format, ap);
133         va_end(ap);
134
135         return str;
136 }
137
138 struct ldb_dn *samdb_search_dn(struct ldb_context *sam_ldb,
139                                TALLOC_CTX *mem_ctx,
140                                const struct ldb_dn *basedn,
141                                const char *format, ...) _PRINTF_ATTRIBUTE(4,5)
142 {
143         va_list ap;
144         struct ldb_dn *ret;
145         struct ldb_message **res = NULL;
146         int count;
147
148         va_start(ap, format);
149         count = gendb_search_v(sam_ldb, mem_ctx, basedn, &res, NULL, format, ap);
150         va_end(ap);
151
152         if (count != 1) return NULL;
153
154         ret = talloc_steal(mem_ctx, res[0]->dn);
155         talloc_free(res);
156
157         return ret;
158 }
159
160 /*
161   search the sam for a dom_sid attribute in exactly 1 record
162 */
163 struct dom_sid *samdb_search_dom_sid(struct ldb_context *sam_ldb,
164                                      TALLOC_CTX *mem_ctx,
165                                      const struct ldb_dn *basedn,
166                                      const char *attr_name,
167                                      const char *format, ...) _PRINTF_ATTRIBUTE(5,6)
168 {
169         va_list ap;
170         int count;
171         struct ldb_message **res;
172         const char *attrs[2] = { NULL, NULL };
173         struct dom_sid *sid;
174
175         attrs[0] = attr_name;
176
177         va_start(ap, format);
178         count = gendb_search_v(sam_ldb, mem_ctx, basedn, &res, attrs, format, ap);
179         va_end(ap);
180         if (count > 1) {                
181                 DEBUG(1,("samdb: search for %s %s not single valued (count=%d)\n", 
182                          attr_name, format, count));
183         }
184         if (count != 1) {
185                 talloc_free(res);
186                 return NULL;
187         }
188         sid = samdb_result_dom_sid(mem_ctx, res[0], attr_name);
189         talloc_free(res);
190         return sid;     
191 }
192
193 /*
194   return the count of the number of records in the sam matching the query
195 */
196 int samdb_search_count(struct ldb_context *sam_ldb,
197                        TALLOC_CTX *mem_ctx,
198                        const struct ldb_dn *basedn,
199                        const char *format, ...) _PRINTF_ATTRIBUTE(4,5)
200 {
201         va_list ap;
202         struct ldb_message **res;
203         const char * const attrs[] = { NULL };
204         int ret;
205
206         va_start(ap, format);
207         ret = gendb_search_v(sam_ldb, mem_ctx, basedn, &res, attrs, format, ap);
208         va_end(ap);
209
210         return ret;
211 }
212
213
214 /*
215   search the sam for a single integer attribute in exactly 1 record
216 */
217 uint_t samdb_search_uint(struct ldb_context *sam_ldb,
218                          TALLOC_CTX *mem_ctx,
219                          uint_t default_value,
220                          const struct ldb_dn *basedn,
221                          const char *attr_name,
222                          const char *format, ...) _PRINTF_ATTRIBUTE(6,7)
223 {
224         va_list ap;
225         int count;
226         struct ldb_message **res;
227         const char *attrs[2] = { NULL, NULL };
228
229         attrs[0] = attr_name;
230
231         va_start(ap, format);
232         count = gendb_search_v(sam_ldb, mem_ctx, basedn, &res, attrs, format, ap);
233         va_end(ap);
234
235         if (count != 1) {
236                 return default_value;
237         }
238
239         return samdb_result_uint(res[0], attr_name, default_value);
240 }
241
242 /*
243   search the sam for a single signed 64 bit integer attribute in exactly 1 record
244 */
245 int64_t samdb_search_int64(struct ldb_context *sam_ldb,
246                            TALLOC_CTX *mem_ctx,
247                            int64_t default_value,
248                            const struct ldb_dn *basedn,
249                            const char *attr_name,
250                            const char *format, ...) _PRINTF_ATTRIBUTE(6,7)
251 {
252         va_list ap;
253         int count;
254         struct ldb_message **res;
255         const char *attrs[2] = { NULL, NULL };
256
257         attrs[0] = attr_name;
258
259         va_start(ap, format);
260         count = gendb_search_v(sam_ldb, mem_ctx, basedn, &res, attrs, format, ap);
261         va_end(ap);
262
263         if (count != 1) {
264                 return default_value;
265         }
266
267         return samdb_result_int64(res[0], attr_name, default_value);
268 }
269
270 /*
271   search the sam for multipe records each giving a single string attribute
272   return the number of matches, or -1 on error
273 */
274 int samdb_search_string_multiple(struct ldb_context *sam_ldb,
275                                  TALLOC_CTX *mem_ctx,
276                                  const struct ldb_dn *basedn,
277                                  const char ***strs,
278                                  const char *attr_name,
279                                  const char *format, ...) _PRINTF_ATTRIBUTE(6,7)
280 {
281         va_list ap;
282         int count, i;
283         const char *attrs[2] = { NULL, NULL };
284         struct ldb_message **res = NULL;
285
286         attrs[0] = attr_name;
287
288         va_start(ap, format);
289         count = gendb_search_v(sam_ldb, mem_ctx, basedn, &res, attrs, format, ap);
290         va_end(ap);
291
292         if (count <= 0) {
293                 return count;
294         }
295
296         /* make sure its single valued */
297         for (i=0;i<count;i++) {
298                 if (res[i]->num_elements != 1) {
299                         DEBUG(1,("samdb: search for %s %s not single valued\n", 
300                                  attr_name, format));
301                         talloc_free(res);
302                         return -1;
303                 }
304         }
305
306         *strs = talloc_array(mem_ctx, const char *, count+1);
307         if (! *strs) {
308                 talloc_free(res);
309                 return -1;
310         }
311
312         for (i=0;i<count;i++) {
313                 (*strs)[i] = samdb_result_string(res[i], attr_name, NULL);
314         }
315         (*strs)[count] = NULL;
316
317         return count;
318 }
319
320 /*
321   pull a uint from a result set. 
322 */
323 uint_t samdb_result_uint(struct ldb_message *msg, const char *attr, uint_t default_value)
324 {
325         return ldb_msg_find_uint(msg, attr, default_value);
326 }
327
328 /*
329   pull a (signed) int64 from a result set. 
330 */
331 int64_t samdb_result_int64(struct ldb_message *msg, const char *attr, int64_t default_value)
332 {
333         return ldb_msg_find_int64(msg, attr, default_value);
334 }
335
336 /*
337   pull a string from a result set. 
338 */
339 const char *samdb_result_string(struct ldb_message *msg, const char *attr, 
340                                 const char *default_value)
341 {
342         return ldb_msg_find_string(msg, attr, default_value);
343 }
344
345 struct ldb_dn *samdb_result_dn(TALLOC_CTX *mem_ctx, struct ldb_message *msg,
346                                const char *attr, struct ldb_dn *default_value)
347 {
348         const char *string = samdb_result_string(msg, attr, NULL);
349         if (string == NULL) return default_value;
350         return ldb_dn_explode(mem_ctx, string);
351 }
352
353 /*
354   pull a rid from a objectSid in a result set. 
355 */
356 uint32_t samdb_result_rid_from_sid(TALLOC_CTX *mem_ctx, struct ldb_message *msg, 
357                                    const char *attr, uint32_t default_value)
358 {
359         struct dom_sid *sid;
360         uint32_t rid;
361
362         sid = samdb_result_dom_sid(mem_ctx, msg, attr);
363         if (sid == NULL) {
364                 return default_value;
365         }
366         rid = sid->sub_auths[sid->num_auths-1];
367         talloc_free(sid);
368         return rid;
369 }
370
371 /*
372   pull a dom_sid structure from a objectSid in a result set. 
373 */
374 struct dom_sid *samdb_result_dom_sid(TALLOC_CTX *mem_ctx, struct ldb_message *msg, 
375                                      const char *attr)
376 {
377         const struct ldb_val *v;
378         struct dom_sid *sid;
379         NTSTATUS status;
380         v = ldb_msg_find_ldb_val(msg, attr);
381         if (v == NULL) {
382                 return NULL;
383         }
384         sid = talloc(mem_ctx, struct dom_sid);
385         if (sid == NULL) {
386                 return NULL;
387         }
388         status = ndr_pull_struct_blob(v, sid, sid, 
389                                       (ndr_pull_flags_fn_t)ndr_pull_dom_sid);
390         if (!NT_STATUS_IS_OK(status)) {
391                 talloc_free(sid);
392                 return NULL;
393         }
394         return sid;
395 }
396
397 /*
398   pull a guid structure from a objectGUID in a result set. 
399 */
400 struct GUID samdb_result_guid(struct ldb_message *msg, const char *attr)
401 {
402         const struct ldb_val *v;
403         NTSTATUS status;
404         struct GUID guid;
405         TALLOC_CTX *mem_ctx;
406
407         ZERO_STRUCT(guid);
408
409         v = ldb_msg_find_ldb_val(msg, attr);
410         if (!v) return guid;
411
412         mem_ctx = talloc_named_const(NULL, 0, "samdb_result_guid");
413         if (!mem_ctx) return guid;
414         status = ndr_pull_struct_blob(v, mem_ctx, &guid, 
415                                       (ndr_pull_flags_fn_t)ndr_pull_GUID);
416         talloc_free(mem_ctx);
417         if (!NT_STATUS_IS_OK(status)) {
418                 return guid;
419         }
420
421         return guid;
422 }
423
424 /*
425   pull a sid prefix from a objectSid in a result set. 
426   this is used to find the domain sid for a user
427 */
428 struct dom_sid *samdb_result_sid_prefix(TALLOC_CTX *mem_ctx, struct ldb_message *msg, 
429                                         const char *attr)
430 {
431         struct dom_sid *sid = samdb_result_dom_sid(mem_ctx, msg, attr);
432         if (!sid || sid->num_auths < 1) return NULL;
433         sid->num_auths--;
434         return sid;
435 }
436
437 /*
438   pull a NTTIME in a result set. 
439 */
440 NTTIME samdb_result_nttime(struct ldb_message *msg, const char *attr, NTTIME default_value)
441 {
442         const char *str = ldb_msg_find_string(msg, attr, NULL);
443         if (!str) return default_value;
444         return nttime_from_string(str);
445 }
446
447 /*
448   pull a uint64_t from a result set. 
449 */
450 uint64_t samdb_result_uint64(struct ldb_message *msg, const char *attr, uint64_t default_value)
451 {
452         return ldb_msg_find_uint64(msg, attr, default_value);
453 }
454
455
456 /*
457   construct the allow_password_change field from the PwdLastSet attribute and the 
458   domain password settings
459 */
460 NTTIME samdb_result_allow_password_change(struct ldb_context *sam_ldb, 
461                                           TALLOC_CTX *mem_ctx, 
462                                           const struct ldb_dn *domain_dn, 
463                                           struct ldb_message *msg, 
464                                           const char *attr)
465 {
466         uint64_t attr_time = samdb_result_uint64(msg, attr, 0);
467         int64_t minPwdAge;
468
469         if (attr_time == 0) {
470                 return 0;
471         }
472
473         minPwdAge = samdb_search_int64(sam_ldb, mem_ctx, 0, domain_dn, "minPwdAge", NULL);
474
475         /* yes, this is a -= not a += as minPwdAge is stored as the negative
476            of the number of 100-nano-seconds */
477         attr_time -= minPwdAge;
478
479         return attr_time;
480 }
481
482 /*
483   construct the force_password_change field from the PwdLastSet attribute and the 
484   domain password settings
485 */
486 NTTIME samdb_result_force_password_change(struct ldb_context *sam_ldb, 
487                                           TALLOC_CTX *mem_ctx, 
488                                           const struct ldb_dn *domain_dn, 
489                                           struct ldb_message *msg, 
490                                           const char *attr)
491 {
492         uint64_t attr_time = samdb_result_uint64(msg, attr, 0);
493         int64_t maxPwdAge;
494
495         if (attr_time == 0) {
496                 return 0;
497         }
498
499         maxPwdAge = samdb_search_int64(sam_ldb, mem_ctx, 0, domain_dn, "maxPwdAge", NULL);
500         if (maxPwdAge == 0) {
501                 return 0;
502         } else {
503                 attr_time -= maxPwdAge;
504         }
505
506         return attr_time;
507 }
508
509 /*
510   pull a samr_Password structutre from a result set. 
511 */
512 struct samr_Password samdb_result_hash(struct ldb_message *msg, const char *attr)
513 {
514         struct samr_Password hash;
515         const struct ldb_val *val = ldb_msg_find_ldb_val(msg, attr);
516         ZERO_STRUCT(hash);
517         if (val) {
518                 memcpy(hash.hash, val->data, MIN(val->length, sizeof(hash.hash)));
519         }
520         return hash;
521 }
522
523 /*
524   pull an array of samr_Password structutres from a result set. 
525 */
526 uint_t samdb_result_hashes(TALLOC_CTX *mem_ctx, struct ldb_message *msg, 
527                            const char *attr, struct samr_Password **hashes)
528 {
529         uint_t count = 0;
530         const struct ldb_val *val = ldb_msg_find_ldb_val(msg, attr);
531         int i;
532
533         *hashes = NULL;
534         if (!val) {
535                 return 0;
536         }
537         count = val->length / 16;
538         if (count == 0) {
539                 return 0;
540         }
541
542         *hashes = talloc_array(mem_ctx, struct samr_Password, count);
543         if (! *hashes) {
544                 return 0;
545         }
546
547         for (i=0;i<count;i++) {
548                 memcpy((*hashes)[i].hash, (i*16)+(char *)val->data, 16);
549         }
550
551         return count;
552 }
553
554 NTSTATUS samdb_result_passwords(TALLOC_CTX *mem_ctx, struct ldb_message *msg, 
555                                 struct samr_Password **lm_pwd, struct samr_Password **nt_pwd) 
556 {
557
558         const char *unicodePwd = samdb_result_string(msg, "unicodePwd", NULL);
559         
560         struct samr_Password *lmPwdHash, *ntPwdHash;
561         if (unicodePwd) {
562                 if (nt_pwd) {
563                         ntPwdHash = talloc(mem_ctx, struct samr_Password);
564                         if (!ntPwdHash) {
565                                 return NT_STATUS_NO_MEMORY;
566                         }
567                         
568                         E_md4hash(unicodePwd, ntPwdHash->hash);
569                         *nt_pwd = ntPwdHash;
570                 }
571
572                 if (lm_pwd) {
573                         BOOL lm_hash_ok;
574                 
575                         lmPwdHash = talloc(mem_ctx, struct samr_Password);
576                         if (!lmPwdHash) {
577                                 return NT_STATUS_NO_MEMORY;
578                         }
579                         
580                         /* compute the new nt and lm hashes */
581                         lm_hash_ok = E_deshash(unicodePwd, lmPwdHash->hash);
582                         
583                         if (lm_hash_ok) {
584                                 *lm_pwd = lmPwdHash;
585                         } else {
586                                 *lm_pwd = NULL;
587                         }
588                 }
589         } else {
590                 if (nt_pwd) {
591                         int num_nt;
592                         num_nt = samdb_result_hashes(mem_ctx, msg, "ntPwdHash", &ntPwdHash);
593                         if (num_nt == 0) {
594                                 *nt_pwd = NULL;
595                         } else if (num_nt > 1) {
596                                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
597                         } else {
598                                 *nt_pwd = &ntPwdHash[0];
599                         }
600                 }
601                 if (lm_pwd) {
602                         int num_lm;
603                         num_lm = samdb_result_hashes(mem_ctx, msg, "lmPwdHash", &lmPwdHash);
604                         if (num_lm == 0) {
605                                 *lm_pwd = NULL;
606                         } else if (num_lm > 1) {
607                                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
608                         } else {
609                                 *lm_pwd = &lmPwdHash[0];
610                         }
611                 }
612                 
613         }
614         return NT_STATUS_OK;
615 }
616
617 /*
618   pull a samr_LogonHours structutre from a result set. 
619 */
620 struct samr_LogonHours samdb_result_logon_hours(TALLOC_CTX *mem_ctx, struct ldb_message *msg, const char *attr)
621 {
622         struct samr_LogonHours hours;
623         const int units_per_week = 168;
624         const struct ldb_val *val = ldb_msg_find_ldb_val(msg, attr);
625         ZERO_STRUCT(hours);
626         hours.bits = talloc_array(mem_ctx, uint8_t, units_per_week);
627         if (!hours.bits) {
628                 return hours;
629         }
630         hours.units_per_week = units_per_week;
631         memset(hours.bits, 0xFF, units_per_week);
632         if (val) {
633                 memcpy(hours.bits, val->data, MIN(val->length, units_per_week));
634         }
635         return hours;
636 }
637
638 /*
639   pull a set of account_flags from a result set. 
640 */
641 uint16_t samdb_result_acct_flags(struct ldb_message *msg, const char *attr)
642 {
643         uint_t userAccountControl = ldb_msg_find_uint(msg, attr, 0);
644         return samdb_uf2acb(userAccountControl);
645 }
646
647 /*
648   copy from a template record to a message
649 */
650 int samdb_copy_template(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, 
651                         struct ldb_message *msg, const char *expression)
652 {
653         struct ldb_message **res, *t;
654         int ret, i, j;
655         
656
657         /* pull the template record */
658         ret = gendb_search(sam_ldb, mem_ctx, NULL, &res, NULL, "%s", expression);
659         if (ret != 1) {
660                 DEBUG(1,("samdb: ERROR: template '%s' matched %d records\n", 
661                          expression, ret));
662                 return -1;
663         }
664         t = res[0];
665
666         for (i=0;i<t->num_elements;i++) {
667                 struct ldb_message_element *el = &t->elements[i];
668                 /* some elements should not be copied from the template */
669                 if (strcasecmp(el->name, "cn") == 0 ||
670                     strcasecmp(el->name, "name") == 0 ||
671                     strcasecmp(el->name, "sAMAccountName") == 0) {
672                         continue;
673                 }
674                 for (j=0;j<el->num_values;j++) {
675                         if (strcasecmp(el->name, "objectClass") == 0 &&
676                             (strcasecmp((char *)el->values[j].data, "Template") == 0 ||
677                              strcasecmp((char *)el->values[j].data, "userTemplate") == 0 ||
678                              strcasecmp((char *)el->values[j].data, "groupTemplate") == 0 ||
679                              strcasecmp((char *)el->values[j].data, "foreignSecurityTemplate") == 0 ||
680                              strcasecmp((char *)el->values[j].data, "aliasTemplate") == 0 || 
681                              strcasecmp((char *)el->values[j].data, "trustedDomainTemplate") == 0 || 
682                              strcasecmp((char *)el->values[j].data, "secretTemplate") == 0)) {
683                                 continue;
684                         }
685                         samdb_msg_add_string(sam_ldb, mem_ctx, msg, el->name, 
686                                              (char *)el->values[j].data);
687                 }
688         }
689
690         return 0;
691 }
692
693
694 /*
695   add a string element to a message
696 */
697 int samdb_msg_add_string(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
698                          const char *attr_name, const char *str)
699 {
700         char *s = talloc_strdup(mem_ctx, str);
701         char *a = talloc_strdup(mem_ctx, attr_name);
702         if (s == NULL || a == NULL) {
703                 return -1;
704         }
705         return ldb_msg_add_string(msg, a, s);
706 }
707
708 /*
709   add a dom_sid element to a message
710 */
711 int samdb_msg_add_dom_sid(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
712                          const char *attr_name, struct dom_sid *sid)
713 {
714         struct ldb_val v;
715         NTSTATUS status;
716         status = ndr_push_struct_blob(&v, mem_ctx, sid, 
717                                       (ndr_push_flags_fn_t)ndr_push_dom_sid);
718         if (!NT_STATUS_IS_OK(status)) {
719                 return -1;
720         }
721         return ldb_msg_add_value(msg, attr_name, &v);
722 }
723
724
725 /*
726   add a delete element operation to a message
727 */
728 int samdb_msg_add_delete(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
729                          const char *attr_name)
730 {
731         char *a = talloc_strdup(mem_ctx, attr_name);
732         if (a == NULL) {
733                 return -1;
734         }
735         /* we use an empty replace rather than a delete, as it allows for 
736            samdb_replace() to be used everywhere */
737         return ldb_msg_add_empty(msg, a, LDB_FLAG_MOD_REPLACE);
738 }
739
740 /*
741   add a add attribute value to a message
742 */
743 int samdb_msg_add_addval(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
744                          const char *attr_name, const char *value)
745 {
746         struct ldb_message_element *el;
747         char *a, *v;
748         int ret;
749         a = talloc_strdup(mem_ctx, attr_name);
750         if (a == NULL)
751                 return -1;
752         v = talloc_strdup(mem_ctx, value);
753         if (v == NULL)
754                 return -1;
755         ret = ldb_msg_add_string(msg, a, v);
756         if (ret != 0)
757                 return ret;
758         el = ldb_msg_find_element(msg, a);
759         if (el == NULL)
760                 return -1;
761         el->flags = LDB_FLAG_MOD_ADD;
762         return 0;
763 }
764
765 /*
766   add a delete attribute value to a message
767 */
768 int samdb_msg_add_delval(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
769                          const char *attr_name, const char *value)
770 {
771         struct ldb_message_element *el;
772         char *a, *v;
773         int ret;
774         a = talloc_strdup(mem_ctx, attr_name);
775         if (a == NULL)
776                 return -1;
777         v = talloc_strdup(mem_ctx, value);
778         if (v == NULL)
779                 return -1;
780         ret = ldb_msg_add_string(msg, a, v);
781         if (ret != 0)
782                 return ret;
783         el = ldb_msg_find_element(msg, a);
784         if (el == NULL)
785                 return -1;
786         el->flags = LDB_FLAG_MOD_DELETE;
787         return 0;
788 }
789
790 /*
791   add a uint_t element to a message
792 */
793 int samdb_msg_add_uint(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
794                        const char *attr_name, uint_t v)
795 {
796         const char *s = talloc_asprintf(mem_ctx, "%u", v);
797         return samdb_msg_add_string(sam_ldb, mem_ctx, msg, attr_name, s);
798 }
799
800 /*
801   add a (signed) int64_t element to a message
802 */
803 int samdb_msg_add_int64(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
804                         const char *attr_name, int64_t v)
805 {
806         const char *s = talloc_asprintf(mem_ctx, "%lld", (long long)v);
807         return samdb_msg_add_string(sam_ldb, mem_ctx, msg, attr_name, s);
808 }
809
810 /*
811   add a uint64_t element to a message
812 */
813 int samdb_msg_add_uint64(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
814                         const char *attr_name, uint64_t v)
815 {
816         const char *s = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)v);
817         return samdb_msg_add_string(sam_ldb, mem_ctx, msg, attr_name, s);
818 }
819
820 /*
821   add a samr_Password element to a message
822 */
823 int samdb_msg_add_hash(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
824                        const char *attr_name, struct samr_Password *hash)
825 {
826         struct ldb_val val;
827         val.data = talloc_memdup(mem_ctx, hash->hash, 16);
828         if (!val.data) {
829                 return -1;
830         }
831         val.length = 16;
832         return ldb_msg_add_value(msg, attr_name, &val);
833 }
834
835 /*
836   add a samr_Password array to a message
837 */
838 int samdb_msg_add_hashes(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
839                          const char *attr_name, struct samr_Password *hashes, uint_t count)
840 {
841         struct ldb_val val;
842         int i;
843         val.data = talloc_array_size(mem_ctx, 16, count);
844         val.length = count*16;
845         if (!val.data) {
846                 return -1;
847         }
848         for (i=0;i<count;i++) {
849                 memcpy(i*16 + (char *)val.data, hashes[i].hash, 16);
850         }
851         return ldb_msg_add_value(msg, attr_name, &val);
852 }
853
854 /*
855   add a acct_flags element to a message
856 */
857 int samdb_msg_add_acct_flags(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
858                              const char *attr_name, uint32_t v)
859 {
860         return samdb_msg_add_uint(sam_ldb, mem_ctx, msg, attr_name, samdb_acb2uf(v));
861 }
862
863 /*
864   add a logon_hours element to a message
865 */
866 int samdb_msg_add_logon_hours(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
867                               const char *attr_name, struct samr_LogonHours *hours)
868 {
869         struct ldb_val val;
870         val.length = hours->units_per_week / 8;
871         val.data = hours->bits;
872         return ldb_msg_add_value(msg, attr_name, &val);
873 }
874
875 /*
876   add a general value element to a message
877 */
878 int samdb_msg_add_value(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
879                               const char *attr_name, const struct ldb_val *val)
880 {
881         return ldb_msg_add_value(msg, attr_name, val);
882 }
883
884 /*
885   sets a general value element to a message
886 */
887 int samdb_msg_set_value(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
888                         const char *attr_name, const struct ldb_val *val)
889 {
890         struct ldb_message_element *el;
891
892         el = ldb_msg_find_element(msg, attr_name);
893         if (el) {
894                 el->num_values = 0;
895         }
896         return ldb_msg_add_value(msg, attr_name, val);
897 }
898
899 /*
900   set a string element in a message
901 */
902 int samdb_msg_set_string(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
903                          const char *attr_name, const char *str)
904 {
905         struct ldb_message_element *el;
906
907         el = ldb_msg_find_element(msg, attr_name);
908         if (el) {
909                 el->num_values = 0;
910         }
911         return samdb_msg_add_string(sam_ldb, mem_ctx, msg, attr_name, str);
912 }
913
914 /*
915   add a record
916 */
917 int samdb_add(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg)
918 {
919         return ldb_add(sam_ldb, msg);
920 }
921
922 /*
923   delete a record
924 */
925 int samdb_delete(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, const struct ldb_dn *dn)
926 {
927         return ldb_delete(sam_ldb, dn);
928 }
929
930 /*
931   modify a record
932 */
933 int samdb_modify(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg)
934 {
935         return ldb_modify(sam_ldb, msg);
936 }
937
938 /*
939   replace elements in a record
940 */
941 int samdb_replace(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg)
942 {
943         int i;
944
945         /* mark all the message elements as LDB_FLAG_MOD_REPLACE */
946         for (i=0;i<msg->num_elements;i++) {
947                 msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
948         }
949
950         /* modify the samdb record */
951         return samdb_modify(sam_ldb, mem_ctx, msg);
952 }
953
954 /*
955   return a default security descriptor
956 */
957 struct security_descriptor *samdb_default_security_descriptor(TALLOC_CTX *mem_ctx)
958 {
959         struct security_descriptor *sd;
960
961         sd = security_descriptor_initialise(mem_ctx);
962
963         return sd;
964 }
965
966 struct ldb_dn *samdb_base_dn(TALLOC_CTX *mem_ctx) 
967 {
968         TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
969         int server_role = lp_server_role();
970         const char **split_realm;
971         struct ldb_dn *dn;
972         
973         if (!tmp_ctx) {
974                 return NULL;
975         }
976
977         if ((server_role == ROLE_DOMAIN_PDC)
978             || (server_role == ROLE_DOMAIN_BDC)) {
979                 int i;
980                 split_realm = str_list_make(tmp_ctx, lp_realm(), ".");
981                 if (!split_realm) {
982                         talloc_free(tmp_ctx);
983                         return NULL;
984                 }
985                 dn = NULL;
986                 i = str_list_length(split_realm);
987                 i--;
988                 for (; i >= 0; i--) {
989                         dn = ldb_dn_build_child(tmp_ctx, "dc", split_realm[i], dn);
990                         if (!dn) {
991                                 talloc_free(tmp_ctx);
992                                 return NULL;
993                         }
994                 }
995                 return dn;
996         }
997         return ldb_dn_string_compose(mem_ctx, NULL, "cn=%s", lp_netbios_name());
998 }
999
1000
1001 /*
1002   work out the domain sid for the current open ldb
1003 */
1004 const struct dom_sid *samdb_domain_sid(struct ldb_context *ldb)
1005 {
1006         const char *attrs[] = { "rootDomainNamingContext", NULL };
1007         int ret;
1008         struct ldb_result *res = NULL;
1009         TALLOC_CTX *tmp_ctx;
1010         struct dom_sid *domain_sid;
1011         const char *basedn_s;
1012         struct ldb_dn *basedn;
1013
1014         /* see if we have a cached copy */
1015         domain_sid = ldb_get_opaque(ldb, "cache.domain_sid");
1016         if (domain_sid) {
1017                 return domain_sid;
1018         }
1019
1020         tmp_ctx = talloc_new(ldb);
1021         if (tmp_ctx == NULL) {
1022                 goto failed;
1023         }
1024
1025         basedn = ldb_dn_explode(tmp_ctx, "");
1026         if (basedn == NULL) {
1027                 goto failed;
1028         }
1029         
1030         /* find the basedn of the domain from the rootdse */
1031         ret = ldb_search(ldb, basedn, LDB_SCOPE_BASE, NULL, attrs, &res);
1032         talloc_steal(tmp_ctx, res);
1033         if (ret != LDB_SUCCESS || res->count != 1) {
1034                 goto failed;
1035         }
1036
1037         basedn_s = ldb_msg_find_string(res->msgs[0], "rootDomainNamingContext", NULL);
1038         if (basedn_s == NULL) {
1039                 goto failed;
1040         }
1041
1042         basedn = ldb_dn_explode(tmp_ctx, basedn_s);
1043         if (basedn == NULL) {
1044                 goto failed;
1045         }
1046
1047         /* find the domain_sid */
1048         domain_sid = samdb_search_dom_sid(ldb, tmp_ctx, basedn, 
1049                                           "objectSid", "objectClass=domainDNS");
1050         if (domain_sid == NULL) {
1051                 goto failed;
1052         }
1053
1054         /* cache the domain_sid in the ldb */
1055         if (ldb_set_opaque(ldb, "cache.domain_sid", domain_sid) != LDB_SUCCESS) {
1056                 goto failed;
1057         }
1058
1059         talloc_steal(ldb, domain_sid);
1060         talloc_free(tmp_ctx);
1061
1062         return domain_sid;
1063
1064 failed:
1065         DEBUG(1,("Failed to find domain_sid for open ldb\n"));
1066         talloc_free(tmp_ctx);
1067         return NULL;
1068 }