/* * 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 * * 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 "winbindd.h" #include "winbindd_rpc.h" #include "../librpc/gen_ndr/cli_samr.h" #include "rpc_client/cli_samr.h" #include "../librpc/gen_ndr/srv_samr.h" #include "../librpc/gen_ndr/cli_lsa.h" #include "rpc_client/cli_lsarpc.h" #include "../librpc/gen_ndr/srv_lsa.h" #undef DBGC_CLASS #define DBGC_CLASS DBGC_WINBIND static NTSTATUS open_internal_samr_pipe(TALLOC_CTX *mem_ctx, struct rpc_pipe_client **samr_pipe) { static struct rpc_pipe_client *cli = NULL; struct auth_serversupplied_info *server_info = NULL; NTSTATUS status; if (cli != NULL) { goto done; } if (server_info == NULL) { status = make_server_info_system(mem_ctx, &server_info); if (!NT_STATUS_IS_OK(status)) { DEBUG(0, ("open_samr_pipe: Could not create auth_serversupplied_info: %s\n", nt_errstr(status))); return status; } } /* create a samr connection */ status = rpc_pipe_open_internal(talloc_autofree_context(), &ndr_table_samr.syntax_id, rpc_samr_dispatch, server_info, &cli); if (!NT_STATUS_IS_OK(status)) { DEBUG(0, ("open_samr_pipe: Could not connect to samr_pipe: %s\n", nt_errstr(status))); return status; } done: if (samr_pipe) { *samr_pipe = cli; } return NT_STATUS_OK; } static 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; struct policy_handle samr_connect_hnd; status = open_internal_samr_pipe(mem_ctx, samr_pipe); if (!NT_STATUS_IS_OK(status)) { return status; } status = rpccli_samr_Connect2((*samr_pipe), mem_ctx, (*samr_pipe)->desthost, SEC_FLAG_MAXIMUM_ALLOWED, &samr_connect_hnd); if (!NT_STATUS_IS_OK(status)) { return status; } status = rpccli_samr_OpenDomain((*samr_pipe), mem_ctx, &samr_connect_hnd, SEC_FLAG_MAXIMUM_ALLOWED, &domain->sid, samr_domain_hnd); return status; } static NTSTATUS open_internal_lsa_pipe(TALLOC_CTX *mem_ctx, struct rpc_pipe_client **lsa_pipe) { static struct rpc_pipe_client *cli = NULL; struct auth_serversupplied_info *server_info = NULL; NTSTATUS status; if (cli != NULL) { goto done; } if (server_info == NULL) { status = make_server_info_system(mem_ctx, &server_info); if (!NT_STATUS_IS_OK(status)) { DEBUG(0, ("open_samr_pipe: Could not create auth_serversupplied_info: %s\n", nt_errstr(status))); return status; } } /* create a samr connection */ status = rpc_pipe_open_internal(talloc_autofree_context(), &ndr_table_lsarpc.syntax_id, rpc_lsarpc_dispatch, server_info, &cli); if (!NT_STATUS_IS_OK(status)) { DEBUG(0, ("open_samr_pipe: Could not connect to samr_pipe: %s\n", nt_errstr(status))); return status; } done: if (lsa_pipe) { *lsa_pipe = cli; } return NT_STATUS_OK; } static NTSTATUS open_internal_lsa_conn(TALLOC_CTX *mem_ctx, struct rpc_pipe_client **lsa_pipe, struct policy_handle *lsa_hnd) { NTSTATUS status; status = open_internal_lsa_pipe(mem_ctx, lsa_pipe); if (!NT_STATUS_IS_OK(status)) { return status; } status = rpccli_lsa_open_policy((*lsa_pipe), mem_ctx, true, SEC_FLAG_MAXIMUM_ALLOWED, lsa_hnd); return status; } /********************************************************************* 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 acct_info **pinfo) { struct rpc_pipe_client *samr_pipe; struct policy_handle dom_pol; struct acct_info *info = NULL; uint32_t num_info = 0; TALLOC_CTX *tmp_ctx; NTSTATUS status; DEBUG(3,("sam_enum_dom_groups\n")); if (pnum_info) { *pnum_info = 0; } tmp_ctx = talloc_stackframe(); if (tmp_ctx == NULL) { return NT_STATUS_NO_MEMORY; } status = open_internal_samr_conn(tmp_ctx, domain, &samr_pipe, &dom_pol); if (!NT_STATUS_IS_OK(status)) { goto error; } status = rpc_enum_dom_groups(tmp_ctx, samr_pipe, &dom_pol, &num_info, &info); if (!NT_STATUS_IS_OK(status)) { goto error; } if (pnum_info) { *pnum_info = num_info; } if (pinfo) { *pinfo = talloc_move(mem_ctx, &info); } error: 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 *pnum_info, struct wbint_userinfo **pinfo) { struct rpc_pipe_client *samr_pipe = NULL; struct policy_handle dom_pol; struct wbint_userinfo *info = NULL; uint32_t num_info = 0; TALLOC_CTX *tmp_ctx; NTSTATUS status; DEBUG(3,("samr_query_user_list\n")); if (pnum_info) { *pnum_info = 0; } tmp_ctx = talloc_stackframe(); if (tmp_ctx == NULL) { return NT_STATUS_NO_MEMORY; } status = open_internal_samr_conn(tmp_ctx, domain, &samr_pipe, &dom_pol); if (!NT_STATUS_IS_OK(status)) { goto done; } status = rpc_query_user_list(tmp_ctx, samr_pipe, &dom_pol, &domain->sid, &num_info, &info); 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; } /* Lookup user information from a rid or username. */ static NTSTATUS sam_query_user(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx, const struct dom_sid *user_sid, struct wbint_userinfo *user_info) { struct rpc_pipe_client *samr_pipe; struct policy_handle dom_pol, user_pol; union samr_UserInfo *info = NULL; TALLOC_CTX *tmp_ctx; uint32_t user_rid; NTSTATUS status; DEBUG(3,("samr: query_user\n")); if (!sid_peek_check_rid(&domain->sid, user_sid, &user_rid)) { return NT_STATUS_UNSUCCESSFUL; } if (user_info) { user_info->homedir = NULL; user_info->shell = NULL; user_info->primary_gid = (gid_t) -1; } tmp_ctx = talloc_stackframe(); if (tmp_ctx == NULL) { return NT_STATUS_NO_MEMORY; } status = open_internal_samr_conn(tmp_ctx, domain, &samr_pipe, &dom_pol); if (!NT_STATUS_IS_OK(status)) { goto error; } /* Get user handle */ status = rpccli_samr_OpenUser(samr_pipe, tmp_ctx, &dom_pol, SEC_FLAG_MAXIMUM_ALLOWED, user_rid, &user_pol); if (!NT_STATUS_IS_OK(status)) { goto error; } /* Get user info */ status = rpccli_samr_QueryUserInfo(samr_pipe, tmp_ctx, &user_pol, 0x15, &info); rpccli_samr_Close(samr_pipe, tmp_ctx, &user_pol); if (!NT_STATUS_IS_OK(status)) { goto error; } sid_compose(&user_info->user_sid, &domain->sid, user_rid); sid_compose(&user_info->group_sid, &domain->sid, info->info21.primary_gid); if (user_info) { user_info->acct_name = talloc_strdup(mem_ctx, info->info21.account_name.string); if (user_info->acct_name == NULL) { status = NT_STATUS_NO_MEMORY; goto error; } user_info->full_name = talloc_strdup(mem_ctx, info->info21.full_name.string); if (user_info->acct_name == NULL) { status = NT_STATUS_NO_MEMORY; goto error; } user_info->homedir = NULL; user_info->shell = NULL; user_info->primary_gid = (gid_t)-1; } status = NT_STATUS_OK; error: 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 *trusts) { struct rpc_pipe_client *lsa_pipe; struct netr_DomainTrust *array = NULL; struct policy_handle lsa_policy; uint32_t enum_ctx = 0; uint32_t count = 0; TALLOC_CTX *tmp_ctx; NTSTATUS status; DEBUG(3,("samr: trusted domains\n")); tmp_ctx = talloc_stackframe(); if (tmp_ctx == NULL) { return NT_STATUS_NO_MEMORY; } status = open_internal_lsa_conn(tmp_ctx, &lsa_pipe, &lsa_policy); if (!NT_STATUS_IS_OK(status)) { goto error; } do { struct lsa_DomainList dom_list; uint32_t start_idx; uint32_t i; /* * We don't run into deadlocks here, cause winbind_off() is * called in the main function. */ status = rpccli_lsa_EnumTrustDom(lsa_pipe, tmp_ctx, &lsa_policy, &enum_ctx, &dom_list, (uint32_t) -1); if (!NT_STATUS_IS_OK(status)) { if (!NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) { goto error; } } start_idx = trusts->count; count += dom_list.count; array = talloc_realloc(tmp_ctx, array, struct netr_DomainTrust, count); if (array == NULL) { status = NT_STATUS_NO_MEMORY; goto error; } for (i = 0; i < dom_list.count; i++) { struct netr_DomainTrust *trust = &array[i]; struct dom_sid *sid; ZERO_STRUCTP(trust); trust->netbios_name = talloc_move(array, &dom_list.domains[i].name.string); trust->dns_name = NULL; sid = talloc(array, struct dom_sid); if (sid == NULL) { status = NT_STATUS_NO_MEMORY; goto error; } sid_copy(sid, dom_list.domains[i].sid); trust->sid = sid; } } while (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)); if (trusts) { trusts->count = count; trusts->array = talloc_move(mem_ctx, &array); } error: 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, group_pol; uint32_t samr_access = SEC_FLAG_MAXIMUM_ALLOWED; struct samr_RidTypeArray *rids = NULL; uint32_t group_rid; uint32_t *rid_mem = NULL; uint32_t num_names = 0; uint32_t total_names = 0; struct dom_sid *sid_mem = NULL; char **names = NULL; uint32_t *name_types = NULL; struct lsa_Strings tmp_names; struct samr_Ids tmp_types; uint32_t j, r; TALLOC_CTX *tmp_ctx; NTSTATUS status; DEBUG(3,("samr: lookup groupmem\n")); if (pnum_names) { pnum_names = 0; } if (!sid_peek_check_rid(&domain->sid, group_sid, &group_rid)) { return NT_STATUS_UNSUCCESSFUL; } tmp_ctx = talloc_stackframe(); if (tmp_ctx == NULL) { return NT_STATUS_NO_MEMORY; } status = open_internal_samr_conn(tmp_ctx, domain, &samr_pipe, &dom_pol); if (!NT_STATUS_IS_OK(status)) { goto error; } status = rpccli_samr_OpenGroup(samr_pipe, tmp_ctx, &dom_pol, samr_access, group_rid, &group_pol); if (!NT_STATUS_IS_OK(status)) { goto error; } /* * Step #1: Get a list of user rids that are the members of the group. */ status = rpccli_samr_QueryGroupMember(samr_pipe, tmp_ctx, &group_pol, &rids); rpccli_samr_Close(samr_pipe, tmp_ctx, &group_pol); if (!NT_STATUS_IS_OK(status)) { goto error; } if (rids == NULL || rids->count == 0) { pnum_names = 0; pnames = NULL; pname_types = NULL; psid_mem = NULL; status = NT_STATUS_OK; goto error; } num_names = rids->count; rid_mem = rids->rids; /* * Step #2: Convert list of rids into list of usernames. */ #define MAX_LOOKUP_RIDS 900 if (num_names > 0) { names = TALLOC_ZERO_ARRAY(tmp_ctx, char *, num_names); name_types = TALLOC_ZERO_ARRAY(tmp_ctx, uint32_t, num_names); sid_mem = TALLOC_ZERO_ARRAY(tmp_ctx, struct dom_sid, num_names); if (names == NULL || name_types == NULL || sid_mem == NULL) { status = NT_STATUS_NO_MEMORY; goto error; } } for (j = 0; j < num_names; j++) { sid_compose(&sid_mem[j], &domain->sid, rid_mem[j]); } status = rpccli_samr_LookupRids(samr_pipe, tmp_ctx, &dom_pol, num_names, rid_mem, &tmp_names, &tmp_types); if (!NT_STATUS_IS_OK(status)) { if (!NT_STATUS_EQUAL(status, STATUS_SOME_UNMAPPED)) { goto error; } } /* Copy result into array. The talloc system will take care of freeing the temporary arrays later on. */ if (tmp_names.count != tmp_types.count) { status = NT_STATUS_UNSUCCESSFUL; goto error; } for (r = 0; r < tmp_names.count; r++) { if (tmp_types.ids[r] == SID_NAME_UNKNOWN) { continue; } names[total_names] = fill_domain_username_talloc(names, domain->name, tmp_names.names[r].string, true); name_types[total_names] = tmp_types.ids[r]; total_names++; } if (pnum_names) { *pnum_names = total_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); } error: 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 *num_entries, struct 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 *num_entries, struct wbint_userinfo **info) { /* We don't have users */ *num_entries = 0; *info = NULL; return NT_STATUS_OK; } /* Lookup user information from a rid or username. */ static NTSTATUS builtin_query_user(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx, const struct dom_sid *user_sid, struct wbint_userinfo *user_info) { return NT_STATUS_NO_SUCH_USER; } /* 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 acct_info **pinfo) { struct rpc_pipe_client *samr_pipe; struct policy_handle dom_pol; struct acct_info *info = NULL; uint32_t num_info = 0; TALLOC_CTX *tmp_ctx; NTSTATUS status; DEBUG(3,("samr: enum local groups\n")); if (pnum_info) { *pnum_info = 0; } tmp_ctx = talloc_stackframe(); if (tmp_ctx == NULL) { return NT_STATUS_NO_MEMORY; } status = open_internal_samr_conn(tmp_ctx, domain, &samr_pipe, &dom_pol); if (!NT_STATUS_IS_OK(status)) { goto done; } status = rpc_enum_local_groups(mem_ctx, samr_pipe, &dom_pol, &num_info, &info); 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, struct dom_sid *psid, enum lsa_SidType *ptype) { struct rpc_pipe_client *lsa_pipe; struct policy_handle lsa_policy; struct dom_sid sid; enum lsa_SidType type; TALLOC_CTX *tmp_ctx; NTSTATUS status; DEBUG(3,("sam_name_to_sid\n")); tmp_ctx = talloc_stackframe(); if (tmp_ctx == NULL) { return NT_STATUS_NO_MEMORY; } status = open_internal_lsa_conn(tmp_ctx, &lsa_pipe, &lsa_policy); if (!NT_STATUS_IS_OK(status)) { goto done; } status = rpc_name_to_sid(tmp_ctx, lsa_pipe, &lsa_policy, domain_name, name, flags, &sid, &type); if (!NT_STATUS_IS_OK(status)) { goto done; } if (psid) { sid_copy(psid, &sid); } if (ptype) { *ptype = type; } done: 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 *lsa_pipe; struct policy_handle lsa_policy; char *domain_name = NULL; char *name = NULL; enum lsa_SidType type; TALLOC_CTX *tmp_ctx; NTSTATUS status; DEBUG(3,("sam_sid_to_name\n")); /* Paranoia check */ if (!sid_check_is_in_builtin(sid) && !sid_check_is_in_our_domain(sid) && !sid_check_is_in_unix_users(sid) && !sid_check_is_unix_users(sid) && !sid_check_is_in_unix_groups(sid) && !sid_check_is_unix_groups(sid) && !sid_check_is_in_wellknown_domain(sid)) { DEBUG(0, ("sam_sid_to_name: possible deadlock - trying to " "lookup SID %s\n", sid_string_dbg(sid))); return NT_STATUS_NONE_MAPPED; } tmp_ctx = talloc_stackframe(); if (tmp_ctx == NULL) { return NT_STATUS_NO_MEMORY; } status = open_internal_lsa_conn(tmp_ctx, &lsa_pipe, &lsa_policy); if (!NT_STATUS_IS_OK(status)) { goto done; } status = rpc_sid_to_name(tmp_ctx, lsa_pipe, &lsa_policy, domain, sid, &domain_name, &name, &type); if (ptype) { *ptype = type; } if (pname) { *pname = talloc_move(mem_ctx, &name); } if (pdomain_name) { *pdomain_name = talloc_move(mem_ctx, &domain_name); } done: TALLOC_FREE(tmp_ctx); return status; } static NTSTATUS common_rids_to_names(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx, const struct dom_sid *sid, uint32 *rids, size_t num_rids, char **pdomain_name, char ***pnames, enum lsa_SidType **ptypes) { struct rpc_pipe_client *lsa_pipe; struct policy_handle lsa_policy; enum lsa_SidType *types = NULL; char *domain_name = NULL; char **domains = NULL; char **names = NULL; struct dom_sid *sids; size_t i; TALLOC_CTX *tmp_ctx; NTSTATUS status; DEBUG(3,("samr: rids to names for domain %s\n", domain->name)); tmp_ctx = talloc_stackframe(); if (tmp_ctx == NULL) { return NT_STATUS_NO_MEMORY; } status = open_internal_lsa_conn(tmp_ctx, &lsa_pipe, &lsa_policy); if (!NT_STATUS_IS_OK(status)) { goto error; } if (num_rids) { sids = TALLOC_ARRAY(tmp_ctx, struct dom_sid, num_rids); if (sids == NULL) { status = NT_STATUS_NO_MEMORY; goto error; } } else { sids = NULL; } for (i = 0; i < num_rids; i++) { if (!sid_compose(&sids[i], sid, rids[i])) { status = NT_STATUS_INTERNAL_ERROR; goto error; } } /* * We don't run into deadlocks here, cause winbind_off() is called in * the main function. */ status = rpccli_lsa_lookup_sids(lsa_pipe, tmp_ctx, &lsa_policy, num_rids, sids, &domains, &names, &types); if (!NT_STATUS_IS_OK(status) && !NT_STATUS_EQUAL(status, STATUS_SOME_UNMAPPED)) { DEBUG(2,("rids_to_names: failed to lookup sids: %s\n", nt_errstr(status))); goto error; } for (i = 0; i < num_rids; i++) { char *mapped_name = NULL; NTSTATUS map_status; if (types[i] != SID_NAME_UNKNOWN) { map_status = normalize_name_map(tmp_ctx, domain, names[i], &mapped_name); if (NT_STATUS_IS_OK(map_status) || NT_STATUS_EQUAL(map_status, NT_STATUS_FILE_RENAMED)) { TALLOC_FREE(names[i]); names[i] = talloc_strdup(names, mapped_name); if (names[i] == NULL) { status = NT_STATUS_NO_MEMORY; goto error; } } domain_name = domains[i]; } } if (pdomain_name) { *pdomain_name = talloc_strdup(mem_ctx, domain_name); if (*pdomain_name == NULL) { status = NT_STATUS_NO_MEMORY; goto error; } } if (ptypes) { *ptypes = talloc_move(mem_ctx, &types); } if (pnames) { *pnames = talloc_move(mem_ctx, &names); } error: TALLOC_FREE(tmp_ctx); return status; } static NTSTATUS common_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; union samr_DomainInfo *info = NULL; TALLOC_CTX *tmp_ctx; NTSTATUS status; DEBUG(3,("samr: lockout policy\n")); tmp_ctx = talloc_stackframe(); if (tmp_ctx == NULL) { return NT_STATUS_NO_MEMORY; } status = open_internal_samr_conn(tmp_ctx, domain, &samr_pipe, &dom_pol); if (!NT_STATUS_IS_OK(status)) { goto error; } status = rpccli_samr_QueryDomainInfo(samr_pipe, mem_ctx, &dom_pol, 12, &info); if (!NT_STATUS_IS_OK(status)) { goto error; } *lockout_policy = info->info12; error: TALLOC_FREE(tmp_ctx); return status; } static NTSTATUS common_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; union samr_DomainInfo *info = NULL; TALLOC_CTX *tmp_ctx; NTSTATUS status; DEBUG(3,("samr: password policy\n")); tmp_ctx = talloc_stackframe(); if (tmp_ctx == NULL) { return NT_STATUS_NO_MEMORY; } status = open_internal_samr_conn(tmp_ctx, domain, &samr_pipe, &dom_pol); if (!NT_STATUS_IS_OK(status)) { goto error; } status = rpccli_samr_QueryDomainInfo(samr_pipe, mem_ctx, &dom_pol, 1, &info); if (!NT_STATUS_IS_OK(status)) { goto error; } *passwd_policy = info->info1; error: TALLOC_FREE(tmp_ctx); return status; } /* Lookup groups a user is a member of. I wish Unix had a call like this! */ static NTSTATUS common_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, usr_pol; uint32_t samr_access = SEC_FLAG_MAXIMUM_ALLOWED; struct samr_RidWithAttributeArray *rid_array = NULL; struct dom_sid *user_grpsids = NULL; uint32_t num_groups = 0, i; uint32_t user_rid; TALLOC_CTX *tmp_ctx; NTSTATUS status; DEBUG(3,("samr: lookup usergroups\n")); if (!sid_peek_check_rid(&domain->sid, user_sid, &user_rid)) { return NT_STATUS_UNSUCCESSFUL; } if (pnum_groups) { *pnum_groups = 0; } if (puser_grpsids) { *puser_grpsids = NULL; } tmp_ctx = talloc_stackframe(); if (tmp_ctx == NULL) { return NT_STATUS_NO_MEMORY; } status = open_internal_samr_conn(tmp_ctx, domain, &samr_pipe, &dom_pol); if (!NT_STATUS_IS_OK(status)) { goto error; } /* Get user handle */ status = rpccli_samr_OpenUser(samr_pipe, tmp_ctx, &dom_pol, samr_access, user_rid, &usr_pol); if (!NT_STATUS_IS_OK(status)) { return status; } /* Query user rids */ status = rpccli_samr_GetGroupsForUser(samr_pipe, tmp_ctx, &usr_pol, &rid_array); num_groups = rid_array->count; rpccli_samr_Close(samr_pipe, tmp_ctx, &usr_pol); if (!NT_STATUS_IS_OK(status) || num_groups == 0) { return status; } user_grpsids = TALLOC_ARRAY(tmp_ctx, struct dom_sid, num_groups); if (user_grpsids == NULL) { status = NT_STATUS_NO_MEMORY; goto error; } for (i = 0; i < num_groups; i++) { sid_compose(&(user_grpsids[i]), &domain->sid, rid_array->rids[i].rid); } if (pnum_groups) { *pnum_groups = num_groups; } if (puser_grpsids) { *puser_grpsids = talloc_move(mem_ctx, &user_grpsids); } error: TALLOC_FREE(tmp_ctx); return status; } static NTSTATUS common_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) { #define MAX_SAM_ENTRIES_W2K 0x400 /* 1024 */ struct rpc_pipe_client *samr_pipe; struct policy_handle dom_pol; uint32_t num_query_sids = 0; uint32_t num_queries = 1; uint32_t num_aliases = 0; uint32_t total_sids = 0; uint32_t rangesize = MAX_SAM_ENTRIES_W2K; uint32_t i; struct samr_Ids alias_rids_query; TALLOC_CTX *tmp_ctx; NTSTATUS status; DEBUG(3,("samr: lookup useraliases\n")); if (pnum_aliases) { *pnum_aliases = 0; } if (palias_rids) { *palias_rids = NULL; } tmp_ctx = talloc_stackframe(); if (tmp_ctx == NULL) { return NT_STATUS_NO_MEMORY; } status = open_internal_samr_conn(tmp_ctx, domain, &samr_pipe, &dom_pol); if (!NT_STATUS_IS_OK(status)) { goto error; } do { /* prepare query */ struct lsa_SidArray sid_array; ZERO_STRUCT(sid_array); num_query_sids = MIN(num_sids - total_sids, rangesize); DEBUG(10,("rpc: lookup_useraliases: entering query %d for %d sids\n", num_queries, num_query_sids)); if (num_query_sids) { sid_array.sids = TALLOC_ZERO_ARRAY(tmp_ctx, struct lsa_SidPtr, num_query_sids); if (sid_array.sids == NULL) { status = NT_STATUS_NO_MEMORY; goto error; } } else { sid_array.sids = NULL; } for (i = 0; i < num_query_sids; i++) { sid_array.sids[i].sid = sid_dup_talloc(tmp_ctx, &sids[total_sids++]); if (sid_array.sids[i].sid == NULL) { status = NT_STATUS_NO_MEMORY; goto error; } } sid_array.num_sids = num_query_sids; /* do request */ status = rpccli_samr_GetAliasMembership(samr_pipe, tmp_ctx, &dom_pol, &sid_array, &alias_rids_query); if (!NT_STATUS_IS_OK(status)) { goto error; } if (palias_rids) { /* process output */ for (i = 0; i < alias_rids_query.count; i++) { size_t na = num_aliases; if (!add_rid_to_array_unique(mem_ctx, alias_rids_query.ids[i], palias_rids, &na)) { status = NT_STATUS_NO_MEMORY; goto error; } num_aliases = na; } } num_queries++; } while (total_sids < num_sids); if (pnum_aliases) { *pnum_aliases = num_aliases; } error: TALLOC_FREE(tmp_ctx); return status; #undef MAX_SAM_ENTRIES_W2K } /* find the sequence number for a domain */ static NTSTATUS common_sequence_number(struct winbindd_domain *domain, uint32_t *seq) { struct rpc_pipe_client *samr_pipe; struct policy_handle dom_pol; union samr_DomainInfo *info = NULL; bool got_seq_num = false; TALLOC_CTX *mem_ctx; NTSTATUS status; DEBUG(3,("samr: sequence number\n")); mem_ctx = talloc_init("common_sequence_number"); if (mem_ctx == NULL) { return NT_STATUS_NO_MEMORY; } if (seq) { *seq = DOM_SEQUENCE_NONE; } status = open_internal_samr_conn(mem_ctx, domain, &samr_pipe, &dom_pol); if (!NT_STATUS_IS_OK(status)) { goto error; } /* query domain info */ status = rpccli_samr_QueryDomainInfo(samr_pipe, mem_ctx, &dom_pol, 8, &info); if (NT_STATUS_IS_OK(status)) { if (seq) { *seq = info->info8.sequence_num; got_seq_num = true; } goto seq_num; } /* retry with info-level 2 in case the dc does not support info-level 8 * (like all older samba2 and samba3 dc's) - Guenther */ status = rpccli_samr_QueryDomainInfo(samr_pipe, mem_ctx, &dom_pol, 2, &info); if (NT_STATUS_IS_OK(status)) { if (seq) { *seq = info->general.sequence_num; got_seq_num = true; } } seq_num: if (got_seq_num) { DEBUG(10,("domain_sequence_number: for domain %s is %u\n", domain->name, (unsigned)*seq)); } else { DEBUG(10,("domain_sequence_number: failed to get sequence " "number (%u) for domain %s\n", (unsigned) *seq, domain->name )); status = NT_STATUS_OK; } error: talloc_destroy(mem_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 = common_rids_to_names, .query_user = builtin_query_user, .lookup_usergroups = common_lookup_usergroups, .lookup_useraliases = common_lookup_useraliases, .lookup_groupmem = sam_lookup_groupmem, .sequence_number = common_sequence_number, .lockout_policy = common_lockout_policy, .password_policy = common_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 = common_rids_to_names, .query_user = sam_query_user, .lookup_usergroups = common_lookup_usergroups, .lookup_useraliases = common_lookup_useraliases, .lookup_groupmem = sam_lookup_groupmem, .sequence_number = common_sequence_number, .lockout_policy = common_lockout_policy, .password_policy = common_password_policy, .trusted_domains = sam_trusted_domains };