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