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