2414b2795f1b2f411543535a24119688cc8a3f2b
[kai/samba.git] / source / libnet / libnet_samsync_ldb.c
1 /* 
2    Unix SMB/CIFS implementation.
3    
4    Extract the user/system database from a remote SamSync server
5
6    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005
7    Copyright (C) Andrew Tridgell 2004
8    
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 2 of the License, or
12    (at your option) any later version.
13    
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18    
19    You should have received a copy of the GNU General Public License
20    along with this program; if not, write to the Free Software
21    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 */
23
24
25 #include "includes.h"
26 #include "libnet/libnet.h"
27 #include "librpc/gen_ndr/ndr_netlogon.h"
28 #include "librpc/gen_ndr/ndr_samr.h"
29 #include "dlinklist.h"
30 #include "lib/ldb/include/ldb.h"
31
32 struct samsync_ldb_secret {
33         struct samsync_ldb_secret *prev, *next;
34         DATA_BLOB secret;
35         char *name;
36         NTTIME mtime;
37 };
38
39 struct samsync_ldb_trusted_domain {
40         struct samsync_ldb_trusted_domain *prev, *next;
41         struct dom_sid *sid;
42         char *name;
43 };
44
45 struct samsync_ldb_state {
46         struct dom_sid *dom_sid[3];
47         struct ldb_context *sam_ldb;
48         char *base_dn[3];
49         struct samsync_ldb_secret *secrets;
50         struct samsync_ldb_trusted_domain *trusted_domains;
51 };
52
53 static NTSTATUS samsync_ldb_handle_domain(TALLOC_CTX *mem_ctx,
54                                           struct samsync_ldb_state *state,
55                                           struct creds_CredentialState *creds,
56                                           enum netr_SamDatabaseID database,
57                                           struct netr_DELTA_ENUM *delta) 
58 {
59         struct netr_DELTA_DOMAIN *domain = delta->delta_union.domain;
60         const char *domain_name = domain->domain_name.string;
61         struct ldb_message *msg;
62         int ret;
63         
64         if (database == SAM_DATABASE_DOMAIN) {
65                 const char *domain_attrs[] =  {"nETBIOSName", "nCName", NULL};
66                 struct ldb_message **msgs_domain;
67                 int ret_domain;
68                 ret_domain = gendb_search(state->sam_ldb, mem_ctx, NULL, &msgs_domain, domain_attrs,
69                                           "(&(&(nETBIOSName=%s)(objectclass=crossRef))(ncName=*))", 
70                                           domain_name);
71                 if (ret_domain == -1) {
72                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
73                 }
74                 
75                 if (ret_domain != 1) {
76                         return NT_STATUS_NO_SUCH_DOMAIN;                
77                 }
78
79                 state->base_dn[database]
80                         = talloc_steal(state, samdb_result_string(msgs_domain[0], 
81                                                                   "nCName", NULL));
82                 
83                 state->dom_sid[database]
84                         = talloc_steal(state, 
85                                        samdb_search_dom_sid(state->sam_ldb, state,
86                                                             state->base_dn[database], "objectSid", 
87                                                             "dn=%s", state->base_dn[database]));
88         } else if (database == SAM_DATABASE_BUILTIN) {
89                         /* work out the builtin_dn - useful for so many calls its worth
90                            fetching here */
91                 state->base_dn[database]
92                         = talloc_steal(state, 
93                                        samdb_search_string(state->sam_ldb, mem_ctx, NULL,
94                                                            "dn", "objectClass=builtinDomain"));
95                 state->dom_sid[database]
96                         = dom_sid_parse_talloc(state, SID_BUILTIN);
97         } else {
98                 /* PRIVs DB */
99                 return NT_STATUS_INVALID_PARAMETER;
100         }
101
102         msg = ldb_msg_new(mem_ctx);
103         if (msg == NULL) {
104                 return NT_STATUS_NO_MEMORY;
105         }
106
107         msg->dn = talloc_reference(mem_ctx, state->base_dn[database]);
108         if (!msg->dn) {
109                 return NT_STATUS_NO_MEMORY;
110         }
111
112         samdb_msg_add_string(state->sam_ldb, mem_ctx, 
113                              msg, "oEMInformation", domain->comment.string);
114
115         samdb_msg_add_uint64(state->sam_ldb, mem_ctx, 
116                              msg, "forceLogff", domain->force_logoff_time);
117
118         samdb_msg_add_uint(state->sam_ldb, mem_ctx, 
119                           msg, "minPwdLen", domain->min_password_length);
120
121         samdb_msg_add_int64(state->sam_ldb, mem_ctx, 
122                           msg, "maxPwdAge", domain->max_password_age);
123
124         samdb_msg_add_int64(state->sam_ldb, mem_ctx, 
125                           msg, "minPwdAge", domain->min_password_age);
126
127         samdb_msg_add_uint(state->sam_ldb, mem_ctx, 
128                           msg, "pwdHistoryLength", domain->password_history_length);
129
130         samdb_msg_add_uint64(state->sam_ldb, mem_ctx, 
131                              msg, "modifiedCountAtLastProm", 
132                              domain->sequence_num);
133
134         samdb_msg_add_uint64(state->sam_ldb, mem_ctx, 
135                              msg, "creationTime", domain->domain_create_time);
136
137         /* TODO: Account lockout, password properties */
138         
139         ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
140
141         if (ret) {
142                 return NT_STATUS_INTERNAL_ERROR;
143         }
144         return NT_STATUS_OK;
145 }
146
147 static NTSTATUS samsync_ldb_handle_user(TALLOC_CTX *mem_ctx,
148                                         struct samsync_ldb_state *state,
149                                         struct creds_CredentialState *creds,
150                                         enum netr_SamDatabaseID database,
151                                         struct netr_DELTA_ENUM *delta) 
152 {
153         uint32_t rid = delta->delta_id_union.rid;
154         struct netr_DELTA_USER *user = delta->delta_union.user;
155         const char *container, *obj_class;
156         char *cn_name;
157         int cn_name_len;
158
159         struct ldb_message *msg;
160         struct ldb_message **msgs;
161         int ret;
162         uint32_t acb;
163         BOOL add = False;
164         const char *attrs[] = { NULL };
165
166         msg = ldb_msg_new(mem_ctx);
167         if (msg == NULL) {
168                 return NT_STATUS_NO_MEMORY;
169         }
170
171         /* search for the user, by rid */
172         ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
173                            "(&(objectClass=user)(objectSid=%s))", 
174                            ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid))); 
175
176         if (ret == -1) {
177                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
178         } else if (ret == 0) {
179                 add = True;
180         } else if (ret > 1) {
181                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
182         } else {
183                 msg->dn = talloc_steal(msg, msgs[0]->dn);
184         }
185
186
187         cn_name   = talloc_strdup(mem_ctx, user->account_name.string);
188         NT_STATUS_HAVE_NO_MEMORY(cn_name);
189         cn_name_len = strlen(cn_name);
190
191 #define ADD_OR_DEL(type, attrib, field) do {\
192         if (user->field) { \
193                 samdb_msg_add_ ## type(state->sam_ldb, mem_ctx, msg, \
194                                      attrib, user->field); \
195         } else if (!add) { \
196                 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,  \
197                                      attrib); \
198         } \
199         } while (0);
200
201         ADD_OR_DEL(string, "samAccountName", account_name.string);
202         ADD_OR_DEL(string, "displayName", full_name.string);
203
204         if (samdb_msg_add_dom_sid(state->sam_ldb, mem_ctx, msg, 
205                                   "objectSid", dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid))) {
206                 return NT_STATUS_NO_MEMORY; 
207         }
208
209         ADD_OR_DEL(uint, "primaryGroupID", primary_gid);
210         ADD_OR_DEL(string, "homeDirectory", home_directory.string);
211         ADD_OR_DEL(string, "homeDrive", home_drive.string);
212         ADD_OR_DEL(string, "scriptPath", logon_script.string);
213         ADD_OR_DEL(string, "description", description.string);
214         ADD_OR_DEL(string, "userWorkstations", workstations.string);
215
216         ADD_OR_DEL(uint64, "lastLogon", last_logon);
217         ADD_OR_DEL(uint64, "lastLogoff", last_logoff);
218
219         if (samdb_msg_add_logon_hours(state->sam_ldb, mem_ctx, msg, "logonHours", &user->logon_hours) != 0) { 
220                 return NT_STATUS_NO_MEMORY; 
221         }
222
223         ADD_OR_DEL(uint, "badPwdCount", bad_password_count);
224         ADD_OR_DEL(uint, "logonCount", logon_count);
225
226         ADD_OR_DEL(uint64, "pwdLastSet", last_password_change);
227         ADD_OR_DEL(uint64, "accountExpires", acct_expiry);
228         
229         if (samdb_msg_add_acct_flags(state->sam_ldb, mem_ctx, msg, 
230                                      "userAccountConrol", user->acct_flags) != 0) { 
231                 return NT_STATUS_NO_MEMORY; 
232         } 
233         
234         /* Passwords */
235         samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,  
236                                 "unicodePwd"); 
237         if (user->lm_password_present) {
238                 samdb_msg_add_hash(state->sam_ldb, mem_ctx, msg,  
239                                    "lmPwdHash", &user->lmpassword);
240         } else {
241                 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,  
242                                      "lmPwdHash"); 
243         }
244         if (user->nt_password_present) {
245                 samdb_msg_add_hash(state->sam_ldb, mem_ctx, msg,  
246                                    "ntPwdHash", &user->ntpassword);
247         } else {
248                 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,  
249                                      "ntPwdHash"); 
250         }
251             
252         ADD_OR_DEL(string, "comment", comment.string);
253         ADD_OR_DEL(string, "userParameters", parameters.string);
254         ADD_OR_DEL(uint, "countryCode", country_code);
255         ADD_OR_DEL(uint, "codePage", code_page);
256
257         ADD_OR_DEL(string, "profilePath", profile_path.string);
258
259 #undef ADD_OR_DEL
260
261         acb = user->acct_flags;
262         if (acb & (ACB_WSTRUST)) {
263                 cn_name[cn_name_len - 1] = '\0';
264                 container = "Computers";
265                 obj_class = "computer";
266                 
267         } else if (acb & ACB_SVRTRUST) {
268                 if (cn_name[cn_name_len - 1] != '$') {
269                         return NT_STATUS_FOOBAR;                
270                 }
271                 cn_name[cn_name_len - 1] = '\0';
272                 container = "Domain Controllers";
273                 obj_class = "computer";
274         } else {
275                 container = "Users";
276                 obj_class = "user";
277         }
278         if (add) {
279                 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg, 
280                                      "objectClass", obj_class);
281                 msg->dn = talloc_asprintf(mem_ctx, "CN=%s,CN=%s,%s",
282                                           cn_name, container, state->base_dn[database]);
283                 if (!msg->dn) {
284                         return NT_STATUS_NO_MEMORY;             
285                 }
286
287                 ret = samdb_add(state->sam_ldb, mem_ctx, msg);
288                 if (ret != 0) {
289                         DEBUG(0,("Failed to create user record %s\n", msg->dn));
290                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
291                 }
292         } else {
293                 ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
294                 if (ret != 0) {
295                         DEBUG(0,("Failed to modify user record %s\n", msg->dn));
296                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
297                 }
298         }
299
300         return NT_STATUS_OK;
301 }
302
303 static NTSTATUS samsync_ldb_delete_user(TALLOC_CTX *mem_ctx,
304                                         struct samsync_ldb_state *state,
305                                         struct creds_CredentialState *creds,
306                                         enum netr_SamDatabaseID database,
307                                         struct netr_DELTA_ENUM *delta) 
308 {
309         uint32_t rid = delta->delta_id_union.rid;
310         struct ldb_message **msgs;
311         int ret;
312         const char *attrs[] = { NULL };
313
314         /* search for the user, by rid */
315         ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
316                            "(&(objectClass=user)(objectSid=%s))", 
317                            ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid))); 
318
319         if (ret == -1) {
320                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
321         } else if (ret == 0) {
322                 return NT_STATUS_NO_SUCH_USER;
323         } else if (ret > 1) {
324                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
325         }
326
327         ret = samdb_delete(state->sam_ldb, mem_ctx, msgs[0]->dn);
328         if (ret != 0) {
329                 DEBUG(0,("Failed to delete user record %s: %s\n", msgs[0]->dn, ldb_errstring(state->sam_ldb)));
330                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
331         }
332
333         return NT_STATUS_OK;
334 }
335
336 static NTSTATUS samsync_ldb_handle_group(TALLOC_CTX *mem_ctx,
337                                          struct samsync_ldb_state *state,
338                                          struct creds_CredentialState *creds,
339                                          enum netr_SamDatabaseID database,
340                                          struct netr_DELTA_ENUM *delta) 
341 {
342         uint32_t rid = delta->delta_id_union.rid;
343         struct netr_DELTA_GROUP *group = delta->delta_union.group;
344         const char *container, *obj_class;
345         const char *cn_name;
346
347         struct ldb_message *msg;
348         struct ldb_message **msgs;
349         int ret;
350         BOOL add = False;
351         const char *attrs[] = { NULL };
352
353         msg = ldb_msg_new(mem_ctx);
354         if (msg == NULL) {
355                 return NT_STATUS_NO_MEMORY;
356         }
357
358         /* search for the group, by rid */
359         ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
360                            "(&(objectClass=group)(objectSid=%s))", 
361                            ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid))); 
362
363         if (ret == -1) {
364                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
365         } else if (ret == 0) {
366                 add = True;
367         } else if (ret > 1) {
368                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
369         } else {
370                 msg->dn = talloc_steal(msg, msgs[0]->dn);
371         }
372
373         cn_name   = group->group_name.string;
374
375 #define ADD_OR_DEL(type, attrib, field) do {\
376         if (group->field) { \
377                 samdb_msg_add_ ## type(state->sam_ldb, mem_ctx, msg, \
378                                      attrib, group->field); \
379         } else if (!add) { \
380                 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,  \
381                                      attrib); \
382         } \
383         } while (0);
384
385         ADD_OR_DEL(string, "samAccountName", group_name.string);
386
387         if (samdb_msg_add_dom_sid(state->sam_ldb, mem_ctx, msg, 
388                                   "objectSid", dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid))) {
389                 return NT_STATUS_NO_MEMORY; 
390         }
391
392         ADD_OR_DEL(string, "description", description.string);
393
394 #undef ADD_OR_DEL
395
396         container = "Users";
397         obj_class = "group";
398
399         if (add) {
400                 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg, 
401                                      "objectClass", obj_class);
402                 msg->dn = talloc_asprintf(mem_ctx, "CN=%s,CN=%s,%s",
403                                           cn_name, container, state->base_dn[database]);
404                 if (!msg->dn) {
405                         return NT_STATUS_NO_MEMORY;             
406                 }
407
408                 ret = samdb_add(state->sam_ldb, mem_ctx, msg);
409                 if (ret != 0) {
410                         DEBUG(0,("Failed to create group record %s: %s\n", msg->dn, ldb_errstring(state->sam_ldb)));
411                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
412                 }
413         } else {
414                 ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
415                 if (ret != 0) {
416                         DEBUG(0,("Failed to modify group record %s: %s\n", msg->dn, ldb_errstring(state->sam_ldb)));
417                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
418                 }
419         }
420
421         return NT_STATUS_OK;
422 }
423
424 static NTSTATUS samsync_ldb_delete_group(TALLOC_CTX *mem_ctx,
425                                         struct samsync_ldb_state *state,
426                                         struct creds_CredentialState *creds,
427                                         enum netr_SamDatabaseID database,
428                                         struct netr_DELTA_ENUM *delta) 
429 {
430         uint32_t rid = delta->delta_id_union.rid;
431         struct ldb_message **msgs;
432         int ret;
433         const char *attrs[] = { NULL };
434
435         /* search for the group, by rid */
436         ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
437                            "(&(objectClass=group)(objectSid=%s))", 
438                            ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid))); 
439
440         if (ret == -1) {
441                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
442         } else if (ret == 0) {
443                 return NT_STATUS_NO_SUCH_GROUP;
444         } else if (ret > 1) {
445                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
446         }
447         
448         ret = samdb_delete(state->sam_ldb, mem_ctx, msgs[0]->dn);
449         if (ret != 0) {
450                 DEBUG(0,("Failed to delete group record %s: %s\n", msgs[0]->dn, ldb_errstring(state->sam_ldb)));
451                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
452         }
453
454         return NT_STATUS_OK;
455 }
456
457 static NTSTATUS samsync_ldb_handle_group_member(TALLOC_CTX *mem_ctx,
458                                                 struct samsync_ldb_state *state,
459                                                 struct creds_CredentialState *creds,
460                                                 enum netr_SamDatabaseID database,
461                                                 struct netr_DELTA_ENUM *delta) 
462 {
463         uint32_t rid = delta->delta_id_union.rid;
464         struct netr_DELTA_GROUP_MEMBER *group_member = delta->delta_union.group_member;
465         struct ldb_message *msg;
466         struct ldb_message **msgs;
467         int ret;
468         const char *attrs[] = { NULL };
469         int i;
470
471         msg = ldb_msg_new(mem_ctx);
472         if (msg == NULL) {
473                 return NT_STATUS_NO_MEMORY;
474         }
475
476         /* search for the group, by rid */
477         ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
478                            "(&(objectClass=group)(objectSid=%s))", 
479                            ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid))); 
480
481         if (ret == -1) {
482                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
483         } else if (ret == 0) {
484                 return NT_STATUS_NO_SUCH_GROUP;
485         } else if (ret > 1) {
486                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
487         } else {
488                 msg->dn = talloc_steal(msg, msgs[0]->dn);
489         }
490         
491         talloc_free(msgs);
492
493         for (i=0; i<group_member->num_rids; i++) {
494                 /* search for the group, by rid */
495                 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
496                                    "(&(objectClass=user)(objectSid=%s))", 
497                                    ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], group_member->rids[i]))); 
498
499                 if (ret == -1) {
500                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
501                 } else if (ret == 0) {
502                         return NT_STATUS_NO_SUCH_USER;
503                 } else if (ret > 1) {
504                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
505                 } else {
506                         samdb_msg_add_string(state->sam_ldb, mem_ctx, msg, "member", msgs[0]->dn);
507                 }
508         
509                 talloc_free(msgs);
510         }
511
512         ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
513         if (ret != 0) {
514                 DEBUG(0,("Failed to modify group record %s: %s\n", msg->dn, ldb_errstring(state->sam_ldb)));
515                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
516         }
517
518         return NT_STATUS_OK;
519 }
520
521 static NTSTATUS samsync_ldb_handle_alias(TALLOC_CTX *mem_ctx,
522                                          struct samsync_ldb_state *state,
523                                          struct creds_CredentialState *creds,
524                                          enum netr_SamDatabaseID database,
525                                          struct netr_DELTA_ENUM *delta) 
526 {
527         uint32_t rid = delta->delta_id_union.rid;
528         struct netr_DELTA_ALIAS *alias = delta->delta_union.alias;
529         const char *container, *obj_class;
530         const char *cn_name;
531
532         struct ldb_message *msg;
533         struct ldb_message **msgs;
534         int ret;
535         BOOL add = False;
536         const char *attrs[] = { NULL };
537
538         msg = ldb_msg_new(mem_ctx);
539         if (msg == NULL) {
540                 return NT_STATUS_NO_MEMORY;
541         }
542
543         /* search for the alias, by rid */
544         ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
545                            "(&(objectClass=group)(objectSid=%s))", 
546                            ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid))); 
547
548         if (ret == -1) {
549                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
550         } else if (ret == 0) {
551                 add = True;
552         } else if (ret > 1) {
553                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
554         } else {
555                 msg->dn = talloc_steal(mem_ctx, msgs[0]->dn);
556         }
557
558         cn_name   = alias->alias_name.string;
559
560 #define ADD_OR_DEL(type, attrib, field) do {\
561         if (alias->field) { \
562                 samdb_msg_add_ ## type(state->sam_ldb, mem_ctx, msg, \
563                                      attrib, alias->field); \
564         } else if (!add) { \
565                 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,  \
566                                      attrib); \
567         } \
568         } while (0);
569
570         ADD_OR_DEL(string, "samAccountName", alias_name.string);
571
572         if (samdb_msg_add_dom_sid(state->sam_ldb, mem_ctx, msg, 
573                                   "objectSid", dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid))) {
574                 return NT_STATUS_NO_MEMORY; 
575         }
576
577         ADD_OR_DEL(string, "description", description.string);
578
579 #undef ADD_OR_DEL
580
581         samdb_msg_add_uint(state->sam_ldb, mem_ctx, msg, "groupType", 0x80000004);
582
583         container = "Users";
584         obj_class = "group";
585
586         if (add) {
587                 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg, 
588                                      "objectClass", obj_class);
589                 msg->dn = talloc_asprintf(mem_ctx, "CN=%s,CN=%s,%s",
590                                           cn_name, container, state->base_dn[database]);
591                 if (!msg->dn) {
592                         return NT_STATUS_NO_MEMORY;             
593                 }
594
595                 ret = samdb_add(state->sam_ldb, mem_ctx, msg);
596                 if (ret != 0) {
597                         DEBUG(0,("Failed to create alias record %s: %s\n", msg->dn, ldb_errstring(state->sam_ldb)));
598                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
599                 }
600         } else {
601                 ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
602                 if (ret != 0) {
603                         DEBUG(0,("Failed to modify alias record %s: %s\n", msg->dn, ldb_errstring(state->sam_ldb)));
604                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
605                 }
606         }
607
608         return NT_STATUS_OK;
609 }
610
611 static NTSTATUS samsync_ldb_delete_alias(TALLOC_CTX *mem_ctx,
612                                         struct samsync_ldb_state *state,
613                                         struct creds_CredentialState *creds,
614                                         enum netr_SamDatabaseID database,
615                                         struct netr_DELTA_ENUM *delta) 
616 {
617         uint32_t rid = delta->delta_id_union.rid;
618         struct ldb_message **msgs;
619         int ret;
620         const char *attrs[] = { NULL };
621
622         /* search for the alias, by rid */
623         ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
624                            "(&(objectClass=group)(objectSid=%s))", 
625                            ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid))); 
626
627         if (ret == -1) {
628                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
629         } else if (ret == 0) {
630                 return NT_STATUS_NO_SUCH_ALIAS;
631         } else if (ret > 1) {
632                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
633         }
634
635         ret = samdb_delete(state->sam_ldb, mem_ctx, msgs[0]->dn);
636         if (ret != 0) {
637                 DEBUG(0,("Failed to delete alias record %s: %s\n", msgs[0]->dn, ldb_errstring(state->sam_ldb)));
638                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
639         }
640
641         return NT_STATUS_OK;
642 }
643
644 static NTSTATUS samsync_ldb_handle_alias_member(TALLOC_CTX *mem_ctx,
645                                                 struct samsync_ldb_state *state,
646                                                 struct creds_CredentialState *creds,
647                                                 enum netr_SamDatabaseID database,
648                                                 struct netr_DELTA_ENUM *delta) 
649 {
650         uint32_t rid = delta->delta_id_union.rid;
651         struct netr_DELTA_ALIAS_MEMBER *alias_member = delta->delta_union.alias_member;
652         struct ldb_message *msg;
653         struct ldb_message **msgs;
654         int ret;
655         const char *attrs[] = { NULL };
656         int 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 alias, 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                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
670         } else if (ret == 0) {
671                 return NT_STATUS_NO_SUCH_GROUP;
672         } else if (ret > 1) {
673                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
674         } else {
675                 msg->dn = talloc_steal(msg, msgs[0]->dn);
676         }
677         
678         talloc_free(msgs);
679
680         for (i=0; i<alias_member->sids.num_sids; i++) {
681                 /* search for the group, by rid */
682                 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
683                                    "(objectSid=%s)", 
684                                    ldap_encode_ndr_dom_sid(mem_ctx, alias_member->sids.sids[i].sid)); 
685
686                 if (ret == -1) {
687                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
688                 } else if (ret == 0) {
689                         return NT_STATUS_NO_SUCH_USER;
690                 } else if (ret > 1) {
691                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
692                 } else {
693                         samdb_msg_add_string(state->sam_ldb, mem_ctx, msg, "member", msgs[0]->dn);
694                 }
695         
696                 talloc_free(msgs);
697         }
698
699         ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
700         if (ret != 0) {
701                 DEBUG(0,("Failed to modify group record %s: %s\n", msg->dn, 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_account(TALLOC_CTX *mem_ctx,
709                                         struct samsync_ldb_state *state,
710                                         struct creds_CredentialState *creds,
711                                         enum netr_SamDatabaseID database,
712                                         struct netr_DELTA_ENUM *delta) 
713 {
714         struct dom_sid *sid = delta->delta_id_union.sid;
715         struct netr_DELTA_ACCOUNT *account = delta->delta_union.account;
716
717         struct ldb_message *msg;
718         struct ldb_message **msgs;
719         int ret;
720         const char *attrs[] = { NULL };
721         int i;
722
723         msg = ldb_msg_new(mem_ctx);
724         if (msg == NULL) {
725                 return NT_STATUS_NO_MEMORY;
726         }
727
728         /* search for the account, by sid */
729         ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
730                            "(objectSid=%s)", ldap_encode_ndr_dom_sid(mem_ctx, sid)); 
731
732         if (ret == -1) {
733                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
734         } else if (ret == 0) {
735                 return NT_STATUS_NO_SUCH_USER;
736         } else if (ret > 1) {
737                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
738         } else {
739                 msg->dn = talloc_steal(msg, msgs[0]->dn);
740         }
741
742         for (i=0; i< account->privilege_entries; i++) {
743                 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg, "privilage",
744                                      account->privilege_name[i].string);
745         }
746
747         ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
748         if (ret != 0) {
749                 DEBUG(0,("Failed to modify privilage record %s\n", msg->dn));
750                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
751         }
752
753         return NT_STATUS_OK;
754 }
755
756 static NTSTATUS samsync_ldb_delete_account(TALLOC_CTX *mem_ctx,
757                                         struct samsync_ldb_state *state,
758                                         struct creds_CredentialState *creds,
759                                         enum netr_SamDatabaseID database,
760                                         struct netr_DELTA_ENUM *delta) 
761 {
762         struct dom_sid *sid = delta->delta_id_union.sid;
763
764         struct ldb_message *msg;
765         struct ldb_message **msgs;
766         int ret;
767         const char *attrs[] = { NULL };
768
769         msg = ldb_msg_new(mem_ctx);
770         if (msg == NULL) {
771                 return NT_STATUS_NO_MEMORY;
772         }
773
774         /* search for the account, by sid */
775         ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
776                            "(objectSid=%s)", 
777                            ldap_encode_ndr_dom_sid(mem_ctx, sid)); 
778
779         if (ret == -1) {
780                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
781         } else if (ret == 0) {
782                 return NT_STATUS_NO_SUCH_USER;
783         } else if (ret > 1) {
784                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
785         } else {
786                 msg->dn = talloc_steal(msg, msgs[0]->dn);
787         }
788
789         samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,  
790                              "privilage"); 
791
792         ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
793         if (ret != 0) {
794                 DEBUG(0,("Failed to modify privilage record %s\n", msg->dn));
795                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
796         }
797
798         return NT_STATUS_OK;
799 }
800
801 static NTSTATUS libnet_samsync_ldb_fn(TALLOC_CTX *mem_ctx,              
802                                   void *private,                        
803                                   struct creds_CredentialState *creds,
804                                   enum netr_SamDatabaseID database,
805                                   struct netr_DELTA_ENUM *delta,
806                                   char **error_string)
807 {
808         NTSTATUS nt_status = NT_STATUS_OK;
809         struct samsync_ldb_state *state = private;
810
811         *error_string = NULL;
812         switch (delta->delta_type) {
813         case NETR_DELTA_DOMAIN:
814         {
815                 nt_status = samsync_ldb_handle_domain(mem_ctx, 
816                                                       state,
817                                                       creds,
818                                                       database,
819                                                       delta);
820                 break;
821         }
822         case NETR_DELTA_USER:
823         {
824                 nt_status = samsync_ldb_handle_user(mem_ctx, 
825                                                     state,
826                                                     creds,
827                                                     database,
828                                                     delta);
829                 break;
830         }
831         case NETR_DELTA_DELETE_USER:
832         {
833                 nt_status = samsync_ldb_delete_user(mem_ctx, 
834                                                     state,
835                                                     creds,
836                                                     database,
837                                                     delta);
838                 break;
839         }
840         case NETR_DELTA_GROUP:
841         {
842                 nt_status = samsync_ldb_handle_group(mem_ctx, 
843                                                      state,
844                                                      creds,
845                                                      database,
846                                                      delta);
847                 break;
848         }
849         case NETR_DELTA_DELETE_GROUP:
850         {
851                 nt_status = samsync_ldb_delete_group(mem_ctx, 
852                                                     state,
853                                                     creds,
854                                                     database,
855                                                     delta);
856                 break;
857         }
858         case NETR_DELTA_GROUP_MEMBER:
859         {
860                 nt_status = samsync_ldb_handle_group_member(mem_ctx, 
861                                                             state,
862                                                             creds,
863                                                             database,
864                                                             delta);
865                 break;
866         }
867         case NETR_DELTA_ALIAS:
868         {
869                 nt_status = samsync_ldb_handle_alias(mem_ctx, 
870                                                      state,
871                                                      creds,
872                                                      database,
873                                                      delta);
874                 break;
875         }
876         case NETR_DELTA_DELETE_ALIAS:
877         {
878                 nt_status = samsync_ldb_delete_alias(mem_ctx, 
879                                                     state,
880                                                     creds,
881                                                     database,
882                                                     delta);
883                 break;
884         }
885         case NETR_DELTA_ALIAS_MEMBER:
886         {
887                 nt_status = samsync_ldb_handle_alias_member(mem_ctx, 
888                                                             state,
889                                                             creds,
890                                                             database,
891                                                             delta);
892                 break;
893         }
894         case NETR_DELTA_ACCOUNT:
895         {
896                 nt_status = samsync_ldb_handle_account(mem_ctx, 
897                                                      state,
898                                                      creds,
899                                                      database,
900                                                      delta);
901                 break;
902         }
903         case NETR_DELTA_DELETE_ACCOUNT:
904         {
905                 nt_status = samsync_ldb_delete_account(mem_ctx, 
906                                                     state,
907                                                     creds,
908                                                     database,
909                                                     delta);
910                 break;
911         }
912         default:
913                 /* Can't dump them all right now */
914                 break;
915         }
916         return nt_status;
917 }
918
919 static NTSTATUS libnet_samsync_ldb_netlogon(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, struct libnet_samsync_ldb *r)
920 {
921         NTSTATUS nt_status;
922         struct libnet_SamSync r2;
923         struct samsync_ldb_state *state = talloc(mem_ctx, struct samsync_ldb_state);
924
925         if (!state) {
926                 return NT_STATUS_NO_MEMORY;
927         }
928
929         state->secrets = NULL;
930         state->trusted_domains = NULL;
931
932         state->sam_ldb = samdb_connect(state);
933
934         
935
936         r2.error_string = NULL;
937         r2.delta_fn = libnet_samsync_ldb_fn;
938         r2.fn_ctx = state;
939         r2.machine_account = NULL; /* TODO:  Create a machine account, fill this in, and the delete it */
940         nt_status = libnet_SamSync_netlogon(ctx, state, &r2);
941         r->error_string = r2.error_string;
942
943         if (!NT_STATUS_IS_OK(nt_status)) {
944                 talloc_free(state);
945                 return nt_status;
946         }
947         talloc_free(state);
948         return nt_status;
949 }
950
951
952
953 static NTSTATUS libnet_samsync_ldb_generic(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, struct libnet_samsync_ldb *r)
954 {
955         NTSTATUS nt_status;
956         struct libnet_samsync_ldb r2;
957         r2.level = LIBNET_SAMSYNC_LDB_NETLOGON;
958         r2.error_string = NULL;
959         nt_status = libnet_samsync_ldb(ctx, mem_ctx, &r2);
960         r->error_string = r2.error_string;
961         
962         return nt_status;
963 }
964
965 NTSTATUS libnet_samsync_ldb(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, struct libnet_samsync_ldb *r)
966 {
967         switch (r->level) {
968         case LIBNET_SAMSYNC_LDB_GENERIC:
969                 return libnet_samsync_ldb_generic(ctx, mem_ctx, r);
970         case LIBNET_SAMSYNC_LDB_NETLOGON:
971                 return libnet_samsync_ldb_netlogon(ctx, mem_ctx, r);
972         }
973
974         return NT_STATUS_INVALID_LEVEL;
975 }