r12538: Clarify why we are doing the delete here.
[samba.git] / source4 / libnet / libnet_samsync_ldb.c
1 /* 
2    Unix SMB/CIFS implementation.
3    
4    Extract the user/system database from a remote SamSync server
5
6    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005
7    Copyright (C) Andrew Tridgell 2004
8    Copyright (C) Volker Lendecke 2004
9    
10    This program is free software; you can redistribute it and/or modify
11    it under the terms of the GNU General Public License as published by
12    the Free Software Foundation; either version 2 of the License, or
13    (at your option) any later version.
14    
15    This program is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18    GNU General Public License for more details.
19    
20    You should have received a copy of the GNU General Public License
21    along with this program; if not, write to the Free Software
22    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 */
24
25
26 #include "includes.h"
27 #include "libnet/libnet.h"
28 #include "librpc/gen_ndr/ndr_netlogon.h"
29 #include "librpc/gen_ndr/ndr_samr.h"
30 #include "dlinklist.h"
31 #include "lib/ldb/include/ldb.h"
32
33 struct samsync_ldb_secret {
34         struct samsync_ldb_secret *prev, *next;
35         DATA_BLOB secret;
36         char *name;
37         NTTIME mtime;
38 };
39
40 struct samsync_ldb_trusted_domain {
41         struct samsync_ldb_trusted_domain *prev, *next;
42         struct dom_sid *sid;
43         char *name;
44 };
45
46 struct samsync_ldb_state {
47         struct dom_sid *dom_sid[3];
48         struct ldb_context *sam_ldb;
49         struct ldb_dn *base_dn[3];
50         struct samsync_ldb_secret *secrets;
51         struct samsync_ldb_trusted_domain *trusted_domains;
52 };
53
54 static NTSTATUS samsync_ldb_add_foreignSecurityPrincipal(TALLOC_CTX *mem_ctx,
55                                                          struct samsync_ldb_state *state,
56                                                          struct dom_sid *sid,
57                                                          struct ldb_dn **fsp_dn)
58 {
59         const char *sidstr = dom_sid_string(mem_ctx, sid);
60         /* We assume that ForeignSecurityPrincipals are under the BASEDN of the main domain */
61         struct ldb_dn *basedn = samdb_search_dn(state->sam_ldb, mem_ctx,
62                                                 state->base_dn[SAM_DATABASE_DOMAIN],
63                                                 "(&(objectClass=container)(cn=ForeignSecurityPrincipals))");
64         struct ldb_message *msg;
65         int ret;
66
67         if (!sidstr) {
68                 return NT_STATUS_NO_MEMORY;
69         }
70
71         if (basedn == NULL) {
72                 DEBUG(0, ("Failed to find DN for "
73                           "ForeignSecurityPrincipal container\n"));
74                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
75         }
76         
77         msg = ldb_msg_new(mem_ctx);
78         if (msg == NULL) {
79                 return NT_STATUS_NO_MEMORY;
80         }
81
82         /* add core elements to the ldb_message for the alias */
83         msg->dn = ldb_dn_build_child(mem_ctx, "CN", sidstr, basedn);
84         if (msg->dn == NULL)
85                 return NT_STATUS_NO_MEMORY;
86         
87         samdb_msg_add_string(state->sam_ldb, mem_ctx, msg,
88                              "objectClass",
89                              "foreignSecurityPrincipal");
90
91         *fsp_dn = msg->dn;
92
93         /* create the alias */
94         ret = samdb_add(state->sam_ldb, mem_ctx, msg);
95         if (ret != 0) {
96                 DEBUG(0,("Failed to create foreignSecurityPrincipal "
97                          "record %s: %s\n",
98                          ldb_dn_linearize(mem_ctx, msg->dn),
99                          ldb_errstring(state->sam_ldb)));
100                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
101         }
102         return NT_STATUS_OK;
103 }
104
105 static NTSTATUS samsync_ldb_handle_domain(TALLOC_CTX *mem_ctx,
106                                           struct samsync_ldb_state *state,
107                                           struct creds_CredentialState *creds,
108                                           enum netr_SamDatabaseID database,
109                                           struct netr_DELTA_ENUM *delta) 
110 {
111         struct netr_DELTA_DOMAIN *domain = delta->delta_union.domain;
112         const char *domain_name = domain->domain_name.string;
113         struct ldb_message *msg;
114         int ret;
115         
116         if (database == SAM_DATABASE_DOMAIN) {
117                 const char *domain_attrs[] =  {"nETBIOSName", "nCName", NULL};
118                 struct ldb_message **msgs_domain;
119                 int ret_domain;
120
121                 ret_domain = gendb_search(state->sam_ldb, mem_ctx, NULL, &msgs_domain, domain_attrs,
122                                           "(&(&(nETBIOSName=%s)(objectclass=crossRef))(ncName=*))", 
123                                           domain_name);
124                 if (ret_domain == -1) {
125                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
126                 }
127                 
128                 if (ret_domain != 1) {
129                         return NT_STATUS_NO_SUCH_DOMAIN;                
130                 }
131
132                 state->base_dn[database] = samdb_result_dn(state, msgs_domain[0], "nCName", NULL);
133
134                 state->dom_sid[database] = samdb_search_dom_sid(state->sam_ldb, state,
135                                                                 state->base_dn[database], 
136                                                                 "objectSid", NULL);
137         } else if (database == SAM_DATABASE_BUILTIN) {
138                 /* work out the builtin_dn - useful for so many calls its worth
139                    fetching here */
140                 const char *dnstring = samdb_search_string(state->sam_ldb, mem_ctx, NULL,
141                                                            "distinguishedName", "objectClass=builtinDomain");
142                 state->base_dn[database] = ldb_dn_explode(state, dnstring);
143                 state->dom_sid[database] = dom_sid_parse_talloc(state, SID_BUILTIN);
144         } else {
145                 /* PRIVs DB */
146                 return NT_STATUS_INVALID_PARAMETER;
147         }
148
149         msg = ldb_msg_new(mem_ctx);
150         if (msg == NULL) {
151                 return NT_STATUS_NO_MEMORY;
152         }
153
154         msg->dn = talloc_reference(mem_ctx, state->base_dn[database]);
155         if (!msg->dn) {
156                 return NT_STATUS_NO_MEMORY;
157         }
158
159         samdb_msg_add_string(state->sam_ldb, mem_ctx, 
160                              msg, "oEMInformation", domain->comment.string);
161
162         samdb_msg_add_int64(state->sam_ldb, mem_ctx, 
163                              msg, "forceLogoff", domain->force_logoff_time);
164
165         samdb_msg_add_uint(state->sam_ldb, mem_ctx, 
166                           msg, "minPwdLen", domain->min_password_length);
167
168         samdb_msg_add_int64(state->sam_ldb, mem_ctx, 
169                           msg, "maxPwdAge", domain->max_password_age);
170
171         samdb_msg_add_int64(state->sam_ldb, mem_ctx, 
172                           msg, "minPwdAge", domain->min_password_age);
173
174         samdb_msg_add_uint(state->sam_ldb, mem_ctx, 
175                           msg, "pwdHistoryLength", domain->password_history_length);
176
177         samdb_msg_add_uint64(state->sam_ldb, mem_ctx, 
178                              msg, "modifiedCount", 
179                              domain->sequence_num);
180
181         samdb_msg_add_uint64(state->sam_ldb, mem_ctx, 
182                              msg, "creationTime", domain->domain_create_time);
183
184         /* TODO: Account lockout, password properties */
185         
186         ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
187
188         if (ret) {
189                 return NT_STATUS_INTERNAL_ERROR;
190         }
191         return NT_STATUS_OK;
192 }
193
194 static NTSTATUS samsync_ldb_handle_user(TALLOC_CTX *mem_ctx,
195                                         struct samsync_ldb_state *state,
196                                         struct creds_CredentialState *creds,
197                                         enum netr_SamDatabaseID database,
198                                         struct netr_DELTA_ENUM *delta) 
199 {
200         uint32_t rid = delta->delta_id_union.rid;
201         struct netr_DELTA_USER *user = delta->delta_union.user;
202         const char *container, *obj_class;
203         char *cn_name;
204         int cn_name_len;
205
206         struct ldb_message *msg;
207         struct ldb_message **msgs;
208         int ret;
209         uint32_t acb;
210         BOOL add = False;
211         const char *attrs[] = { NULL };
212
213         msg = ldb_msg_new(mem_ctx);
214         if (msg == NULL) {
215                 return NT_STATUS_NO_MEMORY;
216         }
217
218         /* search for the user, by rid */
219         ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database],
220                            &msgs, attrs, "(&(objectClass=user)(objectSid=%s))", 
221                            ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid))); 
222
223         if (ret == -1) {
224                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
225         } else if (ret == 0) {
226                 add = True;
227         } else if (ret > 1) {
228                 DEBUG(0, ("More than one user with SID: %s\n", 
229                           dom_sid_string(mem_ctx, 
230                                          dom_sid_add_rid(mem_ctx, 
231                                                          state->dom_sid[database], 
232                                                          rid))));
233                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
234         } else {
235                 msg->dn = talloc_steal(msg, msgs[0]->dn);
236         }
237
238
239         cn_name   = talloc_strdup(mem_ctx, user->account_name.string);
240         NT_STATUS_HAVE_NO_MEMORY(cn_name);
241         cn_name_len = strlen(cn_name);
242
243 #define ADD_OR_DEL(type, attrib, field) do {\
244         if (user->field) { \
245                 samdb_msg_add_ ## type(state->sam_ldb, mem_ctx, msg, \
246                                      attrib, user->field); \
247         } else if (!add) { \
248                 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,  \
249                                      attrib); \
250         } \
251         } while (0);
252
253         ADD_OR_DEL(string, "samAccountName", account_name.string);
254         ADD_OR_DEL(string, "displayName", full_name.string);
255
256         if (samdb_msg_add_dom_sid(state->sam_ldb, mem_ctx, msg, 
257                                   "objectSid", dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid))) {
258                 return NT_STATUS_NO_MEMORY; 
259         }
260
261         ADD_OR_DEL(uint, "primaryGroupID", primary_gid);
262         ADD_OR_DEL(string, "homeDirectory", home_directory.string);
263         ADD_OR_DEL(string, "homeDrive", home_drive.string);
264         ADD_OR_DEL(string, "scriptPath", logon_script.string);
265         ADD_OR_DEL(string, "description", description.string);
266         ADD_OR_DEL(string, "userWorkstations", workstations.string);
267
268         ADD_OR_DEL(uint64, "lastLogon", last_logon);
269         ADD_OR_DEL(uint64, "lastLogoff", last_logoff);
270
271         if (samdb_msg_add_logon_hours(state->sam_ldb, mem_ctx, msg, "logonHours", &user->logon_hours) != 0) { 
272                 return NT_STATUS_NO_MEMORY; 
273         }
274
275         ADD_OR_DEL(uint, "badPwdCount", bad_password_count);
276         ADD_OR_DEL(uint, "logonCount", logon_count);
277
278         ADD_OR_DEL(uint64, "pwdLastSet", last_password_change);
279         ADD_OR_DEL(uint64, "accountExpires", acct_expiry);
280         
281         if (samdb_msg_add_acct_flags(state->sam_ldb, mem_ctx, msg, 
282                                      "userAccountControl", user->acct_flags) != 0) { 
283                 return NT_STATUS_NO_MEMORY; 
284         } 
285         
286         /* Passwords.  Ensure there is no plaintext stored against
287          * this entry, as we only have hashes */
288         samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,  
289                                 "unicodePwd"); 
290         if (user->lm_password_present) {
291                 samdb_msg_add_hash(state->sam_ldb, mem_ctx, msg,  
292                                    "lmPwdHash", &user->lmpassword);
293         } else {
294                 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,  
295                                      "lmPwdHash"); 
296         }
297         if (user->nt_password_present) {
298                 samdb_msg_add_hash(state->sam_ldb, mem_ctx, msg,  
299                                    "ntPwdHash", &user->ntpassword);
300         } else {
301                 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,  
302                                      "ntPwdHash"); 
303         }
304             
305         ADD_OR_DEL(string, "comment", comment.string);
306         ADD_OR_DEL(string, "userParameters", parameters.string);
307         ADD_OR_DEL(uint, "countryCode", country_code);
308         ADD_OR_DEL(uint, "codePage", code_page);
309
310         ADD_OR_DEL(string, "profilePath", profile_path.string);
311
312 #undef ADD_OR_DEL
313
314         acb = user->acct_flags;
315         if (acb & (ACB_WSTRUST)) {
316                 cn_name[cn_name_len - 1] = '\0';
317                 container = "Computers";
318                 obj_class = "computer";
319                 
320         } else if (acb & ACB_SVRTRUST) {
321                 if (cn_name[cn_name_len - 1] != '$') {
322                         return NT_STATUS_FOOBAR;                
323                 }
324                 cn_name[cn_name_len - 1] = '\0';
325                 container = "Domain Controllers";
326                 obj_class = "computer";
327         } else {
328                 container = "Users";
329                 obj_class = "user";
330         }
331         if (add) {
332                 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg, 
333                                      "objectClass", obj_class);
334                 msg->dn = ldb_dn_string_compose(mem_ctx, state->base_dn[database],
335                                                 "CN=%s, CN=%s", cn_name, container);
336                 if (!msg->dn) {
337                         return NT_STATUS_NO_MEMORY;             
338                 }
339
340                 ret = samdb_add(state->sam_ldb, mem_ctx, msg);
341                 if (ret != 0) {
342                         DEBUG(0,("Failed to create user record %s\n",
343                                  ldb_dn_linearize(mem_ctx, msg->dn)));
344                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
345                 }
346         } else {
347                 ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
348                 if (ret != 0) {
349                         DEBUG(0,("Failed to modify user record %s\n",
350                                  ldb_dn_linearize(mem_ctx, msg->dn)));
351                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
352                 }
353         }
354
355         return NT_STATUS_OK;
356 }
357
358 static NTSTATUS samsync_ldb_delete_user(TALLOC_CTX *mem_ctx,
359                                         struct samsync_ldb_state *state,
360                                         struct creds_CredentialState *creds,
361                                         enum netr_SamDatabaseID database,
362                                         struct netr_DELTA_ENUM *delta) 
363 {
364         uint32_t rid = delta->delta_id_union.rid;
365         struct ldb_message **msgs;
366         int ret;
367         const char *attrs[] = { NULL };
368
369         /* search for the user, by rid */
370         ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database],
371                            &msgs, attrs, "(&(objectClass=user)(objectSid=%s))", 
372                            ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid))); 
373
374         if (ret == -1) {
375                 DEBUG(0, ("gendb_search failed: %s\n", ldb_errstring(state->sam_ldb)));
376                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
377         } else if (ret == 0) {
378                 return NT_STATUS_NO_SUCH_USER;
379         } else if (ret > 1) {
380                 DEBUG(0, ("More than one user with SID: %s\n", 
381                           dom_sid_string(mem_ctx, 
382                                          dom_sid_add_rid(mem_ctx, 
383                                                          state->dom_sid[database], 
384                                                          rid))));
385                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
386         }
387
388         ret = samdb_delete(state->sam_ldb, mem_ctx, msgs[0]->dn);
389         if (ret != 0) {
390                 DEBUG(0,("Failed to delete user record %s: %s\n",
391                          ldb_dn_linearize(mem_ctx, msgs[0]->dn),
392                          ldb_errstring(state->sam_ldb)));
393                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
394         }
395
396         return NT_STATUS_OK;
397 }
398
399 static NTSTATUS samsync_ldb_handle_group(TALLOC_CTX *mem_ctx,
400                                          struct samsync_ldb_state *state,
401                                          struct creds_CredentialState *creds,
402                                          enum netr_SamDatabaseID database,
403                                          struct netr_DELTA_ENUM *delta) 
404 {
405         uint32_t rid = delta->delta_id_union.rid;
406         struct netr_DELTA_GROUP *group = delta->delta_union.group;
407         const char *container, *obj_class;
408         const char *cn_name;
409
410         struct ldb_message *msg;
411         struct ldb_message **msgs;
412         int ret;
413         BOOL add = False;
414         const char *attrs[] = { NULL };
415
416         msg = ldb_msg_new(mem_ctx);
417         if (msg == NULL) {
418                 return NT_STATUS_NO_MEMORY;
419         }
420
421         /* search for the group, by rid */
422         ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
423                            "(&(objectClass=group)(objectSid=%s))", 
424                            ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid))); 
425
426         if (ret == -1) {
427                 DEBUG(0, ("gendb_search failed: %s\n", ldb_errstring(state->sam_ldb)));
428                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
429         } else if (ret == 0) {
430                 add = True;
431         } else if (ret > 1) {
432                 DEBUG(0, ("More than one group/alias with SID: %s\n", 
433                           dom_sid_string(mem_ctx, 
434                                          dom_sid_add_rid(mem_ctx, 
435                                                          state->dom_sid[database], 
436                                                          rid))));
437                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
438         } else {
439                 msg->dn = talloc_steal(msg, msgs[0]->dn);
440         }
441
442         cn_name   = group->group_name.string;
443
444 #define ADD_OR_DEL(type, attrib, field) do {\
445         if (group->field) { \
446                 samdb_msg_add_ ## type(state->sam_ldb, mem_ctx, msg, \
447                                      attrib, group->field); \
448         } else if (!add) { \
449                 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,  \
450                                      attrib); \
451         } \
452         } while (0);
453
454         ADD_OR_DEL(string, "samAccountName", group_name.string);
455
456         if (samdb_msg_add_dom_sid(state->sam_ldb, mem_ctx, msg, 
457                                   "objectSid", dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid))) {
458                 return NT_STATUS_NO_MEMORY; 
459         }
460
461         ADD_OR_DEL(string, "description", description.string);
462
463 #undef ADD_OR_DEL
464
465         container = "Users";
466         obj_class = "group";
467
468         if (add) {
469                 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg, 
470                                      "objectClass", obj_class);
471                 msg->dn = ldb_dn_string_compose(mem_ctx, state->base_dn[database],
472                                                 "CN=%s, CN=%s", cn_name, container);
473                 if (!msg->dn) {
474                         return NT_STATUS_NO_MEMORY;             
475                 }
476
477                 ret = samdb_add(state->sam_ldb, mem_ctx, msg);
478                 if (ret != 0) {
479                         DEBUG(0,("Failed to create group record %s: %s\n",
480                          ldb_dn_linearize(mem_ctx, msg->dn),
481                          ldb_errstring(state->sam_ldb)));
482                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
483                 }
484         } else {
485                 ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
486                 if (ret != 0) {
487                         DEBUG(0,("Failed to modify group record %s: %s\n",
488                          ldb_dn_linearize(mem_ctx, msg->dn),
489                          ldb_errstring(state->sam_ldb)));
490                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
491                 }
492         }
493
494         return NT_STATUS_OK;
495 }
496
497 static NTSTATUS samsync_ldb_delete_group(TALLOC_CTX *mem_ctx,
498                                         struct samsync_ldb_state *state,
499                                         struct creds_CredentialState *creds,
500                                         enum netr_SamDatabaseID database,
501                                         struct netr_DELTA_ENUM *delta) 
502 {
503         uint32_t rid = delta->delta_id_union.rid;
504         struct ldb_message **msgs;
505         int ret;
506         const char *attrs[] = { NULL };
507
508         /* search for the group, by rid */
509         ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
510                            "(&(objectClass=group)(objectSid=%s))", 
511                            ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid))); 
512
513         if (ret == -1) {
514                 DEBUG(0, ("gendb_search failed: %s\n", ldb_errstring(state->sam_ldb)));
515                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
516         } else if (ret == 0) {
517                 return NT_STATUS_NO_SUCH_GROUP;
518         } else if (ret > 1) {
519                 DEBUG(0, ("More than one group/alias with SID: %s\n", 
520                           dom_sid_string(mem_ctx, 
521                                          dom_sid_add_rid(mem_ctx, 
522                                                          state->dom_sid[database], 
523                                                          rid))));
524                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
525         }
526         
527         ret = samdb_delete(state->sam_ldb, mem_ctx, msgs[0]->dn);
528         if (ret != 0) {
529                 DEBUG(0,("Failed to delete group record %s: %s\n",
530                          ldb_dn_linearize(mem_ctx, msgs[0]->dn),
531                          ldb_errstring(state->sam_ldb)));
532                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
533         }
534
535         return NT_STATUS_OK;
536 }
537
538 static NTSTATUS samsync_ldb_handle_group_member(TALLOC_CTX *mem_ctx,
539                                                 struct samsync_ldb_state *state,
540                                                 struct creds_CredentialState *creds,
541                                                 enum netr_SamDatabaseID database,
542                                                 struct netr_DELTA_ENUM *delta) 
543 {
544         uint32_t rid = delta->delta_id_union.rid;
545         struct netr_DELTA_GROUP_MEMBER *group_member = delta->delta_union.group_member;
546         struct ldb_message *msg;
547         struct ldb_message **msgs;
548         int ret;
549         const char *attrs[] = { NULL };
550         int i;
551
552         msg = ldb_msg_new(mem_ctx);
553         if (msg == NULL) {
554                 return NT_STATUS_NO_MEMORY;
555         }
556
557         /* search for the group, by rid */
558         ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
559                            "(&(objectClass=group)(objectSid=%s))", 
560                            ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid))); 
561
562         if (ret == -1) {
563                 DEBUG(0, ("gendb_search failed: %s\n", ldb_errstring(state->sam_ldb)));
564                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
565         } else if (ret == 0) {
566                 return NT_STATUS_NO_SUCH_GROUP;
567         } else if (ret > 1) {
568                 DEBUG(0, ("More than one group/alias with SID: %s\n", 
569                           dom_sid_string(mem_ctx, 
570                                          dom_sid_add_rid(mem_ctx, 
571                                                          state->dom_sid[database], 
572                                                          rid))));
573                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
574         } else {
575                 msg->dn = talloc_steal(msg, msgs[0]->dn);
576         }
577         
578         talloc_free(msgs);
579
580         for (i=0; i<group_member->num_rids; i++) {
581                 /* search for the group, by rid */
582                 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
583                                    "(&(objectClass=user)(objectSid=%s))", 
584                                    ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], group_member->rids[i]))); 
585
586                 if (ret == -1) {
587                         DEBUG(0, ("gendb_search failed: %s\n", ldb_errstring(state->sam_ldb)));
588                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
589                 } else if (ret == 0) {
590                         return NT_STATUS_NO_SUCH_USER;
591                 } else if (ret > 1) {
592                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
593                 } else {
594                         samdb_msg_add_string(state->sam_ldb, mem_ctx, msg, "member", ldb_dn_linearize(mem_ctx, msgs[0]->dn));
595                 }
596         
597                 talloc_free(msgs);
598         }
599
600         ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
601         if (ret != 0) {
602                 DEBUG(0,("Failed to modify group record %s: %s\n",
603                          ldb_dn_linearize(mem_ctx, msg->dn),
604                          ldb_errstring(state->sam_ldb)));
605                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
606         }
607
608         return NT_STATUS_OK;
609 }
610
611 static NTSTATUS samsync_ldb_handle_alias(TALLOC_CTX *mem_ctx,
612                                          struct samsync_ldb_state *state,
613                                          struct creds_CredentialState *creds,
614                                          enum netr_SamDatabaseID database,
615                                          struct netr_DELTA_ENUM *delta) 
616 {
617         uint32_t rid = delta->delta_id_union.rid;
618         struct netr_DELTA_ALIAS *alias = delta->delta_union.alias;
619         const char *container, *obj_class;
620         const char *cn_name;
621
622         struct ldb_message *msg;
623         struct ldb_message **msgs;
624         int ret;
625         BOOL add = False;
626         const char *attrs[] = { NULL };
627
628         msg = ldb_msg_new(mem_ctx);
629         if (msg == NULL) {
630                 return NT_STATUS_NO_MEMORY;
631         }
632
633         /* search for the alias, by rid */
634         ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
635                            "(&(objectClass=group)(objectSid=%s))", 
636                            ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid))); 
637
638         if (ret == -1) {
639                 DEBUG(0, ("gendb_search failed: %s\n", ldb_errstring(state->sam_ldb)));
640                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
641         } else if (ret == 0) {
642                 add = True;
643         } else if (ret > 1) {
644                 DEBUG(0, ("More than one group/alias with SID: %s\n", 
645                           dom_sid_string(mem_ctx, 
646                                          dom_sid_add_rid(mem_ctx, 
647                                                          state->dom_sid[database], 
648                                                          rid))));
649                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
650         } else {
651                 msg->dn = talloc_steal(mem_ctx, msgs[0]->dn);
652         }
653
654         cn_name   = alias->alias_name.string;
655
656 #define ADD_OR_DEL(type, attrib, field) do {\
657         if (alias->field) { \
658                 samdb_msg_add_ ## type(state->sam_ldb, mem_ctx, msg, \
659                                      attrib, alias->field); \
660         } else if (!add) { \
661                 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,  \
662                                      attrib); \
663         } \
664         } while (0);
665
666         ADD_OR_DEL(string, "samAccountName", alias_name.string);
667
668         if (samdb_msg_add_dom_sid(state->sam_ldb, mem_ctx, msg, 
669                                   "objectSid", dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid))) {
670                 return NT_STATUS_NO_MEMORY; 
671         }
672
673         ADD_OR_DEL(string, "description", description.string);
674
675 #undef ADD_OR_DEL
676
677         samdb_msg_add_uint(state->sam_ldb, mem_ctx, msg, "groupType", 0x80000004);
678
679         container = "Users";
680         obj_class = "group";
681
682         if (add) {
683                 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg, 
684                                      "objectClass", obj_class);
685                 msg->dn = ldb_dn_string_compose(mem_ctx, state->base_dn[database],
686                                                 "CN=%s, CN=%s", cn_name, container);
687                 if (!msg->dn) {
688                         return NT_STATUS_NO_MEMORY;             
689                 }
690
691                 ret = samdb_add(state->sam_ldb, mem_ctx, msg);
692                 if (ret != 0) {
693                         DEBUG(0,("Failed to create alias record %s: %s\n",
694                                  ldb_dn_linearize(mem_ctx, msg->dn),
695                                  ldb_errstring(state->sam_ldb)));
696                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
697                 }
698         } else {
699                 ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
700                 if (ret != 0) {
701                         DEBUG(0,("Failed to modify alias record %s: %s\n",
702                                  ldb_dn_linearize(mem_ctx, msg->dn),
703                                  ldb_errstring(state->sam_ldb)));
704                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
705                 }
706         }
707
708         return NT_STATUS_OK;
709 }
710
711 static NTSTATUS samsync_ldb_delete_alias(TALLOC_CTX *mem_ctx,
712                                         struct samsync_ldb_state *state,
713                                         struct creds_CredentialState *creds,
714                                         enum netr_SamDatabaseID database,
715                                         struct netr_DELTA_ENUM *delta) 
716 {
717         uint32_t rid = delta->delta_id_union.rid;
718         struct ldb_message **msgs;
719         int ret;
720         const char *attrs[] = { NULL };
721
722         /* search for the alias, by rid */
723         ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
724                            "(&(objectClass=group)(objectSid=%s))", 
725                            ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid))); 
726
727         if (ret == -1) {
728                 DEBUG(0, ("gendb_search failed: %s\n", ldb_errstring(state->sam_ldb)));
729                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
730         } else if (ret == 0) {
731                 return NT_STATUS_NO_SUCH_ALIAS;
732         } else if (ret > 1) {
733                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
734         }
735
736         ret = samdb_delete(state->sam_ldb, mem_ctx, msgs[0]->dn);
737         if (ret != 0) {
738                 DEBUG(0,("Failed to delete alias record %s: %s\n",
739                          ldb_dn_linearize(mem_ctx, msgs[0]->dn),
740                          ldb_errstring(state->sam_ldb)));
741                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
742         }
743
744         return NT_STATUS_OK;
745 }
746
747 static NTSTATUS samsync_ldb_handle_alias_member(TALLOC_CTX *mem_ctx,
748                                                 struct samsync_ldb_state *state,
749                                                 struct creds_CredentialState *creds,
750                                                 enum netr_SamDatabaseID database,
751                                                 struct netr_DELTA_ENUM *delta) 
752 {
753         uint32_t rid = delta->delta_id_union.rid;
754         struct netr_DELTA_ALIAS_MEMBER *alias_member = delta->delta_union.alias_member;
755         struct ldb_message *msg;
756         struct ldb_message **msgs;
757         int ret;
758         const char *attrs[] = { NULL };
759         int i;
760
761         msg = ldb_msg_new(mem_ctx);
762         if (msg == NULL) {
763                 return NT_STATUS_NO_MEMORY;
764         }
765
766         /* search for the alias, by rid */
767         ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
768                            "(&(objectClass=group)(objectSid=%s))", 
769                            ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid))); 
770
771         if (ret == -1) {
772                 DEBUG(0, ("gendb_search failed: %s\n", ldb_errstring(state->sam_ldb)));
773                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
774         } else if (ret == 0) {
775                 return NT_STATUS_NO_SUCH_GROUP;
776         } else if (ret > 1) {
777                 DEBUG(0, ("More than one group/alias with SID: %s\n", 
778                           dom_sid_string(mem_ctx, 
779                                          dom_sid_add_rid(mem_ctx, 
780                                                          state->dom_sid[database], 
781                                                          rid))));
782                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
783         } else {
784                 msg->dn = talloc_steal(msg, msgs[0]->dn);
785         }
786         
787         talloc_free(msgs);
788
789         for (i=0; i<alias_member->sids.num_sids; i++) {
790                 struct ldb_dn *alias_member_dn;
791                 /* search for members, in the top basedn (normal users are builtin aliases) */
792                 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[SAM_DATABASE_DOMAIN], &msgs, attrs,
793                                    "(objectSid=%s)", 
794                                    ldap_encode_ndr_dom_sid(mem_ctx, alias_member->sids.sids[i].sid)); 
795
796                 if (ret == -1) {
797                         DEBUG(0, ("gendb_search failed: %s\n", ldb_errstring(state->sam_ldb)));
798                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
799                 } else if (ret == 0) {
800                         NTSTATUS nt_status;
801                         nt_status = samsync_ldb_add_foreignSecurityPrincipal(mem_ctx, state,
802                                                                              alias_member->sids.sids[i].sid, 
803                                                                              &alias_member_dn);
804                         if (!NT_STATUS_IS_OK(nt_status)) {
805                                 return nt_status;
806                         }
807                 } else if (ret > 1) {
808                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
809                 } else {
810                         alias_member_dn = msgs[0]->dn;
811                 }
812                 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg, "member", ldb_dn_linearize(mem_ctx, alias_member_dn));
813         
814                 talloc_free(msgs);
815         }
816
817         ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
818         if (ret != 0) {
819                 DEBUG(0,("Failed to modify group record %s: %s\n",
820                          ldb_dn_linearize(mem_ctx, msg->dn),
821                          ldb_errstring(state->sam_ldb)));
822                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
823         }
824
825         return NT_STATUS_OK;
826 }
827
828 static NTSTATUS samsync_ldb_handle_account(TALLOC_CTX *mem_ctx,
829                                         struct samsync_ldb_state *state,
830                                         struct creds_CredentialState *creds,
831                                         enum netr_SamDatabaseID database,
832                                         struct netr_DELTA_ENUM *delta) 
833 {
834         struct dom_sid *sid = delta->delta_id_union.sid;
835         struct netr_DELTA_ACCOUNT *account = delta->delta_union.account;
836
837         struct ldb_message *msg;
838         struct ldb_message **msgs;
839         struct ldb_dn *privilege_dn;
840         int ret;
841         const char *attrs[] = { NULL };
842         int i;
843
844         msg = ldb_msg_new(mem_ctx);
845         if (msg == NULL) {
846                 return NT_STATUS_NO_MEMORY;
847         }
848
849         /* search for the account, by sid, in the top basedn */
850         ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[SAM_DATABASE_DOMAIN], &msgs, attrs,
851                            "(objectSid=%s)", ldap_encode_ndr_dom_sid(mem_ctx, sid)); 
852
853         if (ret == -1) {
854                 DEBUG(0, ("gendb_search failed: %s\n", ldb_errstring(state->sam_ldb)));
855                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
856         } else if (ret == 0) {
857                 NTSTATUS nt_status;
858                 nt_status = samsync_ldb_add_foreignSecurityPrincipal(mem_ctx, state,
859                                                                      sid,
860                                                                      &privilege_dn);
861                 privilege_dn = talloc_steal(msg, privilege_dn);
862                 if (!NT_STATUS_IS_OK(nt_status)) {
863                         return nt_status;
864                 }
865         } else if (ret > 1) {
866                 DEBUG(0, ("More than one account with SID: %s\n", 
867                           dom_sid_string(mem_ctx, sid)));
868                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
869         } else {
870                 privilege_dn = talloc_steal(msg, msgs[0]->dn);
871         }
872
873         msg->dn = privilege_dn;
874
875         for (i=0; i< account->privilege_entries; i++) {
876                 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg, "privilege",
877                                      account->privilege_name[i].string);
878         }
879
880         ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
881         if (ret != 0) {
882                 DEBUG(0,("Failed to modify privilege record %s\n",
883                          ldb_dn_linearize(mem_ctx, msg->dn)));
884                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
885         }
886
887         return NT_STATUS_OK;
888 }
889
890 static NTSTATUS samsync_ldb_delete_account(TALLOC_CTX *mem_ctx,
891                                         struct samsync_ldb_state *state,
892                                         struct creds_CredentialState *creds,
893                                         enum netr_SamDatabaseID database,
894                                         struct netr_DELTA_ENUM *delta) 
895 {
896         struct dom_sid *sid = delta->delta_id_union.sid;
897
898         struct ldb_message *msg;
899         struct ldb_message **msgs;
900         int ret;
901         const char *attrs[] = { NULL };
902
903         msg = ldb_msg_new(mem_ctx);
904         if (msg == NULL) {
905                 return NT_STATUS_NO_MEMORY;
906         }
907
908         /* search for the account, by sid, in the top basedn */
909         ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[SAM_DATABASE_DOMAIN], &msgs, attrs,
910                            "(objectSid=%s)", 
911                            ldap_encode_ndr_dom_sid(mem_ctx, sid)); 
912
913         if (ret == -1) {
914                 DEBUG(0, ("gendb_search failed: %s\n", ldb_errstring(state->sam_ldb)));
915                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
916         } else if (ret == 0) {
917                 return NT_STATUS_NO_SUCH_USER;
918         } else if (ret > 1) {
919                 DEBUG(0, ("More than one account with SID: %s\n", 
920                           dom_sid_string(mem_ctx, sid)));
921                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
922         } else {
923                 msg->dn = talloc_steal(msg, msgs[0]->dn);
924         }
925
926         samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,  
927                              "privilage"); 
928
929         ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
930         if (ret != 0) {
931                 DEBUG(0,("Failed to modify privilege record %s\n",
932                          ldb_dn_linearize(mem_ctx, msg->dn)));
933                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
934         }
935
936         return NT_STATUS_OK;
937 }
938
939 static NTSTATUS libnet_samsync_ldb_fn(TALLOC_CTX *mem_ctx,              
940                                   void *private,                        
941                                   struct creds_CredentialState *creds,
942                                   enum netr_SamDatabaseID database,
943                                   struct netr_DELTA_ENUM *delta,
944                                   char **error_string)
945 {
946         NTSTATUS nt_status = NT_STATUS_OK;
947         struct samsync_ldb_state *state = private;
948
949         *error_string = NULL;
950         switch (delta->delta_type) {
951         case NETR_DELTA_DOMAIN:
952         {
953                 nt_status = samsync_ldb_handle_domain(mem_ctx, 
954                                                       state,
955                                                       creds,
956                                                       database,
957                                                       delta);
958                 break;
959         }
960         case NETR_DELTA_USER:
961         {
962                 nt_status = samsync_ldb_handle_user(mem_ctx, 
963                                                     state,
964                                                     creds,
965                                                     database,
966                                                     delta);
967                 break;
968         }
969         case NETR_DELTA_DELETE_USER:
970         {
971                 nt_status = samsync_ldb_delete_user(mem_ctx, 
972                                                     state,
973                                                     creds,
974                                                     database,
975                                                     delta);
976                 break;
977         }
978         case NETR_DELTA_GROUP:
979         {
980                 nt_status = samsync_ldb_handle_group(mem_ctx, 
981                                                      state,
982                                                      creds,
983                                                      database,
984                                                      delta);
985                 break;
986         }
987         case NETR_DELTA_DELETE_GROUP:
988         {
989                 nt_status = samsync_ldb_delete_group(mem_ctx, 
990                                                     state,
991                                                     creds,
992                                                     database,
993                                                     delta);
994                 break;
995         }
996         case NETR_DELTA_GROUP_MEMBER:
997         {
998                 nt_status = samsync_ldb_handle_group_member(mem_ctx, 
999                                                             state,
1000                                                             creds,
1001                                                             database,
1002                                                             delta);
1003                 break;
1004         }
1005         case NETR_DELTA_ALIAS:
1006         {
1007                 nt_status = samsync_ldb_handle_alias(mem_ctx, 
1008                                                      state,
1009                                                      creds,
1010                                                      database,
1011                                                      delta);
1012                 break;
1013         }
1014         case NETR_DELTA_DELETE_ALIAS:
1015         {
1016                 nt_status = samsync_ldb_delete_alias(mem_ctx, 
1017                                                     state,
1018                                                     creds,
1019                                                     database,
1020                                                     delta);
1021                 break;
1022         }
1023         case NETR_DELTA_ALIAS_MEMBER:
1024         {
1025                 nt_status = samsync_ldb_handle_alias_member(mem_ctx, 
1026                                                             state,
1027                                                             creds,
1028                                                             database,
1029                                                             delta);
1030                 break;
1031         }
1032         case NETR_DELTA_ACCOUNT:
1033         {
1034                 nt_status = samsync_ldb_handle_account(mem_ctx, 
1035                                                      state,
1036                                                      creds,
1037                                                      database,
1038                                                      delta);
1039                 break;
1040         }
1041         case NETR_DELTA_DELETE_ACCOUNT:
1042         {
1043                 nt_status = samsync_ldb_delete_account(mem_ctx, 
1044                                                     state,
1045                                                     creds,
1046                                                     database,
1047                                                     delta);
1048                 break;
1049         }
1050         default:
1051                 /* Can't dump them all right now */
1052                 break;
1053         }
1054         return nt_status;
1055 }
1056
1057 static NTSTATUS libnet_samsync_ldb_netlogon(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, struct libnet_samsync_ldb *r)
1058 {
1059         NTSTATUS nt_status;
1060         struct libnet_SamSync r2;
1061         struct samsync_ldb_state *state = talloc(mem_ctx, struct samsync_ldb_state);
1062
1063         if (!state) {
1064                 return NT_STATUS_NO_MEMORY;
1065         }
1066
1067         state->secrets = NULL;
1068         state->trusted_domains = NULL;
1069
1070         state->sam_ldb = samdb_connect(state, system_session(state));
1071
1072         r2.error_string = NULL;
1073         r2.delta_fn = libnet_samsync_ldb_fn;
1074         r2.fn_ctx = state;
1075         r2.machine_account = NULL; /* TODO:  Create a machine account, fill this in, and the delete it */
1076         nt_status = libnet_SamSync_netlogon(ctx, state, &r2);
1077         r->error_string = r2.error_string;
1078
1079         if (!NT_STATUS_IS_OK(nt_status)) {
1080                 talloc_free(state);
1081                 return nt_status;
1082         }
1083         talloc_free(state);
1084         return nt_status;
1085 }
1086
1087
1088
1089 static NTSTATUS libnet_samsync_ldb_generic(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, struct libnet_samsync_ldb *r)
1090 {
1091         NTSTATUS nt_status;
1092         struct libnet_samsync_ldb r2;
1093         r2.level = LIBNET_SAMSYNC_LDB_NETLOGON;
1094         r2.error_string = NULL;
1095         nt_status = libnet_samsync_ldb(ctx, mem_ctx, &r2);
1096         r->error_string = r2.error_string;
1097         
1098         return nt_status;
1099 }
1100
1101 NTSTATUS libnet_samsync_ldb(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, struct libnet_samsync_ldb *r)
1102 {
1103         switch (r->level) {
1104         case LIBNET_SAMSYNC_LDB_GENERIC:
1105                 return libnet_samsync_ldb_generic(ctx, mem_ctx, r);
1106         case LIBNET_SAMSYNC_LDB_NETLOGON:
1107                 return libnet_samsync_ldb_netlogon(ctx, mem_ctx, r);
1108         }
1109
1110         return NT_STATUS_INVALID_LEVEL;
1111 }