diff options
Diffstat (limited to 'source3/winbindd/winbindd_samr.c')
-rw-r--r-- | source3/winbindd/winbindd_samr.c | 1355 |
1 files changed, 1355 insertions, 0 deletions
diff --git a/source3/winbindd/winbindd_samr.c b/source3/winbindd/winbindd_samr.c new file mode 100644 index 0000000..92dd185 --- /dev/null +++ b/source3/winbindd/winbindd_samr.c @@ -0,0 +1,1355 @@ +/* + * Unix SMB/CIFS implementation. + * + * Winbind rpc backend functions + * + * Copyright (c) 2000-2003 Tim Potter + * Copyright (c) 2001 Andrew Tridgell + * Copyright (c) 2005 Volker Lendecke + * Copyright (c) 2008 Guenther Deschner (pidl conversion) + * Copyright (c) 2010 Andreas Schneider <asn@samba.org> + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "includes.h" +#include "winbindd.h" +#include "winbindd_rpc.h" +#include "lib/util_unixsids.h" +#include "rpc_client/rpc_client.h" +#include "rpc_client/cli_pipe.h" +#include "../librpc/gen_ndr/ndr_samr_c.h" +#include "rpc_client/cli_samr.h" +#include "../librpc/gen_ndr/ndr_lsa_c.h" +#include "rpc_client/cli_lsarpc.h" +#include "rpc_server/rpc_ncacn_np.h" +#include "../libcli/security/security.h" +#include "passdb/machine_sid.h" +#include "auth.h" +#include "source3/lib/global_contexts.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_WINBIND + +/* + * The other end of this won't go away easily, so we can trust it + * + * It is either a long-lived process with the same lifetime as + * winbindd or a part of this process + */ +struct winbind_internal_pipes { + struct tevent_timer *shutdown_timer; + struct rpc_pipe_client *samr_pipe; + struct policy_handle samr_domain_hnd; + struct rpc_pipe_client *lsa_pipe; + struct policy_handle lsa_hnd; +}; + + +NTSTATUS open_internal_samr_conn(TALLOC_CTX *mem_ctx, + struct winbindd_domain *domain, + struct rpc_pipe_client **samr_pipe, + struct policy_handle *samr_domain_hnd) +{ + NTSTATUS status, result; + struct policy_handle samr_connect_hnd; + struct dcerpc_binding_handle *b; + + status = wb_open_internal_pipe(mem_ctx, &ndr_table_samr, samr_pipe); + if (!NT_STATUS_IS_OK(status)) { + DBG_ERR("Could not connect to %s pipe: %s\n", + ndr_table_samr.name, nt_errstr(status)); + return status; + } + + b = (*samr_pipe)->binding_handle; + + status = dcerpc_samr_Connect2(b, mem_ctx, + (*samr_pipe)->desthost, + SEC_FLAG_MAXIMUM_ALLOWED, + &samr_connect_hnd, + &result); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + if (!NT_STATUS_IS_OK(result)) { + return result; + } + + status = dcerpc_samr_OpenDomain(b, mem_ctx, + &samr_connect_hnd, + SEC_FLAG_MAXIMUM_ALLOWED, + &domain->sid, + samr_domain_hnd, + &result); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + return result; +} + +NTSTATUS open_internal_lsa_conn(TALLOC_CTX *mem_ctx, + struct rpc_pipe_client **lsa_pipe, + struct policy_handle *lsa_hnd) +{ + NTSTATUS status; + + status = wb_open_internal_pipe(mem_ctx, &ndr_table_lsarpc, lsa_pipe); + if (!NT_STATUS_IS_OK(status)) { + DBG_ERR("Could not connect to %s pipe: %s\n", + ndr_table_lsarpc.name, nt_errstr(status)); + return status; + } + + status = rpccli_lsa_open_policy((*lsa_pipe), + mem_ctx, + true, + SEC_FLAG_MAXIMUM_ALLOWED, + lsa_hnd); + + return status; +} + +static void cached_internal_pipe_close( + struct tevent_context *ev, + struct tevent_timer *te, + struct timeval current_time, + void *private_data) +{ + struct winbindd_domain *domain = talloc_get_type_abort( + private_data, struct winbindd_domain); + /* + * Freeing samr_pipes closes the cached pipes. + * + * We can do a hard close because at the time of this commit + * we only use sychronous calls to external pipes. So we can't + * have any outstanding requests. Also, we don't set + * dcerpc_binding_handle_set_sync_ev in winbind, so we don't + * get nested event loops. Once we start to get async in + * winbind children, we need to check for outstanding calls + */ + TALLOC_FREE(domain->backend_data.samr_pipes); +} + +static NTSTATUS open_cached_internal_pipe_conn( + struct winbindd_domain *domain, + struct rpc_pipe_client **samr_pipe, + struct policy_handle *samr_domain_hnd, + struct rpc_pipe_client **lsa_pipe, + struct policy_handle *lsa_hnd) +{ + struct winbind_internal_pipes *internal_pipes = + domain->backend_data.samr_pipes; + + if (internal_pipes == NULL) { + TALLOC_CTX *frame = talloc_stackframe(); + NTSTATUS status; + + internal_pipes = talloc_zero(frame, + struct winbind_internal_pipes); + + status = open_internal_samr_conn( + internal_pipes, + domain, + &internal_pipes->samr_pipe, + &internal_pipes->samr_domain_hnd); + if (!NT_STATUS_IS_OK(status)) { + TALLOC_FREE(frame); + return status; + } + + status = open_internal_lsa_conn(internal_pipes, + &internal_pipes->lsa_pipe, + &internal_pipes->lsa_hnd); + + if (!NT_STATUS_IS_OK(status)) { + TALLOC_FREE(frame); + return status; + } + + internal_pipes->shutdown_timer = tevent_add_timer( + global_event_context(), + internal_pipes, + timeval_current_ofs(5, 0), + cached_internal_pipe_close, + domain); + if (internal_pipes->shutdown_timer == NULL) { + TALLOC_FREE(frame); + return NT_STATUS_NO_MEMORY; + } + + domain->backend_data.samr_pipes = + talloc_steal(domain, internal_pipes); + + TALLOC_FREE(frame); + } + + if (samr_domain_hnd) { + *samr_domain_hnd = internal_pipes->samr_domain_hnd; + } + + if (samr_pipe) { + *samr_pipe = internal_pipes->samr_pipe; + } + + if (lsa_hnd) { + *lsa_hnd = internal_pipes->lsa_hnd; + } + + if (lsa_pipe) { + *lsa_pipe = internal_pipes->lsa_pipe; + } + + tevent_update_timer( + internal_pipes->shutdown_timer, + timeval_current_ofs(5, 0)); + + return NT_STATUS_OK; +} + +static bool reset_connection_on_error(struct winbindd_domain *domain, + struct rpc_pipe_client *p, + NTSTATUS status) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + + if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) || + NT_STATUS_EQUAL(status, NT_STATUS_IO_DEVICE_ERROR)) + { + TALLOC_FREE(domain->backend_data.samr_pipes); + return true; + } + + if (!dcerpc_binding_handle_is_connected(b)) { + TALLOC_FREE(domain->backend_data.samr_pipes); + return true; + } + + return false; +} + +/********************************************************************* + SAM specific functions. +*********************************************************************/ + +/* List all domain groups */ +static NTSTATUS sam_enum_dom_groups(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + uint32_t *pnum_info, + struct wb_acct_info **pinfo) +{ + struct rpc_pipe_client *samr_pipe; + struct policy_handle dom_pol = { 0 }; + struct wb_acct_info *info = NULL; + uint32_t num_info = 0; + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + NTSTATUS status; + bool retry = false; + + DEBUG(3,("sam_enum_dom_groups\n")); + + if (pnum_info) { + *pnum_info = 0; + } + +again: + status = open_cached_internal_pipe_conn(domain, + &samr_pipe, + &dom_pol, + NULL, + NULL); + if (!NT_STATUS_IS_OK(status)) { + TALLOC_FREE(tmp_ctx); + return status; + } + + status = rpc_enum_dom_groups(tmp_ctx, + samr_pipe, + &dom_pol, + &num_info, + &info); + + if (!retry && reset_connection_on_error(domain, samr_pipe, status)) { + retry = true; + goto again; + } + + if (!NT_STATUS_IS_OK(status)) { + TALLOC_FREE(tmp_ctx); + return status; + } + + if (pnum_info) { + *pnum_info = num_info; + } + + if (pinfo) { + *pinfo = talloc_move(mem_ctx, &info); + } + + TALLOC_FREE(tmp_ctx); + return status; +} + +/* Query display info for a domain */ +static NTSTATUS sam_query_user_list(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + uint32_t **prids) +{ + struct rpc_pipe_client *samr_pipe = NULL; + struct policy_handle dom_pol = { 0 }; + uint32_t *rids = NULL; + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + NTSTATUS status; + bool retry = false; + + DEBUG(3,("samr_query_user_list\n")); + +again: + status = open_cached_internal_pipe_conn(domain, + &samr_pipe, + &dom_pol, + NULL, + NULL); + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + + status = rpc_query_user_list(tmp_ctx, + samr_pipe, + &dom_pol, + &domain->sid, + &rids); + if (!retry && reset_connection_on_error(domain, samr_pipe, status)) { + retry = true; + goto again; + } + + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + + if (prids != NULL) { + *prids = talloc_move(mem_ctx, &rids); + } + +done: + TALLOC_FREE(rids); + TALLOC_FREE(tmp_ctx); + return status; +} + +/* get a list of trusted domains - builtin domain */ +static NTSTATUS sam_trusted_domains(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + struct netr_DomainTrustList *ptrust_list) +{ + struct rpc_pipe_client *lsa_pipe; + struct policy_handle lsa_policy = { 0 }; + struct netr_DomainTrust *trusts = NULL; + uint32_t num_trusts = 0; + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + NTSTATUS status; + bool retry = false; + + DEBUG(3,("samr: trusted domains\n")); + + if (ptrust_list) { + ZERO_STRUCTP(ptrust_list); + } + +again: + status = open_cached_internal_pipe_conn(domain, + NULL, + NULL, + &lsa_pipe, + &lsa_policy); + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + + status = rpc_trusted_domains(tmp_ctx, + lsa_pipe, + &lsa_policy, + &num_trusts, + &trusts); + + if (!retry && reset_connection_on_error(domain, lsa_pipe, status)) { + retry = true; + goto again; + } + + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + + if (ptrust_list) { + ptrust_list->count = num_trusts; + ptrust_list->array = talloc_move(mem_ctx, &trusts); + } + +done: + TALLOC_FREE(tmp_ctx); + return status; +} + +/* Lookup group membership given a rid. */ +static NTSTATUS sam_lookup_groupmem(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + const struct dom_sid *group_sid, + enum lsa_SidType type, + uint32_t *pnum_names, + struct dom_sid **psid_mem, + char ***pnames, + uint32_t **pname_types) +{ + struct rpc_pipe_client *samr_pipe; + struct policy_handle dom_pol = { 0 }; + + uint32_t num_names = 0; + struct dom_sid *sid_mem = NULL; + char **names = NULL; + uint32_t *name_types = NULL; + + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + NTSTATUS status; + bool retry = false; + + DEBUG(3,("sam_lookup_groupmem\n")); + + /* Paranoia check */ + if (sid_check_is_in_builtin(group_sid) && (type != SID_NAME_ALIAS)) { + /* There's no groups, only aliases in BUILTIN */ + status = NT_STATUS_NO_SUCH_GROUP; + goto done; + } + + if (pnum_names) { + *pnum_names = 0; + } + +again: + status = open_cached_internal_pipe_conn(domain, + &samr_pipe, + &dom_pol, + NULL, + NULL); + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + + status = rpc_lookup_groupmem(tmp_ctx, + samr_pipe, + &dom_pol, + domain->name, + &domain->sid, + group_sid, + type, + &num_names, + &sid_mem, + &names, + &name_types); + + if (!retry && reset_connection_on_error(domain, samr_pipe, status)) { + retry = true; + goto again; + } + + if (pnum_names) { + *pnum_names = num_names; + } + + if (pnames) { + *pnames = talloc_move(mem_ctx, &names); + } + + if (pname_types) { + *pname_types = talloc_move(mem_ctx, &name_types); + } + + if (psid_mem) { + *psid_mem = talloc_move(mem_ctx, &sid_mem); + } + +done: + TALLOC_FREE(tmp_ctx); + return status; +} + +/********************************************************************* + BUILTIN specific functions. +*********************************************************************/ + +/* List all domain groups */ +static NTSTATUS builtin_enum_dom_groups(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + uint32_t *num_entries, + struct wb_acct_info **info) +{ + /* BUILTIN doesn't have domain groups */ + *num_entries = 0; + *info = NULL; + return NT_STATUS_OK; +} + +/* Query display info for a domain */ +static NTSTATUS builtin_query_user_list(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + uint32_t **rids) +{ + /* We don't have users */ + *rids = NULL; + return NT_STATUS_OK; +} + +/* get a list of trusted domains - builtin domain */ +static NTSTATUS builtin_trusted_domains(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + struct netr_DomainTrustList *trusts) +{ + ZERO_STRUCTP(trusts); + return NT_STATUS_OK; +} + +/********************************************************************* + COMMON functions. +*********************************************************************/ + +/* List all local groups (aliases) */ +static NTSTATUS sam_enum_local_groups(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + uint32_t *pnum_info, + struct wb_acct_info **pinfo) +{ + struct rpc_pipe_client *samr_pipe; + struct policy_handle dom_pol = { 0 }; + struct wb_acct_info *info = NULL; + uint32_t num_info = 0; + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + NTSTATUS status; + bool retry = false; + + DEBUG(3,("samr: enum local groups\n")); + + if (pnum_info) { + *pnum_info = 0; + } + +again: + status = open_cached_internal_pipe_conn(domain, + &samr_pipe, + &dom_pol, + NULL, + NULL); + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + + status = rpc_enum_local_groups(mem_ctx, + samr_pipe, + &dom_pol, + &num_info, + + &info); + if (!retry && reset_connection_on_error(domain, samr_pipe, status)) { + retry = true; + goto again; + } + + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + + if (pnum_info) { + *pnum_info = num_info; + } + + if (pinfo) { + *pinfo = talloc_move(mem_ctx, &info); + } + +done: + TALLOC_FREE(tmp_ctx); + return status; +} + +/* convert a single name to a sid in a domain */ +static NTSTATUS sam_name_to_sid(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + const char *domain_name, + const char *name, + uint32_t flags, + const char **pdom_name, + struct dom_sid *psid, + enum lsa_SidType *ptype) +{ + struct rpc_pipe_client *samr_pipe = NULL; + struct dcerpc_binding_handle *h = NULL; + struct policy_handle dom_pol = { .handle_type = 0, }; + struct dom_sid sid; + const char *dom_name = domain_name; + struct lsa_String lsa_name = { .string = name }; + struct samr_Ids rids = { .count = 0 }; + struct samr_Ids types = { .count = 0 }; + enum lsa_SidType type; + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + NTSTATUS status = NT_STATUS_NONE_MAPPED; + NTSTATUS result; + bool retry = false; + bool ok; + + DBG_NOTICE("%s\\%s\n", domain_name, name); + + if (strequal(domain_name, unix_users_domain_name())) { + struct passwd *pwd = NULL; + + if (name[0] == '\0') { + sid_copy(&sid, &global_sid_Unix_Users); + type = SID_NAME_DOMAIN; + goto done; + } + + pwd = Get_Pwnam_alloc(tmp_ctx, name); + if (pwd == NULL) { + goto fail; + } + ok = sid_compose(&sid, &global_sid_Unix_Users, pwd->pw_uid); + if (!ok) { + status = NT_STATUS_INTERNAL_ERROR; + goto fail; + } + type = SID_NAME_USER; + goto done; + } + + if (strequal(domain_name, unix_groups_domain_name())) { + struct group *grp = NULL; + + if (name[0] == '\0') { + sid_copy(&sid, &global_sid_Unix_Groups); + type = SID_NAME_DOMAIN; + goto done; + } + + grp = getgrnam(name); + if (grp == NULL) { + goto fail; + } + ok = sid_compose(&sid, &global_sid_Unix_Groups, grp->gr_gid); + if (!ok) { + status = NT_STATUS_INTERNAL_ERROR; + goto fail; + } + type = SID_NAME_DOM_GRP; + goto done; + } + + if (name[0] == '\0') { + sid_copy(&sid, &domain->sid); + type = SID_NAME_DOMAIN; + goto done; + } + + ok = lookup_wellknown_name(tmp_ctx, name, &sid, &dom_name); + if (ok) { + type = SID_NAME_WKN_GRP; + goto done; + } + + { + char *normalized = NULL; + NTSTATUS nstatus = normalize_name_unmap( + tmp_ctx, name, &normalized); + if (NT_STATUS_IS_OK(nstatus) || + NT_STATUS_EQUAL(nstatus, NT_STATUS_FILE_RENAMED)) { + lsa_name.string = normalized; + } + } + +again: + status = open_cached_internal_pipe_conn( + domain, &samr_pipe, &dom_pol, NULL, NULL); + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + h = samr_pipe->binding_handle; + + status = dcerpc_samr_LookupNames( + h, tmp_ctx, &dom_pol, 1, &lsa_name, &rids, &types, &result); + + if (!retry && reset_connection_on_error(domain, samr_pipe, status)) { + retry = true; + goto again; + } + + if (!NT_STATUS_IS_OK(status)) { + DBG_DEBUG("dcerpc_samr_LookupNames returned %s\n", + nt_errstr(status)); + goto fail; + } + if (!NT_STATUS_IS_OK(result)) { + DBG_DEBUG("dcerpc_samr_LookupNames resulted in %s\n", + nt_errstr(status)); + status = result; + goto fail; + } + + sid_compose(&sid, &domain->sid, rids.ids[0]); + type = types.ids[0]; + +done: + if (pdom_name != NULL) { + *pdom_name = talloc_strdup(mem_ctx, dom_name); + if (*pdom_name == NULL) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + } + + if (psid) { + sid_copy(psid, &sid); + } + if (ptype) { + *ptype = type; + } + + status = NT_STATUS_OK; +fail: + TALLOC_FREE(tmp_ctx); + return status; +} + +/* convert a domain SID to a user or group name */ +static NTSTATUS sam_sid_to_name(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + const struct dom_sid *sid, + char **pdomain_name, + char **pname, + enum lsa_SidType *ptype) +{ + struct rpc_pipe_client *samr_pipe = NULL; + struct dcerpc_binding_handle *h = NULL; + struct policy_handle dom_pol = { .handle_type = 0, }; + const char *domain_name = ""; + const char *name = ""; + enum lsa_SidType type = SID_NAME_USE_NONE; + struct lsa_Strings names = { .count = 0, }; + struct samr_Ids types = { .count = 0 }; + struct dom_sid domain_sid; + uint32_t rid; + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + NTSTATUS status = NT_STATUS_NONE_MAPPED; + NTSTATUS result; + bool retry = false; + bool ok; + + DEBUG(3,("sam_sid_to_name\n")); + + if (sid_check_is_unix_users(sid)) { + domain_name = unix_users_domain_name(); + type = SID_NAME_DOMAIN; + goto done; + } + if (sid_check_is_in_unix_users(sid)) { + struct passwd *pwd = NULL; + + ok = sid_peek_rid(sid, &rid); + if (!ok) { + goto fail; + } + pwd = getpwuid(rid); + if (pwd == NULL) { + goto fail; + } + + domain_name = unix_users_domain_name(); + name = talloc_strdup(tmp_ctx, pwd->pw_name); + if (name == NULL) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + type = SID_NAME_USER; + goto done; + } + + if (sid_check_is_unix_groups(sid)) { + domain_name = unix_groups_domain_name(); + type = SID_NAME_DOMAIN; + goto done; + } + if (sid_check_is_in_unix_groups(sid)) { + struct group *grp = NULL; + + ok = sid_peek_rid(sid, &rid); + if (!ok) { + goto fail; + } + grp = getgrgid(rid); + if (grp == NULL) { + goto fail; + } + + domain_name = unix_groups_domain_name(); + name = talloc_strdup(tmp_ctx, grp->gr_name); + if (name == NULL) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + type = SID_NAME_DOM_GRP; + goto done; + } + + ok = lookup_wellknown_sid(tmp_ctx, sid, &domain_name, &name); + if (ok) { + type = SID_NAME_WKN_GRP; + goto done; + } + + if (dom_sid_equal(sid, &domain->sid)) { + domain_name = domain->name; + type = SID_NAME_DOMAIN; + goto done; + } + + sid_copy(&domain_sid, sid); + ok = sid_split_rid(&domain_sid, &rid); + if (!ok) { + goto fail; + } + + if (!dom_sid_equal(&domain_sid, &domain->sid)) { + goto fail; + } + +again: + status = open_cached_internal_pipe_conn( + domain, &samr_pipe, &dom_pol, NULL, NULL); + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + h = samr_pipe->binding_handle; + + status = dcerpc_samr_LookupRids( + h, tmp_ctx, &dom_pol, 1, &rid, &names, &types, &result); + + if (!retry && reset_connection_on_error(domain, samr_pipe, status)) { + retry = true; + goto again; + } + + if (!NT_STATUS_IS_OK(status)) { + DBG_DEBUG("dcerpc_samr_LookupRids failed: %s\n", + nt_errstr(status)); + goto fail; + } + if (!NT_STATUS_IS_OK(result)) { + DBG_DEBUG("dcerpc_samr_LookupRids resulted in %s\n", + nt_errstr(result)); + status = result; + goto fail; + } + + domain_name = domain->name; + name = names.names[0].string; + type = types.ids[0]; + + if (name != NULL) { + char *normalized = NULL; + NTSTATUS nstatus = normalize_name_map( + tmp_ctx, domain_name, name, &normalized); + if (NT_STATUS_IS_OK(nstatus) || + NT_STATUS_EQUAL(nstatus, NT_STATUS_FILE_RENAMED)) { + name = normalized; + } + } + +done: + if (ptype) { + *ptype = type; + } + + if (pname) { + *pname = talloc_strdup(mem_ctx, name); + if (*pname == NULL) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + } + + if (pdomain_name) { + *pdomain_name = talloc_strdup(mem_ctx, domain_name); + if (*pdomain_name == NULL) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + } + + status = NT_STATUS_OK; +fail: + TALLOC_FREE(tmp_ctx); + return status; +} + +static NTSTATUS sam_rids_to_names(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + const struct dom_sid *domain_sid, + uint32_t *rids, + size_t num_rids, + char **pdomain_name, + char ***pnames, + enum lsa_SidType **ptypes) +{ + struct rpc_pipe_client *samr_pipe = NULL; + struct dcerpc_binding_handle *h = NULL; + struct policy_handle dom_pol = { .handle_type = 0, }; + enum lsa_SidType *types = NULL; + char **names = NULL; + const char *domain_name = NULL; + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + NTSTATUS status = NT_STATUS_NO_MEMORY; + NTSTATUS result; + bool retry = false; + uint32_t i; + + DEBUG(3,("sam_rids_to_names for %s\n", domain->name)); + + types = talloc_array(tmp_ctx, enum lsa_SidType, num_rids); + if (types == NULL) { + goto fail; + } + + names = talloc_array(tmp_ctx, char *, num_rids); + if (names == NULL) { + goto fail; + } + + if (sid_check_is_unix_users(domain_sid)) { + domain_name = unix_users_domain_name(); + domain_sid = &global_sid_Unix_Users; + } + if (sid_check_is_unix_groups(domain_sid)) { + domain_name = unix_groups_domain_name(); + domain_sid = &global_sid_Unix_Groups; + } + + /* Here we're only interested in the domain name being set */ + sid_check_is_wellknown_domain(domain_sid, &domain_name); + + if (domain_name != NULL) { + uint32_t num_mapped = 0; + + /* + * Do unix users/groups and wkn in a loop. There is no + * getpwuids() call & friends anyway + */ + + for (i=0; i<num_rids; i++) { + struct dom_sid sid; + char *name = NULL; + + sid_compose(&sid, domain_sid, rids[i]); + + types[i] = SID_NAME_UNKNOWN; + names[i] = NULL; + + status = sam_sid_to_name( + domain, + tmp_ctx, + &sid, + NULL, + &name, + &types[i]); + if (NT_STATUS_IS_OK(status)) { + names[i] = talloc_move(names, &name); + num_mapped += 1; + } + } + + status = NT_STATUS_NONE_MAPPED; + if (num_mapped > 0) { + status = (num_mapped == num_rids) ? + NT_STATUS_OK : STATUS_SOME_UNMAPPED; + } + goto done; + } + + domain_name = domain->name; + +again: + status = open_cached_internal_pipe_conn( + domain, &samr_pipe, &dom_pol, NULL, NULL); + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + h = samr_pipe->binding_handle; + + /* + * Magic number 1000 comes from samr.idl + */ + + for (i = 0; i < num_rids; i += 1000) { + uint32_t num_lookup_rids = MIN(num_rids - i, 1000); + struct lsa_Strings lsa_names = { + .count = 0, + }; + struct samr_Ids samr_types = { + .count = 0, + }; + uint32_t j; + + status = dcerpc_samr_LookupRids(h, + tmp_ctx, + &dom_pol, + num_lookup_rids, + &rids[i], + &lsa_names, + &samr_types, + &result); + + if (!retry && + reset_connection_on_error(domain, samr_pipe, status)) { + retry = true; + goto again; + } + + if (!NT_STATUS_IS_OK(status)) { + DBG_DEBUG("dcerpc_samr_LookupRids failed: %s\n", + nt_errstr(status)); + goto fail; + } + if (!NT_STATUS_IS_OK(result) && + !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) { + DBG_DEBUG("dcerpc_samr_LookupRids resulted in %s\n", + nt_errstr(result)); + status = result; + goto fail; + } + + for (j = 0; j < num_lookup_rids; j++) { + uint32_t dst = i + j; + + types[dst] = samr_types.ids[j]; + names[dst] = talloc_move( + names, + discard_const_p(char *, + &lsa_names.names[j].string)); + if (names[dst] != NULL) { + char *normalized = NULL; + NTSTATUS nstatus = + normalize_name_map(names, + domain_name, + names[dst], + &normalized); + if (NT_STATUS_IS_OK(nstatus) || + NT_STATUS_EQUAL(nstatus, + NT_STATUS_FILE_RENAMED)) { + names[dst] = normalized; + } + } + } + + TALLOC_FREE(samr_types.ids); + TALLOC_FREE(lsa_names.names); + } + +done: + if (pdomain_name) { + *pdomain_name = talloc_strdup(mem_ctx, domain_name); + if (*pdomain_name == NULL) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + } + + if (ptypes) { + *ptypes = talloc_move(mem_ctx, &types); + } + + if (pnames) { + *pnames = talloc_move(mem_ctx, &names); + } + +fail: + TALLOC_FREE(tmp_ctx); + return status; +} + +static NTSTATUS sam_lockout_policy(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + struct samr_DomInfo12 *lockout_policy) +{ + struct rpc_pipe_client *samr_pipe; + struct policy_handle dom_pol = { 0 }; + union samr_DomainInfo *info = NULL; + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + NTSTATUS status, result; + struct dcerpc_binding_handle *b = NULL; + bool retry = false; + + DEBUG(3,("sam_lockout_policy\n")); + +again: + status = open_cached_internal_pipe_conn(domain, + &samr_pipe, + &dom_pol, + NULL, + NULL); + if (!NT_STATUS_IS_OK(status)) { + goto error; + } + + b = samr_pipe->binding_handle; + + status = dcerpc_samr_QueryDomainInfo(b, + mem_ctx, + &dom_pol, + DomainLockoutInformation, + &info, + &result); + + if (!retry && reset_connection_on_error(domain, samr_pipe, status)) { + retry = true; + goto again; + } + + if (!NT_STATUS_IS_OK(status)) { + goto error; + } + if (!NT_STATUS_IS_OK(result)) { + status = result; + goto error; + } + + *lockout_policy = info->info12; + +error: + TALLOC_FREE(tmp_ctx); + return status; +} + +static NTSTATUS sam_password_policy(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + struct samr_DomInfo1 *passwd_policy) +{ + struct rpc_pipe_client *samr_pipe; + struct policy_handle dom_pol = { 0 }; + union samr_DomainInfo *info = NULL; + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + NTSTATUS status, result; + struct dcerpc_binding_handle *b = NULL; + bool retry = false; + + DEBUG(3,("sam_password_policy\n")); + +again: + status = open_cached_internal_pipe_conn(domain, + &samr_pipe, + &dom_pol, + NULL, + NULL); + if (!NT_STATUS_IS_OK(status)) { + goto error; + } + + b = samr_pipe->binding_handle; + + status = dcerpc_samr_QueryDomainInfo(b, + mem_ctx, + &dom_pol, + DomainPasswordInformation, + &info, + &result); + + if (!retry && reset_connection_on_error(domain, samr_pipe, status)) { + retry = true; + goto again; + } + + if (!NT_STATUS_IS_OK(status)) { + goto error; + } + if (!NT_STATUS_IS_OK(result)) { + status = result; + goto error; + } + + *passwd_policy = info->info1; + +error: + TALLOC_FREE(tmp_ctx); + return status; +} + +/* Lookup groups a user is a member of. */ +static NTSTATUS sam_lookup_usergroups(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + const struct dom_sid *user_sid, + uint32_t *pnum_groups, + struct dom_sid **puser_grpsids) +{ + struct rpc_pipe_client *samr_pipe; + struct policy_handle dom_pol; + struct dom_sid *user_grpsids = NULL; + uint32_t num_groups = 0; + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + NTSTATUS status; + bool retry = false; + + DEBUG(3,("sam_lookup_usergroups\n")); + + ZERO_STRUCT(dom_pol); + + if (pnum_groups) { + *pnum_groups = 0; + } + +again: + status = open_cached_internal_pipe_conn(domain, + &samr_pipe, + &dom_pol, + NULL, + NULL); + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + + status = rpc_lookup_usergroups(tmp_ctx, + samr_pipe, + &dom_pol, + &domain->sid, + user_sid, + &num_groups, + &user_grpsids); + + if (!retry && reset_connection_on_error(domain, samr_pipe, status)) { + retry = true; + goto again; + } + + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + + if (pnum_groups) { + *pnum_groups = num_groups; + } + + if (puser_grpsids) { + *puser_grpsids = talloc_move(mem_ctx, &user_grpsids); + } + +done: + + TALLOC_FREE(tmp_ctx); + return status; +} + +static NTSTATUS sam_lookup_useraliases(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + uint32_t num_sids, + const struct dom_sid *sids, + uint32_t *pnum_aliases, + uint32_t **palias_rids) +{ + struct rpc_pipe_client *samr_pipe; + struct policy_handle dom_pol = { 0 }; + uint32_t num_aliases = 0; + uint32_t *alias_rids = NULL; + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + NTSTATUS status; + bool retry = false; + + DEBUG(3,("sam_lookup_useraliases\n")); + + if (pnum_aliases) { + *pnum_aliases = 0; + } + +again: + status = open_cached_internal_pipe_conn(domain, + &samr_pipe, + &dom_pol, + NULL, + NULL); + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + + status = rpc_lookup_useraliases(tmp_ctx, + samr_pipe, + &dom_pol, + num_sids, + sids, + &num_aliases, + &alias_rids); + + if (!retry && reset_connection_on_error(domain, samr_pipe, status)) { + retry = true; + goto again; + } + + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + + if (pnum_aliases) { + *pnum_aliases = num_aliases; + } + + if (palias_rids) { + *palias_rids = talloc_move(mem_ctx, &alias_rids); + } + +done: + + TALLOC_FREE(tmp_ctx); + return status; +} + +/* the rpc backend methods are exposed via this structure */ +struct winbindd_methods builtin_passdb_methods = { + .consistent = false, + + .query_user_list = builtin_query_user_list, + .enum_dom_groups = builtin_enum_dom_groups, + .enum_local_groups = sam_enum_local_groups, + .name_to_sid = sam_name_to_sid, + .sid_to_name = sam_sid_to_name, + .rids_to_names = sam_rids_to_names, + .lookup_usergroups = sam_lookup_usergroups, + .lookup_useraliases = sam_lookup_useraliases, + .lookup_groupmem = sam_lookup_groupmem, + .lockout_policy = sam_lockout_policy, + .password_policy = sam_password_policy, + .trusted_domains = builtin_trusted_domains +}; + +/* the rpc backend methods are exposed via this structure */ +struct winbindd_methods sam_passdb_methods = { + .consistent = false, + + .query_user_list = sam_query_user_list, + .enum_dom_groups = sam_enum_dom_groups, + .enum_local_groups = sam_enum_local_groups, + .name_to_sid = sam_name_to_sid, + .sid_to_name = sam_sid_to_name, + .rids_to_names = sam_rids_to_names, + .lookup_usergroups = sam_lookup_usergroups, + .lookup_useraliases = sam_lookup_useraliases, + .lookup_groupmem = sam_lookup_groupmem, + .lockout_policy = sam_lockout_policy, + .password_policy = sam_password_policy, + .trusted_domains = sam_trusted_domains +}; |