s3:libnet: convert libnet_dssync_passdb to only use dbwrap wrapper functions.
[kai/samba.git] / source3 / libnet / libnet_dssync_passdb.c
1 /*
2    Unix SMB/CIFS implementation.
3
4    Copyright (C) Guenther Deschner <gd@samba.org> 2008
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3 of the License, or
9    (at your option) any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with this program.  If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "includes.h"
21 #include "system/passwd.h"
22 #include "libnet/libnet_dssync.h"
23 #include "../libcli/security/security.h"
24 #include "../libds/common/flags.h"
25 #include "../librpc/gen_ndr/ndr_drsuapi.h"
26 #include "util_tdb.h"
27 #include "dbwrap/dbwrap.h"
28 #include "dbwrap/dbwrap_rbt.h"
29 #include "../libds/common/flag_mapping.h"
30 #include "passdb.h"
31
32 /****************************************************************
33 ****************************************************************/
34
35 struct dssync_passdb {
36         struct pdb_methods *methods;
37         struct db_context *all;
38         struct db_context *aliases;
39         struct db_context *groups;
40 };
41
42 struct dssync_passdb_obj {
43         struct dssync_passdb_obj *self;
44         uint32_t type;
45         struct drsuapi_DsReplicaObjectListItemEx *cur;
46         TDB_DATA key;
47         TDB_DATA data;
48         struct db_context *members;
49 };
50
51 struct dssync_passdb_mem {
52         struct dssync_passdb_mem *self;
53         bool active;
54         struct drsuapi_DsReplicaObjectIdentifier3 *cur;
55         struct dssync_passdb_obj *obj;
56         TDB_DATA key;
57         TDB_DATA data;
58 };
59
60 static NTSTATUS dssync_insert_obj(struct dssync_passdb *pctx,
61                                   struct db_context *db,
62                                   struct dssync_passdb_obj *obj)
63 {
64         NTSTATUS status;
65         struct db_record *rec;
66         TDB_DATA value;
67
68         rec = dbwrap_fetch_locked(db, talloc_tos(), obj->key);
69         if (rec == NULL) {
70                 return NT_STATUS_NO_MEMORY;
71         }
72
73         value = dbwrap_record_get_value(rec);
74         if (value.dsize != 0) {
75                 abort();
76         }
77
78         status = dbwrap_record_store(rec, obj->data, TDB_INSERT);
79         if (!NT_STATUS_IS_OK(status)) {
80                 TALLOC_FREE(rec);
81                 return status;
82         }
83         TALLOC_FREE(rec);
84         return NT_STATUS_OK;
85 }
86
87 static struct dssync_passdb_obj *dssync_parse_obj(const TDB_DATA data)
88 {
89         struct dssync_passdb_obj *obj;
90
91         if (data.dsize != sizeof(obj)) {
92                 return NULL;
93         }
94
95         /*
96          * we need to copy the pointer to avoid alignment problems
97          * on some systems.
98          */
99         memcpy(&obj, data.dptr, sizeof(obj));
100
101         return talloc_get_type_abort(obj, struct dssync_passdb_obj);
102 }
103
104 static struct dssync_passdb_obj *dssync_search_obj_by_guid(struct dssync_passdb *pctx,
105                                                            struct db_context *db,
106                                                            const struct GUID *guid)
107 {
108         struct dssync_passdb_obj *obj;
109         TDB_DATA key;
110         TDB_DATA data;
111         NTSTATUS status;
112
113         key = make_tdb_data((const uint8_t *)(const void *)guid,
114                              sizeof(*guid));
115
116         status = dbwrap_fetch(db, talloc_tos(), key, &data);
117         if (!NT_STATUS_IS_OK(status)) {
118                 return NULL;
119         }
120
121         obj = dssync_parse_obj(data);
122         return obj;
123 }
124
125 static NTSTATUS dssync_create_obj(struct dssync_passdb *pctx,
126                                   struct db_context *db,
127                                   uint32_t type,
128                                   struct drsuapi_DsReplicaObjectListItemEx *cur,
129                                   struct dssync_passdb_obj **_obj)
130 {
131         NTSTATUS status;
132         struct dssync_passdb_obj *obj;
133
134         obj = talloc_zero(pctx, struct dssync_passdb_obj);
135         if (obj == NULL) {
136                 return NT_STATUS_NO_MEMORY;
137         }
138         obj->self = obj;
139         obj->cur = cur;
140         obj->type = type;
141         obj->key = make_tdb_data((const uint8_t *)(void *)&cur->object.identifier->guid,
142                                    sizeof(cur->object.identifier->guid));
143         obj->data = make_tdb_data((const uint8_t *)(void *)&obj->self,
144                                   sizeof(obj->self));
145
146         obj->members = db_open_rbt(obj);
147         if (obj->members == NULL) {
148                 return NT_STATUS_NO_MEMORY;
149         }
150
151         status = dssync_insert_obj(pctx, db, obj);
152         if (!NT_STATUS_IS_OK(status)) {
153                 TALLOC_FREE(obj);
154                 return status;
155         }
156         *_obj = obj;
157         return NT_STATUS_OK;
158 }
159
160 static NTSTATUS dssync_insert_mem(struct dssync_passdb *pctx,
161                                   struct dssync_passdb_obj *obj,
162                                   struct dssync_passdb_mem *mem)
163 {
164         NTSTATUS status;
165         struct db_record *rec;
166         TDB_DATA value;
167
168         rec = dbwrap_fetch_locked(obj->members, talloc_tos(), mem->key);
169         if (rec == NULL) {
170                 return NT_STATUS_NO_MEMORY;
171         }
172
173         value = dbwrap_record_get_value(rec);
174         if (value.dsize != 0) {
175                 abort();
176         }
177
178         status = dbwrap_record_store(rec, mem->data, TDB_INSERT);
179         if (!NT_STATUS_IS_OK(status)) {
180                 TALLOC_FREE(rec);
181                 return status;
182         }
183         TALLOC_FREE(rec);
184         return NT_STATUS_OK;
185 }
186
187 static NTSTATUS dssync_create_mem(struct dssync_passdb *pctx,
188                                   struct dssync_passdb_obj *obj,
189                                   bool active,
190                                   struct drsuapi_DsReplicaObjectIdentifier3 *cur,
191                                   struct dssync_passdb_mem **_mem)
192 {
193         NTSTATUS status;
194         struct dssync_passdb_mem *mem;
195
196         mem = talloc_zero(pctx, struct dssync_passdb_mem);
197         if (mem == NULL) {
198                 return NT_STATUS_NO_MEMORY;
199         }
200         mem->self = mem;
201         mem->cur = cur;
202         mem->active = active;
203         mem->obj = NULL;
204         mem->key = make_tdb_data((const uint8_t *)(void *)&cur->guid,
205                                    sizeof(cur->guid));
206         mem->data = make_tdb_data((const uint8_t *)(void *)&mem->self,
207                                   sizeof(mem->self));
208
209         status = dssync_insert_mem(pctx, obj, mem);
210         if (!NT_STATUS_IS_OK(status)) {
211                 TALLOC_FREE(obj);
212                 return status;
213         }
214         *_mem = mem;
215         return NT_STATUS_OK;
216 }
217
218 static struct dssync_passdb_mem *dssync_parse_mem(const TDB_DATA data)
219 {
220         struct dssync_passdb_mem *mem;
221
222         if (data.dsize != sizeof(mem)) {
223                 return NULL;
224         }
225
226         /*
227          * we need to copy the pointer to avoid alignment problems
228          * on some systems.
229          */
230         memcpy(&mem, data.dptr, sizeof(mem));
231
232         return talloc_get_type_abort(mem, struct dssync_passdb_mem);
233 }
234
235 static NTSTATUS passdb_startup(struct dssync_context *ctx, TALLOC_CTX *mem_ctx,
236                                struct replUpToDateVectorBlob **pold_utdv)
237 {
238         NTSTATUS status;
239         struct dssync_passdb *pctx;
240
241         pctx = talloc_zero(mem_ctx, struct dssync_passdb);
242         if (pctx == NULL) {
243                 return NT_STATUS_NO_MEMORY;
244         }
245
246         if (ctx->output_filename) {
247                 status = make_pdb_method_name(&pctx->methods, ctx->output_filename);
248         } else {
249                 status = make_pdb_method_name(&pctx->methods, lp_passdb_backend());
250         }
251
252         if (!NT_STATUS_IS_OK(status)) {
253                 return status;
254         }
255
256         pctx->all = db_open_rbt(pctx);
257         if (pctx->all == NULL) {
258                 return NT_STATUS_NO_MEMORY;
259         }
260         pctx->aliases = db_open_rbt(pctx);
261         if (pctx->aliases == NULL) {
262                 return NT_STATUS_NO_MEMORY;
263         }
264         pctx->groups = db_open_rbt(pctx);
265         if (pctx->groups == NULL) {
266                 return NT_STATUS_NO_MEMORY;
267         }
268
269         ctx->private_data = pctx;
270
271         return status;
272 }
273
274 /****************************************************************
275 ****************************************************************/
276
277 struct dssync_passdb_traverse_amembers {
278         struct dssync_context *ctx;
279         struct dssync_passdb_obj *obj;
280         const char *name;
281         uint32_t idx;
282 };
283
284 struct dssync_passdb_traverse_aliases {
285         struct dssync_context *ctx;
286         const char *name;
287         uint32_t idx;
288 };
289
290 static int dssync_passdb_traverse_amembers(struct db_record *rec,
291                                            void *private_data)
292 {
293         struct dssync_passdb_traverse_amembers *state =
294                 (struct dssync_passdb_traverse_amembers *)private_data;
295         struct dssync_passdb *pctx =
296                 talloc_get_type_abort(state->ctx->private_data,
297                 struct dssync_passdb);
298         struct dssync_passdb_mem *mem;
299         NTSTATUS status;
300         struct dom_sid alias_sid;
301         struct dom_sid member_sid;
302         const char *member_dn;
303         size_t num_members;
304         size_t i;
305         struct dom_sid *members;
306         bool is_member = false;
307         const char *action;
308         TDB_DATA value;
309
310         state->idx++;
311
312         alias_sid = state->obj->cur->object.identifier->sid;
313
314         value = dbwrap_record_get_value(rec);
315         mem = dssync_parse_mem(value);
316         if (mem == NULL) {
317                 return -1;
318         }
319
320         member_sid = mem->cur->sid;
321         member_dn = mem->cur->dn;
322
323         mem->obj = dssync_search_obj_by_guid(pctx, pctx->all, &mem->cur->guid);
324         if (mem->obj == NULL) {
325                 DEBUG(0,("alias[%s] member[%s] can't resolve member - ignoring\n",
326                          sid_string_dbg(&alias_sid),
327                          is_null_sid(&member_sid)?
328                          sid_string_dbg(&member_sid):
329                          member_dn));
330                 return 0;
331         }
332
333         switch (mem->obj->type) {
334         case ATYPE_DISTRIBUTION_LOCAL_GROUP:
335         case ATYPE_DISTRIBUTION_GLOBAL_GROUP:
336                 DEBUG(0, ("alias[%s] ignore distribution group [%s]\n",
337                           sid_string_dbg(&alias_sid),
338                           member_dn));
339                 return 0;
340         default:
341                 break;
342         }
343
344         DEBUG(0,("alias[%s] member[%s]\n",
345                  sid_string_dbg(&alias_sid),
346                  sid_string_dbg(&member_sid)));
347
348         status = pdb_enum_aliasmem(&alias_sid, talloc_tos(),
349                                    &members, &num_members);
350         if (!NT_STATUS_IS_OK(status)) {
351                 DEBUG(0, ("Could not find current alias members %s - %s\n",
352                           sid_string_dbg(&alias_sid),
353                           nt_errstr(status)));
354                 return -1;
355         }
356
357         for (i=0; i < num_members; i++) {
358                 bool match;
359
360                 match = dom_sid_equal(&members[i], &member_sid);
361                 if (match) {
362                         is_member = true;
363                         break;
364                 }
365         }
366
367         status = NT_STATUS_OK;
368         action = "none";
369         if (!is_member && mem->active) {
370                 action = "add";
371                 pdb_add_aliasmem(&alias_sid, &member_sid);
372         } else if (is_member && !mem->active) {
373                 action = "delete";
374                 pdb_del_aliasmem(&alias_sid, &member_sid);
375         }
376         if (!NT_STATUS_IS_OK(status)) {
377                 DEBUG(0, ("Could not %s %s as alias members of %s - %s\n",
378                           action,
379                           sid_string_dbg(&member_sid),
380                           sid_string_dbg(&alias_sid),
381                           nt_errstr(status)));
382                 return -1;
383         }
384
385         return 0;
386 }
387
388 static int dssync_passdb_traverse_aliases(struct db_record *rec,
389                                           void *private_data)
390 {
391         struct dssync_passdb_traverse_aliases *state =
392                 (struct dssync_passdb_traverse_aliases *)private_data;
393         struct dssync_passdb *pctx =
394                 talloc_get_type_abort(state->ctx->private_data,
395                 struct dssync_passdb);
396         struct dssync_passdb_traverse_amembers mstate;
397         struct dssync_passdb_obj *obj;
398         TDB_DATA value;
399         NTSTATUS status;
400
401         state->idx++;
402         if (pctx->methods == NULL) {
403                 return -1;
404         }
405
406         value = dbwrap_record_get_value(rec);
407         obj = dssync_parse_obj(value);
408         if (obj == NULL) {
409                 return -1;
410         }
411
412         ZERO_STRUCT(mstate);
413         mstate.ctx = state->ctx;
414         mstate.name = "members";
415         mstate.obj = obj;
416         status = dbwrap_traverse_read(obj->members,
417                                       dssync_passdb_traverse_amembers,
418                                       &mstate, NULL);
419         if (!NT_STATUS_IS_OK(status)) {
420                 return -1;
421         }
422
423         return 0;
424 }
425
426 struct dssync_passdb_traverse_gmembers {
427         struct dssync_context *ctx;
428         struct dssync_passdb_obj *obj;
429         const char *name;
430         uint32_t idx;
431 };
432
433 struct dssync_passdb_traverse_groups {
434         struct dssync_context *ctx;
435         const char *name;
436         uint32_t idx;
437 };
438
439 static int dssync_passdb_traverse_gmembers(struct db_record *rec,
440                                            void *private_data)
441 {
442         struct dssync_passdb_traverse_gmembers *state =
443                 (struct dssync_passdb_traverse_gmembers *)private_data;
444         struct dssync_passdb *pctx =
445                 talloc_get_type_abort(state->ctx->private_data,
446                 struct dssync_passdb);
447         struct dssync_passdb_mem *mem;
448         NTSTATUS status;
449         char *nt_member = NULL;
450         char **unix_members;
451         struct dom_sid group_sid;
452         struct dom_sid member_sid;
453         struct samu *member = NULL;
454         const char *member_dn = NULL;
455         GROUP_MAP map;
456         struct group *grp;
457         uint32_t rid;
458         bool is_unix_member = false;
459         TDB_DATA value;
460
461         state->idx++;
462
463         group_sid = state->obj->cur->object.identifier->sid;
464
465         status = dom_sid_split_rid(talloc_tos(), &group_sid, NULL, &rid);
466         if (!NT_STATUS_IS_OK(status)) {
467                 return -1;
468         }
469
470         value = dbwrap_record_get_value(rec);
471
472         mem = dssync_parse_mem(value);
473         if (mem == NULL) {
474                 return -1;
475         }
476
477         member_sid = mem->cur->sid;
478         member_dn = mem->cur->dn;
479
480         mem->obj = dssync_search_obj_by_guid(pctx, pctx->all, &mem->cur->guid);
481         if (mem->obj == NULL) {
482                 DEBUG(0,("group[%s] member[%s] can't resolve member - ignoring\n",
483                          sid_string_dbg(&group_sid),
484                          is_null_sid(&member_sid)?
485                          sid_string_dbg(&member_sid):
486                          member_dn));
487                 return 0;
488         }
489
490         member_sid = mem->obj->cur->object.identifier->sid;
491         member_dn = mem->obj->cur->object.identifier->dn;
492
493         switch (mem->obj->type) {
494         case ATYPE_SECURITY_LOCAL_GROUP:
495         case ATYPE_SECURITY_GLOBAL_GROUP:
496                 DEBUG(0, ("Group[%s] ignore member group [%s]\n",
497                           sid_string_dbg(&group_sid),
498                           sid_string_dbg(&member_sid)));
499                 return 0;
500
501         case ATYPE_DISTRIBUTION_LOCAL_GROUP:
502         case ATYPE_DISTRIBUTION_GLOBAL_GROUP:
503                 DEBUG(0, ("Group[%s] ignore distribution group [%s]\n",
504                           sid_string_dbg(&group_sid),
505                           member_dn));
506                 return 0;
507         default:
508                 break;
509         }
510
511         if (!get_domain_group_from_sid(group_sid, &map)) {
512                 DEBUG(0, ("Could not find global group %s\n",
513                           sid_string_dbg(&group_sid)));
514                 //return NT_STATUS_NO_SUCH_GROUP;
515                 return -1;
516         }
517
518         if (!(grp = getgrgid(map.gid))) {
519                 DEBUG(0, ("Could not find unix group %lu\n", (unsigned long)map.gid));
520                 //return NT_STATUS_NO_SUCH_GROUP;
521                 return -1;
522         }
523
524         DEBUG(0,("Group members of %s: ", grp->gr_name));
525
526         if ( !(member = samu_new(talloc_tos())) ) {
527                 //return NT_STATUS_NO_MEMORY;
528                 return -1;
529         }
530
531         if (!pdb_getsampwsid(member, &member_sid)) {
532                 DEBUG(1, ("Found bogus group member: (member_sid=%s group=%s)\n",
533                           sid_string_tos(&member_sid), grp->gr_name));
534                 TALLOC_FREE(member);
535                 return -1;
536         }
537
538         if (pdb_get_group_rid(member) == rid) {
539                 DEBUGADD(0,("%s(primary),", pdb_get_username(member)));
540                 TALLOC_FREE(member);
541                 return -1;
542         }
543
544         DEBUGADD(0,("%s,", pdb_get_username(member)));
545         nt_member = talloc_strdup(talloc_tos(), pdb_get_username(member));
546         TALLOC_FREE(member);
547
548         DEBUGADD(0,("\n"));
549
550         unix_members = grp->gr_mem;
551
552         while (*unix_members) {
553                 if (strcmp(*unix_members, nt_member) == 0) {
554                         is_unix_member = true;
555                         break;
556                 }
557                 unix_members += 1;
558         }
559
560         if (!is_unix_member && mem->active) {
561                 smb_add_user_group(grp->gr_name, nt_member);
562         } else if (is_unix_member && !mem->active) {
563                 smb_delete_user_group(grp->gr_name, nt_member);
564         }
565
566         return 0;
567 }
568
569 static int dssync_passdb_traverse_groups(struct db_record *rec,
570                                          void *private_data)
571 {
572         struct dssync_passdb_traverse_groups *state =
573                 (struct dssync_passdb_traverse_groups *)private_data;
574         struct dssync_passdb *pctx =
575                 talloc_get_type_abort(state->ctx->private_data,
576                 struct dssync_passdb);
577         struct dssync_passdb_traverse_gmembers mstate;
578         struct dssync_passdb_obj *obj;
579         TDB_DATA value;
580         NTSTATUS status;
581
582         state->idx++;
583         if (pctx->methods == NULL) {
584                 return -1;
585         }
586
587         value = dbwrap_record_get_value(rec);
588
589         obj = dssync_parse_obj(value);
590         if (obj == NULL) {
591                 return -1;
592         }
593
594         ZERO_STRUCT(mstate);
595         mstate.ctx = state->ctx;
596         mstate.name = "members";
597         mstate.obj = obj;
598         status = dbwrap_traverse_read(obj->members,
599                                       dssync_passdb_traverse_gmembers,
600                                       &mstate, NULL);
601         if (!NT_STATUS_IS_OK(status)) {
602                 return -1;
603         }
604
605         return 0;
606 }
607
608 static NTSTATUS passdb_finish(struct dssync_context *ctx, TALLOC_CTX *mem_ctx,
609                               struct replUpToDateVectorBlob *new_utdv)
610 {
611         struct dssync_passdb *pctx =
612                 talloc_get_type_abort(ctx->private_data,
613                 struct dssync_passdb);
614         struct dssync_passdb_traverse_aliases astate;
615         struct dssync_passdb_traverse_groups gstate;
616         NTSTATUS status;
617
618         ZERO_STRUCT(astate);
619         astate.ctx = ctx;
620         astate.name = "aliases";
621         status = dbwrap_traverse_read(pctx->aliases,
622                                       dssync_passdb_traverse_aliases,
623                                       &astate, NULL);
624         if (!NT_STATUS_IS_OK(status)) {
625                 return NT_STATUS_INTERNAL_ERROR;
626         }
627
628         ZERO_STRUCT(gstate);
629         gstate.ctx = ctx;
630         gstate.name = "groups";
631         status = dbwrap_traverse_read(pctx->groups,
632                                       dssync_passdb_traverse_groups,
633                                       &gstate, NULL);
634         if (!NT_STATUS_IS_OK(status)) {
635                 return NT_STATUS_INTERNAL_ERROR;
636         }
637
638         TALLOC_FREE(pctx->methods);
639         TALLOC_FREE(pctx);
640
641         return NT_STATUS_OK;
642 }
643
644 /****************************************************************
645 ****************************************************************/
646
647 static NTSTATUS smb_create_user(TALLOC_CTX *mem_ctx,
648                                 uint32_t acct_flags,
649                                 const char *account,
650                                 struct passwd **passwd_p)
651 {
652         struct passwd *passwd;
653         char *add_script = NULL;
654
655         passwd = Get_Pwnam_alloc(mem_ctx, account);
656         if (passwd) {
657                 *passwd_p = passwd;
658                 return NT_STATUS_OK;
659         }
660
661         /* Create appropriate user */
662         if (acct_flags & ACB_NORMAL) {
663                 add_script = talloc_strdup(mem_ctx, lp_adduser_script());
664         } else if ( (acct_flags & ACB_WSTRUST) ||
665                     (acct_flags & ACB_SVRTRUST) ||
666                     (acct_flags & ACB_DOMTRUST) ) {
667                 add_script = talloc_strdup(mem_ctx, lp_addmachine_script());
668         } else {
669                 DEBUG(1, ("Unknown user type: %s\n",
670                           pdb_encode_acct_ctrl(acct_flags, NEW_PW_FORMAT_SPACE_PADDED_LEN)));
671                 return NT_STATUS_UNSUCCESSFUL;
672         }
673
674         if (!add_script) {
675                 return NT_STATUS_NO_MEMORY;
676         }
677
678         if (*add_script) {
679                 int add_ret;
680                 add_script = talloc_all_string_sub(mem_ctx, add_script,
681                                                    "%u", account);
682                 if (!add_script) {
683                         return NT_STATUS_NO_MEMORY;
684                 }
685                 add_ret = smbrun(add_script, NULL);
686                 DEBUG(add_ret ? 0 : 1,("fetch_account: Running the command `%s' "
687                          "gave %d\n", add_script, add_ret));
688                 if (add_ret == 0) {
689                         smb_nscd_flush_user_cache();
690                 }
691         }
692
693         /* try and find the possible unix account again */
694         passwd = Get_Pwnam_alloc(mem_ctx, account);
695         if (!passwd) {
696                 return NT_STATUS_NO_SUCH_USER;
697         }
698
699         *passwd_p = passwd;
700
701         return NT_STATUS_OK;
702 }
703
704 static struct drsuapi_DsReplicaAttribute *find_drsuapi_attr(
705                         const struct drsuapi_DsReplicaObjectListItemEx *cur,
706                         uint32_t attid)
707 {
708         int i = 0;
709
710         for (i = 0; i < cur->object.attribute_ctr.num_attributes; i++) {
711                 struct drsuapi_DsReplicaAttribute *attr;
712
713                 attr = &cur->object.attribute_ctr.attributes[i];
714
715                 if (attr->attid == attid) {
716                         return attr;
717                 }
718         }
719
720         return NULL;
721 }
722
723 static NTSTATUS find_drsuapi_attr_string(TALLOC_CTX *mem_ctx,
724                                          const struct drsuapi_DsReplicaObjectListItemEx *cur,
725                                          uint32_t attid,
726                                          uint32_t *_count,
727                                          char ***_array)
728 {
729         struct drsuapi_DsReplicaAttribute *attr;
730         char **array;
731         uint32_t a;
732
733         attr = find_drsuapi_attr(cur, attid);
734         if (attr == NULL) {
735                 return NT_STATUS_PROPSET_NOT_FOUND;
736         }
737
738         array = talloc_array(mem_ctx, char *, attr->value_ctr.num_values);
739         if (array == NULL) {
740                 return NT_STATUS_NO_MEMORY;
741         }
742
743         for (a = 0; a < attr->value_ctr.num_values; a++) {
744                 const DATA_BLOB *blob;
745                 ssize_t ret;
746
747                 blob = attr->value_ctr.values[a].blob;
748
749                 if (blob == NULL) {
750                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
751                 }
752
753                 ret = pull_string_talloc(array, NULL, 0, &array[a],
754                                          blob->data, blob->length,
755                                          STR_UNICODE);
756                 if (ret == -1) {
757                         //TODO
758                         return NT_STATUS_INTERNAL_ERROR;
759                 }
760         }
761
762         *_count = attr->value_ctr.num_values;
763         *_array = array;
764         return NT_STATUS_OK;
765 }
766
767 static NTSTATUS find_drsuapi_attr_int32(TALLOC_CTX *mem_ctx,
768                                         const struct drsuapi_DsReplicaObjectListItemEx *cur,
769                                         uint32_t attid,
770                                         uint32_t *_count,
771                                         int32_t **_array)
772 {
773         struct drsuapi_DsReplicaAttribute *attr;
774         int32_t *array;
775         uint32_t a;
776
777         attr = find_drsuapi_attr(cur, attid);
778         if (attr == NULL) {
779                 return NT_STATUS_PROPSET_NOT_FOUND;
780         }
781
782         array = talloc_array(mem_ctx, int32_t, attr->value_ctr.num_values);
783         if (array == NULL) {
784                 return NT_STATUS_NO_MEMORY;
785         }
786
787         for (a = 0; a < attr->value_ctr.num_values; a++) {
788                 const DATA_BLOB *blob;
789
790                 blob = attr->value_ctr.values[a].blob;
791
792                 if (blob == NULL) {
793                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
794                 }
795
796                 if (blob->length != 4) {
797                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
798                 }
799
800                 array[a] = IVAL(blob->data, 0);
801         }
802
803         *_count = attr->value_ctr.num_values;
804         *_array = array;
805         return NT_STATUS_OK;
806 }
807
808 static NTSTATUS find_drsuapi_attr_blob(TALLOC_CTX *mem_ctx,
809                                        const struct drsuapi_DsReplicaObjectListItemEx *cur,
810                                        uint32_t attid,
811                                        uint32_t *_count,
812                                        DATA_BLOB **_array)
813 {
814         struct drsuapi_DsReplicaAttribute *attr;
815         DATA_BLOB *array;
816         uint32_t a;
817
818         attr = find_drsuapi_attr(cur, attid);
819         if (attr == NULL) {
820                 return NT_STATUS_PROPSET_NOT_FOUND;
821         }
822
823         array = talloc_array(mem_ctx, DATA_BLOB, attr->value_ctr.num_values);
824         if (array == NULL) {
825                 return NT_STATUS_NO_MEMORY;
826         }
827
828         for (a = 0; a < attr->value_ctr.num_values; a++) {
829                 const DATA_BLOB *blob;
830
831                 blob = attr->value_ctr.values[a].blob;
832
833                 if (blob == NULL) {
834                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
835                 }
836
837                 array[a] = data_blob_talloc(array, blob->data, blob->length);
838                 if (array[a].length != blob->length) {
839                         return NT_STATUS_NO_MEMORY;
840                 }
841         }
842         *_count = attr->value_ctr.num_values;
843         *_array = array;
844         return NT_STATUS_OK;
845 }
846
847 static NTSTATUS find_drsuapi_attr_int64(TALLOC_CTX *mem_ctx,
848                                         const struct drsuapi_DsReplicaObjectListItemEx *cur,
849                                         uint32_t attid,
850                                         uint32_t *_count,
851                                         int64_t **_array)
852 {
853         struct drsuapi_DsReplicaAttribute *attr;
854         int64_t *array;
855         uint32_t a;
856
857         attr = find_drsuapi_attr(cur, attid);
858         if (attr == NULL) {
859                 return NT_STATUS_PROPSET_NOT_FOUND;
860         }
861
862         array = talloc_array(mem_ctx, int64_t, attr->value_ctr.num_values);
863         if (array == NULL) {
864                 return NT_STATUS_NO_MEMORY;
865         }
866
867         for (a = 0; a < attr->value_ctr.num_values; a++) {
868                 const DATA_BLOB *blob;
869
870                 blob = attr->value_ctr.values[a].blob;
871
872                 if (blob == NULL) {
873                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
874                 }
875
876                 if (blob->length != 8) {
877                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
878                 }
879
880                 array[a] = BVAL(blob->data, 0);
881         }
882         *_count = attr->value_ctr.num_values;
883         *_array = array;
884         return NT_STATUS_OK;
885 }
886
887 static NTSTATUS find_drsuapi_attr_dn(TALLOC_CTX *mem_ctx,
888                                      const struct drsuapi_DsReplicaObjectListItemEx *cur,
889                                      uint32_t attid,
890                                      uint32_t *_count,
891                                      struct drsuapi_DsReplicaObjectIdentifier3 **_array)
892 {
893         struct drsuapi_DsReplicaAttribute *attr;
894         struct drsuapi_DsReplicaObjectIdentifier3 *array;
895         uint32_t a;
896
897         attr = find_drsuapi_attr(cur, attid);
898         if (attr == NULL) {
899                 return NT_STATUS_PROPSET_NOT_FOUND;
900         }
901
902         array = talloc_array(mem_ctx,
903                              struct drsuapi_DsReplicaObjectIdentifier3,
904                              attr->value_ctr.num_values);
905         if (array == NULL) {
906                 return NT_STATUS_NO_MEMORY;
907         }
908
909         for (a = 0; a < attr->value_ctr.num_values; a++) {
910                 const DATA_BLOB *blob;
911                 enum ndr_err_code ndr_err;
912                 NTSTATUS status;
913
914                 blob = attr->value_ctr.values[a].blob;
915
916                 if (blob == NULL) {
917                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
918                 }
919
920                 /* windows sometimes sends an extra two pad bytes here */
921                 ndr_err = ndr_pull_struct_blob(blob, array, &array[a],
922                                 (ndr_pull_flags_fn_t)ndr_pull_drsuapi_DsReplicaObjectIdentifier3);
923                 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
924                         status = ndr_map_error2ntstatus(ndr_err);
925                         return status;
926                 }
927         }
928         *_count = attr->value_ctr.num_values;
929         *_array = array;
930         return NT_STATUS_OK;
931 }
932
933 #define GET_BLOB_EX(attr, needed) do { \
934         NTSTATUS _status; \
935         uint32_t _cnt; \
936         DATA_BLOB *_vals = NULL; \
937         attr = data_blob_null; \
938         _status = find_drsuapi_attr_blob(mem_ctx, cur, \
939                                          DRSUAPI_ATTID_ ## attr, \
940                                          &_cnt, &_vals); \
941         if (NT_STATUS_EQUAL(_status, NT_STATUS_PROPSET_NOT_FOUND)) { \
942                 if (!needed) { \
943                         _status = NT_STATUS_OK; \
944                         _cnt = 0; \
945                 } \
946         } \
947         if (!NT_STATUS_IS_OK(_status)) { \
948                 DEBUG(0,(__location__ "attr[%s] %s\n", \
949                         #attr, nt_errstr(_status))); \
950                 return _status; \
951         } \
952         if (_cnt == 0) { \
953                 if (needed) { \
954                         talloc_free(_vals); \
955                         DEBUG(0,(__location__ "attr[%s] count[%u]\n", #attr, _cnt)); \
956                         return NT_STATUS_OBJECT_NAME_NOT_FOUND; \
957                 } \
958         } else if (_cnt > 1) { \
959                 talloc_free(_vals); \
960                 DEBUG(0,(__location__ "attr[%s] count[%u]\n", #attr, _cnt)); \
961                 return NT_STATUS_INTERNAL_DB_CORRUPTION; \
962         } else { \
963                 attr = _vals[0]; \
964                 (void)talloc_steal(mem_ctx, _vals[0].data); \
965         } \
966         talloc_free(_vals); \
967 } while(0)
968
969 #define GET_STRING_EX(attr, needed) do { \
970         NTSTATUS _status; \
971         uint32_t _cnt; \
972         char **_vals = NULL; \
973         attr = NULL; \
974         _status = find_drsuapi_attr_string(mem_ctx, cur, \
975                                            DRSUAPI_ATTID_ ## attr, \
976                                            &_cnt, &_vals); \
977         if (NT_STATUS_EQUAL(_status, NT_STATUS_PROPSET_NOT_FOUND)) { \
978                 if (!needed) { \
979                         _status = NT_STATUS_OK; \
980                         _cnt = 0; \
981                 } \
982         } \
983         if (!NT_STATUS_IS_OK(_status)) { \
984                 DEBUG(0,(__location__ "attr[%s] %s\n", \
985                         #attr, nt_errstr(_status))); \
986                 return _status; \
987         } \
988         if (_cnt == 0) { \
989                 if (needed) { \
990                         talloc_free(_vals); \
991                         DEBUG(0,(__location__ "attr[%s] count[%u]\n", #attr, _cnt)); \
992                         return NT_STATUS_OBJECT_NAME_NOT_FOUND; \
993                 } \
994         } else if (_cnt > 1) { \
995                 talloc_free(_vals); \
996                 DEBUG(0,(__location__ "attr[%s] count[%u]\n", #attr, _cnt)); \
997                 return NT_STATUS_INTERNAL_DB_CORRUPTION; \
998         } else { \
999                 attr = talloc_move(mem_ctx, &_vals[0]); \
1000         } \
1001         talloc_free(_vals); \
1002 } while(0)
1003
1004 #define GET_UINT32_EX(attr, needed) do { \
1005         NTSTATUS _status; \
1006         uint32_t _cnt; \
1007         int32_t*_vals = NULL; \
1008         attr = 0; \
1009         _status = find_drsuapi_attr_int32(mem_ctx, cur, \
1010                                           DRSUAPI_ATTID_ ## attr, \
1011                                           &_cnt, &_vals); \
1012         if (NT_STATUS_EQUAL(_status, NT_STATUS_PROPSET_NOT_FOUND)) { \
1013                 if (!needed) { \
1014                         _status = NT_STATUS_OK; \
1015                         _cnt = 0; \
1016                 } \
1017         } \
1018         if (!NT_STATUS_IS_OK(_status)) { \
1019                 DEBUG(0,(__location__ "attr[%s] %s\n", \
1020                         #attr, nt_errstr(_status))); \
1021                 return _status; \
1022         } \
1023         if (_cnt == 0) { \
1024                 if (needed) { \
1025                         talloc_free(_vals); \
1026                         DEBUG(0,(__location__ "attr[%s] count[%u]\n", #attr, _cnt)); \
1027                         return NT_STATUS_OBJECT_NAME_NOT_FOUND; \
1028                 } \
1029         } else if (_cnt > 1) { \
1030                 talloc_free(_vals); \
1031                 DEBUG(0,(__location__ "attr[%s] count[%u]\n", #attr, _cnt)); \
1032                 return NT_STATUS_INTERNAL_DB_CORRUPTION; \
1033         } else { \
1034                 attr = (uint32_t)_vals[0]; \
1035         } \
1036         talloc_free(_vals); \
1037 } while(0)
1038
1039 #define GET_UINT64_EX(attr, needed) do { \
1040         NTSTATUS _status; \
1041         uint32_t _cnt; \
1042         int64_t *_vals = NULL; \
1043         attr = 0; \
1044         _status = find_drsuapi_attr_int64(mem_ctx, cur, \
1045                                           DRSUAPI_ATTID_ ## attr, \
1046                                           &_cnt, &_vals); \
1047         if (NT_STATUS_EQUAL(_status, NT_STATUS_PROPSET_NOT_FOUND)) { \
1048                 if (!needed) { \
1049                         _status = NT_STATUS_OK; \
1050                         _cnt = 0; \
1051                 } \
1052         } \
1053         if (!NT_STATUS_IS_OK(_status)) { \
1054                 DEBUG(0,(__location__ "attr[%s] %s\n", \
1055                         #attr, nt_errstr(_status))); \
1056                 return _status; \
1057         } \
1058         if (_cnt == 0) { \
1059                 if (needed) { \
1060                         talloc_free(_vals); \
1061                         DEBUG(0,(__location__ "attr[%s] count[%u]\n", #attr, _cnt)); \
1062                         return NT_STATUS_OBJECT_NAME_NOT_FOUND; \
1063                 } \
1064         } else if (_cnt > 1) { \
1065                 talloc_free(_vals); \
1066                 DEBUG(0,(__location__ "attr[%s] count[%u]\n", #attr, _cnt)); \
1067                 return NT_STATUS_INTERNAL_DB_CORRUPTION; \
1068         } else { \
1069                 attr = (uint64_t)_vals[0]; \
1070         } \
1071         talloc_free(_vals); \
1072 } while(0)
1073
1074 #define GET_BLOB(attr) GET_BLOB_EX(attr, false)
1075 #define GET_STRING(attr) GET_STRING_EX(attr, false)
1076 #define GET_UINT32(attr) GET_UINT32_EX(attr, false)
1077 #define GET_UINT64(attr) GET_UINT64_EX(attr, false)
1078
1079 /* Convert a struct samu_DELTA to a struct samu. */
1080 #define STRING_CHANGED (old_string && !new_string) ||\
1081                     (!old_string && new_string) ||\
1082                 (old_string && new_string && (strcmp(old_string, new_string) != 0))
1083
1084 #define STRING_CHANGED_NC(s1,s2) ((s1) && !(s2)) ||\
1085                     (!(s1) && (s2)) ||\
1086                 ((s1) && (s2) && (strcmp((s1), (s2)) != 0))
1087
1088 /****************************************************************
1089 ****************************************************************/
1090
1091 static NTSTATUS sam_account_from_object(struct samu *account,
1092                                 struct drsuapi_DsReplicaObjectListItemEx *cur)
1093 {
1094         TALLOC_CTX *mem_ctx = account;
1095         const char *old_string, *new_string;
1096         time_t unix_time, stored_time;
1097         uchar zero_buf[16];
1098         NTSTATUS status;
1099
1100         NTTIME lastLogon;
1101         NTTIME lastLogoff;
1102         NTTIME pwdLastSet;
1103         NTTIME accountExpires;
1104         const char *sAMAccountName;
1105         const char *displayName;
1106         const char *homeDirectory;
1107         const char *homeDrive;
1108         const char *scriptPath;
1109         const char *profilePath;
1110         const char *description;
1111         const char *userWorkstations;
1112         const char *comment;
1113         DATA_BLOB userParameters;
1114         struct dom_sid objectSid;
1115         uint32_t primaryGroupID;
1116         uint32_t userAccountControl;
1117         DATA_BLOB logonHours;
1118         uint32_t badPwdCount;
1119         uint32_t logonCount;
1120         DATA_BLOB unicodePwd;
1121         DATA_BLOB dBCSPwd;
1122
1123         uint32_t rid = 0;
1124         uint32_t acct_flags;
1125         uint32_t units_per_week;
1126
1127         memset(zero_buf, '\0', sizeof(zero_buf));
1128
1129         objectSid = cur->object.identifier->sid;
1130         GET_STRING_EX(sAMAccountName, true);
1131         DEBUG(0,("sam_account_from_object(%s, %s) start\n",
1132                  sAMAccountName, sid_string_dbg(&objectSid)));
1133         GET_UINT64(lastLogon);
1134         GET_UINT64(lastLogoff);
1135         GET_UINT64(pwdLastSet);
1136         GET_UINT64(accountExpires);
1137         GET_STRING(displayName);
1138         GET_STRING(homeDirectory);
1139         GET_STRING(homeDrive);
1140         GET_STRING(scriptPath);
1141         GET_STRING(profilePath);
1142         GET_STRING(description);
1143         GET_STRING(userWorkstations);
1144         GET_STRING(comment);
1145         GET_BLOB(userParameters);
1146         GET_UINT32(primaryGroupID);
1147         GET_UINT32(userAccountControl);
1148         GET_BLOB(logonHours);
1149         GET_UINT32(badPwdCount);
1150         GET_UINT32(logonCount);
1151         GET_BLOB(unicodePwd);
1152         GET_BLOB(dBCSPwd);
1153
1154         status = dom_sid_split_rid(mem_ctx, &objectSid, NULL, &rid);
1155         if (!NT_STATUS_IS_OK(status)) {
1156                 return status;
1157         }
1158         acct_flags = ds_uf2acb(userAccountControl);
1159
1160         /* Username, fullname, home dir, dir drive, logon script, acct
1161            desc, workstations, profile. */
1162
1163         if (sAMAccountName) {
1164                 old_string = pdb_get_nt_username(account);
1165                 new_string = sAMAccountName;
1166
1167                 if (STRING_CHANGED) {
1168                         pdb_set_nt_username(account, new_string, PDB_CHANGED);
1169                 }
1170
1171                 /* Unix username is the same - for sanity */
1172                 old_string = pdb_get_username( account );
1173                 if (STRING_CHANGED) {
1174                         pdb_set_username(account, new_string, PDB_CHANGED);
1175                 }
1176         }
1177
1178         if (displayName) {
1179                 old_string = pdb_get_fullname(account);
1180                 new_string = displayName;
1181
1182                 if (STRING_CHANGED)
1183                         pdb_set_fullname(account, new_string, PDB_CHANGED);
1184         }
1185
1186         if (homeDirectory) {
1187                 old_string = pdb_get_homedir(account);
1188                 new_string = homeDirectory;
1189
1190                 if (STRING_CHANGED)
1191                         pdb_set_homedir(account, new_string, PDB_CHANGED);
1192         }
1193
1194         if (homeDrive) {
1195                 old_string = pdb_get_dir_drive(account);
1196                 new_string = homeDrive;
1197
1198                 if (STRING_CHANGED)
1199                         pdb_set_dir_drive(account, new_string, PDB_CHANGED);
1200         }
1201
1202         if (scriptPath) {
1203                 old_string = pdb_get_logon_script(account);
1204                 new_string = scriptPath;
1205
1206                 if (STRING_CHANGED)
1207                         pdb_set_logon_script(account, new_string, PDB_CHANGED);
1208         }
1209
1210         if (description) {
1211                 old_string = pdb_get_acct_desc(account);
1212                 new_string = description;
1213
1214                 if (STRING_CHANGED)
1215                         pdb_set_acct_desc(account, new_string, PDB_CHANGED);
1216         }
1217
1218         if (userWorkstations) {
1219                 old_string = pdb_get_workstations(account);
1220                 new_string = userWorkstations;
1221
1222                 if (STRING_CHANGED)
1223                         pdb_set_workstations(account, new_string, PDB_CHANGED);
1224         }
1225
1226         if (profilePath) {
1227                 old_string = pdb_get_profile_path(account);
1228                 new_string = profilePath;
1229
1230                 if (STRING_CHANGED)
1231                         pdb_set_profile_path(account, new_string, PDB_CHANGED);
1232         }
1233
1234         if (userParameters.data) {
1235                 char *newstr;
1236                 old_string = pdb_get_munged_dial(account);
1237                 newstr = (userParameters.length == 0) ? NULL :
1238                         base64_encode_data_blob(talloc_tos(), userParameters);
1239
1240                 if (STRING_CHANGED_NC(old_string, newstr))
1241                         pdb_set_munged_dial(account, newstr, PDB_CHANGED);
1242                 TALLOC_FREE(newstr);
1243         }
1244
1245         /* User and group sid */
1246         if (rid != 0 && pdb_get_user_rid(account) != rid) {
1247                 pdb_set_user_sid_from_rid(account, rid, PDB_CHANGED);
1248         }
1249         if (primaryGroupID != 0 && pdb_get_group_rid(account) != primaryGroupID) {
1250                 pdb_set_group_sid_from_rid(account, primaryGroupID, PDB_CHANGED);
1251         }
1252
1253         /* Logon and password information */
1254         if (!nt_time_is_zero(&lastLogon)) {
1255                 unix_time = nt_time_to_unix(lastLogon);
1256                 stored_time = pdb_get_logon_time(account);
1257                 if (stored_time != unix_time)
1258                         pdb_set_logon_time(account, unix_time, PDB_CHANGED);
1259         }
1260
1261         if (!nt_time_is_zero(&lastLogoff)) {
1262                 unix_time = nt_time_to_unix(lastLogoff);
1263                 stored_time = pdb_get_logoff_time(account);
1264                 if (stored_time != unix_time)
1265                         pdb_set_logoff_time(account, unix_time,PDB_CHANGED);
1266         }
1267
1268         /* Logon Divs */
1269         units_per_week = logonHours.length * 8;
1270
1271         if (pdb_get_logon_divs(account) != units_per_week) {
1272                 pdb_set_logon_divs(account, units_per_week, PDB_CHANGED);
1273         }
1274
1275         /* Logon Hours Len */
1276         if (units_per_week/8 != pdb_get_hours_len(account)) {
1277                 pdb_set_hours_len(account, units_per_week/8, PDB_CHANGED);
1278         }
1279
1280         /* Logon Hours */
1281         if (logonHours.data) {
1282                 char oldstr[44], newstr[44];
1283                 pdb_sethexhours(oldstr, pdb_get_hours(account));
1284                 pdb_sethexhours(newstr, logonHours.data);
1285                 if (!strequal(oldstr, newstr)) {
1286                         pdb_set_hours(account, logonHours.data,
1287                                       logonHours.length, PDB_CHANGED);
1288                 }
1289         }
1290
1291         if (pdb_get_bad_password_count(account) != badPwdCount)
1292                 pdb_set_bad_password_count(account, badPwdCount, PDB_CHANGED);
1293
1294         if (pdb_get_logon_count(account) != logonCount)
1295                 pdb_set_logon_count(account, logonCount, PDB_CHANGED);
1296
1297         if (!nt_time_is_zero(&pwdLastSet)) {
1298                 unix_time = nt_time_to_unix(pwdLastSet);
1299                 stored_time = pdb_get_pass_last_set_time(account);
1300                 if (stored_time != unix_time)
1301                         pdb_set_pass_last_set_time(account, unix_time, PDB_CHANGED);
1302         } else {
1303                 /* no last set time, make it now */
1304                 pdb_set_pass_last_set_time(account, time(NULL), PDB_CHANGED);
1305         }
1306
1307         if (!nt_time_is_zero(&accountExpires)) {
1308                 unix_time = nt_time_to_unix(accountExpires);
1309                 stored_time = pdb_get_kickoff_time(account);
1310                 if (stored_time != unix_time)
1311                         pdb_set_kickoff_time(account, unix_time, PDB_CHANGED);
1312         }
1313
1314         /* Decode hashes from password hash
1315            Note that win2000 may send us all zeros for the hashes if it doesn't
1316            think this channel is secure enough - don't set the passwords at all
1317            in that case
1318         */
1319         if (dBCSPwd.length == 16 && memcmp(dBCSPwd.data, zero_buf, 16) != 0) {
1320                 pdb_set_lanman_passwd(account, dBCSPwd.data, PDB_CHANGED);
1321         }
1322
1323         if (unicodePwd.length == 16 && memcmp(unicodePwd.data, zero_buf, 16) != 0) {
1324                 pdb_set_nt_passwd(account, unicodePwd.data, PDB_CHANGED);
1325         }
1326
1327         /* TODO: history */
1328
1329         /* TODO: account expiry time */
1330
1331         pdb_set_acct_ctrl(account, acct_flags, PDB_CHANGED);
1332
1333         pdb_set_domain(account, lp_workgroup(), PDB_CHANGED);
1334
1335         DEBUG(0,("sam_account_from_object(%s, %s) done\n",
1336                  sAMAccountName, sid_string_dbg(&objectSid)));
1337         return NT_STATUS_OK;
1338 }
1339
1340 /****************************************************************
1341 ****************************************************************/
1342
1343 static NTSTATUS handle_account_object(struct dssync_passdb *pctx,
1344                                       TALLOC_CTX *mem_ctx,
1345                                       struct dssync_passdb_obj *obj)
1346 {
1347         struct drsuapi_DsReplicaObjectListItemEx *cur = obj->cur;
1348         NTSTATUS status;
1349         fstring account;
1350         struct samu *sam_account=NULL;
1351         GROUP_MAP map;
1352         struct group *grp;
1353         struct dom_sid user_sid;
1354         struct dom_sid group_sid;
1355         struct passwd *passwd = NULL;
1356         uint32_t acct_flags;
1357         uint32_t rid;
1358
1359         const char *sAMAccountName;
1360         uint32_t sAMAccountType;
1361         uint32_t userAccountControl;
1362
1363         user_sid = cur->object.identifier->sid;
1364         GET_STRING_EX(sAMAccountName, true);
1365         GET_UINT32_EX(sAMAccountType, true);
1366         GET_UINT32_EX(userAccountControl, true);
1367
1368         status = dom_sid_split_rid(mem_ctx, &user_sid, NULL, &rid);
1369         if (!NT_STATUS_IS_OK(status)) {
1370                 return status;
1371         }
1372
1373         fstrcpy(account, sAMAccountName);
1374         if (rid == DOMAIN_RID_GUEST) {
1375                 /*
1376                  * pdb_getsampwsid() has special handling for DOMAIN_RID_GUEST
1377                  * that's why we need to ignore it here.
1378                  *
1379                  * pdb_smbpasswd.c also has some DOMAIN_RID_GUEST related
1380                  * code...
1381                  */
1382                 DEBUG(0,("Ignore %s - %s\n", account, sid_string_dbg(&user_sid)));
1383                 return NT_STATUS_OK;
1384         }
1385         DEBUG(0,("Creating account: %s\n", account));
1386
1387         if ( !(sam_account = samu_new(mem_ctx)) ) {
1388                 return NT_STATUS_NO_MEMORY;
1389         }
1390
1391         acct_flags = ds_uf2acb(userAccountControl);
1392         status = smb_create_user(sam_account, acct_flags, account, &passwd);
1393         if (!NT_STATUS_IS_OK(status)) {
1394                 DEBUG(0,("Could not create posix account info for '%s'- %s\n",
1395                         account, nt_errstr(status)));
1396                 TALLOC_FREE(sam_account);
1397                 return status;
1398         }
1399
1400         DEBUG(3, ("Attempting to find SID %s for user %s in the passdb\n",
1401                   sid_string_dbg(&user_sid), account));
1402         if (!pdb_getsampwsid(sam_account, &user_sid)) {
1403                 sam_account_from_object(sam_account, cur);
1404                 DEBUG(3, ("Attempting to add user SID %s for user %s in the passdb\n",
1405                           sid_string_dbg(&user_sid),
1406                           pdb_get_username(sam_account)));
1407                 if (!NT_STATUS_IS_OK(pdb_add_sam_account(sam_account))) {
1408                         DEBUG(1, ("SAM Account for %s failed to be added to the passdb!\n",
1409                                   account));
1410                         TALLOC_FREE(sam_account);
1411                         return NT_STATUS_ACCESS_DENIED;
1412                 }
1413         } else {
1414                 sam_account_from_object(sam_account, cur);
1415                 DEBUG(3, ("Attempting to update user SID %s for user %s in the passdb\n",
1416                           sid_string_dbg(&user_sid),
1417                           pdb_get_username(sam_account)));
1418                 if (!NT_STATUS_IS_OK(pdb_update_sam_account(sam_account))) {
1419                         DEBUG(1, ("SAM Account for %s failed to be updated in the passdb!\n",
1420                                   account));
1421                         TALLOC_FREE(sam_account);
1422                         return NT_STATUS_ACCESS_DENIED;
1423                 }
1424         }
1425
1426         if (pdb_get_group_sid(sam_account) == NULL) {
1427                 TALLOC_FREE(sam_account);
1428                 return NT_STATUS_UNSUCCESSFUL;
1429         }
1430
1431         group_sid = *pdb_get_group_sid(sam_account);
1432
1433         if (!pdb_getgrsid(&map, group_sid)) {
1434                 DEBUG(0, ("Primary group of %s has no mapping!\n",
1435                           pdb_get_username(sam_account)));
1436         } else {
1437                 if (map.gid != passwd->pw_gid) {
1438                         if (!(grp = getgrgid(map.gid))) {
1439                                 DEBUG(0, ("Could not find unix group %lu for user %s (group SID=%s)\n",
1440                                           (unsigned long)map.gid, pdb_get_username(sam_account),
1441                                           sid_string_dbg(&group_sid)));
1442                         } else {
1443                                 smb_set_primary_group(grp->gr_name, pdb_get_username(sam_account));
1444                         }
1445                 }
1446         }
1447
1448         if ( !passwd ) {
1449                 DEBUG(1, ("No unix user for this account (%s), cannot adjust mappings\n",
1450                         pdb_get_username(sam_account)));
1451         }
1452
1453         TALLOC_FREE(sam_account);
1454         return NT_STATUS_OK;
1455 }
1456
1457 /****************************************************************
1458 ****************************************************************/
1459
1460 static NTSTATUS handle_alias_object(struct dssync_passdb *pctx,
1461                                     TALLOC_CTX *mem_ctx,
1462                                     struct dssync_passdb_obj *obj)
1463 {
1464         struct drsuapi_DsReplicaObjectListItemEx *cur = obj->cur;
1465         NTSTATUS status;
1466         fstring name;
1467         fstring comment;
1468         struct group *grp = NULL;
1469         struct dom_sid group_sid;
1470         uint32_t rid = 0;
1471         struct dom_sid *dom_sid = NULL;
1472         fstring sid_string;
1473         GROUP_MAP map;
1474         bool insert = true;
1475
1476         const char *sAMAccountName;
1477         uint32_t sAMAccountType;
1478         uint32_t groupType;
1479         const char *description;
1480         uint32_t i;
1481         uint32_t num_members = 0;
1482         struct drsuapi_DsReplicaObjectIdentifier3 *members = NULL;
1483
1484         group_sid = cur->object.identifier->sid;
1485         GET_STRING_EX(sAMAccountName, true);
1486         GET_UINT32_EX(sAMAccountType, true);
1487         GET_UINT32_EX(groupType, true);
1488         GET_STRING(description);
1489
1490         status = find_drsuapi_attr_dn(obj, cur, DRSUAPI_ATTID_member,
1491                                       &num_members, &members);
1492         if (NT_STATUS_EQUAL(status, NT_STATUS_PROPSET_NOT_FOUND)) {
1493                 status = NT_STATUS_OK;
1494         }
1495         if (!NT_STATUS_IS_OK(status)) {
1496                 return status;
1497         }
1498
1499         fstrcpy(name, sAMAccountName);
1500         fstrcpy(comment, description);
1501
1502         dom_sid_split_rid(mem_ctx, &group_sid, &dom_sid, &rid);
1503
1504         sid_to_fstring(sid_string, &group_sid);
1505         DEBUG(0,("Creating alias[%s] - %s members[%u]\n",
1506                   name, sid_string, num_members));
1507
1508         status = dssync_insert_obj(pctx, pctx->aliases, obj);
1509         if (!NT_STATUS_IS_OK(status)) {
1510                 return status;
1511         }
1512
1513         if (pdb_getgrsid(&map, group_sid)) {
1514                 if ( map.gid != -1 )
1515                         grp = getgrgid(map.gid);
1516                 insert = false;
1517         }
1518
1519         if (grp == NULL) {
1520                 gid_t gid;
1521
1522                 /* No group found from mapping, find it from its name. */
1523                 if ((grp = getgrnam(name)) == NULL) {
1524
1525                         /* No appropriate group found, create one */
1526
1527                         DEBUG(0,("Creating unix group: '%s'\n", name));
1528
1529                         if (smb_create_group(name, &gid) != 0)
1530                                 return NT_STATUS_ACCESS_DENIED;
1531
1532                         if ((grp = getgrgid(gid)) == NULL)
1533                                 return NT_STATUS_ACCESS_DENIED;
1534                 }
1535         }
1536
1537         map.gid = grp->gr_gid;
1538         map.sid = group_sid;
1539
1540         if (dom_sid_equal(dom_sid, &global_sid_Builtin)) {
1541                 /*
1542                  * pdb_ldap does not like SID_NAME_WKN_GRP...
1543                  *
1544                  * map.sid_name_use = SID_NAME_WKN_GRP;
1545                  */
1546                 map.sid_name_use = SID_NAME_ALIAS;
1547         } else {
1548                 map.sid_name_use = SID_NAME_ALIAS;
1549         }
1550
1551         strlcpy(map.nt_name, name, sizeof(map.nt_name));
1552         if (description) {
1553                 strlcpy(map.comment, comment, sizeof(map.comment));
1554         } else {
1555                 strlcpy(map.comment, "", sizeof(map.comment));
1556         }
1557
1558         if (insert)
1559                 pdb_add_group_mapping_entry(&map);
1560         else
1561                 pdb_update_group_mapping_entry(&map);
1562
1563         for (i=0; i < num_members; i++) {
1564                 struct dssync_passdb_mem *mem;
1565
1566                 status = dssync_create_mem(pctx, obj,
1567                                            true /* active */,
1568                                            &members[i], &mem);
1569                 if (!NT_STATUS_IS_OK(status)) {
1570                         return status;
1571                 }
1572         }
1573
1574         return NT_STATUS_OK;
1575 }
1576
1577 /****************************************************************
1578 ****************************************************************/
1579
1580 static NTSTATUS handle_group_object(struct dssync_passdb *pctx,
1581                                     TALLOC_CTX *mem_ctx,
1582                                     struct dssync_passdb_obj *obj)
1583 {
1584         struct drsuapi_DsReplicaObjectListItemEx *cur = obj->cur;
1585         NTSTATUS status;
1586         fstring name;
1587         fstring comment;
1588         struct group *grp = NULL;
1589         struct dom_sid group_sid;
1590         fstring sid_string;
1591         GROUP_MAP map;
1592         bool insert = true;
1593
1594         const char *sAMAccountName;
1595         uint32_t sAMAccountType;
1596         uint32_t groupType;
1597         const char *description;
1598         uint32_t i;
1599         uint32_t num_members = 0;
1600         struct drsuapi_DsReplicaObjectIdentifier3 *members = NULL;
1601
1602         group_sid = cur->object.identifier->sid;
1603         GET_STRING_EX(sAMAccountName, true);
1604         GET_UINT32_EX(sAMAccountType, true);
1605         GET_UINT32_EX(groupType, true);
1606         GET_STRING(description);
1607
1608         status = find_drsuapi_attr_dn(obj, cur, DRSUAPI_ATTID_member,
1609                                       &num_members, &members);
1610         if (NT_STATUS_EQUAL(status, NT_STATUS_PROPSET_NOT_FOUND)) {
1611                 status = NT_STATUS_OK;
1612         }
1613         if (!NT_STATUS_IS_OK(status)) {
1614                 return status;
1615         }
1616
1617         fstrcpy(name, sAMAccountName);
1618         fstrcpy(comment, description);
1619
1620         sid_to_fstring(sid_string, &group_sid);
1621         DEBUG(0,("Creating group[%s] - %s members [%u]\n",
1622                   name, sid_string, num_members));
1623
1624         status = dssync_insert_obj(pctx, pctx->groups, obj);
1625         if (!NT_STATUS_IS_OK(status)) {
1626                 return status;
1627         }
1628
1629         if (pdb_getgrsid(&map, group_sid)) {
1630                 if ( map.gid != -1 )
1631                         grp = getgrgid(map.gid);
1632                 insert = false;
1633         }
1634
1635         if (grp == NULL) {
1636                 gid_t gid;
1637
1638                 /* No group found from mapping, find it from its name. */
1639                 if ((grp = getgrnam(name)) == NULL) {
1640
1641                         /* No appropriate group found, create one */
1642
1643                         DEBUG(0,("Creating unix group: '%s'\n", name));
1644
1645                         if (smb_create_group(name, &gid) != 0)
1646                                 return NT_STATUS_ACCESS_DENIED;
1647
1648                         if ((grp = getgrnam(name)) == NULL)
1649                                 return NT_STATUS_ACCESS_DENIED;
1650                 }
1651         }
1652
1653         map.gid = grp->gr_gid;
1654         map.sid = group_sid;
1655         map.sid_name_use = SID_NAME_DOM_GRP;
1656         strlcpy(map.nt_name, name, sizeof(map.nt_name));
1657         if (description) {
1658                 strlcpy(map.comment, comment, sizeof(map.comment));
1659         } else {
1660                 strlcpy(map.comment, "", sizeof(map.comment));
1661         }
1662
1663         if (insert)
1664                 pdb_add_group_mapping_entry(&map);
1665         else
1666                 pdb_update_group_mapping_entry(&map);
1667
1668         for (i=0; i < num_members; i++) {
1669                 struct dssync_passdb_mem *mem;
1670
1671                 status = dssync_create_mem(pctx, obj,
1672                                            true /* active */,
1673                                            &members[i], &mem);
1674                 if (!NT_STATUS_IS_OK(status)) {
1675                         return status;
1676                 }
1677         }
1678
1679         return NT_STATUS_OK;
1680 }
1681
1682 /****************************************************************
1683 ****************************************************************/
1684
1685 static NTSTATUS handle_interdomain_trust_object(struct dssync_passdb *pctx,
1686                                                 TALLOC_CTX *mem_ctx,
1687                                                 struct dssync_passdb_obj *obj)
1688 {
1689         struct drsuapi_DsReplicaObjectListItemEx *cur = obj->cur;
1690         DEBUG(0,("trust: %s\n", cur->object.identifier->dn));
1691         return NT_STATUS_NOT_IMPLEMENTED;
1692 }
1693
1694 /****************************************************************
1695 ****************************************************************/
1696
1697 struct dssync_object_table_t {
1698         uint32_t type;
1699         NTSTATUS (*fn) (struct dssync_passdb *pctx,
1700                         TALLOC_CTX *mem_ctx,
1701                         struct dssync_passdb_obj *obj);
1702 };
1703
1704 static const struct dssync_object_table_t dssync_object_table[] = {
1705         { ATYPE_NORMAL_ACCOUNT,         handle_account_object },
1706         { ATYPE_WORKSTATION_TRUST,      handle_account_object },
1707         { ATYPE_SECURITY_LOCAL_GROUP,   handle_alias_object },
1708         { ATYPE_SECURITY_GLOBAL_GROUP,  handle_group_object },
1709         { ATYPE_INTERDOMAIN_TRUST,      handle_interdomain_trust_object },
1710 };
1711
1712 /****************************************************************
1713 ****************************************************************/
1714
1715 static NTSTATUS parse_object(struct dssync_passdb *pctx,
1716                              TALLOC_CTX *mem_ctx,
1717                              struct drsuapi_DsReplicaObjectListItemEx *cur)
1718 {
1719         NTSTATUS status = NT_STATUS_OK;
1720         DATA_BLOB *blob;
1721         int i = 0;
1722         int a = 0;
1723         struct drsuapi_DsReplicaAttribute *attr;
1724
1725         char *name = NULL;
1726         uint32_t uacc = 0;
1727         uint32_t sam_type = 0;
1728
1729         DEBUG(3, ("parsing object '%s'\n", cur->object.identifier->dn));
1730
1731         for (i=0; i < cur->object.attribute_ctr.num_attributes; i++) {
1732
1733                 attr = &cur->object.attribute_ctr.attributes[i];
1734
1735                 if (attr->value_ctr.num_values != 1) {
1736                         continue;
1737                 }
1738
1739                 if (!attr->value_ctr.values[0].blob) {
1740                         continue;
1741                 }
1742
1743                 blob = attr->value_ctr.values[0].blob;
1744
1745                 switch (attr->attid) {
1746                         case DRSUAPI_ATTID_sAMAccountName:
1747                                 pull_string_talloc(mem_ctx, NULL, 0, &name,
1748                                                    blob->data, blob->length,
1749                                                    STR_UNICODE);
1750                                 break;
1751                         case DRSUAPI_ATTID_sAMAccountType:
1752                                 sam_type = IVAL(blob->data, 0);
1753                                 break;
1754                         case DRSUAPI_ATTID_userAccountControl:
1755                                 uacc = IVAL(blob->data, 0);
1756                                 break;
1757                         default:
1758                                 break;
1759                 }
1760         }
1761
1762         for (a=0; a < ARRAY_SIZE(dssync_object_table); a++) {
1763                 if (sam_type == dssync_object_table[a].type) {
1764                         if (dssync_object_table[a].fn) {
1765                                 struct dssync_passdb_obj *obj;
1766                                 status = dssync_create_obj(pctx, pctx->all,
1767                                                            sam_type, cur, &obj);
1768                                 if (!NT_STATUS_IS_OK(status)) {
1769                                         break;
1770                                 }
1771                                 status = dssync_object_table[a].fn(pctx,
1772                                                                    mem_ctx,
1773                                                                    obj);
1774                                 break;
1775                         }
1776                 }
1777         }
1778
1779         return status;
1780 }
1781
1782 static NTSTATUS parse_link(struct dssync_passdb *pctx,
1783                            TALLOC_CTX *mem_ctx,
1784                            struct drsuapi_DsReplicaLinkedAttribute *cur)
1785 {
1786         struct drsuapi_DsReplicaObjectIdentifier3 *id3;
1787         const DATA_BLOB *blob;
1788         enum ndr_err_code ndr_err;
1789         NTSTATUS status;
1790         bool active = false;
1791         struct dssync_passdb_mem *mem;
1792         struct dssync_passdb_obj *obj;
1793
1794         if (cur->attid != DRSUAPI_ATTID_member) {
1795                 return NT_STATUS_OK;
1796         }
1797
1798         if (cur->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE) {
1799                 active = true;
1800         }
1801
1802         DEBUG(3, ("parsing link '%s' - %s\n",
1803                   cur->identifier->dn, active?"adding":"deleting"));
1804
1805         blob = cur->value.blob;
1806
1807         if (blob == NULL) {
1808                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1809         }
1810
1811         obj = dssync_search_obj_by_guid(pctx, pctx->all, &cur->identifier->guid);
1812         if (obj == NULL) {
1813                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1814         }
1815
1816         id3 = talloc_zero(obj, struct drsuapi_DsReplicaObjectIdentifier3);
1817         if (id3 == NULL) {
1818                 return NT_STATUS_NO_MEMORY;
1819         }
1820
1821         /* windows sometimes sends an extra two pad bytes here */
1822         ndr_err = ndr_pull_struct_blob(blob, id3, id3,
1823                         (ndr_pull_flags_fn_t)ndr_pull_drsuapi_DsReplicaObjectIdentifier3);
1824         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1825                 status = ndr_map_error2ntstatus(ndr_err);
1826                 return status;
1827         }
1828
1829         status = dssync_create_mem(pctx, obj,
1830                                    active,
1831                                    id3, &mem);
1832         if (!NT_STATUS_IS_OK(status)) {
1833                 return status;
1834         }
1835
1836         return NT_STATUS_OK;
1837 }
1838
1839 /****************************************************************
1840 ****************************************************************/
1841
1842 static NTSTATUS passdb_process_objects(struct dssync_context *ctx,
1843                                        TALLOC_CTX *mem_ctx,
1844                                        struct drsuapi_DsReplicaObjectListItemEx *cur,
1845                                        struct drsuapi_DsReplicaOIDMapping_Ctr *mapping_ctr)
1846 {
1847         NTSTATUS status = NT_STATUS_OK;
1848         struct dssync_passdb *pctx =
1849                 talloc_get_type_abort(ctx->private_data,
1850                 struct dssync_passdb);
1851
1852         for (; cur; cur = cur->next_object) {
1853                 status = parse_object(pctx, mem_ctx, cur);
1854                 if (!NT_STATUS_IS_OK(status)) {
1855                         goto out;
1856                 }
1857         }
1858
1859  out:
1860         return status;
1861 }
1862
1863 /****************************************************************
1864 ****************************************************************/
1865
1866 static NTSTATUS passdb_process_links(struct dssync_context *ctx,
1867                                      TALLOC_CTX *mem_ctx,
1868                                      uint32_t count,
1869                                      struct drsuapi_DsReplicaLinkedAttribute *links,
1870                                      struct drsuapi_DsReplicaOIDMapping_Ctr *mapping_ctr)
1871 {
1872         NTSTATUS status = NT_STATUS_OK;
1873         struct dssync_passdb *pctx =
1874                 talloc_get_type_abort(ctx->private_data,
1875                 struct dssync_passdb);
1876         uint32_t i;
1877
1878         for (i = 0; i < count; i++) {
1879                 status = parse_link(pctx, mem_ctx, &links[i]);
1880                 if (!NT_STATUS_IS_OK(status)) {
1881                         goto out;
1882                 }
1883         }
1884
1885  out:
1886         return status;
1887 }
1888
1889 /****************************************************************
1890 ****************************************************************/
1891
1892 const struct dssync_ops libnet_dssync_passdb_ops = {
1893         .startup                = passdb_startup,
1894         .process_objects        = passdb_process_objects,
1895         .process_links          = passdb_process_links,
1896         .finish                 = passdb_finish,
1897 };