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