s3:libnet: pass in struct netlogon_creds_cli_context from the caller.
[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         map = talloc_zero(NULL, GROUP_MAP);
512         if (!map) {
513                 return -1;
514         }
515
516         if (!get_domain_group_from_sid(group_sid, map)) {
517                 DEBUG(0, ("Could not find global group %s\n",
518                           sid_string_dbg(&group_sid)));
519                 //return NT_STATUS_NO_SUCH_GROUP;
520                 TALLOC_FREE(map);
521                 return -1;
522         }
523
524         if (!(grp = getgrgid(map->gid))) {
525                 DEBUG(0, ("Could not find unix group %lu\n",
526                                                 (unsigned long)map->gid));
527                 //return NT_STATUS_NO_SUCH_GROUP;
528                 TALLOC_FREE(map);
529                 return -1;
530         }
531
532         TALLOC_FREE(map);
533
534         DEBUG(0,("Group members of %s: ", grp->gr_name));
535
536         if ( !(member = samu_new(talloc_tos())) ) {
537                 //return NT_STATUS_NO_MEMORY;
538                 return -1;
539         }
540
541         if (!pdb_getsampwsid(member, &member_sid)) {
542                 DEBUG(1, ("Found bogus group member: (member_sid=%s group=%s)\n",
543                           sid_string_tos(&member_sid), grp->gr_name));
544                 TALLOC_FREE(member);
545                 return -1;
546         }
547
548         if (pdb_get_group_rid(member) == rid) {
549                 DEBUGADD(0,("%s(primary),", pdb_get_username(member)));
550                 TALLOC_FREE(member);
551                 return -1;
552         }
553
554         DEBUGADD(0,("%s,", pdb_get_username(member)));
555         nt_member = talloc_strdup(talloc_tos(), pdb_get_username(member));
556         TALLOC_FREE(member);
557
558         DEBUGADD(0,("\n"));
559
560         unix_members = grp->gr_mem;
561
562         while (*unix_members) {
563                 if (strcmp(*unix_members, nt_member) == 0) {
564                         is_unix_member = true;
565                         break;
566                 }
567                 unix_members += 1;
568         }
569
570         if (!is_unix_member && mem->active) {
571                 smb_add_user_group(grp->gr_name, nt_member);
572         } else if (is_unix_member && !mem->active) {
573                 smb_delete_user_group(grp->gr_name, nt_member);
574         }
575
576         return 0;
577 }
578
579 static int dssync_passdb_traverse_groups(struct db_record *rec,
580                                          void *private_data)
581 {
582         struct dssync_passdb_traverse_groups *state =
583                 (struct dssync_passdb_traverse_groups *)private_data;
584         struct dssync_passdb *pctx =
585                 talloc_get_type_abort(state->ctx->private_data,
586                 struct dssync_passdb);
587         struct dssync_passdb_traverse_gmembers mstate;
588         struct dssync_passdb_obj *obj;
589         TDB_DATA value;
590         NTSTATUS status;
591
592         state->idx++;
593         if (pctx->methods == NULL) {
594                 return -1;
595         }
596
597         value = dbwrap_record_get_value(rec);
598
599         obj = dssync_parse_obj(value);
600         if (obj == NULL) {
601                 return -1;
602         }
603
604         ZERO_STRUCT(mstate);
605         mstate.ctx = state->ctx;
606         mstate.name = "members";
607         mstate.obj = obj;
608         status = dbwrap_traverse_read(obj->members,
609                                       dssync_passdb_traverse_gmembers,
610                                       &mstate, NULL);
611         if (!NT_STATUS_IS_OK(status)) {
612                 return -1;
613         }
614
615         return 0;
616 }
617
618 static NTSTATUS passdb_finish(struct dssync_context *ctx, TALLOC_CTX *mem_ctx,
619                               struct replUpToDateVectorBlob *new_utdv)
620 {
621         struct dssync_passdb *pctx =
622                 talloc_get_type_abort(ctx->private_data,
623                 struct dssync_passdb);
624         struct dssync_passdb_traverse_aliases astate;
625         struct dssync_passdb_traverse_groups gstate;
626         NTSTATUS status;
627
628         ZERO_STRUCT(astate);
629         astate.ctx = ctx;
630         astate.name = "aliases";
631         status = dbwrap_traverse_read(pctx->aliases,
632                                       dssync_passdb_traverse_aliases,
633                                       &astate, NULL);
634         if (!NT_STATUS_IS_OK(status)) {
635                 return NT_STATUS_INTERNAL_ERROR;
636         }
637
638         ZERO_STRUCT(gstate);
639         gstate.ctx = ctx;
640         gstate.name = "groups";
641         status = dbwrap_traverse_read(pctx->groups,
642                                       dssync_passdb_traverse_groups,
643                                       &gstate, NULL);
644         if (!NT_STATUS_IS_OK(status)) {
645                 return NT_STATUS_INTERNAL_ERROR;
646         }
647
648         TALLOC_FREE(pctx->methods);
649         TALLOC_FREE(pctx);
650
651         return NT_STATUS_OK;
652 }
653
654 /****************************************************************
655 ****************************************************************/
656
657 static NTSTATUS smb_create_user(TALLOC_CTX *mem_ctx,
658                                 uint32_t acct_flags,
659                                 const char *account,
660                                 struct passwd **passwd_p)
661 {
662         struct passwd *passwd;
663         char *add_script = NULL;
664
665         passwd = Get_Pwnam_alloc(mem_ctx, account);
666         if (passwd) {
667                 *passwd_p = passwd;
668                 return NT_STATUS_OK;
669         }
670
671         /* Create appropriate user */
672         if (acct_flags & ACB_NORMAL) {
673                 add_script = lp_adduser_script(mem_ctx);
674         } else if ( (acct_flags & ACB_WSTRUST) ||
675                     (acct_flags & ACB_SVRTRUST) ||
676                     (acct_flags & ACB_DOMTRUST) ) {
677                 add_script = lp_addmachine_script(mem_ctx);
678         } else {
679                 DEBUG(1, ("Unknown user type: %s\n",
680                           pdb_encode_acct_ctrl(acct_flags, NEW_PW_FORMAT_SPACE_PADDED_LEN)));
681                 return NT_STATUS_UNSUCCESSFUL;
682         }
683
684         if (!add_script) {
685                 return NT_STATUS_NO_MEMORY;
686         }
687
688         if (*add_script) {
689                 int add_ret;
690                 add_script = talloc_all_string_sub(mem_ctx, add_script,
691                                                    "%u", account);
692                 if (!add_script) {
693                         return NT_STATUS_NO_MEMORY;
694                 }
695                 add_ret = smbrun(add_script, NULL);
696                 DEBUG(add_ret ? 0 : 1,("fetch_account: Running the command `%s' "
697                          "gave %d\n", add_script, add_ret));
698                 if (add_ret == 0) {
699                         smb_nscd_flush_user_cache();
700                 }
701         }
702
703         /* try and find the possible unix account again */
704         passwd = Get_Pwnam_alloc(mem_ctx, account);
705         if (!passwd) {
706                 return NT_STATUS_NO_SUCH_USER;
707         }
708
709         *passwd_p = passwd;
710
711         return NT_STATUS_OK;
712 }
713
714 static struct drsuapi_DsReplicaAttribute *find_drsuapi_attr(
715                         const struct drsuapi_DsReplicaObjectListItemEx *cur,
716                         uint32_t attid)
717 {
718         int i = 0;
719
720         for (i = 0; i < cur->object.attribute_ctr.num_attributes; i++) {
721                 struct drsuapi_DsReplicaAttribute *attr;
722
723                 attr = &cur->object.attribute_ctr.attributes[i];
724
725                 if (attr->attid == attid) {
726                         return attr;
727                 }
728         }
729
730         return NULL;
731 }
732
733 static NTSTATUS find_drsuapi_attr_string(TALLOC_CTX *mem_ctx,
734                                          const struct drsuapi_DsReplicaObjectListItemEx *cur,
735                                          uint32_t attid,
736                                          uint32_t *_count,
737                                          char ***_array)
738 {
739         struct drsuapi_DsReplicaAttribute *attr;
740         char **array;
741         uint32_t a;
742
743         attr = find_drsuapi_attr(cur, attid);
744         if (attr == NULL) {
745                 return NT_STATUS_PROPSET_NOT_FOUND;
746         }
747
748         array = talloc_array(mem_ctx, char *, attr->value_ctr.num_values);
749         if (array == NULL) {
750                 return NT_STATUS_NO_MEMORY;
751         }
752
753         for (a = 0; a < attr->value_ctr.num_values; a++) {
754                 const DATA_BLOB *blob;
755                 ssize_t ret;
756
757                 blob = attr->value_ctr.values[a].blob;
758
759                 if (blob == NULL) {
760                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
761                 }
762
763                 ret = pull_string_talloc(array, NULL, 0, &array[a],
764                                          blob->data, blob->length,
765                                          STR_UNICODE);
766                 if (ret == -1) {
767                         //TODO
768                         return NT_STATUS_INTERNAL_ERROR;
769                 }
770         }
771
772         *_count = attr->value_ctr.num_values;
773         *_array = array;
774         return NT_STATUS_OK;
775 }
776
777 static NTSTATUS find_drsuapi_attr_int32(TALLOC_CTX *mem_ctx,
778                                         const struct drsuapi_DsReplicaObjectListItemEx *cur,
779                                         uint32_t attid,
780                                         uint32_t *_count,
781                                         int32_t **_array)
782 {
783         struct drsuapi_DsReplicaAttribute *attr;
784         int32_t *array;
785         uint32_t a;
786
787         attr = find_drsuapi_attr(cur, attid);
788         if (attr == NULL) {
789                 return NT_STATUS_PROPSET_NOT_FOUND;
790         }
791
792         array = talloc_array(mem_ctx, int32_t, attr->value_ctr.num_values);
793         if (array == NULL) {
794                 return NT_STATUS_NO_MEMORY;
795         }
796
797         for (a = 0; a < attr->value_ctr.num_values; a++) {
798                 const DATA_BLOB *blob;
799
800                 blob = attr->value_ctr.values[a].blob;
801
802                 if (blob == NULL) {
803                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
804                 }
805
806                 if (blob->length != 4) {
807                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
808                 }
809
810                 array[a] = IVAL(blob->data, 0);
811         }
812
813         *_count = attr->value_ctr.num_values;
814         *_array = array;
815         return NT_STATUS_OK;
816 }
817
818 static NTSTATUS find_drsuapi_attr_blob(TALLOC_CTX *mem_ctx,
819                                        const struct drsuapi_DsReplicaObjectListItemEx *cur,
820                                        uint32_t attid,
821                                        uint32_t *_count,
822                                        DATA_BLOB **_array)
823 {
824         struct drsuapi_DsReplicaAttribute *attr;
825         DATA_BLOB *array;
826         uint32_t a;
827
828         attr = find_drsuapi_attr(cur, attid);
829         if (attr == NULL) {
830                 return NT_STATUS_PROPSET_NOT_FOUND;
831         }
832
833         array = talloc_array(mem_ctx, DATA_BLOB, attr->value_ctr.num_values);
834         if (array == NULL) {
835                 return NT_STATUS_NO_MEMORY;
836         }
837
838         for (a = 0; a < attr->value_ctr.num_values; a++) {
839                 const DATA_BLOB *blob;
840
841                 blob = attr->value_ctr.values[a].blob;
842
843                 if (blob == NULL) {
844                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
845                 }
846
847                 array[a] = data_blob_talloc(array, blob->data, blob->length);
848                 if (array[a].length != blob->length) {
849                         return NT_STATUS_NO_MEMORY;
850                 }
851         }
852         *_count = attr->value_ctr.num_values;
853         *_array = array;
854         return NT_STATUS_OK;
855 }
856
857 static NTSTATUS find_drsuapi_attr_int64(TALLOC_CTX *mem_ctx,
858                                         const struct drsuapi_DsReplicaObjectListItemEx *cur,
859                                         uint32_t attid,
860                                         uint32_t *_count,
861                                         int64_t **_array)
862 {
863         struct drsuapi_DsReplicaAttribute *attr;
864         int64_t *array;
865         uint32_t a;
866
867         attr = find_drsuapi_attr(cur, attid);
868         if (attr == NULL) {
869                 return NT_STATUS_PROPSET_NOT_FOUND;
870         }
871
872         array = talloc_array(mem_ctx, int64_t, attr->value_ctr.num_values);
873         if (array == NULL) {
874                 return NT_STATUS_NO_MEMORY;
875         }
876
877         for (a = 0; a < attr->value_ctr.num_values; a++) {
878                 const DATA_BLOB *blob;
879
880                 blob = attr->value_ctr.values[a].blob;
881
882                 if (blob == NULL) {
883                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
884                 }
885
886                 if (blob->length != 8) {
887                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
888                 }
889
890                 array[a] = BVAL(blob->data, 0);
891         }
892         *_count = attr->value_ctr.num_values;
893         *_array = array;
894         return NT_STATUS_OK;
895 }
896
897 static NTSTATUS find_drsuapi_attr_dn(TALLOC_CTX *mem_ctx,
898                                      const struct drsuapi_DsReplicaObjectListItemEx *cur,
899                                      uint32_t attid,
900                                      uint32_t *_count,
901                                      struct drsuapi_DsReplicaObjectIdentifier3 **_array)
902 {
903         struct drsuapi_DsReplicaAttribute *attr;
904         struct drsuapi_DsReplicaObjectIdentifier3 *array;
905         uint32_t a;
906
907         attr = find_drsuapi_attr(cur, attid);
908         if (attr == NULL) {
909                 return NT_STATUS_PROPSET_NOT_FOUND;
910         }
911
912         array = talloc_array(mem_ctx,
913                              struct drsuapi_DsReplicaObjectIdentifier3,
914                              attr->value_ctr.num_values);
915         if (array == NULL) {
916                 return NT_STATUS_NO_MEMORY;
917         }
918
919         for (a = 0; a < attr->value_ctr.num_values; a++) {
920                 const DATA_BLOB *blob;
921                 enum ndr_err_code ndr_err;
922                 NTSTATUS status;
923
924                 blob = attr->value_ctr.values[a].blob;
925
926                 if (blob == NULL) {
927                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
928                 }
929
930                 /* windows sometimes sends an extra two pad bytes here */
931                 ndr_err = ndr_pull_struct_blob(blob, array, &array[a],
932                                 (ndr_pull_flags_fn_t)ndr_pull_drsuapi_DsReplicaObjectIdentifier3);
933                 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
934                         status = ndr_map_error2ntstatus(ndr_err);
935                         return status;
936                 }
937         }
938         *_count = attr->value_ctr.num_values;
939         *_array = array;
940         return NT_STATUS_OK;
941 }
942
943 #define GET_BLOB_EX(attr, needed) do { \
944         NTSTATUS _status; \
945         uint32_t _cnt; \
946         DATA_BLOB *_vals = NULL; \
947         attr = data_blob_null; \
948         _status = find_drsuapi_attr_blob(mem_ctx, cur, \
949                                          DRSUAPI_ATTID_ ## attr, \
950                                          &_cnt, &_vals); \
951         if (NT_STATUS_EQUAL(_status, NT_STATUS_PROPSET_NOT_FOUND)) { \
952                 if (!needed) { \
953                         _status = NT_STATUS_OK; \
954                         _cnt = 0; \
955                 } \
956         } \
957         if (!NT_STATUS_IS_OK(_status)) { \
958                 DEBUG(0,(__location__ "attr[%s] %s\n", \
959                         #attr, nt_errstr(_status))); \
960                 return _status; \
961         } \
962         if (_cnt == 0) { \
963                 if (needed) { \
964                         talloc_free(_vals); \
965                         DEBUG(0,(__location__ "attr[%s] count[%u]\n", #attr, _cnt)); \
966                         return NT_STATUS_OBJECT_NAME_NOT_FOUND; \
967                 } \
968         } else if (_cnt > 1) { \
969                 talloc_free(_vals); \
970                 DEBUG(0,(__location__ "attr[%s] count[%u]\n", #attr, _cnt)); \
971                 return NT_STATUS_INTERNAL_DB_CORRUPTION; \
972         } else { \
973                 attr = _vals[0]; \
974                 (void)talloc_steal(mem_ctx, _vals[0].data); \
975         } \
976         talloc_free(_vals); \
977 } while(0)
978
979 #define GET_STRING_EX(attr, needed) do { \
980         NTSTATUS _status; \
981         uint32_t _cnt; \
982         char **_vals = NULL; \
983         attr = NULL; \
984         _status = find_drsuapi_attr_string(mem_ctx, cur, \
985                                            DRSUAPI_ATTID_ ## attr, \
986                                            &_cnt, &_vals); \
987         if (NT_STATUS_EQUAL(_status, NT_STATUS_PROPSET_NOT_FOUND)) { \
988                 if (!needed) { \
989                         _status = NT_STATUS_OK; \
990                         _cnt = 0; \
991                 } \
992         } \
993         if (!NT_STATUS_IS_OK(_status)) { \
994                 DEBUG(0,(__location__ "attr[%s] %s\n", \
995                         #attr, nt_errstr(_status))); \
996                 return _status; \
997         } \
998         if (_cnt == 0) { \
999                 if (needed) { \
1000                         talloc_free(_vals); \
1001                         DEBUG(0,(__location__ "attr[%s] count[%u]\n", #attr, _cnt)); \
1002                         return NT_STATUS_OBJECT_NAME_NOT_FOUND; \
1003                 } \
1004         } else if (_cnt > 1) { \
1005                 talloc_free(_vals); \
1006                 DEBUG(0,(__location__ "attr[%s] count[%u]\n", #attr, _cnt)); \
1007                 return NT_STATUS_INTERNAL_DB_CORRUPTION; \
1008         } else { \
1009                 attr = talloc_move(mem_ctx, &_vals[0]); \
1010         } \
1011         talloc_free(_vals); \
1012 } while(0)
1013
1014 #define GET_UINT32_EX(attr, needed) do { \
1015         NTSTATUS _status; \
1016         uint32_t _cnt; \
1017         int32_t*_vals = NULL; \
1018         attr = 0; \
1019         _status = find_drsuapi_attr_int32(mem_ctx, cur, \
1020                                           DRSUAPI_ATTID_ ## attr, \
1021                                           &_cnt, &_vals); \
1022         if (NT_STATUS_EQUAL(_status, NT_STATUS_PROPSET_NOT_FOUND)) { \
1023                 if (!needed) { \
1024                         _status = NT_STATUS_OK; \
1025                         _cnt = 0; \
1026                 } \
1027         } \
1028         if (!NT_STATUS_IS_OK(_status)) { \
1029                 DEBUG(0,(__location__ "attr[%s] %s\n", \
1030                         #attr, nt_errstr(_status))); \
1031                 return _status; \
1032         } \
1033         if (_cnt == 0) { \
1034                 if (needed) { \
1035                         talloc_free(_vals); \
1036                         DEBUG(0,(__location__ "attr[%s] count[%u]\n", #attr, _cnt)); \
1037                         return NT_STATUS_OBJECT_NAME_NOT_FOUND; \
1038                 } \
1039         } else if (_cnt > 1) { \
1040                 talloc_free(_vals); \
1041                 DEBUG(0,(__location__ "attr[%s] count[%u]\n", #attr, _cnt)); \
1042                 return NT_STATUS_INTERNAL_DB_CORRUPTION; \
1043         } else { \
1044                 attr = (uint32_t)_vals[0]; \
1045         } \
1046         talloc_free(_vals); \
1047 } while(0)
1048
1049 #define GET_UINT64_EX(attr, needed) do { \
1050         NTSTATUS _status; \
1051         uint32_t _cnt; \
1052         int64_t *_vals = NULL; \
1053         attr = 0; \
1054         _status = find_drsuapi_attr_int64(mem_ctx, cur, \
1055                                           DRSUAPI_ATTID_ ## attr, \
1056                                           &_cnt, &_vals); \
1057         if (NT_STATUS_EQUAL(_status, NT_STATUS_PROPSET_NOT_FOUND)) { \
1058                 if (!needed) { \
1059                         _status = NT_STATUS_OK; \
1060                         _cnt = 0; \
1061                 } \
1062         } \
1063         if (!NT_STATUS_IS_OK(_status)) { \
1064                 DEBUG(0,(__location__ "attr[%s] %s\n", \
1065                         #attr, nt_errstr(_status))); \
1066                 return _status; \
1067         } \
1068         if (_cnt == 0) { \
1069                 if (needed) { \
1070                         talloc_free(_vals); \
1071                         DEBUG(0,(__location__ "attr[%s] count[%u]\n", #attr, _cnt)); \
1072                         return NT_STATUS_OBJECT_NAME_NOT_FOUND; \
1073                 } \
1074         } else if (_cnt > 1) { \
1075                 talloc_free(_vals); \
1076                 DEBUG(0,(__location__ "attr[%s] count[%u]\n", #attr, _cnt)); \
1077                 return NT_STATUS_INTERNAL_DB_CORRUPTION; \
1078         } else { \
1079                 attr = (uint64_t)_vals[0]; \
1080         } \
1081         talloc_free(_vals); \
1082 } while(0)
1083
1084 #define GET_BLOB(attr) GET_BLOB_EX(attr, false)
1085 #define GET_STRING(attr) GET_STRING_EX(attr, false)
1086 #define GET_UINT32(attr) GET_UINT32_EX(attr, false)
1087 #define GET_UINT64(attr) GET_UINT64_EX(attr, false)
1088
1089 /* Convert a struct samu_DELTA to a struct samu. */
1090 #define STRING_CHANGED (old_string && !new_string) ||\
1091                     (!old_string && new_string) ||\
1092                 (old_string && new_string && (strcmp(old_string, new_string) != 0))
1093
1094 #define STRING_CHANGED_NC(s1,s2) ((s1) && !(s2)) ||\
1095                     (!(s1) && (s2)) ||\
1096                 ((s1) && (s2) && (strcmp((s1), (s2)) != 0))
1097
1098 /****************************************************************
1099 ****************************************************************/
1100
1101 static NTSTATUS sam_account_from_object(struct samu *account,
1102                                 struct drsuapi_DsReplicaObjectListItemEx *cur)
1103 {
1104         TALLOC_CTX *mem_ctx = account;
1105         const char *old_string, *new_string;
1106         time_t unix_time, stored_time;
1107         uchar zero_buf[16];
1108         NTSTATUS status;
1109
1110         NTTIME lastLogon;
1111         NTTIME lastLogoff;
1112         NTTIME pwdLastSet;
1113         NTTIME accountExpires;
1114         const char *sAMAccountName;
1115         const char *displayName;
1116         const char *homeDirectory;
1117         const char *homeDrive;
1118         const char *scriptPath;
1119         const char *profilePath;
1120         const char *description;
1121         const char *userWorkstations;
1122         const char *comment;
1123         DATA_BLOB userParameters;
1124         struct dom_sid objectSid;
1125         uint32_t primaryGroupID;
1126         uint32_t userAccountControl;
1127         DATA_BLOB logonHours;
1128         uint32_t badPwdCount;
1129         uint32_t logonCount;
1130         DATA_BLOB unicodePwd;
1131         DATA_BLOB dBCSPwd;
1132
1133         uint32_t rid = 0;
1134         uint32_t acct_flags;
1135         uint32_t units_per_week;
1136
1137         memset(zero_buf, '\0', sizeof(zero_buf));
1138
1139         objectSid = cur->object.identifier->sid;
1140         GET_STRING_EX(sAMAccountName, true);
1141         DEBUG(0,("sam_account_from_object(%s, %s) start\n",
1142                  sAMAccountName, sid_string_dbg(&objectSid)));
1143         GET_UINT64(lastLogon);
1144         GET_UINT64(lastLogoff);
1145         GET_UINT64(pwdLastSet);
1146         GET_UINT64(accountExpires);
1147         GET_STRING(displayName);
1148         GET_STRING(homeDirectory);
1149         GET_STRING(homeDrive);
1150         GET_STRING(scriptPath);
1151         GET_STRING(profilePath);
1152         GET_STRING(description);
1153         GET_STRING(userWorkstations);
1154         GET_STRING(comment);
1155         GET_BLOB(userParameters);
1156         GET_UINT32(primaryGroupID);
1157         GET_UINT32(userAccountControl);
1158         GET_BLOB(logonHours);
1159         GET_UINT32(badPwdCount);
1160         GET_UINT32(logonCount);
1161         GET_BLOB(unicodePwd);
1162         GET_BLOB(dBCSPwd);
1163
1164         status = dom_sid_split_rid(mem_ctx, &objectSid, NULL, &rid);
1165         if (!NT_STATUS_IS_OK(status)) {
1166                 return status;
1167         }
1168         acct_flags = ds_uf2acb(userAccountControl);
1169
1170         /* Username, fullname, home dir, dir drive, logon script, acct
1171            desc, workstations, profile. */
1172
1173         if (sAMAccountName) {
1174                 old_string = pdb_get_nt_username(account);
1175                 new_string = sAMAccountName;
1176
1177                 if (STRING_CHANGED) {
1178                         pdb_set_nt_username(account, new_string, PDB_CHANGED);
1179                 }
1180
1181                 /* Unix username is the same - for sanity */
1182                 old_string = pdb_get_username( account );
1183                 if (STRING_CHANGED) {
1184                         pdb_set_username(account, new_string, PDB_CHANGED);
1185                 }
1186         }
1187
1188         if (displayName) {
1189                 old_string = pdb_get_fullname(account);
1190                 new_string = displayName;
1191
1192                 if (STRING_CHANGED)
1193                         pdb_set_fullname(account, new_string, PDB_CHANGED);
1194         }
1195
1196         if (homeDirectory) {
1197                 old_string = pdb_get_homedir(account);
1198                 new_string = homeDirectory;
1199
1200                 if (STRING_CHANGED)
1201                         pdb_set_homedir(account, new_string, PDB_CHANGED);
1202         }
1203
1204         if (homeDrive) {
1205                 old_string = pdb_get_dir_drive(account);
1206                 new_string = homeDrive;
1207
1208                 if (STRING_CHANGED)
1209                         pdb_set_dir_drive(account, new_string, PDB_CHANGED);
1210         }
1211
1212         if (scriptPath) {
1213                 old_string = pdb_get_logon_script(account);
1214                 new_string = scriptPath;
1215
1216                 if (STRING_CHANGED)
1217                         pdb_set_logon_script(account, new_string, PDB_CHANGED);
1218         }
1219
1220         if (description) {
1221                 old_string = pdb_get_acct_desc(account);
1222                 new_string = description;
1223
1224                 if (STRING_CHANGED)
1225                         pdb_set_acct_desc(account, new_string, PDB_CHANGED);
1226         }
1227
1228         if (userWorkstations) {
1229                 old_string = pdb_get_workstations(account);
1230                 new_string = userWorkstations;
1231
1232                 if (STRING_CHANGED)
1233                         pdb_set_workstations(account, new_string, PDB_CHANGED);
1234         }
1235
1236         if (profilePath) {
1237                 old_string = pdb_get_profile_path(account);
1238                 new_string = profilePath;
1239
1240                 if (STRING_CHANGED)
1241                         pdb_set_profile_path(account, new_string, PDB_CHANGED);
1242         }
1243
1244         if (userParameters.data) {
1245                 char *newstr;
1246                 old_string = pdb_get_munged_dial(account);
1247                 newstr = (userParameters.length == 0) ? NULL :
1248                         base64_encode_data_blob(talloc_tos(), userParameters);
1249
1250                 if (STRING_CHANGED_NC(old_string, newstr))
1251                         pdb_set_munged_dial(account, newstr, PDB_CHANGED);
1252                 TALLOC_FREE(newstr);
1253         }
1254
1255         /* User and group sid */
1256         if (rid != 0 && pdb_get_user_rid(account) != rid) {
1257                 pdb_set_user_sid_from_rid(account, rid, PDB_CHANGED);
1258         }
1259         if (primaryGroupID != 0 && pdb_get_group_rid(account) != primaryGroupID) {
1260                 pdb_set_group_sid_from_rid(account, primaryGroupID, PDB_CHANGED);
1261         }
1262
1263         /* Logon and password information */
1264         if (!nt_time_is_zero(&lastLogon)) {
1265                 unix_time = nt_time_to_unix(lastLogon);
1266                 stored_time = pdb_get_logon_time(account);
1267                 if (stored_time != unix_time)
1268                         pdb_set_logon_time(account, unix_time, PDB_CHANGED);
1269         }
1270
1271         if (!nt_time_is_zero(&lastLogoff)) {
1272                 unix_time = nt_time_to_unix(lastLogoff);
1273                 stored_time = pdb_get_logoff_time(account);
1274                 if (stored_time != unix_time)
1275                         pdb_set_logoff_time(account, unix_time,PDB_CHANGED);
1276         }
1277
1278         /* Logon Divs */
1279         units_per_week = logonHours.length * 8;
1280
1281         if (pdb_get_logon_divs(account) != units_per_week) {
1282                 pdb_set_logon_divs(account, units_per_week, PDB_CHANGED);
1283         }
1284
1285         /* Logon Hours Len */
1286         if (units_per_week/8 != pdb_get_hours_len(account)) {
1287                 pdb_set_hours_len(account, units_per_week/8, PDB_CHANGED);
1288         }
1289
1290         /* Logon Hours */
1291         if (logonHours.data) {
1292                 char oldstr[44], newstr[44];
1293                 pdb_sethexhours(oldstr, pdb_get_hours(account));
1294                 pdb_sethexhours(newstr, logonHours.data);
1295                 if (!strequal(oldstr, newstr)) {
1296                         pdb_set_hours(account, logonHours.data,
1297                                       logonHours.length, PDB_CHANGED);
1298                 }
1299         }
1300
1301         if (pdb_get_bad_password_count(account) != badPwdCount)
1302                 pdb_set_bad_password_count(account, badPwdCount, PDB_CHANGED);
1303
1304         if (pdb_get_logon_count(account) != logonCount)
1305                 pdb_set_logon_count(account, logonCount, PDB_CHANGED);
1306
1307         if (!nt_time_is_zero(&pwdLastSet)) {
1308                 unix_time = nt_time_to_unix(pwdLastSet);
1309                 stored_time = pdb_get_pass_last_set_time(account);
1310                 if (stored_time != unix_time)
1311                         pdb_set_pass_last_set_time(account, unix_time, PDB_CHANGED);
1312         } else {
1313                 /* no last set time, make it now */
1314                 pdb_set_pass_last_set_time(account, time(NULL), PDB_CHANGED);
1315         }
1316
1317         if (!nt_time_is_zero(&accountExpires)) {
1318                 unix_time = nt_time_to_unix(accountExpires);
1319                 stored_time = pdb_get_kickoff_time(account);
1320                 if (stored_time != unix_time)
1321                         pdb_set_kickoff_time(account, unix_time, PDB_CHANGED);
1322         }
1323
1324         /* Decode hashes from password hash
1325            Note that win2000 may send us all zeros for the hashes if it doesn't
1326            think this channel is secure enough - don't set the passwords at all
1327            in that case
1328         */
1329         if (dBCSPwd.length == 16 && memcmp(dBCSPwd.data, zero_buf, 16) != 0) {
1330                 pdb_set_lanman_passwd(account, dBCSPwd.data, PDB_CHANGED);
1331         }
1332
1333         if (unicodePwd.length == 16 && memcmp(unicodePwd.data, zero_buf, 16) != 0) {
1334                 pdb_set_nt_passwd(account, unicodePwd.data, PDB_CHANGED);
1335         }
1336
1337         /* TODO: history */
1338
1339         /* TODO: account expiry time */
1340
1341         pdb_set_acct_ctrl(account, acct_flags, PDB_CHANGED);
1342
1343         pdb_set_domain(account, lp_workgroup(), PDB_CHANGED);
1344
1345         DEBUG(0,("sam_account_from_object(%s, %s) done\n",
1346                  sAMAccountName, sid_string_dbg(&objectSid)));
1347         return NT_STATUS_OK;
1348 }
1349
1350 /****************************************************************
1351 ****************************************************************/
1352
1353 static NTSTATUS handle_account_object(struct dssync_passdb *pctx,
1354                                       TALLOC_CTX *mem_ctx,
1355                                       struct dssync_passdb_obj *obj)
1356 {
1357         struct drsuapi_DsReplicaObjectListItemEx *cur = obj->cur;
1358         NTSTATUS status;
1359         fstring account;
1360         struct samu *sam_account=NULL;
1361         GROUP_MAP *map;
1362         struct group *grp;
1363         struct dom_sid user_sid;
1364         struct dom_sid group_sid;
1365         struct passwd *passwd = NULL;
1366         uint32_t acct_flags;
1367         uint32_t rid;
1368
1369         const char *sAMAccountName;
1370         uint32_t sAMAccountType;
1371         uint32_t userAccountControl;
1372
1373         user_sid = cur->object.identifier->sid;
1374         GET_STRING_EX(sAMAccountName, true);
1375         GET_UINT32_EX(sAMAccountType, true);
1376         GET_UINT32_EX(userAccountControl, true);
1377
1378         status = dom_sid_split_rid(mem_ctx, &user_sid, NULL, &rid);
1379         if (!NT_STATUS_IS_OK(status)) {
1380                 return status;
1381         }
1382
1383         fstrcpy(account, sAMAccountName);
1384         if (rid == DOMAIN_RID_GUEST) {
1385                 /*
1386                  * pdb_getsampwsid() has special handling for DOMAIN_RID_GUEST
1387                  * that's why we need to ignore it here.
1388                  *
1389                  * pdb_smbpasswd.c also has some DOMAIN_RID_GUEST related
1390                  * code...
1391                  */
1392                 DEBUG(0,("Ignore %s - %s\n", account, sid_string_dbg(&user_sid)));
1393                 return NT_STATUS_OK;
1394         }
1395         DEBUG(0,("Creating account: %s\n", account));
1396
1397         if ( !(sam_account = samu_new(mem_ctx)) ) {
1398                 return NT_STATUS_NO_MEMORY;
1399         }
1400
1401         acct_flags = ds_uf2acb(userAccountControl);
1402         status = smb_create_user(sam_account, acct_flags, account, &passwd);
1403         if (!NT_STATUS_IS_OK(status)) {
1404                 DEBUG(0,("Could not create posix account info for '%s'- %s\n",
1405                         account, nt_errstr(status)));
1406                 TALLOC_FREE(sam_account);
1407                 return status;
1408         }
1409
1410         DEBUG(3, ("Attempting to find SID %s for user %s in the passdb\n",
1411                   sid_string_dbg(&user_sid), account));
1412         if (!pdb_getsampwsid(sam_account, &user_sid)) {
1413                 sam_account_from_object(sam_account, cur);
1414                 DEBUG(3, ("Attempting to add user SID %s for user %s in the passdb\n",
1415                           sid_string_dbg(&user_sid),
1416                           pdb_get_username(sam_account)));
1417                 if (!NT_STATUS_IS_OK(pdb_add_sam_account(sam_account))) {
1418                         DEBUG(1, ("SAM Account for %s failed to be added to the passdb!\n",
1419                                   account));
1420                         TALLOC_FREE(sam_account);
1421                         return NT_STATUS_ACCESS_DENIED;
1422                 }
1423         } else {
1424                 sam_account_from_object(sam_account, cur);
1425                 DEBUG(3, ("Attempting to update user SID %s for user %s in the passdb\n",
1426                           sid_string_dbg(&user_sid),
1427                           pdb_get_username(sam_account)));
1428                 if (!NT_STATUS_IS_OK(pdb_update_sam_account(sam_account))) {
1429                         DEBUG(1, ("SAM Account for %s failed to be updated in the passdb!\n",
1430                                   account));
1431                         TALLOC_FREE(sam_account);
1432                         return NT_STATUS_ACCESS_DENIED;
1433                 }
1434         }
1435
1436         if (pdb_get_group_sid(sam_account) == NULL) {
1437                 TALLOC_FREE(sam_account);
1438                 return NT_STATUS_UNSUCCESSFUL;
1439         }
1440
1441         group_sid = *pdb_get_group_sid(sam_account);
1442
1443         map = talloc_zero(NULL, GROUP_MAP);
1444         if (!map) {
1445                 return NT_STATUS_NO_MEMORY;
1446         }
1447
1448         if (!pdb_getgrsid(map, group_sid)) {
1449                 DEBUG(0, ("Primary group of %s has no mapping!\n",
1450                           pdb_get_username(sam_account)));
1451         } else {
1452                 if (map->gid != passwd->pw_gid) {
1453                         if (!(grp = getgrgid(map->gid))) {
1454                                 DEBUG(0, ("Could not find unix group %lu for user %s (group SID=%s)\n",
1455                                           (unsigned long)map->gid, pdb_get_username(sam_account),
1456                                           sid_string_dbg(&group_sid)));
1457                         } else {
1458                                 smb_set_primary_group(grp->gr_name, pdb_get_username(sam_account));
1459                         }
1460                 }
1461         }
1462
1463         TALLOC_FREE(map);
1464
1465         if ( !passwd ) {
1466                 DEBUG(1, ("No unix user for this account (%s), cannot adjust mappings\n",
1467                         pdb_get_username(sam_account)));
1468         }
1469
1470         TALLOC_FREE(sam_account);
1471         return NT_STATUS_OK;
1472 }
1473
1474 /****************************************************************
1475 ****************************************************************/
1476
1477 static NTSTATUS handle_alias_object(struct dssync_passdb *pctx,
1478                                     TALLOC_CTX *mem_ctx,
1479                                     struct dssync_passdb_obj *obj)
1480 {
1481         struct drsuapi_DsReplicaObjectListItemEx *cur = obj->cur;
1482         NTSTATUS status;
1483         struct group *grp = NULL;
1484         struct dom_sid group_sid;
1485         uint32_t rid = 0;
1486         struct dom_sid *dom_sid = NULL;
1487         fstring sid_string;
1488         GROUP_MAP *map;
1489         bool insert = true;
1490
1491         const char *sAMAccountName;
1492         uint32_t sAMAccountType;
1493         uint32_t groupType;
1494         const char *description;
1495         uint32_t i;
1496         uint32_t num_members = 0;
1497         struct drsuapi_DsReplicaObjectIdentifier3 *members = NULL;
1498
1499         group_sid = cur->object.identifier->sid;
1500         GET_STRING_EX(sAMAccountName, true);
1501         GET_UINT32_EX(sAMAccountType, true);
1502         GET_UINT32_EX(groupType, true);
1503         GET_STRING(description);
1504
1505         status = find_drsuapi_attr_dn(obj, cur, DRSUAPI_ATTID_member,
1506                                       &num_members, &members);
1507         if (NT_STATUS_EQUAL(status, NT_STATUS_PROPSET_NOT_FOUND)) {
1508                 status = NT_STATUS_OK;
1509         }
1510         if (!NT_STATUS_IS_OK(status)) {
1511                 return status;
1512         }
1513
1514         dom_sid_split_rid(mem_ctx, &group_sid, &dom_sid, &rid);
1515
1516         map = talloc_zero(mem_ctx, GROUP_MAP);
1517         if (map == NULL) {
1518                 status = NT_STATUS_NO_MEMORY;
1519                 goto done;
1520         }
1521
1522         map->nt_name = talloc_strdup(map, sAMAccountName);
1523         if (map->nt_name == NULL) {
1524                 status = NT_STATUS_NO_MEMORY;
1525                 goto done;
1526         }
1527
1528         if (description) {
1529                 map->comment = talloc_strdup(map, description);
1530         } else {
1531                 map->comment = talloc_strdup(map, "");
1532         }
1533         if (map->comment == NULL) {
1534                 status = NT_STATUS_NO_MEMORY;
1535                 goto done;
1536         }
1537
1538         sid_to_fstring(sid_string, &group_sid);
1539         DEBUG(0,("Creating alias[%s] - %s members[%u]\n",
1540                   map->nt_name, sid_string, num_members));
1541
1542         status = dssync_insert_obj(pctx, pctx->aliases, obj);
1543         if (!NT_STATUS_IS_OK(status)) {
1544                 goto done;
1545         }
1546
1547         if (pdb_getgrsid(map, group_sid)) {
1548                 if (map->gid != -1) {
1549                         grp = getgrgid(map->gid);
1550                 }
1551                 insert = false;
1552         }
1553
1554         if (grp == NULL) {
1555                 gid_t gid;
1556
1557                 /* No group found from mapping, find it from its name. */
1558                 if ((grp = getgrnam(map->nt_name)) == NULL) {
1559
1560                         /* No appropriate group found, create one */
1561
1562                         DEBUG(0, ("Creating unix group: '%s'\n",
1563                                                         map->nt_name));
1564
1565                         if (smb_create_group(map->nt_name, &gid) != 0) {
1566                                 status = NT_STATUS_ACCESS_DENIED;
1567                                 goto done;
1568                         }
1569
1570                         if ((grp = getgrgid(gid)) == NULL) {
1571                                 status = NT_STATUS_ACCESS_DENIED;
1572                                 goto done;
1573                         }
1574                 }
1575         }
1576
1577         map->gid = grp->gr_gid;
1578         map->sid = group_sid;
1579
1580         if (dom_sid_equal(dom_sid, &global_sid_Builtin)) {
1581                 /*
1582                  * pdb_ldap does not like SID_NAME_WKN_GRP...
1583                  *
1584                  * map.sid_name_use = SID_NAME_WKN_GRP;
1585                  */
1586                 map->sid_name_use = SID_NAME_ALIAS;
1587         } else {
1588                 map->sid_name_use = SID_NAME_ALIAS;
1589         }
1590
1591         if (insert) {
1592                 pdb_add_group_mapping_entry(map);
1593         } else {
1594                 pdb_update_group_mapping_entry(map);
1595         }
1596
1597         for (i=0; i < num_members; i++) {
1598                 struct dssync_passdb_mem *mem;
1599
1600                 status = dssync_create_mem(pctx, obj,
1601                                            true /* active */,
1602                                            &members[i], &mem);
1603                 if (!NT_STATUS_IS_OK(status)) {
1604                         goto done;
1605                 }
1606         }
1607
1608         status = NT_STATUS_OK;
1609
1610 done:
1611         TALLOC_FREE(map);
1612         return status;
1613 }
1614
1615 /****************************************************************
1616 ****************************************************************/
1617
1618 static NTSTATUS handle_group_object(struct dssync_passdb *pctx,
1619                                     TALLOC_CTX *mem_ctx,
1620                                     struct dssync_passdb_obj *obj)
1621 {
1622         struct drsuapi_DsReplicaObjectListItemEx *cur = obj->cur;
1623         NTSTATUS status;
1624         struct group *grp = NULL;
1625         struct dom_sid group_sid;
1626         fstring sid_string;
1627         GROUP_MAP *map;
1628         bool insert = true;
1629
1630         const char *sAMAccountName;
1631         uint32_t sAMAccountType;
1632         uint32_t groupType;
1633         const char *description;
1634         uint32_t i;
1635         uint32_t num_members = 0;
1636         struct drsuapi_DsReplicaObjectIdentifier3 *members = NULL;
1637
1638         group_sid = cur->object.identifier->sid;
1639         GET_STRING_EX(sAMAccountName, true);
1640         GET_UINT32_EX(sAMAccountType, true);
1641         GET_UINT32_EX(groupType, true);
1642         GET_STRING(description);
1643
1644         status = find_drsuapi_attr_dn(obj, cur, DRSUAPI_ATTID_member,
1645                                       &num_members, &members);
1646         if (NT_STATUS_EQUAL(status, NT_STATUS_PROPSET_NOT_FOUND)) {
1647                 status = NT_STATUS_OK;
1648         }
1649         if (!NT_STATUS_IS_OK(status)) {
1650                 return status;
1651         }
1652
1653         map = talloc_zero(NULL, GROUP_MAP);
1654         if (!map) {
1655                 return NT_STATUS_NO_MEMORY;
1656         }
1657
1658         map->nt_name = talloc_strdup(map, sAMAccountName);
1659         if (!map->nt_name) {
1660                 status = NT_STATUS_NO_MEMORY;
1661                 goto done;
1662         }
1663         if (description) {
1664                 map->comment = talloc_strdup(map, description);
1665         } else {
1666                 map->comment = talloc_strdup(map, "");
1667         }
1668         if (!map->comment) {
1669                 status = NT_STATUS_NO_MEMORY;
1670                 goto done;
1671         }
1672
1673         sid_to_fstring(sid_string, &group_sid);
1674         DEBUG(0,("Creating group[%s] - %s members [%u]\n",
1675                   map->nt_name, sid_string, num_members));
1676
1677         status = dssync_insert_obj(pctx, pctx->groups, obj);
1678         if (!NT_STATUS_IS_OK(status)) {
1679                 goto done;
1680         }
1681
1682         if (pdb_getgrsid(map, group_sid)) {
1683                 if (map->gid != -1) {
1684                         grp = getgrgid(map->gid);
1685                 }
1686                 insert = false;
1687         }
1688
1689         if (grp == NULL) {
1690                 gid_t gid;
1691
1692                 /* No group found from mapping, find it from its name. */
1693                 if ((grp = getgrnam(map->nt_name)) == NULL) {
1694
1695                         /* No appropriate group found, create one */
1696
1697                         DEBUG(0, ("Creating unix group: '%s'\n",
1698                                                         map->nt_name));
1699
1700                         if (smb_create_group(map->nt_name, &gid) != 0) {
1701                                 status = NT_STATUS_ACCESS_DENIED;
1702                                 goto done;
1703                         }
1704
1705                         if ((grp = getgrnam(map->nt_name)) == NULL) {
1706                                 status = NT_STATUS_ACCESS_DENIED;
1707                                 goto done;
1708                         }
1709                 }
1710         }
1711
1712         map->gid = grp->gr_gid;
1713         map->sid = group_sid;
1714         map->sid_name_use = SID_NAME_DOM_GRP;
1715
1716         if (insert) {
1717                 pdb_add_group_mapping_entry(map);
1718         } else {
1719                 pdb_update_group_mapping_entry(map);
1720         }
1721
1722         for (i=0; i < num_members; i++) {
1723                 struct dssync_passdb_mem *mem;
1724
1725                 status = dssync_create_mem(pctx, obj,
1726                                            true /* active */,
1727                                            &members[i], &mem);
1728                 if (!NT_STATUS_IS_OK(status)) {
1729                         goto done;
1730                 }
1731         }
1732
1733         status = NT_STATUS_OK;
1734
1735 done:
1736         TALLOC_FREE(map);
1737         return status;
1738 }
1739
1740 /****************************************************************
1741 ****************************************************************/
1742
1743 static NTSTATUS handle_interdomain_trust_object(struct dssync_passdb *pctx,
1744                                                 TALLOC_CTX *mem_ctx,
1745                                                 struct dssync_passdb_obj *obj)
1746 {
1747         struct drsuapi_DsReplicaObjectListItemEx *cur = obj->cur;
1748         DEBUG(0,("trust: %s\n", cur->object.identifier->dn));
1749         return NT_STATUS_NOT_IMPLEMENTED;
1750 }
1751
1752 /****************************************************************
1753 ****************************************************************/
1754
1755 struct dssync_object_table_t {
1756         uint32_t type;
1757         NTSTATUS (*fn) (struct dssync_passdb *pctx,
1758                         TALLOC_CTX *mem_ctx,
1759                         struct dssync_passdb_obj *obj);
1760 };
1761
1762 static const struct dssync_object_table_t dssync_object_table[] = {
1763         { ATYPE_NORMAL_ACCOUNT,         handle_account_object },
1764         { ATYPE_WORKSTATION_TRUST,      handle_account_object },
1765         { ATYPE_SECURITY_LOCAL_GROUP,   handle_alias_object },
1766         { ATYPE_SECURITY_GLOBAL_GROUP,  handle_group_object },
1767         { ATYPE_INTERDOMAIN_TRUST,      handle_interdomain_trust_object },
1768 };
1769
1770 /****************************************************************
1771 ****************************************************************/
1772
1773 static NTSTATUS parse_object(struct dssync_passdb *pctx,
1774                              TALLOC_CTX *mem_ctx,
1775                              struct drsuapi_DsReplicaObjectListItemEx *cur)
1776 {
1777         NTSTATUS status = NT_STATUS_OK;
1778         DATA_BLOB *blob;
1779         int i = 0;
1780         int a = 0;
1781         struct drsuapi_DsReplicaAttribute *attr;
1782
1783         char *name = NULL;
1784         uint32_t uacc = 0;
1785         uint32_t sam_type = 0;
1786
1787         DEBUG(3, ("parsing object '%s'\n", cur->object.identifier->dn));
1788
1789         for (i=0; i < cur->object.attribute_ctr.num_attributes; i++) {
1790
1791                 attr = &cur->object.attribute_ctr.attributes[i];
1792
1793                 if (attr->value_ctr.num_values != 1) {
1794                         continue;
1795                 }
1796
1797                 if (!attr->value_ctr.values[0].blob) {
1798                         continue;
1799                 }
1800
1801                 blob = attr->value_ctr.values[0].blob;
1802
1803                 switch (attr->attid) {
1804                         case DRSUAPI_ATTID_sAMAccountName:
1805                                 pull_string_talloc(mem_ctx, NULL, 0, &name,
1806                                                    blob->data, blob->length,
1807                                                    STR_UNICODE);
1808                                 break;
1809                         case DRSUAPI_ATTID_sAMAccountType:
1810                                 sam_type = IVAL(blob->data, 0);
1811                                 break;
1812                         case DRSUAPI_ATTID_userAccountControl:
1813                                 uacc = IVAL(blob->data, 0);
1814                                 break;
1815                         default:
1816                                 break;
1817                 }
1818         }
1819
1820         for (a=0; a < ARRAY_SIZE(dssync_object_table); a++) {
1821                 if (sam_type == dssync_object_table[a].type) {
1822                         if (dssync_object_table[a].fn) {
1823                                 struct dssync_passdb_obj *obj;
1824                                 status = dssync_create_obj(pctx, pctx->all,
1825                                                            sam_type, cur, &obj);
1826                                 if (!NT_STATUS_IS_OK(status)) {
1827                                         break;
1828                                 }
1829                                 status = dssync_object_table[a].fn(pctx,
1830                                                                    mem_ctx,
1831                                                                    obj);
1832                                 break;
1833                         }
1834                 }
1835         }
1836
1837         return status;
1838 }
1839
1840 static NTSTATUS parse_link(struct dssync_passdb *pctx,
1841                            TALLOC_CTX *mem_ctx,
1842                            struct drsuapi_DsReplicaLinkedAttribute *cur)
1843 {
1844         struct drsuapi_DsReplicaObjectIdentifier3 *id3;
1845         const DATA_BLOB *blob;
1846         enum ndr_err_code ndr_err;
1847         NTSTATUS status;
1848         bool active = false;
1849         struct dssync_passdb_mem *mem;
1850         struct dssync_passdb_obj *obj;
1851
1852         if (cur->attid != DRSUAPI_ATTID_member) {
1853                 return NT_STATUS_OK;
1854         }
1855
1856         if (cur->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE) {
1857                 active = true;
1858         }
1859
1860         DEBUG(3, ("parsing link '%s' - %s\n",
1861                   cur->identifier->dn, active?"adding":"deleting"));
1862
1863         blob = cur->value.blob;
1864
1865         if (blob == NULL) {
1866                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1867         }
1868
1869         obj = dssync_search_obj_by_guid(pctx, pctx->all, &cur->identifier->guid);
1870         if (obj == NULL) {
1871                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1872         }
1873
1874         id3 = talloc_zero(obj, struct drsuapi_DsReplicaObjectIdentifier3);
1875         if (id3 == NULL) {
1876                 return NT_STATUS_NO_MEMORY;
1877         }
1878
1879         /* windows sometimes sends an extra two pad bytes here */
1880         ndr_err = ndr_pull_struct_blob(blob, id3, id3,
1881                         (ndr_pull_flags_fn_t)ndr_pull_drsuapi_DsReplicaObjectIdentifier3);
1882         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1883                 status = ndr_map_error2ntstatus(ndr_err);
1884                 return status;
1885         }
1886
1887         status = dssync_create_mem(pctx, obj,
1888                                    active,
1889                                    id3, &mem);
1890         if (!NT_STATUS_IS_OK(status)) {
1891                 return status;
1892         }
1893
1894         return NT_STATUS_OK;
1895 }
1896
1897 /****************************************************************
1898 ****************************************************************/
1899
1900 static NTSTATUS passdb_process_objects(struct dssync_context *ctx,
1901                                        TALLOC_CTX *mem_ctx,
1902                                        struct drsuapi_DsReplicaObjectListItemEx *cur,
1903                                        struct drsuapi_DsReplicaOIDMapping_Ctr *mapping_ctr)
1904 {
1905         NTSTATUS status = NT_STATUS_OK;
1906         struct dssync_passdb *pctx =
1907                 talloc_get_type_abort(ctx->private_data,
1908                 struct dssync_passdb);
1909
1910         for (; cur; cur = cur->next_object) {
1911                 status = parse_object(pctx, mem_ctx, cur);
1912                 if (!NT_STATUS_IS_OK(status)) {
1913                         goto out;
1914                 }
1915         }
1916
1917  out:
1918         return status;
1919 }
1920
1921 /****************************************************************
1922 ****************************************************************/
1923
1924 static NTSTATUS passdb_process_links(struct dssync_context *ctx,
1925                                      TALLOC_CTX *mem_ctx,
1926                                      uint32_t count,
1927                                      struct drsuapi_DsReplicaLinkedAttribute *links,
1928                                      struct drsuapi_DsReplicaOIDMapping_Ctr *mapping_ctr)
1929 {
1930         NTSTATUS status = NT_STATUS_OK;
1931         struct dssync_passdb *pctx =
1932                 talloc_get_type_abort(ctx->private_data,
1933                 struct dssync_passdb);
1934         uint32_t i;
1935
1936         for (i = 0; i < count; i++) {
1937                 status = parse_link(pctx, mem_ctx, &links[i]);
1938                 if (!NT_STATUS_IS_OK(status)) {
1939                         goto out;
1940                 }
1941         }
1942
1943  out:
1944         return status;
1945 }
1946
1947 /****************************************************************
1948 ****************************************************************/
1949
1950 const struct dssync_ops libnet_dssync_passdb_ops = {
1951         .startup                = passdb_startup,
1952         .process_objects        = passdb_process_objects,
1953         .process_links          = passdb_process_links,
1954         .finish                 = passdb_finish,
1955 };