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