r25446: Merge some changes I made on the way home from SFO:
[jelmer/samba4-debian.git] / source / 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 3 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, see <http://www.gnu.org/licenses/>.
22 */
23
24
25 #include "includes.h"
26 #include "libnet/libnet.h"
27 #include "libcli/ldap/ldap.h"
28 #include "dsdb/samdb/samdb.h"
29 #include "auth/auth.h"
30 #include "librpc/gen_ndr/ndr_misc.h"
31 #include "db_wrap.h"
32 #include "libcli/security/security.h"
33 #include "librpc/rpc/dcerpc.h"
34 #include "param/param.h"
35
36 struct samsync_ldb_secret {
37         struct samsync_ldb_secret *prev, *next;
38         DATA_BLOB secret;
39         char *name;
40         NTTIME mtime;
41 };
42
43 struct samsync_ldb_trusted_domain {
44         struct samsync_ldb_trusted_domain *prev, *next;
45         struct dom_sid *sid;
46         char *name;
47 };
48
49 struct samsync_ldb_state {
50         /* Values from the LSA lookup */
51         const struct libnet_SamSync_state *samsync_state;
52
53         struct dom_sid *dom_sid[3];
54         struct ldb_context *sam_ldb, *remote_ldb;
55         struct ldb_dn *base_dn[3];
56         struct samsync_ldb_secret *secrets;
57         struct samsync_ldb_trusted_domain *trusted_domains;
58 };
59
60 static NTSTATUS samsync_ldb_add_foreignSecurityPrincipal(TALLOC_CTX *mem_ctx,
61                                                          struct samsync_ldb_state *state,
62                                                          struct dom_sid *sid,
63                                                          struct ldb_dn **fsp_dn,
64                                                          char **error_string)
65 {
66         const char *sidstr = dom_sid_string(mem_ctx, sid);
67         /* We assume that ForeignSecurityPrincipals are under the BASEDN of the main domain */
68         struct ldb_dn *basedn = samdb_search_dn(state->sam_ldb, mem_ctx,
69                                                 state->base_dn[SAM_DATABASE_DOMAIN],
70                                                 "(&(objectClass=container)(cn=ForeignSecurityPrincipals))");
71         struct ldb_message *msg;
72         int ret;
73
74         if (!sidstr) {
75                 return NT_STATUS_NO_MEMORY;
76         }
77
78         if (basedn == NULL) {
79                 *error_string = talloc_asprintf(mem_ctx, 
80                                                 "Failed to find DN for "
81                                                 "ForeignSecurityPrincipal container under %s",
82                                                 ldb_dn_get_linearized(state->base_dn[SAM_DATABASE_DOMAIN]));
83                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
84         }
85         
86         msg = ldb_msg_new(mem_ctx);
87         if (msg == NULL) {
88                 return NT_STATUS_NO_MEMORY;
89         }
90
91         /* add core elements to the ldb_message for the alias */
92         msg->dn = basedn;
93         if ( ! ldb_dn_add_child_fmt(msg->dn, "CN=%s", sidstr))
94                 return NT_STATUS_UNSUCCESSFUL;
95         
96         samdb_msg_add_string(state->sam_ldb, mem_ctx, msg,
97                              "objectClass",
98                              "foreignSecurityPrincipal");
99
100         *fsp_dn = msg->dn;
101
102         /* create the alias */
103         ret = samdb_add(state->sam_ldb, mem_ctx, msg);
104         if (ret != 0) {
105                 *error_string = talloc_asprintf(mem_ctx, "Failed to create foreignSecurityPrincipal "
106                                                 "record %s: %s",
107                                                 ldb_dn_get_linearized(msg->dn),
108                                                 ldb_errstring(state->sam_ldb));
109                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
110         }
111         return NT_STATUS_OK;
112 }
113
114 static NTSTATUS samsync_ldb_handle_domain(TALLOC_CTX *mem_ctx,
115                                           struct samsync_ldb_state *state,
116                                           enum netr_SamDatabaseID database,
117                                           struct netr_DELTA_ENUM *delta,
118                                           char **error_string) 
119 {
120         struct netr_DELTA_DOMAIN *domain = delta->delta_union.domain;
121         const char *domain_name = domain->domain_name.string;
122         struct ldb_message *msg;
123         int ret;
124         
125         msg = ldb_msg_new(mem_ctx);
126         if (msg == NULL) {
127                 return NT_STATUS_NO_MEMORY;
128         }
129
130         if (database == SAM_DATABASE_DOMAIN) {
131                 struct ldb_dn *partitions_basedn;
132                 const char *domain_attrs[] =  {"nETBIOSName", "nCName", NULL};
133                 struct ldb_message **msgs_domain;
134                 int ret_domain;
135
136                 partitions_basedn = samdb_partitions_dn(state->sam_ldb, mem_ctx);
137
138                 ret_domain = gendb_search(state->sam_ldb, mem_ctx, partitions_basedn, &msgs_domain, domain_attrs,
139                                           "(&(&(nETBIOSName=%s)(objectclass=crossRef))(ncName=*))", 
140                                           domain_name);
141                 if (ret_domain == -1) {
142                         *error_string = talloc_asprintf(mem_ctx, "gendb_search for domain failed: %s", ldb_errstring(state->sam_ldb));
143                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
144                 }
145                 
146                 if (ret_domain != 1) {
147                         *error_string = talloc_asprintf(mem_ctx, "Failed to find existing domain record for %s: %d results", domain_name,
148                                                         ret_domain);
149                         return NT_STATUS_NO_SUCH_DOMAIN;                
150                 }
151
152                 state->base_dn[database] = samdb_result_dn(state->sam_ldb, state, msgs_domain[0], "nCName", NULL);
153
154                 if (state->dom_sid[database]) {
155                         /* Update the domain sid with the incoming
156                          * domain (found on LSA pipe, database sid may
157                          * be random) */
158                         samdb_msg_add_dom_sid(state->sam_ldb, mem_ctx, 
159                                               msg, "objectSid", state->dom_sid[database]);
160                 } else {
161                         /* Well, we will have to use the one from the database */
162                         state->dom_sid[database] = samdb_search_dom_sid(state->sam_ldb, state,
163                                                                         state->base_dn[database], 
164                                                                         "objectSid", NULL);
165                 }
166
167                 if (state->samsync_state->domain_guid) {
168                         NTSTATUS nt_status;
169                         struct ldb_val v;
170                         nt_status = ndr_push_struct_blob(&v, msg, state->samsync_state->domain_guid,
171                                                          (ndr_push_flags_fn_t)ndr_push_GUID);
172                         if (!NT_STATUS_IS_OK(nt_status)) {
173                                 *error_string = talloc_asprintf(mem_ctx, "ndr_push of domain GUID failed!");
174                                 return nt_status;
175                         }
176                         
177                         ldb_msg_add_value(msg, "objectGUID", &v, NULL);
178                 }
179         } else if (database == SAM_DATABASE_BUILTIN) {
180                 /* work out the builtin_dn - useful for so many calls its worth
181                    fetching here */
182                 const char *dnstring = samdb_search_string(state->sam_ldb, mem_ctx, NULL,
183                                                            "distinguishedName", "objectClass=builtinDomain");
184                 state->base_dn[database] = ldb_dn_new(state, state->sam_ldb, dnstring);
185                 if ( ! ldb_dn_validate(state->base_dn[database])) {
186                         return NT_STATUS_INTERNAL_ERROR;
187                 }
188         } else {
189                 /* PRIVs DB */
190                 return NT_STATUS_INVALID_PARAMETER;
191         }
192
193         msg->dn = talloc_reference(mem_ctx, state->base_dn[database]);
194         if (!msg->dn) {
195                 return NT_STATUS_NO_MEMORY;
196         }
197
198         samdb_msg_add_string(state->sam_ldb, mem_ctx, 
199                              msg, "oEMInformation", domain->comment.string);
200
201         samdb_msg_add_int64(state->sam_ldb, mem_ctx, 
202                             msg, "forceLogoff", domain->force_logoff_time);
203
204         samdb_msg_add_uint(state->sam_ldb, mem_ctx, 
205                            msg, "minPwdLen", domain->min_password_length);
206
207         samdb_msg_add_int64(state->sam_ldb, mem_ctx, 
208                             msg, "maxPwdAge", domain->max_password_age);
209
210         samdb_msg_add_int64(state->sam_ldb, mem_ctx, 
211                             msg, "minPwdAge", domain->min_password_age);
212
213         samdb_msg_add_uint(state->sam_ldb, mem_ctx, 
214                            msg, "pwdHistoryLength", domain->password_history_length);
215
216         samdb_msg_add_uint64(state->sam_ldb, mem_ctx, 
217                              msg, "modifiedCount", 
218                              domain->sequence_num);
219
220         samdb_msg_add_uint64(state->sam_ldb, mem_ctx, 
221                              msg, "creationTime", domain->domain_create_time);
222
223         /* TODO: Account lockout, password properties */
224         
225         ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
226
227         if (ret) {
228                 return NT_STATUS_INTERNAL_ERROR;
229         }
230         return NT_STATUS_OK;
231 }
232
233 static NTSTATUS samsync_ldb_handle_user(TALLOC_CTX *mem_ctx,
234                                         struct samsync_ldb_state *state,
235                                         enum netr_SamDatabaseID database,
236                                         struct netr_DELTA_ENUM *delta,
237                                         char **error_string) 
238 {
239         uint32_t rid = delta->delta_id_union.rid;
240         struct netr_DELTA_USER *user = delta->delta_union.user;
241         const char *container, *obj_class;
242         char *cn_name;
243         int cn_name_len;
244         const struct dom_sid *user_sid;
245         struct ldb_message *msg;
246         struct ldb_message **msgs;
247         struct ldb_message **remote_msgs = NULL;
248         int ret, i;
249         uint32_t acb;
250         BOOL add = False;
251         const char *attrs[] = { NULL };
252         /* we may change this to a global search, then fill in only the things not in ldap later */
253         const char *remote_attrs[] = { "userPrincipalName", "servicePrincipalName", 
254                                        "msDS-KeyVersionNumber", "objectGUID", NULL};
255
256         user_sid = dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid);
257         if (!user_sid) {
258                 return NT_STATUS_NO_MEMORY;
259         }
260
261         msg = ldb_msg_new(mem_ctx);
262         if (msg == NULL) {
263                 return NT_STATUS_NO_MEMORY;
264         }
265
266         msg->dn = NULL;
267         /* search for the user, by rid */
268         ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database],
269                            &msgs, attrs, "(&(objectClass=user)(objectSid=%s))", 
270                            ldap_encode_ndr_dom_sid(mem_ctx, user_sid));
271
272         if (ret == -1) {
273                 *error_string = talloc_asprintf(mem_ctx, "LDB for user %s failed: %s", 
274                                                 dom_sid_string(mem_ctx, user_sid),
275                                                 ldb_errstring(state->sam_ldb));
276                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
277         } else if (ret == 0) {
278                 add = True;
279         } else if (ret > 1) {
280                 *error_string = talloc_asprintf(mem_ctx, "More than one user with SID: %s in local LDB", 
281                                                 dom_sid_string(mem_ctx, user_sid));
282                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
283         } else {
284                 msg->dn = msgs[0]->dn;
285                 talloc_steal(msg, msgs[0]->dn);
286         }
287
288         /* and do the same on the remote database */
289         if (state->remote_ldb) {
290                 ret = gendb_search(state->remote_ldb, mem_ctx, state->base_dn[database],
291                                    &remote_msgs, remote_attrs, "(&(objectClass=user)(objectSid=%s))", 
292                                    ldap_encode_ndr_dom_sid(mem_ctx, user_sid));
293                 
294                 if (ret == -1) {
295                         *error_string = talloc_asprintf(mem_ctx, "remote LDAP for user %s failed: %s", 
296                                                         dom_sid_string(mem_ctx, user_sid),
297                                                         ldb_errstring(state->remote_ldb));
298                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
299                 } else if (ret == 0) {
300                         *error_string = talloc_asprintf(mem_ctx, "User exists in samsync but not in remote LDAP domain! (base: %s, SID: %s)", 
301                                                         ldb_dn_get_linearized(state->base_dn[database]),
302                                                         dom_sid_string(mem_ctx, user_sid));
303                         return NT_STATUS_NO_SUCH_USER;
304                 } else if (ret > 1) {
305                         *error_string = talloc_asprintf(mem_ctx, "More than one user in remote LDAP domain with SID: %s", 
306                                                         dom_sid_string(mem_ctx, user_sid));
307                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
308                         
309                         /* Try to put things in the same location as the remote server */
310                 } else if (add) {
311                         msg->dn = remote_msgs[0]->dn;
312                         talloc_steal(msg, remote_msgs[0]->dn);
313                 }
314         }
315
316         cn_name   = talloc_strdup(mem_ctx, user->account_name.string);
317         NT_STATUS_HAVE_NO_MEMORY(cn_name);
318         cn_name_len = strlen(cn_name);
319
320 #define ADD_OR_DEL(type, attrib, field) do {                            \
321                 if (user->field) {                                      \
322                         samdb_msg_add_ ## type(state->sam_ldb, mem_ctx, msg, \
323                                                attrib, user->field);    \
324                 } else if (!add) {                                      \
325                         samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg, \
326                                              attrib);                   \
327                 }                                                       \
328         } while (0);
329
330         ADD_OR_DEL(string, "samAccountName", account_name.string);
331         ADD_OR_DEL(string, "displayName", full_name.string);
332
333         if (samdb_msg_add_dom_sid(state->sam_ldb, mem_ctx, msg, 
334                                   "objectSid", dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid))) {
335                 return NT_STATUS_NO_MEMORY; 
336         }
337
338         ADD_OR_DEL(uint, "primaryGroupID", primary_gid);
339         ADD_OR_DEL(string, "homeDirectory", home_directory.string);
340         ADD_OR_DEL(string, "homeDrive", home_drive.string);
341         ADD_OR_DEL(string, "scriptPath", logon_script.string);
342         ADD_OR_DEL(string, "description", description.string);
343         ADD_OR_DEL(string, "userWorkstations", workstations.string);
344
345         ADD_OR_DEL(uint64, "lastLogon", last_logon);
346         ADD_OR_DEL(uint64, "lastLogoff", last_logoff);
347
348         if (samdb_msg_add_logon_hours(state->sam_ldb, mem_ctx, msg, "logonHours", &user->logon_hours) != 0) { 
349                 return NT_STATUS_NO_MEMORY; 
350         }
351
352         ADD_OR_DEL(uint, "badPwdCount", bad_password_count);
353         ADD_OR_DEL(uint, "logonCount", logon_count);
354
355         ADD_OR_DEL(uint64, "pwdLastSet", last_password_change);
356         ADD_OR_DEL(uint64, "accountExpires", acct_expiry);
357         
358         if (samdb_msg_add_acct_flags(state->sam_ldb, mem_ctx, msg, 
359                                      "userAccountControl", user->acct_flags) != 0) { 
360                 return NT_STATUS_NO_MEMORY; 
361         } 
362         
363         if (!add) {
364                 /* Passwords.  Ensure there is no plaintext stored against
365                  * this entry, as we only have hashes */
366                 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,  
367                                      "sambaPassword"); 
368         }
369         if (user->lm_password_present) {
370                 samdb_msg_add_hash(state->sam_ldb, mem_ctx, msg,  
371                                    "dBCSPwd", &user->lmpassword);
372         } else if (!add) {
373                 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,  
374                                      "dBCSPwd"); 
375         }
376         if (user->nt_password_present) {
377                 samdb_msg_add_hash(state->sam_ldb, mem_ctx, msg,  
378                                    "unicodePwd", &user->ntpassword);
379         } else if (!add) {
380                 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,  
381                                      "unicodePwd"); 
382         }
383             
384         ADD_OR_DEL(string, "comment", comment.string);
385         ADD_OR_DEL(string, "userParameters", parameters.string);
386         ADD_OR_DEL(uint, "countryCode", country_code);
387         ADD_OR_DEL(uint, "codePage", code_page);
388
389         ADD_OR_DEL(string, "profilePath", profile_path.string);
390
391 #undef ADD_OR_DEL
392
393         for (i=0; remote_attrs[i]; i++) {
394                 struct ldb_message_element *el = ldb_msg_find_element(remote_msgs[0], remote_attrs[i]);
395                 if (!el) {
396                         samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,  
397                                              remote_attrs[i]); 
398                 } else {
399                         ldb_msg_add(msg, el, LDB_FLAG_MOD_REPLACE);
400                 }
401         }
402
403         acb = user->acct_flags;
404         if (acb & (ACB_WSTRUST)) {
405                 cn_name[cn_name_len - 1] = '\0';
406                 container = "Computers";
407                 obj_class = "computer";
408                 
409         } else if (acb & ACB_SVRTRUST) {
410                 if (cn_name[cn_name_len - 1] != '$') {
411                         return NT_STATUS_FOOBAR;                
412                 }
413                 cn_name[cn_name_len - 1] = '\0';
414                 container = "Domain Controllers";
415                 obj_class = "computer";
416         } else {
417                 container = "Users";
418                 obj_class = "user";
419         }
420         if (add) {
421                 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg, 
422                                      "objectClass", obj_class);
423                 if (!msg->dn) {
424                         msg->dn = ldb_dn_copy(mem_ctx, state->base_dn[database]);
425                         ldb_dn_add_child_fmt(msg->dn, "CN=%s,CN=%s", cn_name, container);
426                         if (!msg->dn) {
427                                 return NT_STATUS_NO_MEMORY;             
428                         }
429                 }
430
431                 ret = samdb_add(state->sam_ldb, mem_ctx, msg);
432                 if (ret != 0) {
433                         struct ldb_dn *first_try_dn = msg->dn;
434                         /* Try again with the default DN */
435                         if (!remote_msgs) {
436                                 *error_string = talloc_asprintf(mem_ctx, "Failed to create user record.  Tried %s: %s",
437                                                                 ldb_dn_get_linearized(first_try_dn),
438                                                                 ldb_errstring(state->sam_ldb));
439                                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
440                         } else {
441                                 msg->dn = talloc_steal(msg, remote_msgs[0]->dn);
442                                 ret = samdb_add(state->sam_ldb, mem_ctx, msg);
443                                 if (ret != 0) {
444                                         *error_string = talloc_asprintf(mem_ctx, "Failed to create user record.  Tried both %s and %s: %s",
445                                                                         ldb_dn_get_linearized(first_try_dn),
446                                                                         ldb_dn_get_linearized(msg->dn),
447                                                                         ldb_errstring(state->sam_ldb));
448                                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
449                                 }
450                         }
451                 }
452         } else {
453                 ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
454                 if (ret != 0) {
455                         *error_string = talloc_asprintf(mem_ctx, "Failed to modify user record %s: %s",
456                                                         ldb_dn_get_linearized(msg->dn),
457                                                         ldb_errstring(state->sam_ldb));
458                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
459                 }
460         }
461
462         return NT_STATUS_OK;
463 }
464
465 static NTSTATUS samsync_ldb_delete_user(TALLOC_CTX *mem_ctx,
466                                         struct samsync_ldb_state *state,
467                                         enum netr_SamDatabaseID database,
468                                         struct netr_DELTA_ENUM *delta,
469                                         char **error_string) 
470 {
471         uint32_t rid = delta->delta_id_union.rid;
472         struct ldb_message **msgs;
473         int ret;
474         const char *attrs[] = { NULL };
475
476         /* search for the user, by rid */
477         ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database],
478                            &msgs, attrs, "(&(objectClass=user)(objectSid=%s))", 
479                            ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid))); 
480
481         if (ret == -1) {
482                 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
483                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
484         } else if (ret == 0) {
485                 return NT_STATUS_NO_SUCH_USER;
486         } else if (ret > 1) {
487                 *error_string = talloc_asprintf(mem_ctx, "More than one user with SID: %s", 
488                                                 dom_sid_string(mem_ctx, 
489                                                                dom_sid_add_rid(mem_ctx, 
490                                                                                state->dom_sid[database], 
491                                                                                rid)));
492                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
493         }
494
495         ret = samdb_delete(state->sam_ldb, mem_ctx, msgs[0]->dn);
496         if (ret != 0) {
497                 *error_string = talloc_asprintf(mem_ctx, "Failed to delete user record %s: %s",
498                                                 ldb_dn_get_linearized(msgs[0]->dn),
499                                                 ldb_errstring(state->sam_ldb));
500                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
501         }
502
503         return NT_STATUS_OK;
504 }
505
506 static NTSTATUS samsync_ldb_handle_group(TALLOC_CTX *mem_ctx,
507                                          struct samsync_ldb_state *state,
508                                          enum netr_SamDatabaseID database,
509                                          struct netr_DELTA_ENUM *delta,
510                                          char **error_string) 
511 {
512         uint32_t rid = delta->delta_id_union.rid;
513         struct netr_DELTA_GROUP *group = delta->delta_union.group;
514         const char *container, *obj_class;
515         const char *cn_name;
516
517         struct ldb_message *msg;
518         struct ldb_message **msgs;
519         int ret;
520         BOOL add = False;
521         const char *attrs[] = { NULL };
522
523         msg = ldb_msg_new(mem_ctx);
524         if (msg == NULL) {
525                 return NT_STATUS_NO_MEMORY;
526         }
527
528         /* search for the group, by rid */
529         ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
530                            "(&(objectClass=group)(objectSid=%s))", 
531                            ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid))); 
532
533         if (ret == -1) {
534                 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
535                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
536         } else if (ret == 0) {
537                 add = True;
538         } else if (ret > 1) {
539                 *error_string = talloc_asprintf(mem_ctx, "More than one group/alias with SID: %s", 
540                                                 dom_sid_string(mem_ctx, 
541                                                                dom_sid_add_rid(mem_ctx, 
542                                                                                state->dom_sid[database], 
543                                                                                rid)));
544                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
545         } else {
546                 msg->dn = talloc_steal(msg, msgs[0]->dn);
547         }
548
549         cn_name   = group->group_name.string;
550
551 #define ADD_OR_DEL(type, attrib, field) do {                            \
552                 if (group->field) {                                     \
553                         samdb_msg_add_ ## type(state->sam_ldb, mem_ctx, msg, \
554                                                attrib, group->field);   \
555                 } else if (!add) {                                      \
556                         samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg, \
557                                              attrib);                   \
558                 }                                                       \
559         } while (0);
560
561         ADD_OR_DEL(string, "samAccountName", group_name.string);
562
563         if (samdb_msg_add_dom_sid(state->sam_ldb, mem_ctx, msg, 
564                                   "objectSid", dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid))) {
565                 return NT_STATUS_NO_MEMORY; 
566         }
567
568         ADD_OR_DEL(string, "description", description.string);
569
570 #undef ADD_OR_DEL
571
572         container = "Users";
573         obj_class = "group";
574
575         if (add) {
576                 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg, 
577                                      "objectClass", obj_class);
578                 msg->dn = ldb_dn_copy(mem_ctx, state->base_dn[database]);
579                 ldb_dn_add_child_fmt(msg->dn, "CN=%s,CN=%s", cn_name, container);
580                 if (!msg->dn) {
581                         return NT_STATUS_NO_MEMORY;             
582                 }
583
584                 ret = samdb_add(state->sam_ldb, mem_ctx, msg);
585                 if (ret != 0) {
586                         *error_string = talloc_asprintf(mem_ctx, "Failed to create group record %s: %s",
587                                                         ldb_dn_get_linearized(msg->dn),
588                                                         ldb_errstring(state->sam_ldb));
589                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
590                 }
591         } else {
592                 ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
593                 if (ret != 0) {
594                         *error_string = talloc_asprintf(mem_ctx, "Failed to modify group record %s: %s",
595                                                         ldb_dn_get_linearized(msg->dn),
596                                                         ldb_errstring(state->sam_ldb));
597                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
598                 }
599         }
600
601         return NT_STATUS_OK;
602 }
603
604 static NTSTATUS samsync_ldb_delete_group(TALLOC_CTX *mem_ctx,
605                                          struct samsync_ldb_state *state,
606                                          enum netr_SamDatabaseID database,
607                                          struct netr_DELTA_ENUM *delta,
608                                          char **error_string) 
609 {
610         uint32_t rid = delta->delta_id_union.rid;
611         struct ldb_message **msgs;
612         int ret;
613         const char *attrs[] = { NULL };
614
615         /* search for the group, by rid */
616         ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
617                            "(&(objectClass=group)(objectSid=%s))", 
618                            ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid))); 
619
620         if (ret == -1) {
621                 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
622                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
623         } else if (ret == 0) {
624                 return NT_STATUS_NO_SUCH_GROUP;
625         } else if (ret > 1) {
626                 *error_string = talloc_asprintf(mem_ctx, "More than one group/alias with SID: %s", 
627                                                 dom_sid_string(mem_ctx, 
628                                                                dom_sid_add_rid(mem_ctx, 
629                                                                                state->dom_sid[database], 
630                                                                                rid)));
631                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
632         }
633         
634         ret = samdb_delete(state->sam_ldb, mem_ctx, msgs[0]->dn);
635         if (ret != 0) {
636                 *error_string = talloc_asprintf(mem_ctx, "Failed to delete group record %s: %s",
637                                                 ldb_dn_get_linearized(msgs[0]->dn),
638                                                 ldb_errstring(state->sam_ldb));
639                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
640         }
641
642         return NT_STATUS_OK;
643 }
644
645 static NTSTATUS samsync_ldb_handle_group_member(TALLOC_CTX *mem_ctx,
646                                                 struct samsync_ldb_state *state,
647                                                 enum netr_SamDatabaseID database,
648                                                 struct netr_DELTA_ENUM *delta,
649                                                 char **error_string) 
650 {
651         uint32_t rid = delta->delta_id_union.rid;
652         struct netr_DELTA_GROUP_MEMBER *group_member = delta->delta_union.group_member;
653         struct ldb_message *msg;
654         struct ldb_message **msgs;
655         int ret;
656         const char *attrs[] = { NULL };
657         int i;
658
659         msg = ldb_msg_new(mem_ctx);
660         if (msg == NULL) {
661                 return NT_STATUS_NO_MEMORY;
662         }
663
664         /* search for the group, by rid */
665         ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
666                            "(&(objectClass=group)(objectSid=%s))", 
667                            ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid))); 
668
669         if (ret == -1) {
670                 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
671                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
672         } else if (ret == 0) {
673                 return NT_STATUS_NO_SUCH_GROUP;
674         } else if (ret > 1) {
675                 *error_string = talloc_asprintf(mem_ctx, "More than one group/alias with SID: %s", 
676                                                 dom_sid_string(mem_ctx, 
677                                                                dom_sid_add_rid(mem_ctx, 
678                                                                                state->dom_sid[database], 
679                                                                                rid)));
680                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
681         } else {
682                 msg->dn = talloc_steal(msg, msgs[0]->dn);
683         }
684         
685         talloc_free(msgs);
686
687         for (i=0; i<group_member->num_rids; i++) {
688                 /* search for the group, by rid */
689                 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
690                                    "(&(objectClass=user)(objectSid=%s))", 
691                                    ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], group_member->rids[i]))); 
692                 
693                 if (ret == -1) {
694                         *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
695                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
696                 } else if (ret == 0) {
697                         return NT_STATUS_NO_SUCH_USER;
698                 } else if (ret > 1) {
699                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
700                 } else {
701                         samdb_msg_add_string(state->sam_ldb, mem_ctx, msg, "member", ldb_dn_alloc_linearized(mem_ctx, msgs[0]->dn));
702                 }
703                 
704                 talloc_free(msgs);
705         }
706         
707         ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
708         if (ret != 0) {
709                 *error_string = talloc_asprintf(mem_ctx, "Failed to modify group record %s: %s",
710                                                 ldb_dn_get_linearized(msg->dn),
711                                                 ldb_errstring(state->sam_ldb));
712                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
713         }
714
715         return NT_STATUS_OK;
716 }
717
718 static NTSTATUS samsync_ldb_handle_alias(TALLOC_CTX *mem_ctx,
719                                          struct samsync_ldb_state *state,
720                                          enum netr_SamDatabaseID database,
721                                          struct netr_DELTA_ENUM *delta,
722                                          char **error_string) 
723 {
724         uint32_t rid = delta->delta_id_union.rid;
725         struct netr_DELTA_ALIAS *alias = delta->delta_union.alias;
726         const char *container, *obj_class;
727         const char *cn_name;
728
729         struct ldb_message *msg;
730         struct ldb_message **msgs;
731         int ret;
732         BOOL add = False;
733         const char *attrs[] = { NULL };
734
735         msg = ldb_msg_new(mem_ctx);
736         if (msg == NULL) {
737                 return NT_STATUS_NO_MEMORY;
738         }
739
740         /* search for the alias, by rid */
741         ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
742                            "(&(objectClass=group)(objectSid=%s))", 
743                            ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid))); 
744
745         if (ret == -1) {
746                 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
747                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
748         } else if (ret == 0) {
749                 add = True;
750         } else if (ret > 1) {
751                 *error_string = talloc_asprintf(mem_ctx, "More than one group/alias with SID: %s", 
752                                                 dom_sid_string(mem_ctx, 
753                                                                dom_sid_add_rid(mem_ctx, 
754                                                                                state->dom_sid[database], 
755                                                                                rid)));
756                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
757         } else {
758                 msg->dn = talloc_steal(mem_ctx, msgs[0]->dn);
759         }
760
761         cn_name   = alias->alias_name.string;
762
763 #define ADD_OR_DEL(type, attrib, field) do {                            \
764                 if (alias->field) {                                     \
765                         samdb_msg_add_ ## type(state->sam_ldb, mem_ctx, msg, \
766                                                attrib, alias->field);   \
767                 } else if (!add) {                                      \
768                         samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg, \
769                                              attrib);                   \
770                 }                                                       \
771         } while (0);
772
773         ADD_OR_DEL(string, "samAccountName", alias_name.string);
774
775         if (samdb_msg_add_dom_sid(state->sam_ldb, mem_ctx, msg, 
776                                   "objectSid", dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid))) {
777                 return NT_STATUS_NO_MEMORY; 
778         }
779
780         ADD_OR_DEL(string, "description", description.string);
781
782 #undef ADD_OR_DEL
783
784         samdb_msg_add_uint(state->sam_ldb, mem_ctx, msg, "groupType", 0x80000004);
785
786         container = "Users";
787         obj_class = "group";
788
789         if (add) {
790                 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg, 
791                                      "objectClass", obj_class);
792                 msg->dn = ldb_dn_copy(mem_ctx, state->base_dn[database]);
793                 ldb_dn_add_child_fmt(msg->dn, "CN=%s,CN=%s", cn_name, container);
794                 if (!msg->dn) {
795                         return NT_STATUS_NO_MEMORY;             
796                 }
797
798                 ret = samdb_add(state->sam_ldb, mem_ctx, msg);
799                 if (ret != 0) {
800                         *error_string = talloc_asprintf(mem_ctx, "Failed to create alias record %s: %s",
801                                                         ldb_dn_get_linearized(msg->dn),
802                                                         ldb_errstring(state->sam_ldb));
803                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
804                 }
805         } else {
806                 ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
807                 if (ret != 0) {
808                         *error_string = talloc_asprintf(mem_ctx, "Failed to modify alias record %s: %s",
809                                                         ldb_dn_get_linearized(msg->dn),
810                                                         ldb_errstring(state->sam_ldb));
811                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
812                 }
813         }
814
815         return NT_STATUS_OK;
816 }
817
818 static NTSTATUS samsync_ldb_delete_alias(TALLOC_CTX *mem_ctx,
819                                          struct samsync_ldb_state *state,
820                                          enum netr_SamDatabaseID database,
821                                          struct netr_DELTA_ENUM *delta,
822                                          char **error_string) 
823 {
824         uint32_t rid = delta->delta_id_union.rid;
825         struct ldb_message **msgs;
826         int ret;
827         const char *attrs[] = { NULL };
828
829         /* search for the alias, by rid */
830         ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
831                            "(&(objectClass=group)(objectSid=%s))", 
832                            ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid))); 
833
834         if (ret == -1) {
835                 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
836                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
837         } else if (ret == 0) {
838                 return NT_STATUS_NO_SUCH_ALIAS;
839         } else if (ret > 1) {
840                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
841         }
842
843         ret = samdb_delete(state->sam_ldb, mem_ctx, msgs[0]->dn);
844         if (ret != 0) {
845                 *error_string = talloc_asprintf(mem_ctx, "Failed to delete alias record %s: %s",
846                                                 ldb_dn_get_linearized(msgs[0]->dn),
847                                                 ldb_errstring(state->sam_ldb));
848                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
849         }
850
851         return NT_STATUS_OK;
852 }
853
854 static NTSTATUS samsync_ldb_handle_alias_member(TALLOC_CTX *mem_ctx,
855                                                 struct samsync_ldb_state *state,
856                                                 enum netr_SamDatabaseID database,
857                                                 struct netr_DELTA_ENUM *delta,
858                                                 char **error_string) 
859 {
860         uint32_t rid = delta->delta_id_union.rid;
861         struct netr_DELTA_ALIAS_MEMBER *alias_member = delta->delta_union.alias_member;
862         struct ldb_message *msg;
863         struct ldb_message **msgs;
864         int ret;
865         const char *attrs[] = { NULL };
866         int i;
867
868         msg = ldb_msg_new(mem_ctx);
869         if (msg == NULL) {
870                 return NT_STATUS_NO_MEMORY;
871         }
872
873         /* search for the alias, by rid */
874         ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
875                            "(&(objectClass=group)(objectSid=%s))", 
876                            ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid))); 
877                 
878         if (ret == -1) {
879                 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
880                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
881         } else if (ret == 0) {
882                 return NT_STATUS_NO_SUCH_GROUP;
883         } else if (ret > 1) {
884                 *error_string = talloc_asprintf(mem_ctx, "More than one group/alias with SID: %s", 
885                                                 dom_sid_string(mem_ctx, 
886                                                                dom_sid_add_rid(mem_ctx, 
887                                                                                state->dom_sid[database], 
888                                                                                rid)));
889                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
890         } else {
891                 msg->dn = talloc_steal(msg, msgs[0]->dn);
892         }
893         
894         talloc_free(msgs);
895
896         for (i=0; i<alias_member->sids.num_sids; i++) {
897                 struct ldb_dn *alias_member_dn;
898                 /* search for members, in the top basedn (normal users are builtin aliases) */
899                 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[SAM_DATABASE_DOMAIN], &msgs, attrs,
900                                    "(objectSid=%s)", 
901                                    ldap_encode_ndr_dom_sid(mem_ctx, alias_member->sids.sids[i].sid)); 
902
903                 if (ret == -1) {
904                         *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
905                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
906                 } else if (ret == 0) {
907                         NTSTATUS nt_status;
908                         nt_status = samsync_ldb_add_foreignSecurityPrincipal(mem_ctx, state,
909                                                                              alias_member->sids.sids[i].sid, 
910                                                                              &alias_member_dn, 
911                                                                              error_string);
912                         if (!NT_STATUS_IS_OK(nt_status)) {
913                                 return nt_status;
914                         }
915                 } else if (ret > 1) {
916                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
917                 } else {
918                         alias_member_dn = msgs[0]->dn;
919                 }
920                 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg, "member", ldb_dn_alloc_linearized(mem_ctx, alias_member_dn));
921         
922                 talloc_free(msgs);
923         }
924
925         ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
926         if (ret != 0) {
927                 *error_string = talloc_asprintf(mem_ctx, "Failed to modify group record %s: %s",
928                                                 ldb_dn_get_linearized(msg->dn),
929                                                 ldb_errstring(state->sam_ldb));
930                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
931         }
932
933         return NT_STATUS_OK;
934 }
935
936 static NTSTATUS samsync_ldb_handle_account(TALLOC_CTX *mem_ctx,
937                                            struct samsync_ldb_state *state,
938                                            enum netr_SamDatabaseID database,
939                                            struct netr_DELTA_ENUM *delta,
940                                            char **error_string) 
941 {
942         struct dom_sid *sid = delta->delta_id_union.sid;
943         struct netr_DELTA_ACCOUNT *account = delta->delta_union.account;
944
945         struct ldb_message *msg;
946         struct ldb_message **msgs;
947         struct ldb_dn *privilege_dn;
948         int ret;
949         const char *attrs[] = { NULL };
950         int i;
951
952         msg = ldb_msg_new(mem_ctx);
953         if (msg == NULL) {
954                 return NT_STATUS_NO_MEMORY;
955         }
956
957         /* search for the account, by sid, in the top basedn */
958         ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[SAM_DATABASE_DOMAIN], &msgs, attrs,
959                            "(objectSid=%s)", ldap_encode_ndr_dom_sid(mem_ctx, sid)); 
960
961         if (ret == -1) {
962                 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
963                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
964         } else if (ret == 0) {
965                 NTSTATUS nt_status;
966                 nt_status = samsync_ldb_add_foreignSecurityPrincipal(mem_ctx, state,
967                                                                      sid,
968                                                                      &privilege_dn,
969                                                                      error_string);
970                 privilege_dn = talloc_steal(msg, privilege_dn);
971                 if (!NT_STATUS_IS_OK(nt_status)) {
972                         return nt_status;
973                 }
974         } else if (ret > 1) {
975                 *error_string = talloc_asprintf(mem_ctx, "More than one account with SID: %s", 
976                                                 dom_sid_string(mem_ctx, sid));
977                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
978         } else {
979                 privilege_dn = talloc_steal(msg, msgs[0]->dn);
980         }
981
982         msg->dn = privilege_dn;
983
984         for (i=0; i< account->privilege_entries; i++) {
985                 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg, "privilege",
986                                      account->privilege_name[i].string);
987         }
988
989         ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
990         if (ret != 0) {
991                 *error_string = talloc_asprintf(mem_ctx, "Failed to modify privilege record %s",
992                                                 ldb_dn_get_linearized(msg->dn));
993                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
994         }
995
996         return NT_STATUS_OK;
997 }
998
999 static NTSTATUS samsync_ldb_delete_account(TALLOC_CTX *mem_ctx,
1000                                            struct samsync_ldb_state *state,
1001                                            enum netr_SamDatabaseID database,
1002                                            struct netr_DELTA_ENUM *delta,
1003                                            char **error_string) 
1004 {
1005         struct dom_sid *sid = delta->delta_id_union.sid;
1006
1007         struct ldb_message *msg;
1008         struct ldb_message **msgs;
1009         int ret;
1010         const char *attrs[] = { NULL };
1011
1012         msg = ldb_msg_new(mem_ctx);
1013         if (msg == NULL) {
1014                 return NT_STATUS_NO_MEMORY;
1015         }
1016
1017         /* search for the account, by sid, in the top basedn */
1018         ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[SAM_DATABASE_DOMAIN], &msgs, attrs,
1019                            "(objectSid=%s)", 
1020                            ldap_encode_ndr_dom_sid(mem_ctx, sid)); 
1021
1022         if (ret == -1) {
1023                 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
1024                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1025         } else if (ret == 0) {
1026                 return NT_STATUS_NO_SUCH_USER;
1027         } else if (ret > 1) {
1028                 *error_string = talloc_asprintf(mem_ctx, "More than one account with SID: %s", 
1029                                                 dom_sid_string(mem_ctx, sid));
1030                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1031         } else {
1032                 msg->dn = talloc_steal(msg, msgs[0]->dn);
1033         }
1034
1035         samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,  
1036                              "privilage"); 
1037
1038         ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
1039         if (ret != 0) {
1040                 *error_string = talloc_asprintf(mem_ctx, "Failed to modify privilege record %s",
1041                                                 ldb_dn_get_linearized(msg->dn));
1042                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1043         }
1044
1045         return NT_STATUS_OK;
1046 }
1047
1048 static NTSTATUS libnet_samsync_ldb_fn(TALLOC_CTX *mem_ctx,              
1049                                       void *private,                    
1050                                       enum netr_SamDatabaseID database,
1051                                       struct netr_DELTA_ENUM *delta,
1052                                       char **error_string)
1053 {
1054         NTSTATUS nt_status = NT_STATUS_OK;
1055         struct samsync_ldb_state *state = talloc_get_type(private, struct samsync_ldb_state);
1056
1057         *error_string = NULL;
1058         switch (delta->delta_type) {
1059         case NETR_DELTA_DOMAIN:
1060         {
1061                 nt_status = samsync_ldb_handle_domain(mem_ctx, 
1062                                                       state,
1063                                                       database,
1064                                                       delta,
1065                                                       error_string);
1066                 break;
1067         }
1068         case NETR_DELTA_USER:
1069         {
1070                 nt_status = samsync_ldb_handle_user(mem_ctx, 
1071                                                     state,
1072                                                     database,
1073                                                     delta,
1074                                                     error_string);
1075                 break;
1076         }
1077         case NETR_DELTA_DELETE_USER:
1078         {
1079                 nt_status = samsync_ldb_delete_user(mem_ctx, 
1080                                                     state,
1081                                                     database,
1082                                                     delta,
1083                                                     error_string);
1084                 break;
1085         }
1086         case NETR_DELTA_GROUP:
1087         {
1088                 nt_status = samsync_ldb_handle_group(mem_ctx, 
1089                                                      state,
1090                                                      database,
1091                                                      delta,
1092                                                      error_string);
1093                 break;
1094         }
1095         case NETR_DELTA_DELETE_GROUP:
1096         {
1097                 nt_status = samsync_ldb_delete_group(mem_ctx, 
1098                                                      state,
1099                                                      database,
1100                                                      delta,
1101                                                      error_string);
1102                 break;
1103         }
1104         case NETR_DELTA_GROUP_MEMBER:
1105         {
1106                 nt_status = samsync_ldb_handle_group_member(mem_ctx, 
1107                                                             state,
1108                                                             database,
1109                                                             delta,
1110                                                             error_string);
1111                 break;
1112         }
1113         case NETR_DELTA_ALIAS:
1114         {
1115                 nt_status = samsync_ldb_handle_alias(mem_ctx, 
1116                                                      state,
1117                                                      database,
1118                                                      delta,
1119                                                      error_string);
1120                 break;
1121         }
1122         case NETR_DELTA_DELETE_ALIAS:
1123         {
1124                 nt_status = samsync_ldb_delete_alias(mem_ctx, 
1125                                                      state,
1126                                                      database,
1127                                                      delta,
1128                                                      error_string);
1129                 break;
1130         }
1131         case NETR_DELTA_ALIAS_MEMBER:
1132         {
1133                 nt_status = samsync_ldb_handle_alias_member(mem_ctx, 
1134                                                             state,
1135                                                             database,
1136                                                             delta,
1137                                                             error_string);
1138                 break;
1139         }
1140         case NETR_DELTA_ACCOUNT:
1141         {
1142                 nt_status = samsync_ldb_handle_account(mem_ctx, 
1143                                                        state,
1144                                                        database,
1145                                                        delta,
1146                                                        error_string);
1147                 break;
1148         }
1149         case NETR_DELTA_DELETE_ACCOUNT:
1150         {
1151                 nt_status = samsync_ldb_delete_account(mem_ctx, 
1152                                                        state,
1153                                                        database,
1154                                                        delta,
1155                                                        error_string);
1156                 break;
1157         }
1158         default:
1159                 /* Can't dump them all right now */
1160                 break;
1161         }
1162         if (!NT_STATUS_IS_OK(nt_status) && !*error_string) {
1163                 *error_string = talloc_asprintf(mem_ctx, "Failed to handle samsync delta: %s", nt_errstr(nt_status));
1164         }
1165         return nt_status;
1166 }
1167
1168 static NTSTATUS libnet_samsync_ldb_init(TALLOC_CTX *mem_ctx,            
1169                                         void *private,
1170                                         struct libnet_SamSync_state *samsync_state,
1171                                         char **error_string)
1172 {
1173         struct samsync_ldb_state *state = talloc_get_type(private, struct samsync_ldb_state);
1174         const char *server = dcerpc_server_name(samsync_state->netlogon_pipe);
1175         char *ldap_url;
1176
1177         state->samsync_state = samsync_state;
1178
1179         ZERO_STRUCT(state->dom_sid);
1180         if (state->samsync_state->domain_sid) {
1181                 state->dom_sid[SAM_DATABASE_DOMAIN] = dom_sid_dup(state, state->samsync_state->domain_sid);
1182         }
1183
1184         state->dom_sid[SAM_DATABASE_BUILTIN] = dom_sid_parse_talloc(state, SID_BUILTIN);
1185
1186         if (state->samsync_state->realm) {
1187                 if (!server || !*server) {
1188                         /* huh?  how do we not have a server name?  */
1189                         *error_string = talloc_strdup(mem_ctx, "No DCE/RPC server name available.  How did we connect?");
1190                         return NT_STATUS_INVALID_PARAMETER;
1191                 }
1192                 ldap_url = talloc_asprintf(state, "ldap://%s", server);
1193                 
1194                 state->remote_ldb = ldb_wrap_connect(mem_ctx, 
1195                                                      global_loadparm, 
1196                                                      ldap_url, 
1197                                                      NULL, state->samsync_state->machine_net_ctx->cred,
1198                                                      0, NULL);
1199                 if (!state->remote_ldb) {
1200                         *error_string = talloc_asprintf(mem_ctx, "Failed to connect to remote LDAP server at %s (used to extract additional data in SamSync replication)", ldap_url);
1201                         return NT_STATUS_NO_LOGON_SERVERS;
1202                 }
1203         } else {
1204                 state->remote_ldb = NULL;
1205         }
1206         return NT_STATUS_OK;
1207 }
1208
1209 NTSTATUS libnet_samsync_ldb(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, struct libnet_samsync_ldb *r)
1210 {
1211         NTSTATUS nt_status;
1212         struct libnet_SamSync r2;
1213         struct samsync_ldb_state *state = talloc(mem_ctx, struct samsync_ldb_state);
1214
1215         if (!state) {
1216                 return NT_STATUS_NO_MEMORY;
1217         }
1218
1219         state->secrets         = NULL;
1220         state->trusted_domains = NULL;
1221
1222         state->sam_ldb         = ldb_wrap_connect(mem_ctx, 
1223                                                   global_loadparm, 
1224                                                   lp_sam_url(global_loadparm), 
1225                                                   r->in.session_info,
1226                                                   ctx->cred, 0, NULL);
1227
1228         r2.out.error_string    = NULL;
1229         r2.in.binding_string   = r->in.binding_string;
1230         r2.in.rid_crypt        = true;
1231         r2.in.init_fn          = libnet_samsync_ldb_init;
1232         r2.in.delta_fn         = libnet_samsync_ldb_fn;
1233         r2.in.fn_ctx           = state;
1234         r2.in.machine_account  = NULL; /* TODO:  Create a machine account, fill this in, and the delete it */
1235         nt_status              = libnet_SamSync_netlogon(ctx, state, &r2);
1236         r->out.error_string    = r2.out.error_string;
1237         talloc_steal(mem_ctx, r->out.error_string);
1238
1239         if (!NT_STATUS_IS_OK(nt_status)) {
1240                 talloc_free(state);
1241                 return nt_status;
1242         }
1243         talloc_free(state);
1244         return nt_status;
1245 }