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