e9db4a909cb97dfe15940efc13eea092a3c9fe4d
[ira/wip.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 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_ndr.h"
28 #include "dsdb/samdb/samdb.h"
29 #include "auth/auth.h"
30 #include "../lib/util/util_ldb.h"
31 #include "librpc/gen_ndr/ndr_misc.h"
32 #include "ldb_wrap.h"
33 #include "libcli/security/security.h"
34 #include "librpc/rpc/dcerpc.h"
35 #include "param/param.h"
36
37 struct samsync_ldb_secret {
38         struct samsync_ldb_secret *prev, *next;
39         DATA_BLOB secret;
40         char *name;
41         NTTIME mtime;
42 };
43
44 struct samsync_ldb_trusted_domain {
45         struct samsync_ldb_trusted_domain *prev, *next;
46         struct dom_sid *sid;
47         char *name;
48 };
49
50 struct samsync_ldb_state {
51         /* Values from the LSA lookup */
52         const struct libnet_SamSync_state *samsync_state;
53
54         struct dom_sid *dom_sid[3];
55         struct ldb_context *sam_ldb, *remote_ldb, *pdb;
56         struct ldb_dn *base_dn[3];
57         struct samsync_ldb_secret *secrets;
58         struct samsync_ldb_trusted_domain *trusted_domains;
59 };
60
61 static NTSTATUS samsync_ldb_add_foreignSecurityPrincipal(TALLOC_CTX *mem_ctx,
62                                                          struct samsync_ldb_state *state,
63                                                          struct dom_sid *sid,
64                                                          struct ldb_dn **fsp_dn,
65                                                          char **error_string)
66 {
67         const char *sidstr = dom_sid_string(mem_ctx, sid);
68         /* We assume that ForeignSecurityPrincipals are under the BASEDN of the main domain */
69         struct ldb_dn *basedn = samdb_search_dn(state->sam_ldb, mem_ctx,
70                                                 state->base_dn[SAM_DATABASE_DOMAIN],
71                                                 "(&(objectClass=container)(cn=ForeignSecurityPrincipals))");
72         struct ldb_message *msg;
73         int ret;
74
75         if (!sidstr) {
76                 return NT_STATUS_NO_MEMORY;
77         }
78
79         if (basedn == NULL) {
80                 *error_string = talloc_asprintf(mem_ctx, 
81                                                 "Failed to find DN for "
82                                                 "ForeignSecurityPrincipal container under %s",
83                                                 ldb_dn_get_linearized(state->base_dn[SAM_DATABASE_DOMAIN]));
84                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
85         }
86         
87         msg = ldb_msg_new(mem_ctx);
88         if (msg == NULL) {
89                 return NT_STATUS_NO_MEMORY;
90         }
91
92         /* add core elements to the ldb_message for the alias */
93         msg->dn = basedn;
94         if ( ! ldb_dn_add_child_fmt(msg->dn, "CN=%s", sidstr))
95                 return NT_STATUS_UNSUCCESSFUL;
96         
97         samdb_msg_add_string(state->sam_ldb, mem_ctx, msg,
98                              "objectClass",
99                              "foreignSecurityPrincipal");
100
101         *fsp_dn = msg->dn;
102
103         /* create the alias */
104         ret = ldb_add(state->sam_ldb, msg);
105         if (ret != 0) {
106                 *error_string = talloc_asprintf(mem_ctx, "Failed to create foreignSecurityPrincipal "
107                                                 "record %s: %s",
108                                                 ldb_dn_get_linearized(msg->dn),
109                                                 ldb_errstring(state->sam_ldb));
110                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
111         }
112         return NT_STATUS_OK;
113 }
114
115 static NTSTATUS samsync_ldb_handle_domain(TALLOC_CTX *mem_ctx,
116                                           struct samsync_ldb_state *state,
117                                           enum netr_SamDatabaseID database,
118                                           struct netr_DELTA_ENUM *delta,
119                                           char **error_string) 
120 {
121         struct netr_DELTA_DOMAIN *domain = delta->delta_union.domain;
122         const char *domain_name = domain->domain_name.string;
123         struct ldb_message *msg;
124         int ret;
125         
126         msg = ldb_msg_new(mem_ctx);
127         if (msg == NULL) {
128                 return NT_STATUS_NO_MEMORY;
129         }
130
131         if (database == SAM_DATABASE_DOMAIN) {
132                 struct ldb_dn *partitions_basedn;
133                 const char *domain_attrs[] =  {"nETBIOSName", "nCName", NULL};
134                 struct ldb_message **msgs_domain;
135                 int ret_domain;
136
137                 partitions_basedn = samdb_partitions_dn(state->sam_ldb, mem_ctx);
138
139                 ret_domain = gendb_search(state->sam_ldb, mem_ctx, partitions_basedn, &msgs_domain, domain_attrs,
140                                           "(&(&(nETBIOSName=%s)(objectclass=crossRef))(ncName=*))", 
141                                           domain_name);
142                 if (ret_domain == -1) {
143                         *error_string = talloc_asprintf(mem_ctx, "gendb_search for domain failed: %s", ldb_errstring(state->sam_ldb));
144                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
145                 }
146                 
147                 if (ret_domain != 1) {
148                         *error_string = talloc_asprintf(mem_ctx, "Failed to find existing domain record for %s: %d results", domain_name,
149                                                         ret_domain);
150                         return NT_STATUS_NO_SUCH_DOMAIN;                
151                 }
152
153                 state->base_dn[database] = samdb_result_dn(state->sam_ldb, state, msgs_domain[0], "nCName", NULL);
154
155                 if (state->dom_sid[database]) {
156                         /* Update the domain sid with the incoming
157                          * domain (found on LSA pipe, database sid may
158                          * be random) */
159                         samdb_msg_add_dom_sid(state->sam_ldb, mem_ctx, 
160                                               msg, "objectSid", state->dom_sid[database]);
161                 } else {
162                         /* Well, we will have to use the one from the database */
163                         state->dom_sid[database] = samdb_search_dom_sid(state->sam_ldb, state,
164                                                                         state->base_dn[database], 
165                                                                         "objectSid", NULL);
166                 }
167
168                 if (state->samsync_state->domain_guid) {
169                         struct ldb_val v;
170                         NTSTATUS status;
171                         status = GUID_to_ndr_blob(state->samsync_state->domain_guid, msg, &v);
172                         if (!NT_STATUS_IS_OK(status)) {
173                                 *error_string = talloc_asprintf(mem_ctx, "ndr_push of domain GUID failed!");
174                                 return 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->oem_information.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 = dsdb_replace(state->sam_ldb, msg, 0);
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                                      "userPassword"); 
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
386         if (samdb_msg_add_parameters(state->sam_ldb, mem_ctx, msg, "userParameters", &user->parameters) != 0) {
387                 return NT_STATUS_NO_MEMORY;
388         }
389
390         ADD_OR_DEL(uint, "countryCode", country_code);
391         ADD_OR_DEL(uint, "codePage", code_page);
392
393         ADD_OR_DEL(string, "profilePath", profile_path.string);
394
395 #undef ADD_OR_DEL
396
397         for (i=0; remote_attrs[i]; i++) {
398                 struct ldb_message_element *el = ldb_msg_find_element(remote_msgs[0], remote_attrs[i]);
399                 if (!el) {
400                         samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,  
401                                              remote_attrs[i]); 
402                 } else {
403                         ldb_msg_add(msg, el, LDB_FLAG_MOD_REPLACE);
404                 }
405         }
406
407         acb = user->acct_flags;
408         if (acb & (ACB_WSTRUST)) {
409                 cn_name[cn_name_len - 1] = '\0';
410                 container = "Computers";
411                 obj_class = "computer";
412                 
413         } else if (acb & ACB_SVRTRUST) {
414                 if (cn_name[cn_name_len - 1] != '$') {
415                         return NT_STATUS_FOOBAR;                
416                 }
417                 cn_name[cn_name_len - 1] = '\0';
418                 container = "Domain Controllers";
419                 obj_class = "computer";
420         } else {
421                 container = "Users";
422                 obj_class = "user";
423         }
424         if (add) {
425                 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg, 
426                                      "objectClass", obj_class);
427                 if (!msg->dn) {
428                         msg->dn = ldb_dn_copy(mem_ctx, state->base_dn[database]);
429                         ldb_dn_add_child_fmt(msg->dn, "CN=%s,CN=%s", cn_name, container);
430                         if (!msg->dn) {
431                                 return NT_STATUS_NO_MEMORY;             
432                         }
433                 }
434
435                 ret = ldb_add(state->sam_ldb, msg);
436                 if (ret != 0) {
437                         struct ldb_dn *first_try_dn = msg->dn;
438                         /* Try again with the default DN */
439                         if (!remote_msgs) {
440                                 *error_string = talloc_asprintf(mem_ctx, "Failed to create user record.  Tried %s: %s",
441                                                                 ldb_dn_get_linearized(first_try_dn),
442                                                                 ldb_errstring(state->sam_ldb));
443                                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
444                         } else {
445                                 msg->dn = talloc_steal(msg, remote_msgs[0]->dn);
446                                 ret = ldb_add(state->sam_ldb, msg);
447                                 if (ret != 0) {
448                                         *error_string = talloc_asprintf(mem_ctx, "Failed to create user record.  Tried both %s and %s: %s",
449                                                                         ldb_dn_get_linearized(first_try_dn),
450                                                                         ldb_dn_get_linearized(msg->dn),
451                                                                         ldb_errstring(state->sam_ldb));
452                                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
453                                 }
454                         }
455                 }
456         } else {
457                 ret = dsdb_replace(state->sam_ldb, msg, 0);
458                 if (ret != 0) {
459                         *error_string = talloc_asprintf(mem_ctx, "Failed to modify user record %s: %s",
460                                                         ldb_dn_get_linearized(msg->dn),
461                                                         ldb_errstring(state->sam_ldb));
462                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
463                 }
464         }
465
466         return NT_STATUS_OK;
467 }
468
469 static NTSTATUS samsync_ldb_delete_user(TALLOC_CTX *mem_ctx,
470                                         struct samsync_ldb_state *state,
471                                         enum netr_SamDatabaseID database,
472                                         struct netr_DELTA_ENUM *delta,
473                                         char **error_string) 
474 {
475         uint32_t rid = delta->delta_id_union.rid;
476         struct ldb_message **msgs;
477         int ret;
478         const char *attrs[] = { NULL };
479
480         /* search for the user, by rid */
481         ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database],
482                            &msgs, attrs, "(&(objectClass=user)(objectSid=%s))", 
483                            ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid))); 
484
485         if (ret == -1) {
486                 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
487                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
488         } else if (ret == 0) {
489                 return NT_STATUS_NO_SUCH_USER;
490         } else if (ret > 1) {
491                 *error_string = talloc_asprintf(mem_ctx, "More than one user with SID: %s", 
492                                                 dom_sid_string(mem_ctx, 
493                                                                dom_sid_add_rid(mem_ctx, 
494                                                                                state->dom_sid[database], 
495                                                                                rid)));
496                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
497         }
498
499         ret = ldb_delete(state->sam_ldb, msgs[0]->dn);
500         if (ret != 0) {
501                 *error_string = talloc_asprintf(mem_ctx, "Failed to delete user record %s: %s",
502                                                 ldb_dn_get_linearized(msgs[0]->dn),
503                                                 ldb_errstring(state->sam_ldb));
504                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
505         }
506
507         return NT_STATUS_OK;
508 }
509
510 static NTSTATUS samsync_ldb_handle_group(TALLOC_CTX *mem_ctx,
511                                          struct samsync_ldb_state *state,
512                                          enum netr_SamDatabaseID database,
513                                          struct netr_DELTA_ENUM *delta,
514                                          char **error_string) 
515 {
516         uint32_t rid = delta->delta_id_union.rid;
517         struct netr_DELTA_GROUP *group = delta->delta_union.group;
518         const char *container, *obj_class;
519         const char *cn_name;
520
521         struct ldb_message *msg;
522         struct ldb_message **msgs;
523         int ret;
524         bool add = false;
525         const char *attrs[] = { NULL };
526
527         msg = ldb_msg_new(mem_ctx);
528         if (msg == NULL) {
529                 return NT_STATUS_NO_MEMORY;
530         }
531
532         /* search for the group, by rid */
533         ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
534                            "(&(objectClass=group)(objectSid=%s))", 
535                            ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid))); 
536
537         if (ret == -1) {
538                 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
539                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
540         } else if (ret == 0) {
541                 add = true;
542         } else if (ret > 1) {
543                 *error_string = talloc_asprintf(mem_ctx, "More than one group/alias with SID: %s", 
544                                                 dom_sid_string(mem_ctx, 
545                                                                dom_sid_add_rid(mem_ctx, 
546                                                                                state->dom_sid[database], 
547                                                                                rid)));
548                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
549         } else {
550                 msg->dn = talloc_steal(msg, msgs[0]->dn);
551         }
552
553         cn_name   = group->group_name.string;
554
555 #define ADD_OR_DEL(type, attrib, field) do {                            \
556                 if (group->field) {                                     \
557                         samdb_msg_add_ ## type(state->sam_ldb, mem_ctx, msg, \
558                                                attrib, group->field);   \
559                 } else if (!add) {                                      \
560                         samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg, \
561                                              attrib);                   \
562                 }                                                       \
563         } while (0);
564
565         ADD_OR_DEL(string, "samAccountName", group_name.string);
566
567         if (samdb_msg_add_dom_sid(state->sam_ldb, mem_ctx, msg, 
568                                   "objectSid", dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid))) {
569                 return NT_STATUS_NO_MEMORY; 
570         }
571
572         ADD_OR_DEL(string, "description", description.string);
573
574 #undef ADD_OR_DEL
575
576         container = "Users";
577         obj_class = "group";
578
579         if (add) {
580                 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg, 
581                                      "objectClass", obj_class);
582                 msg->dn = ldb_dn_copy(mem_ctx, state->base_dn[database]);
583                 ldb_dn_add_child_fmt(msg->dn, "CN=%s,CN=%s", cn_name, container);
584                 if (!msg->dn) {
585                         return NT_STATUS_NO_MEMORY;             
586                 }
587
588                 ret = ldb_add(state->sam_ldb, msg);
589                 if (ret != 0) {
590                         *error_string = talloc_asprintf(mem_ctx, "Failed to create group record %s: %s",
591                                                         ldb_dn_get_linearized(msg->dn),
592                                                         ldb_errstring(state->sam_ldb));
593                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
594                 }
595         } else {
596                 ret = dsdb_replace(state->sam_ldb, msg, 0);
597                 if (ret != 0) {
598                         *error_string = talloc_asprintf(mem_ctx, "Failed to modify group record %s: %s",
599                                                         ldb_dn_get_linearized(msg->dn),
600                                                         ldb_errstring(state->sam_ldb));
601                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
602                 }
603         }
604
605         return NT_STATUS_OK;
606 }
607
608 static NTSTATUS samsync_ldb_delete_group(TALLOC_CTX *mem_ctx,
609                                          struct samsync_ldb_state *state,
610                                          enum netr_SamDatabaseID database,
611                                          struct netr_DELTA_ENUM *delta,
612                                          char **error_string) 
613 {
614         uint32_t rid = delta->delta_id_union.rid;
615         struct ldb_message **msgs;
616         int ret;
617         const char *attrs[] = { NULL };
618
619         /* search for the group, by rid */
620         ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
621                            "(&(objectClass=group)(objectSid=%s))", 
622                            ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid))); 
623
624         if (ret == -1) {
625                 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
626                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
627         } else if (ret == 0) {
628                 return NT_STATUS_NO_SUCH_GROUP;
629         } else if (ret > 1) {
630                 *error_string = talloc_asprintf(mem_ctx, "More than one group/alias with SID: %s", 
631                                                 dom_sid_string(mem_ctx, 
632                                                                dom_sid_add_rid(mem_ctx, 
633                                                                                state->dom_sid[database], 
634                                                                                rid)));
635                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
636         }
637         
638         ret = ldb_delete(state->sam_ldb, msgs[0]->dn);
639         if (ret != 0) {
640                 *error_string = talloc_asprintf(mem_ctx, "Failed to delete group record %s: %s",
641                                                 ldb_dn_get_linearized(msgs[0]->dn),
642                                                 ldb_errstring(state->sam_ldb));
643                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
644         }
645
646         return NT_STATUS_OK;
647 }
648
649 static NTSTATUS samsync_ldb_handle_group_member(TALLOC_CTX *mem_ctx,
650                                                 struct samsync_ldb_state *state,
651                                                 enum netr_SamDatabaseID database,
652                                                 struct netr_DELTA_ENUM *delta,
653                                                 char **error_string) 
654 {
655         uint32_t rid = delta->delta_id_union.rid;
656         struct netr_DELTA_GROUP_MEMBER *group_member = delta->delta_union.group_member;
657         struct ldb_message *msg;
658         struct ldb_message **msgs;
659         int ret;
660         const char *attrs[] = { NULL };
661         int i;
662
663         msg = ldb_msg_new(mem_ctx);
664         if (msg == NULL) {
665                 return NT_STATUS_NO_MEMORY;
666         }
667
668         /* search for the group, by rid */
669         ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
670                            "(&(objectClass=group)(objectSid=%s))", 
671                            ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid))); 
672
673         if (ret == -1) {
674                 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
675                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
676         } else if (ret == 0) {
677                 return NT_STATUS_NO_SUCH_GROUP;
678         } else if (ret > 1) {
679                 *error_string = talloc_asprintf(mem_ctx, "More than one group/alias with SID: %s", 
680                                                 dom_sid_string(mem_ctx, 
681                                                                dom_sid_add_rid(mem_ctx, 
682                                                                                state->dom_sid[database], 
683                                                                                rid)));
684                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
685         } else {
686                 msg->dn = talloc_steal(msg, msgs[0]->dn);
687         }
688         
689         talloc_free(msgs);
690
691         for (i=0; i<group_member->num_rids; i++) {
692                 /* search for the group, by rid */
693                 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
694                                    "(&(objectClass=user)(objectSid=%s))", 
695                                    ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], group_member->rids[i]))); 
696                 
697                 if (ret == -1) {
698                         *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
699                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
700                 } else if (ret == 0) {
701                         return NT_STATUS_NO_SUCH_USER;
702                 } else if (ret > 1) {
703                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
704                 } else {
705                         samdb_msg_add_string(state->sam_ldb, mem_ctx, msg, "member", ldb_dn_alloc_linearized(mem_ctx, msgs[0]->dn));
706                 }
707                 
708                 talloc_free(msgs);
709         }
710         
711         ret = dsdb_replace(state->sam_ldb, msg, 0);
712         if (ret != 0) {
713                 *error_string = talloc_asprintf(mem_ctx, "Failed to modify group record %s: %s",
714                                                 ldb_dn_get_linearized(msg->dn),
715                                                 ldb_errstring(state->sam_ldb));
716                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
717         }
718
719         return NT_STATUS_OK;
720 }
721
722 static NTSTATUS samsync_ldb_handle_alias(TALLOC_CTX *mem_ctx,
723                                          struct samsync_ldb_state *state,
724                                          enum netr_SamDatabaseID database,
725                                          struct netr_DELTA_ENUM *delta,
726                                          char **error_string) 
727 {
728         uint32_t rid = delta->delta_id_union.rid;
729         struct netr_DELTA_ALIAS *alias = delta->delta_union.alias;
730         const char *container, *obj_class;
731         const char *cn_name;
732
733         struct ldb_message *msg;
734         struct ldb_message **msgs;
735         int ret;
736         bool add = false;
737         const char *attrs[] = { NULL };
738
739         msg = ldb_msg_new(mem_ctx);
740         if (msg == NULL) {
741                 return NT_STATUS_NO_MEMORY;
742         }
743
744         /* search for the alias, by rid */
745         ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
746                            "(&(objectClass=group)(objectSid=%s))", 
747                            ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid))); 
748
749         if (ret == -1) {
750                 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
751                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
752         } else if (ret == 0) {
753                 add = true;
754         } else if (ret > 1) {
755                 *error_string = talloc_asprintf(mem_ctx, "More than one group/alias with SID: %s", 
756                                                 dom_sid_string(mem_ctx, 
757                                                                dom_sid_add_rid(mem_ctx, 
758                                                                                state->dom_sid[database], 
759                                                                                rid)));
760                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
761         } else {
762                 msg->dn = talloc_steal(mem_ctx, msgs[0]->dn);
763         }
764
765         cn_name   = alias->alias_name.string;
766
767 #define ADD_OR_DEL(type, attrib, field) do {                            \
768                 if (alias->field) {                                     \
769                         samdb_msg_add_ ## type(state->sam_ldb, mem_ctx, msg, \
770                                                attrib, alias->field);   \
771                 } else if (!add) {                                      \
772                         samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg, \
773                                              attrib);                   \
774                 }                                                       \
775         } while (0);
776
777         ADD_OR_DEL(string, "samAccountName", alias_name.string);
778
779         if (samdb_msg_add_dom_sid(state->sam_ldb, mem_ctx, msg, 
780                                   "objectSid", dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid))) {
781                 return NT_STATUS_NO_MEMORY; 
782         }
783
784         ADD_OR_DEL(string, "description", description.string);
785
786 #undef ADD_OR_DEL
787
788         samdb_msg_add_uint(state->sam_ldb, mem_ctx, msg, "groupType", 0x80000004);
789
790         container = "Users";
791         obj_class = "group";
792
793         if (add) {
794                 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg, 
795                                      "objectClass", obj_class);
796                 msg->dn = ldb_dn_copy(mem_ctx, state->base_dn[database]);
797                 ldb_dn_add_child_fmt(msg->dn, "CN=%s,CN=%s", cn_name, container);
798                 if (!msg->dn) {
799                         return NT_STATUS_NO_MEMORY;             
800                 }
801
802                 ret = ldb_add(state->sam_ldb, msg);
803                 if (ret != 0) {
804                         *error_string = talloc_asprintf(mem_ctx, "Failed to create alias record %s: %s",
805                                                         ldb_dn_get_linearized(msg->dn),
806                                                         ldb_errstring(state->sam_ldb));
807                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
808                 }
809         } else {
810                 ret = dsdb_replace(state->sam_ldb, msg, 0);
811                 if (ret != 0) {
812                         *error_string = talloc_asprintf(mem_ctx, "Failed to modify alias record %s: %s",
813                                                         ldb_dn_get_linearized(msg->dn),
814                                                         ldb_errstring(state->sam_ldb));
815                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
816                 }
817         }
818
819         return NT_STATUS_OK;
820 }
821
822 static NTSTATUS samsync_ldb_delete_alias(TALLOC_CTX *mem_ctx,
823                                          struct samsync_ldb_state *state,
824                                          enum netr_SamDatabaseID database,
825                                          struct netr_DELTA_ENUM *delta,
826                                          char **error_string) 
827 {
828         uint32_t rid = delta->delta_id_union.rid;
829         struct ldb_message **msgs;
830         int ret;
831         const char *attrs[] = { NULL };
832
833         /* search for the alias, by rid */
834         ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
835                            "(&(objectClass=group)(objectSid=%s))", 
836                            ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid))); 
837
838         if (ret == -1) {
839                 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
840                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
841         } else if (ret == 0) {
842                 return NT_STATUS_NO_SUCH_ALIAS;
843         } else if (ret > 1) {
844                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
845         }
846
847         ret = ldb_delete(state->sam_ldb, msgs[0]->dn);
848         if (ret != 0) {
849                 *error_string = talloc_asprintf(mem_ctx, "Failed to delete alias record %s: %s",
850                                                 ldb_dn_get_linearized(msgs[0]->dn),
851                                                 ldb_errstring(state->sam_ldb));
852                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
853         }
854
855         return NT_STATUS_OK;
856 }
857
858 static NTSTATUS samsync_ldb_handle_alias_member(TALLOC_CTX *mem_ctx,
859                                                 struct samsync_ldb_state *state,
860                                                 enum netr_SamDatabaseID database,
861                                                 struct netr_DELTA_ENUM *delta,
862                                                 char **error_string) 
863 {
864         uint32_t rid = delta->delta_id_union.rid;
865         struct netr_DELTA_ALIAS_MEMBER *alias_member = delta->delta_union.alias_member;
866         struct ldb_message *msg;
867         struct ldb_message **msgs;
868         int ret;
869         const char *attrs[] = { NULL };
870         int i;
871
872         msg = ldb_msg_new(mem_ctx);
873         if (msg == NULL) {
874                 return NT_STATUS_NO_MEMORY;
875         }
876
877         /* search for the alias, by rid */
878         ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
879                            "(&(objectClass=group)(objectSid=%s))", 
880                            ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid))); 
881                 
882         if (ret == -1) {
883                 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
884                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
885         } else if (ret == 0) {
886                 return NT_STATUS_NO_SUCH_GROUP;
887         } else if (ret > 1) {
888                 *error_string = talloc_asprintf(mem_ctx, "More than one group/alias with SID: %s", 
889                                                 dom_sid_string(mem_ctx, 
890                                                                dom_sid_add_rid(mem_ctx, 
891                                                                                state->dom_sid[database], 
892                                                                                rid)));
893                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
894         } else {
895                 msg->dn = talloc_steal(msg, msgs[0]->dn);
896         }
897         
898         talloc_free(msgs);
899
900         for (i=0; i<alias_member->sids.num_sids; i++) {
901                 struct ldb_dn *alias_member_dn;
902                 /* search for members, in the top basedn (normal users are builtin aliases) */
903                 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[SAM_DATABASE_DOMAIN], &msgs, attrs,
904                                    "(objectSid=%s)", 
905                                    ldap_encode_ndr_dom_sid(mem_ctx, alias_member->sids.sids[i].sid)); 
906
907                 if (ret == -1) {
908                         *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
909                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
910                 } else if (ret == 0) {
911                         NTSTATUS nt_status;
912                         nt_status = samsync_ldb_add_foreignSecurityPrincipal(mem_ctx, state,
913                                                                              alias_member->sids.sids[i].sid, 
914                                                                              &alias_member_dn, 
915                                                                              error_string);
916                         if (!NT_STATUS_IS_OK(nt_status)) {
917                                 return nt_status;
918                         }
919                 } else if (ret > 1) {
920                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
921                 } else {
922                         alias_member_dn = msgs[0]->dn;
923                 }
924                 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg, "member", ldb_dn_alloc_linearized(mem_ctx, alias_member_dn));
925         
926                 talloc_free(msgs);
927         }
928
929         ret = dsdb_replace(state->sam_ldb, msg, 0);
930         if (ret != 0) {
931                 *error_string = talloc_asprintf(mem_ctx, "Failed to modify group record %s: %s",
932                                                 ldb_dn_get_linearized(msg->dn),
933                                                 ldb_errstring(state->sam_ldb));
934                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
935         }
936
937         return NT_STATUS_OK;
938 }
939
940 static NTSTATUS samsync_ldb_handle_account(TALLOC_CTX *mem_ctx,
941                                            struct samsync_ldb_state *state,
942                                            enum netr_SamDatabaseID database,
943                                            struct netr_DELTA_ENUM *delta,
944                                            char **error_string) 
945 {
946         struct dom_sid *sid = delta->delta_id_union.sid;
947         struct netr_DELTA_ACCOUNT *account = delta->delta_union.account;
948
949         struct ldb_message *msg;
950         int ret;
951         int i;
952         char *dnstr, *sidstr;
953
954         msg = ldb_msg_new(mem_ctx);
955         if (msg == NULL) {
956                 return NT_STATUS_NO_MEMORY;
957         }
958
959         sidstr = dom_sid_string(msg, sid);
960         NT_STATUS_HAVE_NO_MEMORY_AND_FREE(sidstr, msg);
961
962         dnstr = talloc_asprintf(msg, "sid=%s", sidstr);
963         NT_STATUS_HAVE_NO_MEMORY_AND_FREE(dnstr, msg);
964
965         msg->dn = ldb_dn_new(msg, state->pdb, dnstr);
966         NT_STATUS_HAVE_NO_MEMORY_AND_FREE(msg->dn, msg);
967
968         for (i=0; i< account->privilege_entries; i++) {
969                 samdb_msg_add_string(state->pdb, mem_ctx, msg, "privilege",
970                                      account->privilege_name[i].string);
971         }
972
973         ret = dsdb_replace(state->pdb, msg, 0);
974         if (ret == LDB_ERR_NO_SUCH_OBJECT) {
975                 if (samdb_msg_add_dom_sid(state->pdb, msg, msg, "objectSid", sid) != LDB_SUCCESS) {
976                         talloc_free(msg);
977                         return NT_STATUS_NO_MEMORY;
978                 }
979                 samdb_msg_add_string(state->pdb, msg, msg, "comment", "added via samsync");
980                 ret = ldb_add(state->pdb, msg);         
981         }
982
983         if (ret != 0) {
984                 *error_string = talloc_asprintf(mem_ctx, "Failed to modify privilege record %s",
985                                                 ldb_dn_get_linearized(msg->dn));
986                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
987         }
988
989         return NT_STATUS_OK;
990 }
991
992 static NTSTATUS samsync_ldb_delete_account(TALLOC_CTX *mem_ctx,
993                                            struct samsync_ldb_state *state,
994                                            enum netr_SamDatabaseID database,
995                                            struct netr_DELTA_ENUM *delta,
996                                            char **error_string) 
997 {
998         struct dom_sid *sid = delta->delta_id_union.sid;
999
1000         struct ldb_message *msg;
1001         struct ldb_message **msgs;
1002         int ret;
1003         const char *attrs[] = { NULL };
1004
1005         msg = ldb_msg_new(mem_ctx);
1006         if (msg == NULL) {
1007                 return NT_STATUS_NO_MEMORY;
1008         }
1009
1010         /* search for the account, by sid, in the top basedn */
1011         ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[SAM_DATABASE_DOMAIN], &msgs, attrs,
1012                            "(objectSid=%s)", 
1013                            ldap_encode_ndr_dom_sid(mem_ctx, sid)); 
1014
1015         if (ret == -1) {
1016                 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
1017                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1018         } else if (ret == 0) {
1019                 return NT_STATUS_NO_SUCH_USER;
1020         } else if (ret > 1) {
1021                 *error_string = talloc_asprintf(mem_ctx, "More than one account with SID: %s", 
1022                                                 dom_sid_string(mem_ctx, sid));
1023                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1024         } else {
1025                 msg->dn = talloc_steal(msg, msgs[0]->dn);
1026         }
1027
1028         samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,  
1029                              "privilege");
1030
1031         ret = dsdb_replace(state->sam_ldb, msg, 0);
1032         if (ret != 0) {
1033                 *error_string = talloc_asprintf(mem_ctx, "Failed to modify privilege record %s",
1034                                                 ldb_dn_get_linearized(msg->dn));
1035                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1036         }
1037
1038         return NT_STATUS_OK;
1039 }
1040
1041 static NTSTATUS libnet_samsync_ldb_fn(TALLOC_CTX *mem_ctx,              
1042                                       void *private_data,
1043                                       enum netr_SamDatabaseID database,
1044                                       struct netr_DELTA_ENUM *delta,
1045                                       char **error_string)
1046 {
1047         NTSTATUS nt_status = NT_STATUS_OK;
1048         struct samsync_ldb_state *state = talloc_get_type(private_data, struct samsync_ldb_state);
1049
1050         *error_string = NULL;
1051         switch (delta->delta_type) {
1052         case NETR_DELTA_DOMAIN:
1053         {
1054                 nt_status = samsync_ldb_handle_domain(mem_ctx, 
1055                                                       state,
1056                                                       database,
1057                                                       delta,
1058                                                       error_string);
1059                 break;
1060         }
1061         case NETR_DELTA_USER:
1062         {
1063                 nt_status = samsync_ldb_handle_user(mem_ctx, 
1064                                                     state,
1065                                                     database,
1066                                                     delta,
1067                                                     error_string);
1068                 break;
1069         }
1070         case NETR_DELTA_DELETE_USER:
1071         {
1072                 nt_status = samsync_ldb_delete_user(mem_ctx, 
1073                                                     state,
1074                                                     database,
1075                                                     delta,
1076                                                     error_string);
1077                 break;
1078         }
1079         case NETR_DELTA_GROUP:
1080         {
1081                 nt_status = samsync_ldb_handle_group(mem_ctx, 
1082                                                      state,
1083                                                      database,
1084                                                      delta,
1085                                                      error_string);
1086                 break;
1087         }
1088         case NETR_DELTA_DELETE_GROUP:
1089         {
1090                 nt_status = samsync_ldb_delete_group(mem_ctx, 
1091                                                      state,
1092                                                      database,
1093                                                      delta,
1094                                                      error_string);
1095                 break;
1096         }
1097         case NETR_DELTA_GROUP_MEMBER:
1098         {
1099                 nt_status = samsync_ldb_handle_group_member(mem_ctx, 
1100                                                             state,
1101                                                             database,
1102                                                             delta,
1103                                                             error_string);
1104                 break;
1105         }
1106         case NETR_DELTA_ALIAS:
1107         {
1108                 nt_status = samsync_ldb_handle_alias(mem_ctx, 
1109                                                      state,
1110                                                      database,
1111                                                      delta,
1112                                                      error_string);
1113                 break;
1114         }
1115         case NETR_DELTA_DELETE_ALIAS:
1116         {
1117                 nt_status = samsync_ldb_delete_alias(mem_ctx, 
1118                                                      state,
1119                                                      database,
1120                                                      delta,
1121                                                      error_string);
1122                 break;
1123         }
1124         case NETR_DELTA_ALIAS_MEMBER:
1125         {
1126                 nt_status = samsync_ldb_handle_alias_member(mem_ctx, 
1127                                                             state,
1128                                                             database,
1129                                                             delta,
1130                                                             error_string);
1131                 break;
1132         }
1133         case NETR_DELTA_ACCOUNT:
1134         {
1135                 nt_status = samsync_ldb_handle_account(mem_ctx, 
1136                                                        state,
1137                                                        database,
1138                                                        delta,
1139                                                        error_string);
1140                 break;
1141         }
1142         case NETR_DELTA_DELETE_ACCOUNT:
1143         {
1144                 nt_status = samsync_ldb_delete_account(mem_ctx, 
1145                                                        state,
1146                                                        database,
1147                                                        delta,
1148                                                        error_string);
1149                 break;
1150         }
1151         default:
1152                 /* Can't dump them all right now */
1153                 break;
1154         }
1155         if (!NT_STATUS_IS_OK(nt_status) && !*error_string) {
1156                 *error_string = talloc_asprintf(mem_ctx, "Failed to handle samsync delta: %s", nt_errstr(nt_status));
1157         }
1158         return nt_status;
1159 }
1160
1161 static NTSTATUS libnet_samsync_ldb_init(TALLOC_CTX *mem_ctx,            
1162                                         void *private_data,
1163                                         struct libnet_SamSync_state *samsync_state,
1164                                         char **error_string)
1165 {
1166         struct samsync_ldb_state *state = talloc_get_type(private_data, struct samsync_ldb_state);
1167         const char *server = dcerpc_server_name(samsync_state->netlogon_pipe);
1168         char *ldap_url;
1169
1170         state->samsync_state = samsync_state;
1171
1172         ZERO_STRUCT(state->dom_sid);
1173         if (state->samsync_state->domain_sid) {
1174                 state->dom_sid[SAM_DATABASE_DOMAIN] = dom_sid_dup(state, state->samsync_state->domain_sid);
1175         }
1176
1177         state->dom_sid[SAM_DATABASE_BUILTIN] = dom_sid_parse_talloc(state, SID_BUILTIN);
1178
1179         if (state->samsync_state->realm) {
1180                 if (!server || !*server) {
1181                         /* huh?  how do we not have a server name?  */
1182                         *error_string = talloc_strdup(mem_ctx, "No DCE/RPC server name available.  How did we connect?");
1183                         return NT_STATUS_INVALID_PARAMETER;
1184                 }
1185                 ldap_url = talloc_asprintf(state, "ldap://%s", server);
1186                 
1187                 state->remote_ldb = ldb_wrap_connect(mem_ctx, 
1188                                                      state->samsync_state->machine_net_ctx->event_ctx,
1189                                                      state->samsync_state->machine_net_ctx->lp_ctx, 
1190                                                      ldap_url, 
1191                                                      NULL, state->samsync_state->machine_net_ctx->cred,
1192                                                      0);
1193                 if (!state->remote_ldb) {
1194                         *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);
1195                         return NT_STATUS_NO_LOGON_SERVERS;
1196                 }
1197         } else {
1198                 state->remote_ldb = NULL;
1199         }
1200         return NT_STATUS_OK;
1201 }
1202
1203 NTSTATUS libnet_samsync_ldb(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, struct libnet_samsync_ldb *r)
1204 {
1205         NTSTATUS nt_status;
1206         struct libnet_SamSync r2;
1207         struct samsync_ldb_state *state = talloc(mem_ctx, struct samsync_ldb_state);
1208
1209         if (!state) {
1210                 return NT_STATUS_NO_MEMORY;
1211         }
1212
1213         state->secrets         = NULL;
1214         state->trusted_domains = NULL;
1215
1216         state->sam_ldb         = samdb_connect(mem_ctx, 
1217                                                ctx->event_ctx,
1218                                                ctx->lp_ctx, 
1219                                                r->in.session_info);
1220         if (!state->sam_ldb) {
1221                 return NT_STATUS_INTERNAL_DB_ERROR;
1222         }
1223
1224         state->pdb             = privilege_connect(mem_ctx, 
1225                                                    ctx->event_ctx,
1226                                                    ctx->lp_ctx);
1227         if (!state->pdb) {
1228                 return NT_STATUS_INTERNAL_DB_ERROR;
1229         }
1230
1231         r2.out.error_string    = NULL;
1232         r2.in.binding_string   = r->in.binding_string;
1233         r2.in.init_fn          = libnet_samsync_ldb_init;
1234         r2.in.delta_fn         = libnet_samsync_ldb_fn;
1235         r2.in.fn_ctx           = state;
1236         r2.in.machine_account  = NULL; /* TODO:  Create a machine account, fill this in, and the delete it */
1237         nt_status              = libnet_SamSync_netlogon(ctx, state, &r2);
1238         r->out.error_string    = r2.out.error_string;
1239         talloc_steal(mem_ctx, r->out.error_string);
1240
1241         if (!NT_STATUS_IS_OK(nt_status)) {
1242                 talloc_free(state);
1243                 return nt_status;
1244         }
1245         talloc_free(state);
1246         return nt_status;
1247 }