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