/* Unix SMB/CIFS implementation. Copyright (C) Guenther Deschner 2008 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "includes.h" #include "system/passwd.h" #include "libnet/libnet_dssync.h" #include "../libcli/security/security.h" #include "../libds/common/flags.h" #include "../librpc/gen_ndr/ndr_drsuapi.h" #include "util_tdb.h" #include "dbwrap/dbwrap.h" #include "dbwrap/dbwrap_rbt.h" #include "../libds/common/flag_mapping.h" #include "passdb.h" #include "lib/util/base64.h" #include "lib/util/string_wrappers.h" /**************************************************************** ****************************************************************/ struct dssync_passdb { struct pdb_methods *methods; struct db_context *all; struct db_context *aliases; struct db_context *groups; }; struct dssync_passdb_obj { struct dssync_passdb_obj *self; uint32_t type; struct drsuapi_DsReplicaObjectListItemEx *cur; TDB_DATA key; TDB_DATA data; struct db_context *members; }; struct dssync_passdb_mem { struct dssync_passdb_mem *self; bool active; struct drsuapi_DsReplicaObjectIdentifier3 *cur; struct dssync_passdb_obj *obj; TDB_DATA key; TDB_DATA data; }; static NTSTATUS dssync_insert_obj(struct dssync_passdb *pctx, struct db_context *db, struct dssync_passdb_obj *obj) { NTSTATUS status; struct db_record *rec; TDB_DATA value; rec = dbwrap_fetch_locked(db, talloc_tos(), obj->key); if (rec == NULL) { return NT_STATUS_NO_MEMORY; } value = dbwrap_record_get_value(rec); if (value.dsize != 0) { abort(); } status = dbwrap_record_store(rec, obj->data, TDB_INSERT); if (!NT_STATUS_IS_OK(status)) { TALLOC_FREE(rec); return status; } TALLOC_FREE(rec); return NT_STATUS_OK; } static struct dssync_passdb_obj *dssync_parse_obj(const TDB_DATA data) { struct dssync_passdb_obj *obj; if (data.dsize != sizeof(obj)) { return NULL; } /* * we need to copy the pointer to avoid alignment problems * on some systems. */ memcpy(&obj, data.dptr, sizeof(obj)); return talloc_get_type_abort(obj, struct dssync_passdb_obj); } static struct dssync_passdb_obj *dssync_search_obj_by_guid(struct dssync_passdb *pctx, struct db_context *db, const struct GUID *guid) { struct dssync_passdb_obj *obj; TDB_DATA key; TDB_DATA data; NTSTATUS status; key = make_tdb_data((const uint8_t *)(const void *)guid, sizeof(*guid)); status = dbwrap_fetch(db, talloc_tos(), key, &data); if (!NT_STATUS_IS_OK(status)) { return NULL; } obj = dssync_parse_obj(data); return obj; } static NTSTATUS dssync_create_obj(struct dssync_passdb *pctx, struct db_context *db, uint32_t type, struct drsuapi_DsReplicaObjectListItemEx *cur, struct dssync_passdb_obj **_obj) { NTSTATUS status; struct dssync_passdb_obj *obj; obj = talloc_zero(pctx, struct dssync_passdb_obj); if (obj == NULL) { return NT_STATUS_NO_MEMORY; } obj->self = obj; obj->cur = cur; obj->type = type; obj->key = make_tdb_data((const uint8_t *)(void *)&cur->object.identifier->guid, sizeof(cur->object.identifier->guid)); obj->data = make_tdb_data((const uint8_t *)(void *)&obj->self, sizeof(obj->self)); obj->members = db_open_rbt(obj); if (obj->members == NULL) { return NT_STATUS_NO_MEMORY; } status = dssync_insert_obj(pctx, db, obj); if (!NT_STATUS_IS_OK(status)) { TALLOC_FREE(obj); return status; } *_obj = obj; return NT_STATUS_OK; } static NTSTATUS dssync_insert_mem(struct dssync_passdb *pctx, struct dssync_passdb_obj *obj, struct dssync_passdb_mem *mem) { NTSTATUS status; struct db_record *rec; TDB_DATA value; rec = dbwrap_fetch_locked(obj->members, talloc_tos(), mem->key); if (rec == NULL) { return NT_STATUS_NO_MEMORY; } value = dbwrap_record_get_value(rec); if (value.dsize != 0) { abort(); } status = dbwrap_record_store(rec, mem->data, TDB_INSERT); if (!NT_STATUS_IS_OK(status)) { TALLOC_FREE(rec); return status; } TALLOC_FREE(rec); return NT_STATUS_OK; } static NTSTATUS dssync_create_mem(struct dssync_passdb *pctx, struct dssync_passdb_obj *obj, bool active, struct drsuapi_DsReplicaObjectIdentifier3 *cur, struct dssync_passdb_mem **_mem) { NTSTATUS status; struct dssync_passdb_mem *mem; mem = talloc_zero(pctx, struct dssync_passdb_mem); if (mem == NULL) { return NT_STATUS_NO_MEMORY; } mem->self = mem; mem->cur = cur; mem->active = active; mem->obj = NULL; mem->key = make_tdb_data((const uint8_t *)(void *)&cur->guid, sizeof(cur->guid)); mem->data = make_tdb_data((const uint8_t *)(void *)&mem->self, sizeof(mem->self)); status = dssync_insert_mem(pctx, obj, mem); if (!NT_STATUS_IS_OK(status)) { TALLOC_FREE(obj); return status; } *_mem = mem; return NT_STATUS_OK; } static struct dssync_passdb_mem *dssync_parse_mem(const TDB_DATA data) { struct dssync_passdb_mem *mem; if (data.dsize != sizeof(mem)) { return NULL; } /* * we need to copy the pointer to avoid alignment problems * on some systems. */ memcpy(&mem, data.dptr, sizeof(mem)); return talloc_get_type_abort(mem, struct dssync_passdb_mem); } static NTSTATUS passdb_startup(struct dssync_context *ctx, TALLOC_CTX *mem_ctx, struct replUpToDateVectorBlob **pold_utdv) { NTSTATUS status; struct dssync_passdb *pctx; pctx = talloc_zero(mem_ctx, struct dssync_passdb); if (pctx == NULL) { return NT_STATUS_NO_MEMORY; } if (ctx->output_filename) { status = make_pdb_method_name(&pctx->methods, ctx->output_filename); } else { status = make_pdb_method_name(&pctx->methods, lp_passdb_backend()); } if (!NT_STATUS_IS_OK(status)) { return status; } pctx->all = db_open_rbt(pctx); if (pctx->all == NULL) { return NT_STATUS_NO_MEMORY; } pctx->aliases = db_open_rbt(pctx); if (pctx->aliases == NULL) { return NT_STATUS_NO_MEMORY; } pctx->groups = db_open_rbt(pctx); if (pctx->groups == NULL) { return NT_STATUS_NO_MEMORY; } ctx->private_data = pctx; return status; } /**************************************************************** ****************************************************************/ struct dssync_passdb_traverse_amembers { struct dssync_context *ctx; struct dssync_passdb_obj *obj; const char *name; uint32_t idx; }; struct dssync_passdb_traverse_aliases { struct dssync_context *ctx; const char *name; uint32_t idx; }; static int dssync_passdb_traverse_amembers(struct db_record *rec, void *private_data) { struct dssync_passdb_traverse_amembers *state = (struct dssync_passdb_traverse_amembers *)private_data; struct dssync_passdb *pctx = talloc_get_type_abort(state->ctx->private_data, struct dssync_passdb); struct dssync_passdb_mem *mem; NTSTATUS status; struct dom_sid alias_sid; struct dom_sid member_sid; struct dom_sid_buf buf1, buf2; const char *member_dn; size_t num_members; size_t i; struct dom_sid *members; bool is_member = false; const char *action; TDB_DATA value; state->idx++; alias_sid = state->obj->cur->object.identifier->sid; value = dbwrap_record_get_value(rec); mem = dssync_parse_mem(value); if (mem == NULL) { return -1; } member_sid = mem->cur->sid; member_dn = mem->cur->dn; mem->obj = dssync_search_obj_by_guid(pctx, pctx->all, &mem->cur->guid); if (mem->obj == NULL) { DEBUG(0,("alias[%s] member[%s] can't resolve member - ignoring\n", dom_sid_str_buf(&alias_sid, &buf1), is_null_sid(&member_sid)? dom_sid_str_buf(&member_sid, &buf2): member_dn)); return 0; } switch (mem->obj->type) { case ATYPE_DISTRIBUTION_LOCAL_GROUP: case ATYPE_DISTRIBUTION_GLOBAL_GROUP: DEBUG(0, ("alias[%s] ignore distribution group [%s]\n", dom_sid_str_buf(&alias_sid, &buf1), member_dn)); return 0; default: break; } DEBUG(0,("alias[%s] member[%s]\n", dom_sid_str_buf(&alias_sid, &buf1), dom_sid_str_buf(&member_sid, &buf2))); status = pdb_enum_aliasmem(&alias_sid, talloc_tos(), &members, &num_members); if (!NT_STATUS_IS_OK(status)) { DEBUG(0, ("Could not find current alias members %s - %s\n", dom_sid_str_buf(&alias_sid, &buf1), nt_errstr(status))); return -1; } for (i=0; i < num_members; i++) { bool match; match = dom_sid_equal(&members[i], &member_sid); if (match) { is_member = true; break; } } status = NT_STATUS_OK; action = "none"; if (!is_member && mem->active) { action = "add"; pdb_add_aliasmem(&alias_sid, &member_sid); } else if (is_member && !mem->active) { action = "delete"; pdb_del_aliasmem(&alias_sid, &member_sid); } if (!NT_STATUS_IS_OK(status)) { DEBUG(0, ("Could not %s %s as alias members of %s - %s\n", action, dom_sid_str_buf(&member_sid, &buf1), dom_sid_str_buf(&alias_sid, &buf2), nt_errstr(status))); return -1; } return 0; } static int dssync_passdb_traverse_aliases(struct db_record *rec, void *private_data) { struct dssync_passdb_traverse_aliases *state = (struct dssync_passdb_traverse_aliases *)private_data; struct dssync_passdb *pctx = talloc_get_type_abort(state->ctx->private_data, struct dssync_passdb); struct dssync_passdb_traverse_amembers mstate; struct dssync_passdb_obj *obj; TDB_DATA value; NTSTATUS status; state->idx++; if (pctx->methods == NULL) { return -1; } value = dbwrap_record_get_value(rec); obj = dssync_parse_obj(value); if (obj == NULL) { return -1; } ZERO_STRUCT(mstate); mstate.ctx = state->ctx; mstate.name = "members"; mstate.obj = obj; status = dbwrap_traverse_read(obj->members, dssync_passdb_traverse_amembers, &mstate, NULL); if (!NT_STATUS_IS_OK(status)) { return -1; } return 0; } struct dssync_passdb_traverse_gmembers { struct dssync_context *ctx; struct dssync_passdb_obj *obj; const char *name; uint32_t idx; }; struct dssync_passdb_traverse_groups { struct dssync_context *ctx; const char *name; uint32_t idx; }; static int dssync_passdb_traverse_gmembers(struct db_record *rec, void *private_data) { struct dssync_passdb_traverse_gmembers *state = (struct dssync_passdb_traverse_gmembers *)private_data; struct dssync_passdb *pctx = talloc_get_type_abort(state->ctx->private_data, struct dssync_passdb); struct dssync_passdb_mem *mem; NTSTATUS status; char *nt_member = NULL; char **unix_members; struct dom_sid group_sid; struct dom_sid member_sid; struct dom_sid_buf buf1, buf2; struct samu *member = NULL; const char *member_dn = NULL; GROUP_MAP *map; struct group *grp; uint32_t rid; bool is_unix_member = false; TDB_DATA value; state->idx++; group_sid = state->obj->cur->object.identifier->sid; status = dom_sid_split_rid(talloc_tos(), &group_sid, NULL, &rid); if (!NT_STATUS_IS_OK(status)) { return -1; } value = dbwrap_record_get_value(rec); mem = dssync_parse_mem(value); if (mem == NULL) { return -1; } member_sid = mem->cur->sid; member_dn = mem->cur->dn; mem->obj = dssync_search_obj_by_guid(pctx, pctx->all, &mem->cur->guid); if (mem->obj == NULL) { DEBUG(0,("group[%s] member[%s] can't resolve member - ignoring\n", dom_sid_str_buf(&group_sid, &buf1), is_null_sid(&member_sid)? dom_sid_str_buf(&member_sid, &buf2): member_dn)); return 0; } member_sid = mem->obj->cur->object.identifier->sid; member_dn = mem->obj->cur->object.identifier->dn; switch (mem->obj->type) { case ATYPE_SECURITY_LOCAL_GROUP: case ATYPE_SECURITY_GLOBAL_GROUP: DEBUG(0, ("Group[%s] ignore member group [%s]\n", dom_sid_str_buf(&group_sid, &buf1), dom_sid_str_buf(&member_sid, &buf2))); return 0; case ATYPE_DISTRIBUTION_LOCAL_GROUP: case ATYPE_DISTRIBUTION_GLOBAL_GROUP: DEBUG(0, ("Group[%s] ignore distribution group [%s]\n", dom_sid_str_buf(&group_sid, &buf1), member_dn)); return 0; default: break; } map = talloc_zero(NULL, GROUP_MAP); if (!map) { return -1; } if (!get_domain_group_from_sid(group_sid, map)) { DEBUG(0, ("Could not find global group %s\n", dom_sid_str_buf(&group_sid, &buf1))); //return NT_STATUS_NO_SUCH_GROUP; TALLOC_FREE(map); return -1; } if (!(grp = getgrgid(map->gid))) { DEBUG(0, ("Could not find unix group %lu\n", (unsigned long)map->gid)); //return NT_STATUS_NO_SUCH_GROUP; TALLOC_FREE(map); return -1; } TALLOC_FREE(map); DEBUG(0,("Group members of %s: ", grp->gr_name)); if ( !(member = samu_new(talloc_tos())) ) { //return NT_STATUS_NO_MEMORY; return -1; } if (!pdb_getsampwsid(member, &member_sid)) { DEBUG(1, ("Found bogus group member: (member_sid=%s group=%s)\n", dom_sid_str_buf(&member_sid, &buf1), grp->gr_name)); TALLOC_FREE(member); return -1; } if (pdb_get_group_rid(member) == rid) { DEBUGADD(0,("%s(primary),", pdb_get_username(member))); TALLOC_FREE(member); return -1; } DEBUGADD(0,("%s,", pdb_get_username(member))); nt_member = talloc_strdup(talloc_tos(), pdb_get_username(member)); TALLOC_FREE(member); DEBUGADD(0,("\n")); unix_members = grp->gr_mem; while (*unix_members) { if (strcmp(*unix_members, nt_member) == 0) { is_unix_member = true; break; } unix_members += 1; } if (!is_unix_member && mem->active) { smb_add_user_group(grp->gr_name, nt_member); } else if (is_unix_member && !mem->active) { smb_delete_user_group(grp->gr_name, nt_member); } return 0; } static int dssync_passdb_traverse_groups(struct db_record *rec, void *private_data) { struct dssync_passdb_traverse_groups *state = (struct dssync_passdb_traverse_groups *)private_data; struct dssync_passdb *pctx = talloc_get_type_abort(state->ctx->private_data, struct dssync_passdb); struct dssync_passdb_traverse_gmembers mstate; struct dssync_passdb_obj *obj; TDB_DATA value; NTSTATUS status; state->idx++; if (pctx->methods == NULL) { return -1; } value = dbwrap_record_get_value(rec); obj = dssync_parse_obj(value); if (obj == NULL) { return -1; } ZERO_STRUCT(mstate); mstate.ctx = state->ctx; mstate.name = "members"; mstate.obj = obj; status = dbwrap_traverse_read(obj->members, dssync_passdb_traverse_gmembers, &mstate, NULL); if (!NT_STATUS_IS_OK(status)) { return -1; } return 0; } static NTSTATUS passdb_finish(struct dssync_context *ctx, TALLOC_CTX *mem_ctx, struct replUpToDateVectorBlob *new_utdv) { struct dssync_passdb *pctx = talloc_get_type_abort(ctx->private_data, struct dssync_passdb); struct dssync_passdb_traverse_aliases astate; struct dssync_passdb_traverse_groups gstate; NTSTATUS status; ZERO_STRUCT(astate); astate.ctx = ctx; astate.name = "aliases"; status = dbwrap_traverse_read(pctx->aliases, dssync_passdb_traverse_aliases, &astate, NULL); if (!NT_STATUS_IS_OK(status)) { return NT_STATUS_INTERNAL_ERROR; } ZERO_STRUCT(gstate); gstate.ctx = ctx; gstate.name = "groups"; status = dbwrap_traverse_read(pctx->groups, dssync_passdb_traverse_groups, &gstate, NULL); if (!NT_STATUS_IS_OK(status)) { return NT_STATUS_INTERNAL_ERROR; } TALLOC_FREE(pctx->methods); TALLOC_FREE(pctx); return NT_STATUS_OK; } /**************************************************************** ****************************************************************/ static NTSTATUS smb_create_user(TALLOC_CTX *mem_ctx, uint32_t acct_flags, const char *account, struct passwd **passwd_p) { const struct loadparm_substitution *lp_sub = loadparm_s3_global_substitution(); struct passwd *passwd; char *add_script = NULL; passwd = Get_Pwnam_alloc(mem_ctx, account); if (passwd) { *passwd_p = passwd; return NT_STATUS_OK; } /* Create appropriate user */ if (acct_flags & ACB_NORMAL) { add_script = lp_add_user_script(mem_ctx, lp_sub); } else if ( (acct_flags & ACB_WSTRUST) || (acct_flags & ACB_SVRTRUST) || (acct_flags & ACB_DOMTRUST) ) { add_script = lp_add_machine_script(mem_ctx, lp_sub); } else { DEBUG(1, ("Unknown user type: %s\n", pdb_encode_acct_ctrl(acct_flags, NEW_PW_FORMAT_SPACE_PADDED_LEN))); return NT_STATUS_UNSUCCESSFUL; } if (!add_script) { return NT_STATUS_NO_MEMORY; } if (*add_script) { int add_ret; add_script = talloc_all_string_sub(mem_ctx, add_script, "%u", account); if (!add_script) { return NT_STATUS_NO_MEMORY; } add_ret = smbrun(add_script, NULL, NULL); DEBUG(add_ret ? 0 : 1,("fetch_account: Running the command `%s' " "gave %d\n", add_script, add_ret)); if (add_ret == 0) { smb_nscd_flush_user_cache(); } } /* try and find the possible unix account again */ passwd = Get_Pwnam_alloc(mem_ctx, account); if (!passwd) { return NT_STATUS_NO_SUCH_USER; } *passwd_p = passwd; return NT_STATUS_OK; } static struct drsuapi_DsReplicaAttribute *find_drsuapi_attr( const struct drsuapi_DsReplicaObjectListItemEx *cur, uint32_t attid) { uint32_t i = 0; for (i = 0; i < cur->object.attribute_ctr.num_attributes; i++) { struct drsuapi_DsReplicaAttribute *attr; attr = &cur->object.attribute_ctr.attributes[i]; if (attr->attid == attid) { return attr; } } return NULL; } static NTSTATUS find_drsuapi_attr_string(TALLOC_CTX *mem_ctx, const struct drsuapi_DsReplicaObjectListItemEx *cur, uint32_t attid, uint32_t *_count, char ***_array) { struct drsuapi_DsReplicaAttribute *attr; char **array; uint32_t a; attr = find_drsuapi_attr(cur, attid); if (attr == NULL) { return NT_STATUS_PROPSET_NOT_FOUND; } array = talloc_array(mem_ctx, char *, attr->value_ctr.num_values); if (array == NULL) { return NT_STATUS_NO_MEMORY; } for (a = 0; a < attr->value_ctr.num_values; a++) { const DATA_BLOB *blob; ssize_t ret; blob = attr->value_ctr.values[a].blob; if (blob == NULL) { return NT_STATUS_INTERNAL_DB_CORRUPTION; } ret = pull_string_talloc(array, NULL, 0, &array[a], blob->data, blob->length, STR_UNICODE); if (ret == -1) { //TODO return NT_STATUS_INTERNAL_ERROR; } } *_count = attr->value_ctr.num_values; *_array = array; return NT_STATUS_OK; } static NTSTATUS find_drsuapi_attr_int32(TALLOC_CTX *mem_ctx, const struct drsuapi_DsReplicaObjectListItemEx *cur, uint32_t attid, uint32_t *_count, int32_t **_array) { struct drsuapi_DsReplicaAttribute *attr; int32_t *array; uint32_t a; attr = find_drsuapi_attr(cur, attid); if (attr == NULL) { return NT_STATUS_PROPSET_NOT_FOUND; } array = talloc_array(mem_ctx, int32_t, attr->value_ctr.num_values); if (array == NULL) { return NT_STATUS_NO_MEMORY; } for (a = 0; a < attr->value_ctr.num_values; a++) { const DATA_BLOB *blob; blob = attr->value_ctr.values[a].blob; if (blob == NULL) { return NT_STATUS_INTERNAL_DB_CORRUPTION; } if (blob->length != 4) { return NT_STATUS_INTERNAL_DB_CORRUPTION; } array[a] = IVAL(blob->data, 0); } *_count = attr->value_ctr.num_values; *_array = array; return NT_STATUS_OK; } static NTSTATUS find_drsuapi_attr_blob(TALLOC_CTX *mem_ctx, const struct drsuapi_DsReplicaObjectListItemEx *cur, uint32_t attid, uint32_t *_count, DATA_BLOB **_array) { struct drsuapi_DsReplicaAttribute *attr; DATA_BLOB *array; uint32_t a; attr = find_drsuapi_attr(cur, attid); if (attr == NULL) { return NT_STATUS_PROPSET_NOT_FOUND; } array = talloc_array(mem_ctx, DATA_BLOB, attr->value_ctr.num_values); if (array == NULL) { return NT_STATUS_NO_MEMORY; } for (a = 0; a < attr->value_ctr.num_values; a++) { const DATA_BLOB *blob; blob = attr->value_ctr.values[a].blob; if (blob == NULL) { return NT_STATUS_INTERNAL_DB_CORRUPTION; } array[a] = data_blob_talloc(array, blob->data, blob->length); if (array[a].length != blob->length) { return NT_STATUS_NO_MEMORY; } } *_count = attr->value_ctr.num_values; *_array = array; return NT_STATUS_OK; } static NTSTATUS find_drsuapi_attr_int64(TALLOC_CTX *mem_ctx, const struct drsuapi_DsReplicaObjectListItemEx *cur, uint32_t attid, uint32_t *_count, int64_t **_array) { struct drsuapi_DsReplicaAttribute *attr; int64_t *array; uint32_t a; attr = find_drsuapi_attr(cur, attid); if (attr == NULL) { return NT_STATUS_PROPSET_NOT_FOUND; } array = talloc_array(mem_ctx, int64_t, attr->value_ctr.num_values); if (array == NULL) { return NT_STATUS_NO_MEMORY; } for (a = 0; a < attr->value_ctr.num_values; a++) { const DATA_BLOB *blob; blob = attr->value_ctr.values[a].blob; if (blob == NULL) { return NT_STATUS_INTERNAL_DB_CORRUPTION; } if (blob->length != 8) { return NT_STATUS_INTERNAL_DB_CORRUPTION; } array[a] = BVAL(blob->data, 0); } *_count = attr->value_ctr.num_values; *_array = array; return NT_STATUS_OK; } static NTSTATUS find_drsuapi_attr_dn(TALLOC_CTX *mem_ctx, const struct drsuapi_DsReplicaObjectListItemEx *cur, uint32_t attid, uint32_t *_count, struct drsuapi_DsReplicaObjectIdentifier3 **_array) { struct drsuapi_DsReplicaAttribute *attr; struct drsuapi_DsReplicaObjectIdentifier3 *array; uint32_t a; attr = find_drsuapi_attr(cur, attid); if (attr == NULL) { return NT_STATUS_PROPSET_NOT_FOUND; } array = talloc_array(mem_ctx, struct drsuapi_DsReplicaObjectIdentifier3, attr->value_ctr.num_values); if (array == NULL) { return NT_STATUS_NO_MEMORY; } for (a = 0; a < attr->value_ctr.num_values; a++) { const DATA_BLOB *blob; enum ndr_err_code ndr_err; NTSTATUS status; blob = attr->value_ctr.values[a].blob; if (blob == NULL) { return NT_STATUS_INTERNAL_DB_CORRUPTION; } /* windows sometimes sends an extra two pad bytes here */ ndr_err = ndr_pull_struct_blob(blob, array, &array[a], (ndr_pull_flags_fn_t)ndr_pull_drsuapi_DsReplicaObjectIdentifier3); if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { status = ndr_map_error2ntstatus(ndr_err); return status; } } *_count = attr->value_ctr.num_values; *_array = array; return NT_STATUS_OK; } #define GET_BLOB_EX(attr, needed) do { \ NTSTATUS _status; \ uint32_t _cnt; \ DATA_BLOB *_vals = NULL; \ attr = data_blob_null; \ _status = find_drsuapi_attr_blob(mem_ctx, cur, \ DRSUAPI_ATTID_ ## attr, \ &_cnt, &_vals); \ if (NT_STATUS_EQUAL(_status, NT_STATUS_PROPSET_NOT_FOUND)) { \ if (!needed) { \ _status = NT_STATUS_OK; \ _cnt = 0; \ } \ } \ if (!NT_STATUS_IS_OK(_status)) { \ DEBUG(0,(__location__ "attr[%s] %s\n", \ #attr, nt_errstr(_status))); \ return _status; \ } \ if (_cnt == 0) { \ if (needed) { \ talloc_free(_vals); \ DEBUG(0,(__location__ "attr[%s] count[%u]\n", #attr, _cnt)); \ return NT_STATUS_OBJECT_NAME_NOT_FOUND; \ } \ } else if (_cnt > 1) { \ talloc_free(_vals); \ DEBUG(0,(__location__ "attr[%s] count[%u]\n", #attr, _cnt)); \ return NT_STATUS_INTERNAL_DB_CORRUPTION; \ } else { \ attr = _vals[0]; \ (void)talloc_steal(mem_ctx, _vals[0].data); \ } \ talloc_free(_vals); \ } while(0) #define GET_STRING_EX(attr, needed) do { \ NTSTATUS _status; \ uint32_t _cnt; \ char **_vals = NULL; \ attr = NULL; \ _status = find_drsuapi_attr_string(mem_ctx, cur, \ DRSUAPI_ATTID_ ## attr, \ &_cnt, &_vals); \ if (NT_STATUS_EQUAL(_status, NT_STATUS_PROPSET_NOT_FOUND)) { \ if (!needed) { \ _status = NT_STATUS_OK; \ _cnt = 0; \ } \ } \ if (!NT_STATUS_IS_OK(_status)) { \ DEBUG(0,(__location__ "attr[%s] %s\n", \ #attr, nt_errstr(_status))); \ return _status; \ } \ if (_cnt == 0) { \ if (needed) { \ talloc_free(_vals); \ DEBUG(0,(__location__ "attr[%s] count[%u]\n", #attr, _cnt)); \ return NT_STATUS_OBJECT_NAME_NOT_FOUND; \ } \ } else if (_cnt > 1) { \ talloc_free(_vals); \ DEBUG(0,(__location__ "attr[%s] count[%u]\n", #attr, _cnt)); \ return NT_STATUS_INTERNAL_DB_CORRUPTION; \ } else { \ attr = talloc_move(mem_ctx, &_vals[0]); \ } \ talloc_free(_vals); \ } while(0) #define GET_UINT32_EX(attr, needed) do { \ NTSTATUS _status; \ uint32_t _cnt; \ int32_t*_vals = NULL; \ attr = 0; \ _status = find_drsuapi_attr_int32(mem_ctx, cur, \ DRSUAPI_ATTID_ ## attr, \ &_cnt, &_vals); \ if (NT_STATUS_EQUAL(_status, NT_STATUS_PROPSET_NOT_FOUND)) { \ if (!needed) { \ _status = NT_STATUS_OK; \ _cnt = 0; \ } \ } \ if (!NT_STATUS_IS_OK(_status)) { \ DEBUG(0,(__location__ "attr[%s] %s\n", \ #attr, nt_errstr(_status))); \ return _status; \ } \ if (_cnt == 0) { \ if (needed) { \ talloc_free(_vals); \ DEBUG(0,(__location__ "attr[%s] count[%u]\n", #attr, _cnt)); \ return NT_STATUS_OBJECT_NAME_NOT_FOUND; \ } \ } else if (_cnt > 1) { \ talloc_free(_vals); \ DEBUG(0,(__location__ "attr[%s] count[%u]\n", #attr, _cnt)); \ return NT_STATUS_INTERNAL_DB_CORRUPTION; \ } else { \ attr = (uint32_t)_vals[0]; \ } \ talloc_free(_vals); \ } while(0) #define GET_UINT64_EX(attr, needed) do { \ NTSTATUS _status; \ uint32_t _cnt; \ int64_t *_vals = NULL; \ attr = 0; \ _status = find_drsuapi_attr_int64(mem_ctx, cur, \ DRSUAPI_ATTID_ ## attr, \ &_cnt, &_vals); \ if (NT_STATUS_EQUAL(_status, NT_STATUS_PROPSET_NOT_FOUND)) { \ if (!needed) { \ _status = NT_STATUS_OK; \ _cnt = 0; \ } \ } \ if (!NT_STATUS_IS_OK(_status)) { \ DEBUG(0,(__location__ "attr[%s] %s\n", \ #attr, nt_errstr(_status))); \ return _status; \ } \ if (_cnt == 0) { \ if (needed) { \ talloc_free(_vals); \ DEBUG(0,(__location__ "attr[%s] count[%u]\n", #attr, _cnt)); \ return NT_STATUS_OBJECT_NAME_NOT_FOUND; \ } \ } else if (_cnt > 1) { \ talloc_free(_vals); \ DEBUG(0,(__location__ "attr[%s] count[%u]\n", #attr, _cnt)); \ return NT_STATUS_INTERNAL_DB_CORRUPTION; \ } else { \ attr = (uint64_t)_vals[0]; \ } \ talloc_free(_vals); \ } while(0) #define GET_BLOB(attr) GET_BLOB_EX(attr, false) #define GET_STRING(attr) GET_STRING_EX(attr, false) #define GET_UINT32(attr) GET_UINT32_EX(attr, false) #define GET_UINT64(attr) GET_UINT64_EX(attr, false) /* Convert a struct samu_DELTA to a struct samu. */ #define STRING_CHANGED (old_string && !new_string) ||\ (!old_string && new_string) ||\ (old_string && new_string && (strcmp(old_string, new_string) != 0)) #define STRING_CHANGED_NC(s1,s2) ((s1) && !(s2)) ||\ (!(s1) && (s2)) ||\ ((s1) && (s2) && (strcmp((s1), (s2)) != 0)) /**************************************************************** ****************************************************************/ static NTSTATUS sam_account_from_object(struct samu *account, struct drsuapi_DsReplicaObjectListItemEx *cur) { TALLOC_CTX *mem_ctx = account; const char *old_string, *new_string; struct dom_sid_buf buf; time_t unix_time, stored_time; NTSTATUS status; NTTIME lastLogon; NTTIME lastLogoff; NTTIME pwdLastSet; NTTIME accountExpires; const char *sAMAccountName; const char *displayName; const char *homeDirectory; const char *homeDrive; const char *scriptPath; const char *profilePath; const char *description; const char *userWorkstations; DATA_BLOB userParameters; struct dom_sid objectSid; uint32_t primaryGroupID; uint32_t userAccountControl; DATA_BLOB logonHours; uint32_t badPwdCount; uint32_t logonCount; DATA_BLOB unicodePwd; DATA_BLOB dBCSPwd; uint32_t rid = 0; uint32_t acct_flags; uint32_t units_per_week; objectSid = cur->object.identifier->sid; GET_STRING_EX(sAMAccountName, true); DEBUG(0,("sam_account_from_object(%s, %s) start\n", sAMAccountName, dom_sid_str_buf(&objectSid, &buf))); GET_UINT64(lastLogon); GET_UINT64(lastLogoff); GET_UINT64(pwdLastSet); GET_UINT64(accountExpires); GET_STRING(displayName); GET_STRING(homeDirectory); GET_STRING(homeDrive); GET_STRING(scriptPath); GET_STRING(profilePath); GET_STRING(description); GET_STRING(userWorkstations); GET_BLOB(userParameters); GET_UINT32(primaryGroupID); GET_UINT32(userAccountControl); GET_BLOB(logonHours); GET_UINT32(badPwdCount); GET_UINT32(logonCount); GET_BLOB(unicodePwd); GET_BLOB(dBCSPwd); status = dom_sid_split_rid(mem_ctx, &objectSid, NULL, &rid); if (!NT_STATUS_IS_OK(status)) { return status; } acct_flags = ds_uf2acb(userAccountControl); /* Username, fullname, home dir, dir drive, logon script, acct desc, workstations, profile. */ if (sAMAccountName) { old_string = pdb_get_nt_username(account); new_string = sAMAccountName; if (STRING_CHANGED) { pdb_set_nt_username(account, new_string, PDB_CHANGED); } /* Unix username is the same - for sanity */ old_string = pdb_get_username( account ); if (STRING_CHANGED) { pdb_set_username(account, new_string, PDB_CHANGED); } } if (displayName) { old_string = pdb_get_fullname(account); new_string = displayName; if (STRING_CHANGED) pdb_set_fullname(account, new_string, PDB_CHANGED); } if (homeDirectory) { old_string = pdb_get_homedir(account); new_string = homeDirectory; if (STRING_CHANGED) pdb_set_homedir(account, new_string, PDB_CHANGED); } if (homeDrive) { old_string = pdb_get_dir_drive(account); new_string = homeDrive; if (STRING_CHANGED) pdb_set_dir_drive(account, new_string, PDB_CHANGED); } if (scriptPath) { old_string = pdb_get_logon_script(account); new_string = scriptPath; if (STRING_CHANGED) pdb_set_logon_script(account, new_string, PDB_CHANGED); } if (description) { old_string = pdb_get_acct_desc(account); new_string = description; if (STRING_CHANGED) pdb_set_acct_desc(account, new_string, PDB_CHANGED); } if (userWorkstations) { old_string = pdb_get_workstations(account); new_string = userWorkstations; if (STRING_CHANGED) pdb_set_workstations(account, new_string, PDB_CHANGED); } if (profilePath) { old_string = pdb_get_profile_path(account); new_string = profilePath; if (STRING_CHANGED) pdb_set_profile_path(account, new_string, PDB_CHANGED); } if (userParameters.data) { char *newstr = NULL; old_string = pdb_get_munged_dial(account); if (userParameters.length != 0) { newstr = base64_encode_data_blob(talloc_tos(), userParameters); SMB_ASSERT(newstr != NULL); } if (STRING_CHANGED_NC(old_string, newstr)) pdb_set_munged_dial(account, newstr, PDB_CHANGED); TALLOC_FREE(newstr); } /* User and group sid */ if (rid != 0 && pdb_get_user_rid(account) != rid) { pdb_set_user_sid_from_rid(account, rid, PDB_CHANGED); } if (primaryGroupID != 0 && pdb_get_group_rid(account) != primaryGroupID) { pdb_set_group_sid_from_rid(account, primaryGroupID, PDB_CHANGED); } /* Logon and password information */ if (!nt_time_is_zero(&lastLogon)) { unix_time = nt_time_to_unix(lastLogon); stored_time = pdb_get_logon_time(account); if (stored_time != unix_time) pdb_set_logon_time(account, unix_time, PDB_CHANGED); } if (!nt_time_is_zero(&lastLogoff)) { unix_time = nt_time_to_unix(lastLogoff); stored_time = pdb_get_logoff_time(account); if (stored_time != unix_time) pdb_set_logoff_time(account, unix_time,PDB_CHANGED); } /* Logon Divs */ units_per_week = logonHours.length * 8; if (pdb_get_logon_divs(account) != units_per_week) { pdb_set_logon_divs(account, units_per_week, PDB_CHANGED); } /* Logon Hours Len */ if (units_per_week/8 != pdb_get_hours_len(account)) { pdb_set_hours_len(account, units_per_week/8, PDB_CHANGED); } /* Logon Hours */ if (logonHours.data) { char oldstr[44], newstr[44]; pdb_sethexhours(oldstr, pdb_get_hours(account)); pdb_sethexhours(newstr, logonHours.data); if (!strequal(oldstr, newstr)) { pdb_set_hours(account, logonHours.data, logonHours.length, PDB_CHANGED); } } if (pdb_get_bad_password_count(account) != badPwdCount) pdb_set_bad_password_count(account, badPwdCount, PDB_CHANGED); if (pdb_get_logon_count(account) != logonCount) pdb_set_logon_count(account, logonCount, PDB_CHANGED); if (!nt_time_is_zero(&pwdLastSet)) { unix_time = nt_time_to_unix(pwdLastSet); stored_time = pdb_get_pass_last_set_time(account); if (stored_time != unix_time) pdb_set_pass_last_set_time(account, unix_time, PDB_CHANGED); } else { /* no last set time, make it now */ pdb_set_pass_last_set_time(account, time(NULL), PDB_CHANGED); } if (!nt_time_is_zero(&accountExpires)) { unix_time = nt_time_to_unix(accountExpires); stored_time = pdb_get_kickoff_time(account); if (stored_time != unix_time) pdb_set_kickoff_time(account, unix_time, PDB_CHANGED); } /* Decode hashes from password hash Note that win2000 may send us all zeros for the hashes if it doesn't think this channel is secure enough - don't set the passwords at all in that case */ if (dBCSPwd.length == 16 && !all_zero(dBCSPwd.data, 16)) { pdb_set_lanman_passwd(account, dBCSPwd.data, PDB_CHANGED); } if (unicodePwd.length == 16 && !all_zero(unicodePwd.data, 16)) { pdb_set_nt_passwd(account, unicodePwd.data, PDB_CHANGED); } /* TODO: history */ /* TODO: account expiry time */ pdb_set_acct_ctrl(account, acct_flags, PDB_CHANGED); pdb_set_domain(account, lp_workgroup(), PDB_CHANGED); DEBUG(0,("sam_account_from_object(%s, %s) done\n", sAMAccountName, dom_sid_str_buf(&objectSid, &buf))); return NT_STATUS_OK; } /**************************************************************** ****************************************************************/ static NTSTATUS handle_account_object(struct dssync_passdb *pctx, TALLOC_CTX *mem_ctx, struct dssync_passdb_obj *obj) { struct drsuapi_DsReplicaObjectListItemEx *cur = obj->cur; NTSTATUS status; fstring account; struct samu *sam_account=NULL; GROUP_MAP *map; struct group *grp; struct dom_sid user_sid; struct dom_sid group_sid; struct dom_sid_buf buf; struct passwd *passwd = NULL; uint32_t acct_flags; uint32_t rid; const char *sAMAccountName; uint32_t userAccountControl; user_sid = cur->object.identifier->sid; GET_STRING_EX(sAMAccountName, true); GET_UINT32_EX(userAccountControl, true); status = dom_sid_split_rid(mem_ctx, &user_sid, NULL, &rid); if (!NT_STATUS_IS_OK(status)) { return status; } fstrcpy(account, sAMAccountName); if (rid == DOMAIN_RID_GUEST) { /* * pdb_getsampwsid() has special handling for DOMAIN_RID_GUEST * that's why we need to ignore it here. * * pdb_smbpasswd.c also has some DOMAIN_RID_GUEST related * code... */ DEBUG(0,("Ignore %s - %s\n", account, dom_sid_str_buf(&user_sid, &buf))); return NT_STATUS_OK; } DEBUG(0,("Creating account: %s\n", account)); if ( !(sam_account = samu_new(mem_ctx)) ) { return NT_STATUS_NO_MEMORY; } acct_flags = ds_uf2acb(userAccountControl); status = smb_create_user(sam_account, acct_flags, account, &passwd); if (!NT_STATUS_IS_OK(status)) { DEBUG(0,("Could not create posix account info for '%s'- %s\n", account, nt_errstr(status))); TALLOC_FREE(sam_account); return status; } DEBUG(3, ("Attempting to find SID %s for user %s in the passdb\n", dom_sid_str_buf(&user_sid, &buf), account)); if (!pdb_getsampwsid(sam_account, &user_sid)) { sam_account_from_object(sam_account, cur); DEBUG(3, ("Attempting to add user SID %s for user %s in the passdb\n", dom_sid_str_buf(&user_sid, &buf), pdb_get_username(sam_account))); if (!NT_STATUS_IS_OK(pdb_add_sam_account(sam_account))) { DEBUG(1, ("SAM Account for %s failed to be added to the passdb!\n", account)); TALLOC_FREE(sam_account); return NT_STATUS_ACCESS_DENIED; } } else { sam_account_from_object(sam_account, cur); DEBUG(3, ("Attempting to update user SID %s for user %s in the passdb\n", dom_sid_str_buf(&user_sid, &buf), pdb_get_username(sam_account))); if (!NT_STATUS_IS_OK(pdb_update_sam_account(sam_account))) { DEBUG(1, ("SAM Account for %s failed to be updated in the passdb!\n", account)); TALLOC_FREE(sam_account); return NT_STATUS_ACCESS_DENIED; } } if (pdb_get_group_sid(sam_account) == NULL) { TALLOC_FREE(sam_account); return NT_STATUS_UNSUCCESSFUL; } group_sid = *pdb_get_group_sid(sam_account); map = talloc_zero(NULL, GROUP_MAP); if (!map) { return NT_STATUS_NO_MEMORY; } if (!pdb_getgrsid(map, group_sid)) { DEBUG(0, ("Primary group of %s has no mapping!\n", pdb_get_username(sam_account))); } else { if (map->gid != passwd->pw_gid) { if (!(grp = getgrgid(map->gid))) { DEBUG(0, ("Could not find unix group %lu for user %s (group SID=%s)\n", (unsigned long)map->gid, pdb_get_username(sam_account), dom_sid_str_buf(&group_sid, &buf))); } else { smb_set_primary_group(grp->gr_name, pdb_get_username(sam_account)); } } } TALLOC_FREE(map); if ( !passwd ) { DEBUG(1, ("No unix user for this account (%s), cannot adjust mappings\n", pdb_get_username(sam_account))); } TALLOC_FREE(sam_account); return NT_STATUS_OK; } /**************************************************************** ****************************************************************/ static NTSTATUS handle_alias_object(struct dssync_passdb *pctx, TALLOC_CTX *mem_ctx, struct dssync_passdb_obj *obj) { struct drsuapi_DsReplicaObjectListItemEx *cur = obj->cur; NTSTATUS status; struct group *grp = NULL; struct dom_sid group_sid; uint32_t rid = 0; struct dom_sid *dom_sid = NULL; struct dom_sid_buf sid_str; GROUP_MAP *map; bool insert = true; const char *sAMAccountName; const char *description; uint32_t i; uint32_t num_members = 0; struct drsuapi_DsReplicaObjectIdentifier3 *members = NULL; group_sid = cur->object.identifier->sid; GET_STRING_EX(sAMAccountName, true); GET_STRING(description); status = find_drsuapi_attr_dn(obj, cur, DRSUAPI_ATTID_member, &num_members, &members); if (NT_STATUS_EQUAL(status, NT_STATUS_PROPSET_NOT_FOUND)) { status = NT_STATUS_OK; } if (!NT_STATUS_IS_OK(status)) { return status; } dom_sid_split_rid(mem_ctx, &group_sid, &dom_sid, &rid); map = talloc_zero(mem_ctx, GROUP_MAP); if (map == NULL) { status = NT_STATUS_NO_MEMORY; goto done; } map->nt_name = talloc_strdup(map, sAMAccountName); if (map->nt_name == NULL) { status = NT_STATUS_NO_MEMORY; goto done; } if (description) { map->comment = talloc_strdup(map, description); } else { map->comment = talloc_strdup(map, ""); } if (map->comment == NULL) { status = NT_STATUS_NO_MEMORY; goto done; } DEBUG(0,("Creating alias[%s] - %s members[%u]\n", map->nt_name, dom_sid_str_buf(&group_sid, &sid_str), num_members)); status = dssync_insert_obj(pctx, pctx->aliases, obj); if (!NT_STATUS_IS_OK(status)) { goto done; } if (pdb_getgrsid(map, group_sid)) { if (map->gid != -1) { grp = getgrgid(map->gid); } insert = false; } if (grp == NULL) { gid_t gid; /* No group found from mapping, find it from its name. */ if ((grp = getgrnam(map->nt_name)) == NULL) { /* No appropriate group found, create one */ DEBUG(0, ("Creating unix group: '%s'\n", map->nt_name)); if (smb_create_group(map->nt_name, &gid) != 0) { status = NT_STATUS_ACCESS_DENIED; goto done; } if ((grp = getgrgid(gid)) == NULL) { status = NT_STATUS_ACCESS_DENIED; goto done; } } } map->gid = grp->gr_gid; map->sid = group_sid; if (dom_sid_equal(dom_sid, &global_sid_Builtin)) { /* * pdb_ldap does not like SID_NAME_WKN_GRP... * * map.sid_name_use = SID_NAME_WKN_GRP; */ map->sid_name_use = SID_NAME_ALIAS; } else { map->sid_name_use = SID_NAME_ALIAS; } if (insert) { pdb_add_group_mapping_entry(map); } else { pdb_update_group_mapping_entry(map); } for (i=0; i < num_members; i++) { struct dssync_passdb_mem *mem; status = dssync_create_mem(pctx, obj, true /* active */, &members[i], &mem); if (!NT_STATUS_IS_OK(status)) { goto done; } } status = NT_STATUS_OK; done: TALLOC_FREE(map); return status; } /**************************************************************** ****************************************************************/ static NTSTATUS handle_group_object(struct dssync_passdb *pctx, TALLOC_CTX *mem_ctx, struct dssync_passdb_obj *obj) { struct drsuapi_DsReplicaObjectListItemEx *cur = obj->cur; NTSTATUS status; struct group *grp = NULL; struct dom_sid group_sid; struct dom_sid_buf sid_str; GROUP_MAP *map; bool insert = true; const char *sAMAccountName; const char *description; uint32_t i; uint32_t num_members = 0; struct drsuapi_DsReplicaObjectIdentifier3 *members = NULL; group_sid = cur->object.identifier->sid; GET_STRING_EX(sAMAccountName, true); GET_STRING(description); status = find_drsuapi_attr_dn(obj, cur, DRSUAPI_ATTID_member, &num_members, &members); if (NT_STATUS_EQUAL(status, NT_STATUS_PROPSET_NOT_FOUND)) { status = NT_STATUS_OK; } if (!NT_STATUS_IS_OK(status)) { return status; } map = talloc_zero(NULL, GROUP_MAP); if (!map) { return NT_STATUS_NO_MEMORY; } map->nt_name = talloc_strdup(map, sAMAccountName); if (!map->nt_name) { status = NT_STATUS_NO_MEMORY; goto done; } if (description) { map->comment = talloc_strdup(map, description); } else { map->comment = talloc_strdup(map, ""); } if (!map->comment) { status = NT_STATUS_NO_MEMORY; goto done; } DEBUG(0,("Creating group[%s] - %s members [%u]\n", map->nt_name, dom_sid_str_buf(&group_sid, &sid_str), num_members)); status = dssync_insert_obj(pctx, pctx->groups, obj); if (!NT_STATUS_IS_OK(status)) { goto done; } if (pdb_getgrsid(map, group_sid)) { if (map->gid != -1) { grp = getgrgid(map->gid); } insert = false; } if (grp == NULL) { gid_t gid; /* No group found from mapping, find it from its name. */ if ((grp = getgrnam(map->nt_name)) == NULL) { /* No appropriate group found, create one */ DEBUG(0, ("Creating unix group: '%s'\n", map->nt_name)); if (smb_create_group(map->nt_name, &gid) != 0) { status = NT_STATUS_ACCESS_DENIED; goto done; } if ((grp = getgrnam(map->nt_name)) == NULL) { status = NT_STATUS_ACCESS_DENIED; goto done; } } } map->gid = grp->gr_gid; map->sid = group_sid; map->sid_name_use = SID_NAME_DOM_GRP; if (insert) { pdb_add_group_mapping_entry(map); } else { pdb_update_group_mapping_entry(map); } for (i=0; i < num_members; i++) { struct dssync_passdb_mem *mem; status = dssync_create_mem(pctx, obj, true /* active */, &members[i], &mem); if (!NT_STATUS_IS_OK(status)) { goto done; } } status = NT_STATUS_OK; done: TALLOC_FREE(map); return status; } /**************************************************************** ****************************************************************/ static NTSTATUS handle_interdomain_trust_object(struct dssync_passdb *pctx, TALLOC_CTX *mem_ctx, struct dssync_passdb_obj *obj) { struct drsuapi_DsReplicaObjectListItemEx *cur = obj->cur; DEBUG(0,("trust: %s\n", cur->object.identifier->dn)); return NT_STATUS_NOT_IMPLEMENTED; } /**************************************************************** ****************************************************************/ struct dssync_object_table_t { uint32_t type; NTSTATUS (*fn) (struct dssync_passdb *pctx, TALLOC_CTX *mem_ctx, struct dssync_passdb_obj *obj); }; static const struct dssync_object_table_t dssync_object_table[] = { { ATYPE_NORMAL_ACCOUNT, handle_account_object }, { ATYPE_WORKSTATION_TRUST, handle_account_object }, { ATYPE_SECURITY_LOCAL_GROUP, handle_alias_object }, { ATYPE_SECURITY_GLOBAL_GROUP, handle_group_object }, { ATYPE_INTERDOMAIN_TRUST, handle_interdomain_trust_object }, }; /**************************************************************** ****************************************************************/ static NTSTATUS parse_object(struct dssync_passdb *pctx, TALLOC_CTX *mem_ctx, struct drsuapi_DsReplicaObjectListItemEx *cur) { NTSTATUS status = NT_STATUS_OK; DATA_BLOB *blob = NULL; uint32_t i = 0; size_t a = 0; char *name = NULL; uint32_t sam_type = 0; DEBUG(3, ("parsing object '%s'\n", cur->object.identifier->dn)); for (i=0; i < cur->object.attribute_ctr.num_attributes; i++) { struct drsuapi_DsReplicaAttribute *attr = &cur->object.attribute_ctr.attributes[i]; if (attr->value_ctr.num_values != 1) { continue; } if (!attr->value_ctr.values[0].blob) { continue; } blob = attr->value_ctr.values[0].blob; switch (attr->attid) { case DRSUAPI_ATTID_sAMAccountName: pull_string_talloc(mem_ctx, NULL, 0, &name, blob->data, blob->length, STR_UNICODE); break; case DRSUAPI_ATTID_sAMAccountType: sam_type = IVAL(blob->data, 0); break; default: break; } } for (a=0; a < ARRAY_SIZE(dssync_object_table); a++) { if (sam_type == dssync_object_table[a].type) { if (dssync_object_table[a].fn) { struct dssync_passdb_obj *obj = NULL; status = dssync_create_obj(pctx, pctx->all, sam_type, cur, &obj); if (!NT_STATUS_IS_OK(status)) { break; } status = dssync_object_table[a].fn(pctx, mem_ctx, obj); break; } } } return status; } static NTSTATUS parse_link(struct dssync_passdb *pctx, TALLOC_CTX *mem_ctx, struct drsuapi_DsReplicaLinkedAttribute *cur) { struct drsuapi_DsReplicaObjectIdentifier3 *id3; const DATA_BLOB *blob; enum ndr_err_code ndr_err; NTSTATUS status; bool active = false; struct dssync_passdb_mem *mem; struct dssync_passdb_obj *obj; if (cur->attid != DRSUAPI_ATTID_member) { return NT_STATUS_OK; } if (cur->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE) { active = true; } DEBUG(3, ("parsing link '%s' - %s\n", cur->identifier->dn, active?"adding":"deleting")); blob = cur->value.blob; if (blob == NULL) { return NT_STATUS_INTERNAL_DB_CORRUPTION; } obj = dssync_search_obj_by_guid(pctx, pctx->all, &cur->identifier->guid); if (obj == NULL) { return NT_STATUS_INTERNAL_DB_CORRUPTION; } id3 = talloc_zero(obj, struct drsuapi_DsReplicaObjectIdentifier3); if (id3 == NULL) { return NT_STATUS_NO_MEMORY; } /* windows sometimes sends an extra two pad bytes here */ ndr_err = ndr_pull_struct_blob(blob, id3, id3, (ndr_pull_flags_fn_t)ndr_pull_drsuapi_DsReplicaObjectIdentifier3); if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { status = ndr_map_error2ntstatus(ndr_err); return status; } status = dssync_create_mem(pctx, obj, active, id3, &mem); if (!NT_STATUS_IS_OK(status)) { return status; } return NT_STATUS_OK; } /**************************************************************** ****************************************************************/ static NTSTATUS passdb_process_objects(struct dssync_context *ctx, TALLOC_CTX *mem_ctx, struct drsuapi_DsReplicaObjectListItemEx *cur, struct drsuapi_DsReplicaOIDMapping_Ctr *mapping_ctr) { NTSTATUS status = NT_STATUS_OK; struct dssync_passdb *pctx = talloc_get_type_abort(ctx->private_data, struct dssync_passdb); for (; cur; cur = cur->next_object) { status = parse_object(pctx, mem_ctx, cur); if (!NT_STATUS_IS_OK(status)) { goto out; } } out: return status; } /**************************************************************** ****************************************************************/ static NTSTATUS passdb_process_links(struct dssync_context *ctx, TALLOC_CTX *mem_ctx, uint32_t count, struct drsuapi_DsReplicaLinkedAttribute *links, struct drsuapi_DsReplicaOIDMapping_Ctr *mapping_ctr) { NTSTATUS status = NT_STATUS_OK; struct dssync_passdb *pctx = talloc_get_type_abort(ctx->private_data, struct dssync_passdb); uint32_t i; for (i = 0; i < count; i++) { status = parse_link(pctx, mem_ctx, &links[i]); if (!NT_STATUS_IS_OK(status)) { goto out; } } out: return status; } /**************************************************************** ****************************************************************/ const struct dssync_ops libnet_dssync_passdb_ops = { .startup = passdb_startup, .process_objects = passdb_process_objects, .process_links = passdb_process_links, .finish = passdb_finish, };