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