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