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