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