From 8daa83a594a2e98f39d764422bfbdbc62c9efd44 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 19:20:00 +0200 Subject: Adding upstream version 2:4.20.0+dfsg. Signed-off-by: Daniel Baumann --- source3/rpc_server/lsa/srv_lsa_nt.c | 5235 +++++++++++++++++++++++++++++++++++ 1 file changed, 5235 insertions(+) create mode 100644 source3/rpc_server/lsa/srv_lsa_nt.c (limited to 'source3/rpc_server/lsa') diff --git a/source3/rpc_server/lsa/srv_lsa_nt.c b/source3/rpc_server/lsa/srv_lsa_nt.c new file mode 100644 index 0000000..e2078c6 --- /dev/null +++ b/source3/rpc_server/lsa/srv_lsa_nt.c @@ -0,0 +1,5235 @@ +/* + * Unix SMB/CIFS implementation. + * RPC Pipe client / server routines + * Copyright (C) Andrew Tridgell 1992-1997, + * Copyright (C) Luke Kenneth Casson Leighton 1996-1997, + * Copyright (C) Paul Ashton 1997, + * Copyright (C) Jeremy Allison 2001, 2006. + * Copyright (C) Rafal Szczesniak 2002, + * Copyright (C) Jim McDonough 2002, + * Copyright (C) Simo Sorce 2003. + * Copyright (C) Gerald (Jerry) Carter 2005. + * Copyright (C) Volker Lendecke 2005. + * Copyright (C) Guenther Deschner 2008. + * Copyright (C) Andrew Bartlett 2010. + * + * 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 . + */ + +/* This is the implementation of the lsa server code. */ + +#include "includes.h" +#include "ntdomain.h" +#include "librpc/gen_ndr/ndr_lsa.h" +#include "librpc/gen_ndr/ndr_lsa_scompat.h" +#include "secrets.h" +#include "../librpc/gen_ndr/netlogon.h" +#include "rpc_client/init_lsa.h" +#include "../libcli/security/security.h" +#include "../libcli/security/dom_sid.h" +#include "../librpc/gen_ndr/drsblobs.h" +#include "../librpc/gen_ndr/ndr_drsblobs.h" +#include "../libcli/security/dom_sid.h" +#include "../librpc/gen_ndr/ndr_security.h" +#include "passdb.h" +#include "auth.h" +#include "lib/privileges.h" +#include "rpc_server/srv_access_check.h" +#include "../librpc/gen_ndr/ndr_wkssvc.h" +#include "../libcli/auth/libcli_auth.h" +#include "../libcli/lsarpc/util_lsarpc.h" +#include "lsa.h" +#include "librpc/rpc/dcesrv_core.h" +#include "librpc/rpc/dcerpc_helper.h" +#include "lib/param/loadparm.h" +#include "source3/lib/substitute.h" + +#include "lib/crypto/gnutls_helpers.h" +#include +#include + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_RPC_SRV + +#define MAX_LOOKUP_SIDS 0x5000 /* 20480 */ + +enum lsa_handle_type { + LSA_HANDLE_POLICY_TYPE = 1, + LSA_HANDLE_ACCOUNT_TYPE = 2, + LSA_HANDLE_TRUST_TYPE = 3, + LSA_HANDLE_SECRET_TYPE = 4}; + +struct lsa_info { + struct dom_sid sid; + const char *name; + uint32_t access; + enum lsa_handle_type type; + struct security_descriptor *sd; +}; + +const struct generic_mapping lsa_account_mapping = { + LSA_ACCOUNT_READ, + LSA_ACCOUNT_WRITE, + LSA_ACCOUNT_EXECUTE, + LSA_ACCOUNT_ALL_ACCESS +}; + +const struct generic_mapping lsa_policy_mapping = { + LSA_POLICY_READ, + LSA_POLICY_WRITE, + LSA_POLICY_EXECUTE, + LSA_POLICY_ALL_ACCESS +}; + +const struct generic_mapping lsa_secret_mapping = { + LSA_SECRET_READ, + LSA_SECRET_WRITE, + LSA_SECRET_EXECUTE, + LSA_SECRET_ALL_ACCESS +}; + +const struct generic_mapping lsa_trusted_domain_mapping = { + LSA_TRUSTED_DOMAIN_READ, + LSA_TRUSTED_DOMAIN_WRITE, + LSA_TRUSTED_DOMAIN_EXECUTE, + LSA_TRUSTED_DOMAIN_ALL_ACCESS +}; + +/*************************************************************************** + initialize a lsa_DomainInfo structure. + ***************************************************************************/ + +static void init_dom_query_3(struct lsa_DomainInfo *r, + const char *name, + struct dom_sid *sid) +{ + init_lsa_StringLarge(&r->name, name); + r->sid = sid; +} + +/*************************************************************************** + initialize a lsa_DomainInfo structure. + ***************************************************************************/ + +static void init_dom_query_5(struct lsa_DomainInfo *r, + const char *name, + struct dom_sid *sid) +{ + init_lsa_StringLarge(&r->name, name); + r->sid = sid; +} + +/*************************************************************************** + lookup_lsa_rids. Must be called as root for lookup_name to work. + ***************************************************************************/ + +static NTSTATUS lookup_lsa_rids(TALLOC_CTX *mem_ctx, + struct lsa_RefDomainList *ref, + struct lsa_TranslatedSid *prid, + uint32_t num_entries, + struct lsa_String *name, + int flags, + uint32_t *pmapped_count) +{ + uint32_t mapped_count, i; + + SMB_ASSERT(num_entries <= MAX_LOOKUP_SIDS); + + mapped_count = 0; + *pmapped_count = 0; + + for (i = 0; i < num_entries; i++) { + struct dom_sid sid; + uint32_t rid; + int dom_idx; + const char *full_name; + const char *domain; + enum lsa_SidType type; + + /* Split name into domain and user component */ + + /* follow w2k8 behavior and return the builtin domain when no + * input has been passed in */ + + if (name[i].string) { + full_name = name[i].string; + } else { + full_name = "BUILTIN"; + } + + DEBUG(5, ("lookup_lsa_rids: looking up name %s\n", full_name)); + + if (!lookup_name(mem_ctx, full_name, flags, &domain, NULL, + &sid, &type)) { + type = SID_NAME_UNKNOWN; + } + + switch (type) { + case SID_NAME_USER: + case SID_NAME_DOM_GRP: + case SID_NAME_DOMAIN: + case SID_NAME_ALIAS: + case SID_NAME_WKN_GRP: + DEBUG(5, ("init_lsa_rids: %s found\n", full_name)); + /* Leave these unchanged */ + break; + default: + /* Don't hand out anything but the list above */ + DEBUG(5, ("init_lsa_rids: %s not found\n", full_name)); + type = SID_NAME_UNKNOWN; + break; + } + + rid = 0; + dom_idx = -1; + + if (type != SID_NAME_UNKNOWN) { + if (type == SID_NAME_DOMAIN) { + rid = (uint32_t)-1; + } else { + sid_split_rid(&sid, &rid); + } + dom_idx = init_lsa_ref_domain_list(mem_ctx, ref, domain, &sid); + mapped_count++; + } + + prid[i].sid_type = type; + prid[i].rid = rid; + prid[i].sid_index = dom_idx; + } + + *pmapped_count = mapped_count; + return NT_STATUS_OK; +} + +/*************************************************************************** + lookup_lsa_sids. Must be called as root for lookup_name to work. + ***************************************************************************/ + +static NTSTATUS lookup_lsa_sids(TALLOC_CTX *mem_ctx, + struct lsa_RefDomainList *ref, + struct lsa_TranslatedSid3 *trans_sids, + uint32_t num_entries, + struct lsa_String *name, + int flags, + uint32_t *pmapped_count) +{ + uint32_t mapped_count, i; + + SMB_ASSERT(num_entries <= MAX_LOOKUP_SIDS); + + mapped_count = 0; + *pmapped_count = 0; + + for (i = 0; i < num_entries; i++) { + struct dom_sid sid; + uint32_t rid; + int dom_idx; + const char *full_name; + const char *domain; + enum lsa_SidType type; + + ZERO_STRUCT(sid); + + /* Split name into domain and user component */ + + full_name = name[i].string; + if (full_name == NULL) { + return NT_STATUS_NO_MEMORY; + } + + DEBUG(5, ("lookup_lsa_sids: looking up name %s\n", full_name)); + + if (!lookup_name(mem_ctx, full_name, flags, &domain, NULL, + &sid, &type)) { + type = SID_NAME_UNKNOWN; + } + + switch (type) { + case SID_NAME_USER: + case SID_NAME_DOM_GRP: + case SID_NAME_DOMAIN: + case SID_NAME_ALIAS: + case SID_NAME_WKN_GRP: + DEBUG(5, ("lookup_lsa_sids: %s found\n", full_name)); + /* Leave these unchanged */ + break; + default: + /* Don't hand out anything but the list above */ + DEBUG(5, ("lookup_lsa_sids: %s not found\n", full_name)); + type = SID_NAME_UNKNOWN; + break; + } + + rid = 0; + dom_idx = -1; + + if (type != SID_NAME_UNKNOWN) { + struct dom_sid domain_sid; + sid_copy(&domain_sid, &sid); + sid_split_rid(&domain_sid, &rid); + dom_idx = init_lsa_ref_domain_list(mem_ctx, ref, domain, &domain_sid); + mapped_count++; + } + + /* Initialize the lsa_TranslatedSid3 return. */ + trans_sids[i].sid_type = type; + trans_sids[i].sid = dom_sid_dup(mem_ctx, &sid); + trans_sids[i].sid_index = dom_idx; + } + + *pmapped_count = mapped_count; + return NT_STATUS_OK; +} + +static NTSTATUS make_lsa_object_sd(TALLOC_CTX *mem_ctx, struct security_descriptor **sd, size_t *sd_size, + const struct generic_mapping *map, + struct dom_sid *sid, uint32_t sid_access) +{ + struct dom_sid adm_sid; + struct security_ace ace[5]; + size_t i = 0; + + struct security_acl *psa = NULL; + + /* READ|EXECUTE access for Everyone */ + + init_sec_ace(&ace[i++], &global_sid_World, SEC_ACE_TYPE_ACCESS_ALLOWED, + map->generic_execute | map->generic_read, 0); + + /* Add Full Access 'BUILTIN\Administrators' and 'BUILTIN\Account Operators */ + + init_sec_ace(&ace[i++], &global_sid_Builtin_Administrators, + SEC_ACE_TYPE_ACCESS_ALLOWED, map->generic_all, 0); + init_sec_ace(&ace[i++], &global_sid_Builtin_Account_Operators, + SEC_ACE_TYPE_ACCESS_ALLOWED, map->generic_all, 0); + + /* Add Full Access for Domain Admins */ + sid_compose(&adm_sid, get_global_sam_sid(), DOMAIN_RID_ADMINS); + init_sec_ace(&ace[i++], &adm_sid, SEC_ACE_TYPE_ACCESS_ALLOWED, + map->generic_all, 0); + + /* If we have a sid, give it some special access */ + + if (sid) { + init_sec_ace(&ace[i++], sid, SEC_ACE_TYPE_ACCESS_ALLOWED, + sid_access, 0); + } + + if((psa = make_sec_acl(mem_ctx, NT4_ACL_REVISION, i, ace)) == NULL) + return NT_STATUS_NO_MEMORY; + + if((*sd = make_sec_desc(mem_ctx, SECURITY_DESCRIPTOR_REVISION_1, + SEC_DESC_SELF_RELATIVE, &adm_sid, NULL, NULL, + psa, sd_size)) == NULL) + return NT_STATUS_NO_MEMORY; + + return NT_STATUS_OK; +} + +/*************************************************************************** + ***************************************************************************/ + +static NTSTATUS create_lsa_policy_handle(TALLOC_CTX *mem_ctx, + struct pipes_struct *p, + enum lsa_handle_type type, + uint32_t acc_granted, + struct dom_sid *sid, + const char *name, + const struct security_descriptor *sd, + struct policy_handle *handle) +{ + struct lsa_info *info; + + ZERO_STRUCTP(handle); + + info = talloc_zero(mem_ctx, struct lsa_info); + if (!info) { + return NT_STATUS_NO_MEMORY; + } + + info->type = type; + info->access = acc_granted; + + if (sid) { + sid_copy(&info->sid, sid); + } + + info->name = talloc_strdup(info, name); + + if (sd != NULL) { + info->sd = security_descriptor_copy(info, sd); + if (info->sd == NULL) { + talloc_free(info); + return NT_STATUS_NO_MEMORY; + } + } + + if (!create_policy_hnd(p, handle, type, info)) { + talloc_free(info); + ZERO_STRUCTP(handle); + return NT_STATUS_NO_MEMORY; + } + + return NT_STATUS_OK; +} + +/*************************************************************************** + _lsa_OpenPolicy2 + ***************************************************************************/ + +NTSTATUS _lsa_OpenPolicy2(struct pipes_struct *p, + struct lsa_OpenPolicy2 *r) +{ + struct dcesrv_call_state *dce_call = p->dce_call; + struct auth_session_info *session_info = + dcesrv_call_session_info(dce_call); + struct security_descriptor *psd = NULL; + size_t sd_size; + uint32_t des_access = r->in.access_mask; + uint32_t acc_granted; + NTSTATUS status; + + if (p->transport != NCACN_NP && p->transport != NCALRPC) { + p->fault_state = DCERPC_FAULT_ACCESS_DENIED; + return NT_STATUS_ACCESS_DENIED; + } + + /* Work out max allowed. */ + map_max_allowed_access(session_info->security_token, + session_info->unix_token, + &des_access); + + /* map the generic bits to the lsa policy ones */ + se_map_generic(&des_access, &lsa_policy_mapping); + + /* get the generic lsa policy SD until we store it */ + status = make_lsa_object_sd(p->mem_ctx, &psd, &sd_size, &lsa_policy_mapping, + NULL, 0); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + status = access_check_object(psd, session_info->security_token, + SEC_PRIV_INVALID, SEC_PRIV_INVALID, 0, des_access, + &acc_granted, "_lsa_OpenPolicy2" ); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + status = create_lsa_policy_handle(p->mem_ctx, p, + LSA_HANDLE_POLICY_TYPE, + acc_granted, + get_global_sam_sid(), + NULL, + psd, + r->out.handle); + if (!NT_STATUS_IS_OK(status)) { + return NT_STATUS_OBJECT_NAME_NOT_FOUND; + } + + return NT_STATUS_OK; +} + +/*************************************************************************** + _lsa_OpenPolicy + ***************************************************************************/ + +NTSTATUS _lsa_OpenPolicy(struct pipes_struct *p, + struct lsa_OpenPolicy *r) +{ + struct lsa_OpenPolicy2 o; + + /* _lsa_OpenPolicy2 will check if this is a NCACN_NP connection */ + + o.in.system_name = NULL; /* should be ignored */ + o.in.attr = r->in.attr; + o.in.access_mask = r->in.access_mask; + + o.out.handle = r->out.handle; + + return _lsa_OpenPolicy2(p, &o); +} + +/*************************************************************************** + _lsa_EnumTrustDom - this needs fixing to do more than return NULL ! JRA. + ufff, done :) mimir + ***************************************************************************/ + +NTSTATUS _lsa_EnumTrustDom(struct pipes_struct *p, + struct lsa_EnumTrustDom *r) +{ + struct lsa_info *info; + uint32_t i, count; + struct trustdom_info **domains; + struct lsa_DomainInfo *entries; + NTSTATUS nt_status; + + info = find_policy_by_hnd(p, + r->in.handle, + LSA_HANDLE_POLICY_TYPE, + struct lsa_info, + &nt_status); + if (!NT_STATUS_IS_OK(nt_status)) { + return NT_STATUS_INVALID_HANDLE; + } + + /* check if the user has enough rights */ + if (!(info->access & LSA_POLICY_VIEW_LOCAL_INFORMATION)) + return NT_STATUS_ACCESS_DENIED; + + become_root(); + nt_status = pdb_enum_trusteddoms(p->mem_ctx, &count, &domains); + unbecome_root(); + + if (!NT_STATUS_IS_OK(nt_status)) { + return nt_status; + } + + entries = talloc_zero_array(p->mem_ctx, struct lsa_DomainInfo, count); + if (!entries) { + return NT_STATUS_NO_MEMORY; + } + + for (i=0; iname); + entries[i].sid = &domains[i]->sid; + } + + if (*r->in.resume_handle >= count) { + *r->out.resume_handle = -1; + TALLOC_FREE(entries); + return NT_STATUS_NO_MORE_ENTRIES; + } + + /* return the rest, limit by max_size. Note that we + use the w2k3 element size value of 60 */ + r->out.domains->count = count - *r->in.resume_handle; + r->out.domains->count = MIN(r->out.domains->count, + 1+(r->in.max_size/LSA_ENUM_TRUST_DOMAIN_MULTIPLIER)); + + r->out.domains->domains = entries + *r->in.resume_handle; + + if (r->out.domains->count < count - *r->in.resume_handle) { + *r->out.resume_handle = *r->in.resume_handle + r->out.domains->count; + return STATUS_MORE_ENTRIES; + } + + /* according to MS-LSAD 3.1.4.7.8 output resume handle MUST + * always be larger than the previous input resume handle, in + * particular when hitting the last query it is vital to set the + * resume handle correctly to avoid infinite client loops, as + * seen e.g. with Windows XP SP3 when resume handle is 0 and + * status is NT_STATUS_OK - gd */ + + *r->out.resume_handle = (uint32_t)-1; + + return NT_STATUS_OK; +} + +#define LSA_AUDIT_NUM_CATEGORIES_NT4 7 +#define LSA_AUDIT_NUM_CATEGORIES_WIN2K 9 +#define LSA_AUDIT_NUM_CATEGORIES LSA_AUDIT_NUM_CATEGORIES_NT4 + +/*************************************************************************** + _lsa_QueryInfoPolicy + ***************************************************************************/ + +NTSTATUS _lsa_QueryInfoPolicy(struct pipes_struct *p, + struct lsa_QueryInfoPolicy *r) +{ + NTSTATUS status = NT_STATUS_OK; + struct lsa_info *handle; + struct dom_sid domain_sid; + const char *name; + struct dom_sid *sid = NULL; + union lsa_PolicyInformation *info = NULL; + uint32_t acc_required = 0; + + handle = find_policy_by_hnd(p, + r->in.handle, + LSA_HANDLE_POLICY_TYPE, + struct lsa_info, + &status); + if (!NT_STATUS_IS_OK(status)) { + return NT_STATUS_INVALID_HANDLE; + } + + switch (r->in.level) { + case LSA_POLICY_INFO_AUDIT_LOG: + case LSA_POLICY_INFO_AUDIT_EVENTS: + acc_required = LSA_POLICY_VIEW_AUDIT_INFORMATION; + break; + case LSA_POLICY_INFO_DOMAIN: + acc_required = LSA_POLICY_VIEW_LOCAL_INFORMATION; + break; + case LSA_POLICY_INFO_PD: + acc_required = LSA_POLICY_GET_PRIVATE_INFORMATION; + break; + case LSA_POLICY_INFO_ACCOUNT_DOMAIN: + acc_required = LSA_POLICY_VIEW_LOCAL_INFORMATION; + break; + case LSA_POLICY_INFO_ROLE: + case LSA_POLICY_INFO_REPLICA: + acc_required = LSA_POLICY_VIEW_LOCAL_INFORMATION; + break; + case LSA_POLICY_INFO_QUOTA: + acc_required = LSA_POLICY_VIEW_LOCAL_INFORMATION; + break; + case LSA_POLICY_INFO_MOD: + case LSA_POLICY_INFO_AUDIT_FULL_SET: + /* according to MS-LSAD 3.1.4.4.3 */ + return NT_STATUS_INVALID_PARAMETER; + case LSA_POLICY_INFO_AUDIT_FULL_QUERY: + acc_required = LSA_POLICY_VIEW_AUDIT_INFORMATION; + break; + case LSA_POLICY_INFO_DNS: + case LSA_POLICY_INFO_DNS_INT: + case LSA_POLICY_INFO_L_ACCOUNT_DOMAIN: + acc_required = LSA_POLICY_VIEW_LOCAL_INFORMATION; + break; + default: + break; + } + + if (!(handle->access & acc_required)) { + /* return NT_STATUS_ACCESS_DENIED; */ + } + + info = talloc_zero(p->mem_ctx, union lsa_PolicyInformation); + if (!info) { + return NT_STATUS_NO_MEMORY; + } + + switch (r->in.level) { + /* according to MS-LSAD 3.1.4.4.3 */ + case LSA_POLICY_INFO_MOD: + case LSA_POLICY_INFO_AUDIT_FULL_SET: + case LSA_POLICY_INFO_AUDIT_FULL_QUERY: + return NT_STATUS_INVALID_PARAMETER; + case LSA_POLICY_INFO_AUDIT_LOG: + info->audit_log.percent_full = 0; + info->audit_log.maximum_log_size = 0; + info->audit_log.retention_time = 0; + info->audit_log.shutdown_in_progress = 0; + info->audit_log.time_to_shutdown = 0; + info->audit_log.next_audit_record = 0; + status = NT_STATUS_OK; + break; + case LSA_POLICY_INFO_PD: + info->pd.name.string = NULL; + status = NT_STATUS_OK; + break; + case LSA_POLICY_INFO_REPLICA: + info->replica.source.string = NULL; + info->replica.account.string = NULL; + status = NT_STATUS_OK; + break; + case LSA_POLICY_INFO_QUOTA: + info->quota.paged_pool = 0; + info->quota.non_paged_pool = 0; + info->quota.min_wss = 0; + info->quota.max_wss = 0; + info->quota.pagefile = 0; + info->quota.unknown = 0; + status = NT_STATUS_OK; + break; + case LSA_POLICY_INFO_AUDIT_EVENTS: + { + + uint32_t policy_def = LSA_AUDIT_POLICY_ALL; + + /* check if the user has enough rights */ + if (!(handle->access & LSA_POLICY_VIEW_AUDIT_INFORMATION)) { + DEBUG(10,("_lsa_QueryInfoPolicy: insufficient access rights\n")); + return NT_STATUS_ACCESS_DENIED; + } + + /* fake info: We audit everything. ;) */ + + info->audit_events.auditing_mode = true; + info->audit_events.count = LSA_AUDIT_NUM_CATEGORIES; + info->audit_events.settings = talloc_zero_array(p->mem_ctx, + enum lsa_PolicyAuditPolicy, + info->audit_events.count); + if (!info->audit_events.settings) { + return NT_STATUS_NO_MEMORY; + } + + info->audit_events.settings[LSA_AUDIT_CATEGORY_ACCOUNT_MANAGEMENT] = policy_def; + info->audit_events.settings[LSA_AUDIT_CATEGORY_FILE_AND_OBJECT_ACCESS] = policy_def; + info->audit_events.settings[LSA_AUDIT_CATEGORY_LOGON] = policy_def; + info->audit_events.settings[LSA_AUDIT_CATEGORY_PROCCESS_TRACKING] = policy_def; + info->audit_events.settings[LSA_AUDIT_CATEGORY_SECURITY_POLICY_CHANGES] = policy_def; + info->audit_events.settings[LSA_AUDIT_CATEGORY_SYSTEM] = policy_def; + info->audit_events.settings[LSA_AUDIT_CATEGORY_USE_OF_USER_RIGHTS] = policy_def; + + break; + } + case LSA_POLICY_INFO_DOMAIN: + /* check if the user has enough rights */ + if (!(handle->access & LSA_POLICY_VIEW_LOCAL_INFORMATION)) + return NT_STATUS_ACCESS_DENIED; + + /* Request PolicyPrimaryDomainInformation. */ + switch (lp_server_role()) { + case ROLE_DOMAIN_PDC: + case ROLE_DOMAIN_BDC: + case ROLE_IPA_DC: + name = get_global_sam_name(); + sid = dom_sid_dup(p->mem_ctx, get_global_sam_sid()); + if (!sid) { + return NT_STATUS_NO_MEMORY; + } + break; + case ROLE_DOMAIN_MEMBER: + name = lp_workgroup(); + /* We need to return the Domain SID here. */ + if (secrets_fetch_domain_sid(lp_workgroup(), &domain_sid)) { + sid = dom_sid_dup(p->mem_ctx, &domain_sid); + if (!sid) { + return NT_STATUS_NO_MEMORY; + } + } else { + return NT_STATUS_CANT_ACCESS_DOMAIN_INFO; + } + break; + case ROLE_STANDALONE: + name = lp_workgroup(); + sid = NULL; + break; + default: + return NT_STATUS_CANT_ACCESS_DOMAIN_INFO; + } + init_dom_query_3(&info->domain, name, sid); + break; + case LSA_POLICY_INFO_ACCOUNT_DOMAIN: + /* check if the user has enough rights */ + if (!(handle->access & LSA_POLICY_VIEW_LOCAL_INFORMATION)) + return NT_STATUS_ACCESS_DENIED; + + /* Request PolicyAccountDomainInformation. */ + name = get_global_sam_name(); + sid = get_global_sam_sid(); + + init_dom_query_5(&info->account_domain, name, sid); + break; + case LSA_POLICY_INFO_ROLE: + /* check if the user has enough rights */ + if (!(handle->access & LSA_POLICY_VIEW_LOCAL_INFORMATION)) + return NT_STATUS_ACCESS_DENIED; + + switch (lp_server_role()) { + case ROLE_DOMAIN_BDC: + /* + * only a BDC is a backup controller + * of the domain, it controls. + */ + info->role.role = LSA_ROLE_BACKUP; + break; + default: + /* + * any other role is a primary + * of the domain, it controls. + */ + info->role.role = LSA_ROLE_PRIMARY; + break; + } + break; + case LSA_POLICY_INFO_DNS: + case LSA_POLICY_INFO_DNS_INT: { + struct pdb_domain_info *dominfo; + + if ((pdb_capabilities() & PDB_CAP_ADS) == 0) { + DEBUG(10, ("Not replying to LSA_POLICY_INFO_DNS " + "without ADS passdb backend\n")); + status = NT_STATUS_INVALID_INFO_CLASS; + break; + } + + dominfo = pdb_get_domain_info(info); + if (dominfo == NULL) { + status = NT_STATUS_NO_MEMORY; + break; + } + + init_lsa_StringLarge(&info->dns.name, + dominfo->name); + init_lsa_StringLarge(&info->dns.dns_domain, + dominfo->dns_domain); + init_lsa_StringLarge(&info->dns.dns_forest, + dominfo->dns_forest); + info->dns.domain_guid = dominfo->guid; + info->dns.sid = &dominfo->sid; + break; + } + default: + DEBUG(0,("_lsa_QueryInfoPolicy: unknown info level in Lsa Query: %d\n", + r->in.level)); + status = NT_STATUS_INVALID_INFO_CLASS; + break; + } + + *r->out.info = info; + + return status; +} + +/*************************************************************************** + _lsa_QueryInfoPolicy2 + ***************************************************************************/ + +NTSTATUS _lsa_QueryInfoPolicy2(struct pipes_struct *p, + struct lsa_QueryInfoPolicy2 *r2) +{ + struct lsa_QueryInfoPolicy r; + + if ((pdb_capabilities() & PDB_CAP_ADS) == 0) { + p->fault_state = DCERPC_FAULT_OP_RNG_ERROR; + return NT_STATUS_NOT_IMPLEMENTED; + } + + ZERO_STRUCT(r); + r.in.handle = r2->in.handle; + r.in.level = r2->in.level; + r.out.info = r2->out.info; + + return _lsa_QueryInfoPolicy(p, &r); +} + +/*************************************************************************** + _lsa_lookup_sids_internal + ***************************************************************************/ + +static NTSTATUS _lsa_lookup_sids_internal(struct pipes_struct *p, + TALLOC_CTX *mem_ctx, + uint16_t level, /* input */ + int num_sids, /* input */ + struct lsa_SidPtr *sid, /* input */ + struct lsa_RefDomainList **pp_ref, /* input/output */ + struct lsa_TranslatedName2 **pp_names,/* input/output */ + uint32_t *pp_mapped_count) /* input/output */ +{ + NTSTATUS status; + int i; + const struct dom_sid **sids = NULL; + struct lsa_RefDomainList *ref = NULL; + uint32_t mapped_count = 0; + struct lsa_dom_info *dom_infos = NULL; + struct lsa_name_info *name_infos = NULL; + struct lsa_TranslatedName2 *names = NULL; + + *pp_mapped_count = 0; + *pp_names = NULL; + *pp_ref = NULL; + + if (num_sids == 0) { + return NT_STATUS_OK; + } + + sids = talloc_array(p->mem_ctx, const struct dom_sid *, num_sids); + ref = talloc_zero(p->mem_ctx, struct lsa_RefDomainList); + + if (sids == NULL || ref == NULL) { + return NT_STATUS_NO_MEMORY; + } + + for (i=0; imem_ctx, num_sids, sids, level, + &dom_infos, &name_infos); + + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + names = talloc_array(p->mem_ctx, struct lsa_TranslatedName2, num_sids); + if (names == NULL) { + return NT_STATUS_NO_MEMORY; + } + + for (i=0; itype == SID_NAME_UNKNOWN) { + name->dom_idx = -1; + /* Unknown sids should return the string + * representation of the SID. Windows 2003 behaves + * rather erratic here, in many cases it returns the + * RID as 8 bytes hex, in others it returns the full + * SID. We (Jerry/VL) could not figure out which the + * hard cases are, so leave it with the SID. */ + name->name = dom_sid_string(p->mem_ctx, sids[i]); + if (name->name == NULL) { + return NT_STATUS_NO_MEMORY; + } + } else { + mapped_count += 1; + } + + names[i].sid_type = name->type; + names[i].name.string = name->name; + names[i].sid_index = name->dom_idx; + names[i].unknown = 0; + } + + status = NT_STATUS_NONE_MAPPED; + if (mapped_count > 0) { + status = (mapped_count < num_sids) ? + STATUS_SOME_UNMAPPED : NT_STATUS_OK; + } + + DEBUG(10, ("num_sids %d, mapped_count %d, status %s\n", + num_sids, mapped_count, nt_errstr(status))); + + *pp_mapped_count = mapped_count; + *pp_names = names; + *pp_ref = ref; + + return status; +} + +/*************************************************************************** + _lsa_LookupSids + ***************************************************************************/ + +NTSTATUS _lsa_LookupSids(struct pipes_struct *p, + struct lsa_LookupSids *r) +{ + NTSTATUS status; + struct lsa_info *handle; + int num_sids = r->in.sids->num_sids; + uint32_t mapped_count = 0; + struct lsa_RefDomainList *domains = NULL; + struct lsa_TranslatedName *names_out = NULL; + struct lsa_TranslatedName2 *names = NULL; + int i; + + if (p->transport != NCACN_NP && p->transport != NCALRPC) { + p->fault_state = DCERPC_FAULT_ACCESS_DENIED; + return NT_STATUS_ACCESS_DENIED; + } + + if ((r->in.level < 1) || (r->in.level > 6)) { + return NT_STATUS_INVALID_PARAMETER; + } + + handle = find_policy_by_hnd(p, + r->in.handle, + LSA_HANDLE_POLICY_TYPE, + struct lsa_info, + &status); + if (!NT_STATUS_IS_OK(status)) { + return NT_STATUS_INVALID_HANDLE; + } + + /* check if the user has enough rights */ + if (!(handle->access & LSA_POLICY_LOOKUP_NAMES)) { + return NT_STATUS_ACCESS_DENIED; + } + + if (num_sids > MAX_LOOKUP_SIDS) { + DEBUG(5,("_lsa_LookupSids: limit of %d exceeded, requested %d\n", + MAX_LOOKUP_SIDS, num_sids)); + return NT_STATUS_NONE_MAPPED; + } + + status = _lsa_lookup_sids_internal(p, + p->mem_ctx, + r->in.level, + num_sids, + r->in.sids->sids, + &domains, + &names, + &mapped_count); + + /* Only return here when there is a real error. + NT_STATUS_NONE_MAPPED is a special case as it indicates that none of + the requested sids could be resolved. Older versions of XP (pre SP3) + rely that we return with the string representations of those SIDs in + that case. If we don't, XP crashes - Guenther + */ + + if (NT_STATUS_IS_ERR(status) && + !NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) { + return status; + } + + /* Convert from lsa_TranslatedName2 to lsa_TranslatedName */ + names_out = talloc_array(p->mem_ctx, struct lsa_TranslatedName, + num_sids); + if (!names_out) { + return NT_STATUS_NO_MEMORY; + } + + for (i=0; iout.domains = domains; + r->out.names->count = num_sids; + r->out.names->names = names_out; + *r->out.count = mapped_count; + + return status; +} + +static NTSTATUS _lsa_LookupSids_common(struct pipes_struct *p, + struct lsa_LookupSids2 *r) +{ + struct dcesrv_call_state *dce_call = p->dce_call; + NTSTATUS status; + struct lsa_info *handle; + int num_sids = r->in.sids->num_sids; + uint32_t mapped_count = 0; + struct lsa_RefDomainList *domains = NULL; + struct lsa_TranslatedName2 *names = NULL; + bool check_policy = true; + + switch (dce_call->pkt.u.request.opnum) { + case NDR_LSA_LOOKUPSIDS3: + check_policy = false; + break; + case NDR_LSA_LOOKUPSIDS2: + default: + check_policy = true; + } + + if ((r->in.level < 1) || (r->in.level > 6)) { + return NT_STATUS_INVALID_PARAMETER; + } + + if (check_policy) { + handle = find_policy_by_hnd(p, + r->in.handle, + LSA_HANDLE_POLICY_TYPE, + struct lsa_info, + &status); + if (!NT_STATUS_IS_OK(status)) { + return NT_STATUS_INVALID_HANDLE; + } + + /* check if the user has enough rights */ + if (!(handle->access & LSA_POLICY_LOOKUP_NAMES)) { + return NT_STATUS_ACCESS_DENIED; + } + } + + if (num_sids > MAX_LOOKUP_SIDS) { + DEBUG(5,("_lsa_LookupSids2: limit of %d exceeded, requested %d\n", + MAX_LOOKUP_SIDS, num_sids)); + return NT_STATUS_NONE_MAPPED; + } + + status = _lsa_lookup_sids_internal(p, + p->mem_ctx, + r->in.level, + num_sids, + r->in.sids->sids, + &domains, + &names, + &mapped_count); + + *r->out.domains = domains; + r->out.names->count = num_sids; + r->out.names->names = names; + *r->out.count = mapped_count; + + return status; +} + +/*************************************************************************** + _lsa_LookupSids2 + ***************************************************************************/ + +NTSTATUS _lsa_LookupSids2(struct pipes_struct *p, + struct lsa_LookupSids2 *r) +{ + if (p->transport != NCACN_NP && p->transport != NCALRPC) { + p->fault_state = DCERPC_FAULT_ACCESS_DENIED; + return NT_STATUS_ACCESS_DENIED; + } + + return _lsa_LookupSids_common(p, r); +} + +/*************************************************************************** + _lsa_LookupSids3 + ***************************************************************************/ + +NTSTATUS _lsa_LookupSids3(struct pipes_struct *p, + struct lsa_LookupSids3 *r) +{ + struct dcesrv_call_state *dce_call = p->dce_call; + enum dcerpc_AuthType auth_type = DCERPC_AUTH_TYPE_NONE; + enum dcerpc_AuthLevel auth_level = DCERPC_AUTH_LEVEL_NONE; + struct lsa_LookupSids2 q; + + if (p->transport != NCACN_IP_TCP) { + p->fault_state = DCERPC_FAULT_ACCESS_DENIED; + return NT_STATUS_ACCESS_DENIED; + } + + dcesrv_call_auth_info(dce_call, &auth_type, &auth_level); + + /* No policy handle on this call. Restrict to crypto connections. */ + if (auth_type != DCERPC_AUTH_TYPE_SCHANNEL || + auth_level < DCERPC_AUTH_LEVEL_INTEGRITY) { + DEBUG(1, ("_lsa_LookupSids3: The client %s is not using " + "a secure connection over netlogon\n", + get_remote_machine_name() )); + p->fault_state = DCERPC_FAULT_ACCESS_DENIED; + return NT_STATUS_ACCESS_DENIED; + } + + q.in.handle = NULL; + q.in.sids = r->in.sids; + q.in.level = r->in.level; + q.in.lookup_options = r->in.lookup_options; + q.in.client_revision = r->in.client_revision; + q.in.names = r->in.names; + q.in.count = r->in.count; + + q.out.domains = r->out.domains; + q.out.names = r->out.names; + q.out.count = r->out.count; + + return _lsa_LookupSids_common(p, &q); +} + +/*************************************************************************** + ***************************************************************************/ + +static int lsa_lookup_level_to_flags(enum lsa_LookupNamesLevel level) +{ + int flags; + + switch (level) { + case LSA_LOOKUP_NAMES_ALL: /* 1 */ + flags = LOOKUP_NAME_ALL; + break; + case LSA_LOOKUP_NAMES_DOMAINS_ONLY: /* 2 */ + flags = LOOKUP_NAME_DOMAIN|LOOKUP_NAME_REMOTE|LOOKUP_NAME_ISOLATED; + break; + case LSA_LOOKUP_NAMES_PRIMARY_DOMAIN_ONLY: /* 3 */ + flags = LOOKUP_NAME_DOMAIN|LOOKUP_NAME_ISOLATED; + break; + case LSA_LOOKUP_NAMES_UPLEVEL_TRUSTS_ONLY: /* 4 */ + case LSA_LOOKUP_NAMES_FOREST_TRUSTS_ONLY: /* 5 */ + case LSA_LOOKUP_NAMES_UPLEVEL_TRUSTS_ONLY2: /* 6 */ + case LSA_LOOKUP_NAMES_RODC_REFERRAL_TO_FULL_DC: /* 7 */ + default: + flags = LOOKUP_NAME_NONE; + break; + } + + return flags; +} + +/*************************************************************************** + _lsa_LookupNames + ***************************************************************************/ + +NTSTATUS _lsa_LookupNames(struct pipes_struct *p, + struct lsa_LookupNames *r) +{ + NTSTATUS status = NT_STATUS_NONE_MAPPED; + struct lsa_info *handle; + struct lsa_String *names = r->in.names; + uint32_t num_entries = r->in.num_names; + struct lsa_RefDomainList *domains = NULL; + struct lsa_TranslatedSid *rids = NULL; + uint32_t mapped_count = 0; + int flags = 0; + + if (p->transport != NCACN_NP && p->transport != NCALRPC) { + p->fault_state = DCERPC_FAULT_ACCESS_DENIED; + return NT_STATUS_ACCESS_DENIED; + } + + if (num_entries > MAX_LOOKUP_SIDS) { + num_entries = MAX_LOOKUP_SIDS; + DEBUG(5,("_lsa_LookupNames: truncating name lookup list to %d\n", + num_entries)); + } + + flags = lsa_lookup_level_to_flags(r->in.level); + + domains = talloc_zero(p->mem_ctx, struct lsa_RefDomainList); + if (!domains) { + return NT_STATUS_NO_MEMORY; + } + + if (num_entries) { + rids = talloc_zero_array(p->mem_ctx, struct lsa_TranslatedSid, + num_entries); + if (!rids) { + return NT_STATUS_NO_MEMORY; + } + } else { + rids = NULL; + } + + handle = find_policy_by_hnd(p, + r->in.handle, + LSA_HANDLE_POLICY_TYPE, + struct lsa_info, + &status); + if (!NT_STATUS_IS_OK(status)) { + status = NT_STATUS_INVALID_HANDLE; + goto done; + } + + /* check if the user has enough rights */ + if (!(handle->access & LSA_POLICY_LOOKUP_NAMES)) { + status = NT_STATUS_ACCESS_DENIED; + goto done; + } + + /* set up the LSA Lookup RIDs response */ + become_root(); /* lookup_name can require root privs */ + status = lookup_lsa_rids(p->mem_ctx, domains, rids, num_entries, + names, flags, &mapped_count); + unbecome_root(); + +done: + + if (NT_STATUS_IS_OK(status) && (num_entries != 0) ) { + if (mapped_count == 0) { + status = NT_STATUS_NONE_MAPPED; + } else if (mapped_count != num_entries) { + status = STATUS_SOME_UNMAPPED; + } + } + + *r->out.count = mapped_count; + *r->out.domains = domains; + r->out.sids->sids = rids; + r->out.sids->count = num_entries; + + return status; +} + +/*************************************************************************** + _lsa_LookupNames2 + ***************************************************************************/ + +NTSTATUS _lsa_LookupNames2(struct pipes_struct *p, + struct lsa_LookupNames2 *r) +{ + NTSTATUS status; + struct lsa_LookupNames q; + struct lsa_TransSidArray2 *sid_array2 = r->in.sids; + struct lsa_TransSidArray *sid_array = NULL; + uint32_t i; + + if (p->transport != NCACN_NP && p->transport != NCALRPC) { + p->fault_state = DCERPC_FAULT_ACCESS_DENIED; + return NT_STATUS_ACCESS_DENIED; + } + + sid_array = talloc_zero(p->mem_ctx, struct lsa_TransSidArray); + if (!sid_array) { + return NT_STATUS_NO_MEMORY; + } + + q.in.handle = r->in.handle; + q.in.num_names = r->in.num_names; + q.in.names = r->in.names; + q.in.level = r->in.level; + q.in.sids = sid_array; + q.in.count = r->in.count; + /* we do not know what this is for */ + /* = r->in.unknown1; */ + /* = r->in.unknown2; */ + + q.out.domains = r->out.domains; + q.out.sids = sid_array; + q.out.count = r->out.count; + + status = _lsa_LookupNames(p, &q); + + sid_array2->count = sid_array->count; + sid_array2->sids = talloc_array(p->mem_ctx, struct lsa_TranslatedSid2, sid_array->count); + if (!sid_array2->sids) { + return NT_STATUS_NO_MEMORY; + } + + for (i=0; icount; i++) { + sid_array2->sids[i].sid_type = sid_array->sids[i].sid_type; + sid_array2->sids[i].rid = sid_array->sids[i].rid; + sid_array2->sids[i].sid_index = sid_array->sids[i].sid_index; + sid_array2->sids[i].unknown = 0; + } + + r->out.sids = sid_array2; + + return status; +} + +static NTSTATUS _lsa_LookupNames_common(struct pipes_struct *p, + struct lsa_LookupNames3 *r) +{ + struct dcesrv_call_state *dce_call = p->dce_call; + NTSTATUS status; + struct lsa_info *handle; + struct lsa_String *names = r->in.names; + uint32_t num_entries = r->in.num_names; + struct lsa_RefDomainList *domains = NULL; + struct lsa_TranslatedSid3 *trans_sids = NULL; + uint32_t mapped_count = 0; + int flags = 0; + bool check_policy = true; + + switch (dce_call->pkt.u.request.opnum) { + case NDR_LSA_LOOKUPNAMES4: + check_policy = false; + break; + case NDR_LSA_LOOKUPNAMES3: + default: + check_policy = true; + } + + if (num_entries > MAX_LOOKUP_SIDS) { + num_entries = MAX_LOOKUP_SIDS; + DEBUG(5,("_lsa_LookupNames3: truncating name lookup list to %d\n", num_entries)); + } + + flags = lsa_lookup_level_to_flags(r->in.level); + + domains = talloc_zero(p->mem_ctx, struct lsa_RefDomainList); + if (!domains) { + return NT_STATUS_NO_MEMORY; + } + + if (num_entries) { + trans_sids = talloc_zero_array(p->mem_ctx, struct lsa_TranslatedSid3, + num_entries); + if (!trans_sids) { + return NT_STATUS_NO_MEMORY; + } + } else { + trans_sids = NULL; + } + + if (check_policy) { + + handle = find_policy_by_hnd(p, + r->in.handle, + LSA_HANDLE_POLICY_TYPE, + struct lsa_info, + &status); + if (!NT_STATUS_IS_OK(status)) { + status = NT_STATUS_INVALID_HANDLE; + goto done; + } + + /* check if the user has enough rights */ + if (!(handle->access & LSA_POLICY_LOOKUP_NAMES)) { + status = NT_STATUS_ACCESS_DENIED; + goto done; + } + } + + /* set up the LSA Lookup SIDs response */ + become_root(); /* lookup_name can require root privs */ + status = lookup_lsa_sids(p->mem_ctx, domains, trans_sids, num_entries, + names, flags, &mapped_count); + unbecome_root(); + +done: + + if (NT_STATUS_IS_OK(status)) { + if (mapped_count == 0) { + status = NT_STATUS_NONE_MAPPED; + } else if (mapped_count != num_entries) { + status = STATUS_SOME_UNMAPPED; + } + } + + *r->out.count = mapped_count; + *r->out.domains = domains; + r->out.sids->sids = trans_sids; + r->out.sids->count = num_entries; + + return status; +} + +/*************************************************************************** + _lsa_LookupNames3 + ***************************************************************************/ + +NTSTATUS _lsa_LookupNames3(struct pipes_struct *p, + struct lsa_LookupNames3 *r) +{ + if (p->transport != NCACN_NP && p->transport != NCALRPC) { + p->fault_state = DCERPC_FAULT_ACCESS_DENIED; + return NT_STATUS_ACCESS_DENIED; + } + + return _lsa_LookupNames_common(p, r); +} + +/*************************************************************************** + _lsa_LookupNames4 + ***************************************************************************/ + +NTSTATUS _lsa_LookupNames4(struct pipes_struct *p, + struct lsa_LookupNames4 *r) +{ + struct dcesrv_call_state *dce_call = p->dce_call; + enum dcerpc_AuthType auth_type = DCERPC_AUTH_TYPE_NONE; + enum dcerpc_AuthLevel auth_level = DCERPC_AUTH_LEVEL_NONE; + struct lsa_LookupNames3 q; + + if (p->transport != NCACN_IP_TCP) { + p->fault_state = DCERPC_FAULT_ACCESS_DENIED; + return NT_STATUS_ACCESS_DENIED; + } + + dcesrv_call_auth_info(dce_call, &auth_type, &auth_level); + + /* No policy handle on this call. Restrict to crypto connections. */ + if (auth_type != DCERPC_AUTH_TYPE_SCHANNEL || + auth_level < DCERPC_AUTH_LEVEL_INTEGRITY) { + DEBUG(1, ("_lsa_LookupNames4: The client %s is not using " + "a secure connection over netlogon\n", + get_remote_machine_name())); + p->fault_state = DCERPC_FAULT_ACCESS_DENIED; + return NT_STATUS_ACCESS_DENIED; + } + + q.in.handle = NULL; + q.in.num_names = r->in.num_names; + q.in.names = r->in.names; + q.in.level = r->in.level; + q.in.lookup_options = r->in.lookup_options; + q.in.client_revision = r->in.client_revision; + q.in.sids = r->in.sids; + q.in.count = r->in.count; + + q.out.domains = r->out.domains; + q.out.sids = r->out.sids; + q.out.count = r->out.count; + + return _lsa_LookupNames_common(p, &q); +} + +/*************************************************************************** + _lsa_close. Also weird - needs to check if lsa handle is correct. JRA. + ***************************************************************************/ + +NTSTATUS _lsa_Close(struct pipes_struct *p, struct lsa_Close *r) +{ + NTSTATUS status; + + if (p->transport != NCACN_NP && p->transport != NCALRPC) { + p->fault_state = DCERPC_FAULT_ACCESS_DENIED; + return NT_STATUS_ACCESS_DENIED; + } + + (void)find_policy_by_hnd(p, + r->in.handle, + DCESRV_HANDLE_ANY, + struct lsa_info, + &status); + if (!NT_STATUS_IS_OK(status)) { + return NT_STATUS_INVALID_HANDLE; + } + + close_policy_hnd(p, r->in.handle); + ZERO_STRUCTP(r->out.handle); + return NT_STATUS_OK; +} + +/*************************************************************************** + ***************************************************************************/ + +static NTSTATUS lsa_lookup_trusted_domain_by_sid(TALLOC_CTX *mem_ctx, + const struct dom_sid *sid, + struct trustdom_info **info) +{ + NTSTATUS status; + uint32_t num_domains = 0; + struct trustdom_info **domains = NULL; + int i; + + status = pdb_enum_trusteddoms(mem_ctx, &num_domains, &domains); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + for (i=0; i < num_domains; i++) { + if (dom_sid_equal(&domains[i]->sid, sid)) { + break; + } + } + + if (i == num_domains) { + return NT_STATUS_INVALID_PARAMETER; + } + + *info = domains[i]; + + return NT_STATUS_OK; +} + +/*************************************************************************** + ***************************************************************************/ + +static NTSTATUS lsa_lookup_trusted_domain_by_name(TALLOC_CTX *mem_ctx, + const char *netbios_domain_name, + struct trustdom_info **info_p) +{ + NTSTATUS status; + struct trustdom_info *info; + struct pdb_trusted_domain *td; + + status = pdb_get_trusted_domain(mem_ctx, netbios_domain_name, &td); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + info = talloc(mem_ctx, struct trustdom_info); + if (!info) { + return NT_STATUS_NO_MEMORY; + } + + info->name = talloc_strdup(info, netbios_domain_name); + NT_STATUS_HAVE_NO_MEMORY(info->name); + + sid_copy(&info->sid, &td->security_identifier); + + *info_p = info; + + return NT_STATUS_OK; +} + +/*************************************************************************** + _lsa_OpenSecret + ***************************************************************************/ + +NTSTATUS _lsa_OpenSecret(struct pipes_struct *p, + struct lsa_OpenSecret *r) +{ + struct dcesrv_call_state *dce_call = p->dce_call; + struct auth_session_info *session_info = + dcesrv_call_session_info(dce_call); + struct security_descriptor *psd; + NTSTATUS status; + uint32_t acc_granted; + + (void)find_policy_by_hnd(p, + r->in.handle, + LSA_HANDLE_POLICY_TYPE, + struct lsa_info, + &status); + if (!NT_STATUS_IS_OK(status)) { + return NT_STATUS_INVALID_HANDLE; + } + + if (!r->in.name.string) { + return NT_STATUS_INVALID_PARAMETER; + } + + /* Work out max allowed. */ + map_max_allowed_access(session_info->security_token, + session_info->unix_token, + &r->in.access_mask); + + /* map the generic bits to the lsa policy ones */ + se_map_generic(&r->in.access_mask, &lsa_secret_mapping); + + status = pdb_get_secret(p->mem_ctx, r->in.name.string, + NULL, + NULL, + NULL, + NULL, + &psd); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + status = access_check_object(psd, session_info->security_token, + SEC_PRIV_INVALID, SEC_PRIV_INVALID, 0, + r->in.access_mask, + &acc_granted, "_lsa_OpenSecret"); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + status = create_lsa_policy_handle(p->mem_ctx, p, + LSA_HANDLE_SECRET_TYPE, + acc_granted, + NULL, + r->in.name.string, + psd, + r->out.sec_handle); + if (!NT_STATUS_IS_OK(status)) { + return NT_STATUS_OBJECT_NAME_NOT_FOUND; + } + + return NT_STATUS_OK; +} + +/*************************************************************************** + _lsa_OpenTrustedDomain_base + ***************************************************************************/ + +static NTSTATUS _lsa_OpenTrustedDomain_base(struct pipes_struct *p, + uint32_t access_mask, + struct trustdom_info *info, + struct policy_handle *handle) +{ + struct dcesrv_call_state *dce_call = p->dce_call; + struct auth_session_info *session_info = + dcesrv_call_session_info(dce_call); + struct security_descriptor *psd = NULL; + size_t sd_size; + uint32_t acc_granted; + NTSTATUS status; + + /* des_access is for the account here, not the policy + * handle - so don't check against policy handle. */ + + /* Work out max allowed. */ + map_max_allowed_access(session_info->security_token, + session_info->unix_token, + &access_mask); + + /* map the generic bits to the lsa account ones */ + se_map_generic(&access_mask, &lsa_trusted_domain_mapping); + + /* get the generic lsa account SD until we store it */ + status = make_lsa_object_sd(p->mem_ctx, &psd, &sd_size, + &lsa_trusted_domain_mapping, + NULL, 0); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + status = access_check_object(psd, session_info->security_token, + SEC_PRIV_INVALID, SEC_PRIV_INVALID, 0, + access_mask, &acc_granted, + "_lsa_OpenTrustedDomain"); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + status = create_lsa_policy_handle(p->mem_ctx, p, + LSA_HANDLE_TRUST_TYPE, + acc_granted, + &info->sid, + info->name, + psd, + handle); + if (!NT_STATUS_IS_OK(status)) { + return NT_STATUS_OBJECT_NAME_NOT_FOUND; + } + + return NT_STATUS_OK; +} + +/*************************************************************************** + _lsa_OpenTrustedDomain + ***************************************************************************/ + +NTSTATUS _lsa_OpenTrustedDomain(struct pipes_struct *p, + struct lsa_OpenTrustedDomain *r) +{ + struct trustdom_info *info = NULL; + NTSTATUS status; + + (void)find_policy_by_hnd(p, + r->in.handle, + LSA_HANDLE_POLICY_TYPE, + struct lsa_info, + &status); + if (!NT_STATUS_IS_OK(status)) { + return NT_STATUS_INVALID_HANDLE; + } + + status = lsa_lookup_trusted_domain_by_sid(p->mem_ctx, + r->in.sid, + &info); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + return _lsa_OpenTrustedDomain_base(p, r->in.access_mask, info, + r->out.trustdom_handle); +} + +/*************************************************************************** + _lsa_OpenTrustedDomainByName + ***************************************************************************/ + +NTSTATUS _lsa_OpenTrustedDomainByName(struct pipes_struct *p, + struct lsa_OpenTrustedDomainByName *r) +{ + struct trustdom_info *info = NULL; + NTSTATUS status; + + (void)find_policy_by_hnd(p, + r->in.handle, + LSA_HANDLE_POLICY_TYPE, + struct lsa_info, + &status); + if (!NT_STATUS_IS_OK(status)) { + return NT_STATUS_INVALID_HANDLE; + } + + status = lsa_lookup_trusted_domain_by_name(p->mem_ctx, + r->in.name.string, + &info); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + return _lsa_OpenTrustedDomain_base(p, r->in.access_mask, info, + r->out.trustdom_handle); +} + +static NTSTATUS get_trustdom_auth_blob(struct pipes_struct *p, + TALLOC_CTX *mem_ctx, DATA_BLOB *auth_blob, + struct trustDomainPasswords *auth_struct) +{ + struct dcesrv_call_state *dce_call = p->dce_call; + struct auth_session_info *session_info = + dcesrv_call_session_info(dce_call); + enum ndr_err_code ndr_err; + DATA_BLOB lsession_key; + gnutls_cipher_hd_t cipher_hnd = NULL; + gnutls_datum_t my_session_key; + NTSTATUS status; + int rc; + bool encrypted; + + encrypted = dcerpc_is_transport_encrypted(session_info); + if (lp_weak_crypto() == SAMBA_WEAK_CRYPTO_DISALLOWED && + !encrypted) { + return NT_STATUS_ACCESS_DENIED; + } + + status = session_extract_session_key( + session_info, &lsession_key, KEY_USE_16BYTES); + if (!NT_STATUS_IS_OK(status)) { + return NT_STATUS_INVALID_PARAMETER; + } + + my_session_key = (gnutls_datum_t) { + .data = lsession_key.data, + .size = lsession_key.length, + }; + + GNUTLS_FIPS140_SET_LAX_MODE(); + rc = gnutls_cipher_init(&cipher_hnd, + GNUTLS_CIPHER_ARCFOUR_128, + &my_session_key, + NULL); + if (rc < 0) { + GNUTLS_FIPS140_SET_STRICT_MODE(); + status = gnutls_error_to_ntstatus(rc, NT_STATUS_CRYPTO_SYSTEM_INVALID); + goto out; + } + + rc = gnutls_cipher_decrypt(cipher_hnd, + auth_blob->data, + auth_blob->length); + gnutls_cipher_deinit(cipher_hnd); + GNUTLS_FIPS140_SET_STRICT_MODE(); + if (rc < 0) { + status = gnutls_error_to_ntstatus(rc, NT_STATUS_CRYPTO_SYSTEM_INVALID); + goto out; + } + + ndr_err = ndr_pull_struct_blob(auth_blob, mem_ctx, + auth_struct, + (ndr_pull_flags_fn_t)ndr_pull_trustDomainPasswords); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + status = NT_STATUS_INVALID_PARAMETER; + goto out; + } + + status = NT_STATUS_OK; +out: + return status; +} + +static NTSTATUS get_trustauth_inout_blob(TALLOC_CTX *mem_ctx, + struct trustAuthInOutBlob *iopw, + DATA_BLOB *trustauth_blob) +{ + enum ndr_err_code ndr_err; + + if (iopw->current.count != iopw->count) { + return NT_STATUS_INVALID_PARAMETER; + } + + if (iopw->previous.count > iopw->current.count) { + return NT_STATUS_INVALID_PARAMETER; + } + + if (iopw->previous.count == 0) { + /* + * If the previous credentials are not present + * we need to make a copy. + */ + iopw->previous = iopw->current; + } + + if (iopw->previous.count < iopw->current.count) { + struct AuthenticationInformationArray *c = &iopw->current; + struct AuthenticationInformationArray *p = &iopw->previous; + + /* + * The previous array needs to have the same size + * as the current one. + * + * We may have to fill with TRUST_AUTH_TYPE_NONE + * elements. + */ + p->array = talloc_realloc(mem_ctx, p->array, + struct AuthenticationInformation, + c->count); + if (p->array == NULL) { + return NT_STATUS_NO_MEMORY; + } + + while (p->count < c->count) { + struct AuthenticationInformation *a = + &p->array[p->count++]; + + *a = (struct AuthenticationInformation) { + .LastUpdateTime = p->array[0].LastUpdateTime, + .AuthType = TRUST_AUTH_TYPE_NONE, + }; + } + } + + ndr_err = ndr_push_struct_blob(trustauth_blob, mem_ctx, + iopw, + (ndr_push_flags_fn_t)ndr_push_trustAuthInOutBlob); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + return NT_STATUS_INVALID_PARAMETER; + } + + return NT_STATUS_OK; +} + +/*************************************************************************** + _lsa_CreateTrustedDomainEx2 + ***************************************************************************/ + +NTSTATUS _lsa_CreateTrustedDomainEx2(struct pipes_struct *p, + struct lsa_CreateTrustedDomainEx2 *r) +{ + struct dcesrv_call_state *dce_call = p->dce_call; + struct auth_session_info *session_info = + dcesrv_call_session_info(dce_call); + struct lsa_info *policy; + NTSTATUS status; + uint32_t acc_granted; + struct security_descriptor *psd; + size_t sd_size; + struct pdb_trusted_domain td; + struct trustDomainPasswords auth_struct; + DATA_BLOB auth_blob; + + if (!IS_DC) { + return NT_STATUS_NOT_SUPPORTED; + } + + policy = find_policy_by_hnd(p, + r->in.policy_handle, + LSA_HANDLE_POLICY_TYPE, + struct lsa_info, + &status); + if (!NT_STATUS_IS_OK(status)) { + return NT_STATUS_INVALID_HANDLE; + } + + if (!(policy->access & LSA_POLICY_TRUST_ADMIN)) { + return NT_STATUS_ACCESS_DENIED; + } + + if (session_info->unix_token->uid != sec_initial_uid() && + !nt_token_check_domain_rid( + session_info->security_token, DOMAIN_RID_ADMINS)) { + return NT_STATUS_ACCESS_DENIED; + } + + /* Work out max allowed. */ + map_max_allowed_access(session_info->security_token, + session_info->unix_token, + &r->in.access_mask); + + /* map the generic bits to the lsa policy ones */ + se_map_generic(&r->in.access_mask, &lsa_account_mapping); + + status = make_lsa_object_sd(p->mem_ctx, &psd, &sd_size, + &lsa_trusted_domain_mapping, + NULL, 0); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + status = access_check_object(psd, session_info->security_token, + SEC_PRIV_INVALID, SEC_PRIV_INVALID, 0, + r->in.access_mask, &acc_granted, + "_lsa_CreateTrustedDomainEx2"); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + ZERO_STRUCT(td); + + td.domain_name = talloc_strdup(p->mem_ctx, + r->in.info->domain_name.string); + if (td.domain_name == NULL) { + return NT_STATUS_NO_MEMORY; + } + td.netbios_name = talloc_strdup(p->mem_ctx, + r->in.info->netbios_name.string); + if (td.netbios_name == NULL) { + return NT_STATUS_NO_MEMORY; + } + sid_copy(&td.security_identifier, r->in.info->sid); + td.trust_direction = r->in.info->trust_direction; + td.trust_type = r->in.info->trust_type; + td.trust_attributes = r->in.info->trust_attributes; + + if (r->in.auth_info_internal->auth_blob.size != 0) { + auth_blob.length = r->in.auth_info_internal->auth_blob.size; + auth_blob.data = r->in.auth_info_internal->auth_blob.data; + + status = get_trustdom_auth_blob(p, p->mem_ctx, &auth_blob, &auth_struct); + if (!NT_STATUS_IS_OK(status)) { + return NT_STATUS_UNSUCCESSFUL; + } + + status = get_trustauth_inout_blob(p->mem_ctx, &auth_struct.incoming, &td.trust_auth_incoming); + if (!NT_STATUS_IS_OK(status)) { + return NT_STATUS_UNSUCCESSFUL; + } + + status = get_trustauth_inout_blob(p->mem_ctx, &auth_struct.outgoing, &td.trust_auth_outgoing); + if (!NT_STATUS_IS_OK(status)) { + return NT_STATUS_UNSUCCESSFUL; + } + } else { + td.trust_auth_incoming.data = NULL; + td.trust_auth_incoming.length = 0; + td.trust_auth_outgoing.data = NULL; + td.trust_auth_outgoing.length = 0; + } + + status = pdb_set_trusted_domain(r->in.info->domain_name.string, &td); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + status = create_lsa_policy_handle(p->mem_ctx, p, + LSA_HANDLE_TRUST_TYPE, + acc_granted, + r->in.info->sid, + r->in.info->netbios_name.string, + psd, + r->out.trustdom_handle); + if (!NT_STATUS_IS_OK(status)) { + pdb_del_trusted_domain(r->in.info->netbios_name.string); + return NT_STATUS_OBJECT_NAME_NOT_FOUND; + } + + return NT_STATUS_OK; +} + +/*************************************************************************** + _lsa_CreateTrustedDomainEx + ***************************************************************************/ + +NTSTATUS _lsa_CreateTrustedDomainEx(struct pipes_struct *p, + struct lsa_CreateTrustedDomainEx *r) +{ + p->fault_state = DCERPC_FAULT_OP_RNG_ERROR; + return NT_STATUS_NOT_IMPLEMENTED; +} + +/*************************************************************************** + _lsa_CreateTrustedDomain + ***************************************************************************/ + +NTSTATUS _lsa_CreateTrustedDomain(struct pipes_struct *p, + struct lsa_CreateTrustedDomain *r) +{ + p->fault_state = DCERPC_FAULT_OP_RNG_ERROR; + return NT_STATUS_NOT_IMPLEMENTED; +} + +/*************************************************************************** + _lsa_DeleteTrustedDomain + ***************************************************************************/ + +NTSTATUS _lsa_DeleteTrustedDomain(struct pipes_struct *p, + struct lsa_DeleteTrustedDomain *r) +{ + NTSTATUS status; + struct lsa_info *handle; + struct pdb_trusted_domain *td; + + /* find the connection policy handle. */ + handle = find_policy_by_hnd(p, + r->in.handle, + LSA_HANDLE_POLICY_TYPE, + struct lsa_info, + &status); + if (!NT_STATUS_IS_OK(status)) { + return NT_STATUS_INVALID_HANDLE; + } + + if (!(handle->access & LSA_POLICY_TRUST_ADMIN)) { + return NT_STATUS_ACCESS_DENIED; + } + + status = pdb_get_trusted_domain_by_sid(p->mem_ctx, r->in.dom_sid, &td); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + if (td->netbios_name == NULL || *td->netbios_name == '\0') { + struct dom_sid_buf buf; + DEBUG(10, ("Missing netbios name for trusted domain %s.\n", + dom_sid_str_buf(r->in.dom_sid, &buf))); + return NT_STATUS_UNSUCCESSFUL; + } + + status = pdb_del_trusted_domain(td->netbios_name); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + return NT_STATUS_OK; +} + +/*************************************************************************** + _lsa_CloseTrustedDomainEx + ***************************************************************************/ + +NTSTATUS _lsa_CloseTrustedDomainEx(struct pipes_struct *p, + struct lsa_CloseTrustedDomainEx *r) +{ + return NT_STATUS_NOT_IMPLEMENTED; +} + +/*************************************************************************** + _lsa_QueryTrustedDomainInfo + ***************************************************************************/ + +static NTSTATUS pdb_trusted_domain_2_info_ex(TALLOC_CTX *mem_ctx, + struct pdb_trusted_domain *td, + struct lsa_TrustDomainInfoInfoEx *info_ex) +{ + if (td->domain_name == NULL || + td->netbios_name == NULL || + is_null_sid(&td->security_identifier)) { + return NT_STATUS_INVALID_PARAMETER; + } + + info_ex->domain_name.string = talloc_strdup(mem_ctx, td->domain_name); + info_ex->netbios_name.string = talloc_strdup(mem_ctx, td->netbios_name); + info_ex->sid = dom_sid_dup(mem_ctx, &td->security_identifier); + if (info_ex->domain_name.string == NULL || + info_ex->netbios_name.string == NULL || + info_ex->sid == NULL) { + return NT_STATUS_NO_MEMORY; + } + + info_ex->trust_direction = td->trust_direction; + info_ex->trust_type = td->trust_type; + info_ex->trust_attributes = td->trust_attributes; + + return NT_STATUS_OK; +} + +NTSTATUS _lsa_QueryTrustedDomainInfo(struct pipes_struct *p, + struct lsa_QueryTrustedDomainInfo *r) +{ + NTSTATUS status; + struct lsa_info *handle; + union lsa_TrustedDomainInfo *info; + struct pdb_trusted_domain *td; + uint32_t acc_required; + + /* find the connection policy handle. */ + handle = find_policy_by_hnd(p, + r->in.trustdom_handle, + LSA_HANDLE_TRUST_TYPE, + struct lsa_info, + &status); + if (!NT_STATUS_IS_OK(status)) { + return NT_STATUS_INVALID_HANDLE; + } + + switch (r->in.level) { + case LSA_TRUSTED_DOMAIN_INFO_NAME: + acc_required = LSA_TRUSTED_QUERY_DOMAIN_NAME; + break; + case LSA_TRUSTED_DOMAIN_INFO_CONTROLLERS: + acc_required = LSA_TRUSTED_QUERY_CONTROLLERS; + break; + case LSA_TRUSTED_DOMAIN_INFO_POSIX_OFFSET: + acc_required = LSA_TRUSTED_QUERY_POSIX; + break; + case LSA_TRUSTED_DOMAIN_INFO_PASSWORD: + acc_required = LSA_TRUSTED_QUERY_AUTH; + break; + case LSA_TRUSTED_DOMAIN_INFO_BASIC: + acc_required = LSA_TRUSTED_QUERY_DOMAIN_NAME; + break; + case LSA_TRUSTED_DOMAIN_INFO_INFO_EX: + acc_required = LSA_TRUSTED_QUERY_DOMAIN_NAME; + break; + case LSA_TRUSTED_DOMAIN_INFO_AUTH_INFO: + acc_required = LSA_TRUSTED_QUERY_AUTH; + break; + case LSA_TRUSTED_DOMAIN_INFO_FULL_INFO: + acc_required = LSA_TRUSTED_QUERY_DOMAIN_NAME | + LSA_TRUSTED_QUERY_POSIX | + LSA_TRUSTED_QUERY_AUTH; + break; + case LSA_TRUSTED_DOMAIN_INFO_AUTH_INFO_INTERNAL: + acc_required = LSA_TRUSTED_QUERY_AUTH; + break; + case LSA_TRUSTED_DOMAIN_INFO_FULL_INFO_INTERNAL: + acc_required = LSA_TRUSTED_QUERY_DOMAIN_NAME | + LSA_TRUSTED_QUERY_POSIX | + LSA_TRUSTED_QUERY_AUTH; + break; + case LSA_TRUSTED_DOMAIN_INFO_INFO_EX2_INTERNAL: + acc_required = LSA_TRUSTED_QUERY_DOMAIN_NAME; + break; + case LSA_TRUSTED_DOMAIN_INFO_FULL_INFO_2_INTERNAL: + acc_required = LSA_TRUSTED_QUERY_DOMAIN_NAME | + LSA_TRUSTED_QUERY_POSIX | + LSA_TRUSTED_QUERY_AUTH; + break; + case LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES: + acc_required = LSA_TRUSTED_QUERY_POSIX; + break; + default: + return NT_STATUS_INVALID_PARAMETER; + } + + if (!(handle->access & acc_required)) { + return NT_STATUS_ACCESS_DENIED; + } + + status = pdb_get_trusted_domain_by_sid(p->mem_ctx, &handle->sid, &td); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + info = talloc_zero(p->mem_ctx, union lsa_TrustedDomainInfo); + if (!info) { + return NT_STATUS_NO_MEMORY; + } + + switch (r->in.level) { + case LSA_TRUSTED_DOMAIN_INFO_NAME: + init_lsa_StringLarge(&info->name.netbios_name, td->netbios_name); + break; + case LSA_TRUSTED_DOMAIN_INFO_CONTROLLERS: + return NT_STATUS_INVALID_PARAMETER; + case LSA_TRUSTED_DOMAIN_INFO_POSIX_OFFSET: + info->posix_offset.posix_offset = *td->trust_posix_offset; + break; + case LSA_TRUSTED_DOMAIN_INFO_PASSWORD: + return NT_STATUS_INVALID_INFO_CLASS; + case LSA_TRUSTED_DOMAIN_INFO_BASIC: + return NT_STATUS_INVALID_PARAMETER; + case LSA_TRUSTED_DOMAIN_INFO_INFO_EX: + status = pdb_trusted_domain_2_info_ex(info, td, &info->info_ex); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + break; + case LSA_TRUSTED_DOMAIN_INFO_AUTH_INFO: + return NT_STATUS_INVALID_INFO_CLASS; + case LSA_TRUSTED_DOMAIN_INFO_FULL_INFO: + status = pdb_trusted_domain_2_info_ex(info, td, + &info->full_info.info_ex); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + info->full_info.posix_offset.posix_offset = *td->trust_posix_offset; + status = auth_blob_2_auth_info(p->mem_ctx, + td->trust_auth_incoming, + td->trust_auth_outgoing, + &info->full_info.auth_info); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + break; + case LSA_TRUSTED_DOMAIN_INFO_AUTH_INFO_INTERNAL: + return NT_STATUS_INVALID_INFO_CLASS; + case LSA_TRUSTED_DOMAIN_INFO_FULL_INFO_INTERNAL: + return NT_STATUS_INVALID_INFO_CLASS; + case LSA_TRUSTED_DOMAIN_INFO_INFO_EX2_INTERNAL: + return NT_STATUS_INVALID_PARAMETER; + case LSA_TRUSTED_DOMAIN_INFO_FULL_INFO_2_INTERNAL: + info->full_info2_internal.posix_offset.posix_offset = *td->trust_posix_offset; + status = auth_blob_2_auth_info(p->mem_ctx, + td->trust_auth_incoming, + td->trust_auth_outgoing, + &info->full_info2_internal.auth_info); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + break; + case LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES: + info->enc_types.enc_types = *td->supported_enc_type; + break; + default: + return NT_STATUS_INVALID_PARAMETER; + } + + *r->out.info = info; + + return NT_STATUS_OK; +} + +/*************************************************************************** + _lsa_QueryTrustedDomainInfoBySid + ***************************************************************************/ + +NTSTATUS _lsa_QueryTrustedDomainInfoBySid(struct pipes_struct *p, + struct lsa_QueryTrustedDomainInfoBySid *r) +{ + NTSTATUS status; + struct policy_handle trustdom_handle; + struct lsa_OpenTrustedDomain o; + struct lsa_QueryTrustedDomainInfo q; + struct lsa_Close c; + + o.in.handle = r->in.handle; + o.in.sid = r->in.dom_sid; + o.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + o.out.trustdom_handle = &trustdom_handle; + + status = _lsa_OpenTrustedDomain(p, &o); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + q.in.trustdom_handle = &trustdom_handle; + q.in.level = r->in.level; + q.out.info = r->out.info; + + status = _lsa_QueryTrustedDomainInfo(p, &q); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + c.in.handle = &trustdom_handle; + c.out.handle = &trustdom_handle; + + return _lsa_Close(p, &c); +} + +/*************************************************************************** + _lsa_QueryTrustedDomainInfoByName + ***************************************************************************/ + +NTSTATUS _lsa_QueryTrustedDomainInfoByName(struct pipes_struct *p, + struct lsa_QueryTrustedDomainInfoByName *r) +{ + NTSTATUS status; + struct policy_handle trustdom_handle; + struct lsa_OpenTrustedDomainByName o; + struct lsa_QueryTrustedDomainInfo q; + struct lsa_Close c; + + o.in.handle = r->in.handle; + o.in.name.string = r->in.trusted_domain->string; + o.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + o.out.trustdom_handle = &trustdom_handle; + + status = _lsa_OpenTrustedDomainByName(p, &o); + if (!NT_STATUS_IS_OK(status)) { + if (NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_DOMAIN)) { + return NT_STATUS_OBJECT_NAME_NOT_FOUND; + } + return status; + } + + q.in.trustdom_handle = &trustdom_handle; + q.in.level = r->in.level; + q.out.info = r->out.info; + + status = _lsa_QueryTrustedDomainInfo(p, &q); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + c.in.handle = &trustdom_handle; + c.out.handle = &trustdom_handle; + + return _lsa_Close(p, &c); +} + +/*************************************************************************** + _lsa_CreateSecret + ***************************************************************************/ + +NTSTATUS _lsa_CreateSecret(struct pipes_struct *p, + struct lsa_CreateSecret *r) +{ + struct dcesrv_call_state *dce_call = p->dce_call; + struct auth_session_info *session_info = + dcesrv_call_session_info(dce_call); + NTSTATUS status; + struct lsa_info *handle; + uint32_t acc_granted; + struct security_descriptor *psd; + size_t sd_size; + + /* find the connection policy handle. */ + handle = find_policy_by_hnd(p, + r->in.handle, + LSA_HANDLE_POLICY_TYPE, + struct lsa_info, + &status); + if (!NT_STATUS_IS_OK(status)) { + return NT_STATUS_INVALID_HANDLE; + } + + /* check if the user has enough rights */ + + if (!(handle->access & LSA_POLICY_CREATE_SECRET)) { + return NT_STATUS_ACCESS_DENIED; + } + + /* Work out max allowed. */ + map_max_allowed_access(session_info->security_token, + session_info->unix_token, + &r->in.access_mask); + + /* map the generic bits to the lsa policy ones */ + se_map_generic(&r->in.access_mask, &lsa_secret_mapping); + + status = make_lsa_object_sd(p->mem_ctx, &psd, &sd_size, + &lsa_secret_mapping, + NULL, 0); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + status = access_check_object(psd, session_info->security_token, + SEC_PRIV_INVALID, SEC_PRIV_INVALID, 0, + r->in.access_mask, + &acc_granted, "_lsa_CreateSecret"); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + if (!r->in.name.string) { + return NT_STATUS_INVALID_PARAMETER; + } + + if (strlen(r->in.name.string) > 128) { + return NT_STATUS_NAME_TOO_LONG; + } + + status = pdb_get_secret(p->mem_ctx, r->in.name.string, + NULL, NULL, NULL, NULL, NULL); + if (NT_STATUS_IS_OK(status)) { + return NT_STATUS_OBJECT_NAME_COLLISION; + } + + status = pdb_set_secret(r->in.name.string, NULL, NULL, psd); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + status = create_lsa_policy_handle(p->mem_ctx, p, + LSA_HANDLE_SECRET_TYPE, + acc_granted, + NULL, + r->in.name.string, + psd, + r->out.sec_handle); + if (!NT_STATUS_IS_OK(status)) { + return NT_STATUS_OBJECT_NAME_NOT_FOUND; + } + + return NT_STATUS_OK; +} + +/*************************************************************************** + _lsa_SetSecret + ***************************************************************************/ + +NTSTATUS _lsa_SetSecret(struct pipes_struct *p, + struct lsa_SetSecret *r) +{ + struct dcesrv_call_state *dce_call = p->dce_call; + struct auth_session_info *session_info = + dcesrv_call_session_info(dce_call); + NTSTATUS status; + struct lsa_info *info = NULL; + DATA_BLOB blob_new, blob_old; + DATA_BLOB cleartext_blob_new = data_blob_null; + DATA_BLOB cleartext_blob_old = data_blob_null; + DATA_BLOB *cleartext_blob_new_p = NULL; + DATA_BLOB *cleartext_blob_old_p = NULL; + DATA_BLOB session_key; + + info = find_policy_by_hnd(p, + r->in.sec_handle, + LSA_HANDLE_SECRET_TYPE, + struct lsa_info, + &status); + if (!NT_STATUS_IS_OK(status)) { + return NT_STATUS_INVALID_HANDLE; + } + + if (!(info->access & LSA_SECRET_SET_VALUE)) { + return NT_STATUS_ACCESS_DENIED; + } + + status = session_extract_session_key( + session_info, &session_key, KEY_USE_16BYTES); + if(!NT_STATUS_IS_OK(status)) { + return status; + } + + if (r->in.new_val) { + blob_new = data_blob_const(r->in.new_val->data, + r->in.new_val->length); + + status = sess_decrypt_blob(p->mem_ctx, &blob_new, + &session_key, + &cleartext_blob_new); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + cleartext_blob_new_p = &cleartext_blob_new; + } + + if (r->in.old_val) { + blob_old = data_blob_const(r->in.old_val->data, + r->in.old_val->length); + + status = sess_decrypt_blob(p->mem_ctx, &blob_old, + &session_key, + &cleartext_blob_old); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + cleartext_blob_old_p = &cleartext_blob_old; + } + + status = pdb_set_secret(info->name, cleartext_blob_new_p, cleartext_blob_old_p, NULL); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + +#ifdef DEBUG_PASSWORD + DEBUG(10,("_lsa_SetSecret: successfully set new secret\n")); + dump_data(10, cleartext_blob_new.data, cleartext_blob_new.length); + DEBUG(10,("_lsa_SetSecret: successfully set old secret\n")); + dump_data(10, cleartext_blob_old.data, cleartext_blob_old.length); +#endif + + return NT_STATUS_OK; +} + +/*************************************************************************** + _lsa_QuerySecret + ***************************************************************************/ + +NTSTATUS _lsa_QuerySecret(struct pipes_struct *p, + struct lsa_QuerySecret *r) +{ + struct dcesrv_call_state *dce_call = p->dce_call; + struct auth_session_info *session_info = + dcesrv_call_session_info(dce_call); + struct lsa_info *info = NULL; + DATA_BLOB blob_new, blob_old; + DATA_BLOB blob_new_crypt, blob_old_crypt; + DATA_BLOB session_key; + NTTIME nttime_new, nttime_old; + NTSTATUS status; + + info = find_policy_by_hnd(p, + r->in.sec_handle, + LSA_HANDLE_SECRET_TYPE, + struct lsa_info, + &status); + if (!NT_STATUS_IS_OK(status)) { + return NT_STATUS_INVALID_HANDLE; + } + + if (!(info->access & LSA_SECRET_QUERY_VALUE)) { + return NT_STATUS_ACCESS_DENIED; + } + + status = pdb_get_secret(p->mem_ctx, info->name, + &blob_new, &nttime_new, + &blob_old, &nttime_old, + NULL); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + status = session_extract_session_key( + session_info, &session_key, KEY_USE_16BYTES); + if(!NT_STATUS_IS_OK(status)) { + return status; + } + + if (r->in.new_val) { + if (blob_new.length) { + if (!r->out.new_val->buf) { + r->out.new_val->buf = talloc_zero(p->mem_ctx, struct lsa_DATA_BUF); + } + if (!r->out.new_val->buf) { + return NT_STATUS_NO_MEMORY; + } + + blob_new_crypt = sess_encrypt_blob(p->mem_ctx, &blob_new, + &session_key); + if (!blob_new_crypt.length) { + return NT_STATUS_NO_MEMORY; + } + + r->out.new_val->buf->data = blob_new_crypt.data; + r->out.new_val->buf->length = blob_new_crypt.length; + r->out.new_val->buf->size = blob_new_crypt.length; + } + } + + if (r->in.old_val) { + if (blob_old.length) { + if (!r->out.old_val->buf) { + r->out.old_val->buf = talloc_zero(p->mem_ctx, struct lsa_DATA_BUF); + } + if (!r->out.old_val->buf) { + return NT_STATUS_NO_MEMORY; + } + + blob_old_crypt = sess_encrypt_blob(p->mem_ctx, &blob_old, + &session_key); + if (!blob_old_crypt.length) { + return NT_STATUS_NO_MEMORY; + } + + r->out.old_val->buf->data = blob_old_crypt.data; + r->out.old_val->buf->length = blob_old_crypt.length; + r->out.old_val->buf->size = blob_old_crypt.length; + } + } + + if (r->out.new_mtime) { + *r->out.new_mtime = nttime_new; + } + + if (r->out.old_mtime) { + *r->out.old_mtime = nttime_old; + } + + return NT_STATUS_OK; +} + +/*************************************************************************** + _lsa_DeleteObject + ***************************************************************************/ + +NTSTATUS _lsa_DeleteObject(struct pipes_struct *p, + struct lsa_DeleteObject *r) +{ + NTSTATUS status; + struct lsa_info *info = NULL; + + info = find_policy_by_hnd(p, + r->in.handle, + DCESRV_HANDLE_ANY, + struct lsa_info, + &status); + if (!NT_STATUS_IS_OK(status)) { + return NT_STATUS_INVALID_HANDLE; + } + + if (!(info->access & SEC_STD_DELETE)) { + return NT_STATUS_ACCESS_DENIED; + } + + switch (info->type) { + case LSA_HANDLE_ACCOUNT_TYPE: + status = privilege_delete_account(&info->sid); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(10,("_lsa_DeleteObject: privilege_delete_account gave: %s\n", + nt_errstr(status))); + return status; + } + break; + case LSA_HANDLE_TRUST_TYPE: + if (!pdb_del_trusteddom_pw(info->name)) { + return NT_STATUS_OBJECT_NAME_NOT_FOUND; + } + status = NT_STATUS_OK; + break; + case LSA_HANDLE_SECRET_TYPE: + status = pdb_delete_secret(info->name); + if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) { + return NT_STATUS_INVALID_HANDLE; + } + break; + default: + return NT_STATUS_INVALID_HANDLE; + } + + close_policy_hnd(p, r->in.handle); + ZERO_STRUCTP(r->out.handle); + + return status; +} + +/*************************************************************************** + _lsa_EnumPrivs + ***************************************************************************/ + +NTSTATUS _lsa_EnumPrivs(struct pipes_struct *p, + struct lsa_EnumPrivs *r) +{ + struct lsa_info *handle; + uint32_t i; + uint32_t enum_context = *r->in.resume_handle; + int num_privs = num_privileges_in_short_list(); + struct lsa_PrivEntry *entries = NULL; + NTSTATUS status; + + /* remember that the enum_context starts at 0 and not 1 */ + + if ( enum_context >= num_privs ) + return NT_STATUS_NO_MORE_ENTRIES; + + DEBUG(10,("_lsa_EnumPrivs: enum_context:%d total entries:%d\n", + enum_context, num_privs)); + + handle = find_policy_by_hnd(p, + r->in.handle, + LSA_HANDLE_POLICY_TYPE, + struct lsa_info, + &status); + if (!NT_STATUS_IS_OK(status)) { + return NT_STATUS_INVALID_HANDLE; + } + + /* check if the user has enough rights + I don't know if it's the right one. not documented. */ + + if (!(handle->access & LSA_POLICY_VIEW_LOCAL_INFORMATION)) + return NT_STATUS_ACCESS_DENIED; + + if (num_privs) { + entries = talloc_zero_array(p->mem_ctx, struct lsa_PrivEntry, num_privs); + if (!entries) { + return NT_STATUS_NO_MEMORY; + } + } else { + entries = NULL; + } + + for (i = 0; i < num_privs; i++) { + if( i < enum_context) { + + init_lsa_StringLarge(&entries[i].name, NULL); + + entries[i].luid.low = 0; + entries[i].luid.high = 0; + } else { + + init_lsa_StringLarge(&entries[i].name, sec_privilege_name_from_index(i)); + + entries[i].luid.low = sec_privilege_from_index(i); + entries[i].luid.high = 0; + } + } + + enum_context = num_privs; + + *r->out.resume_handle = enum_context; + r->out.privs->count = num_privs; + r->out.privs->privs = entries; + + return NT_STATUS_OK; +} + +/*************************************************************************** + _lsa_LookupPrivDisplayName + ***************************************************************************/ + +NTSTATUS _lsa_LookupPrivDisplayName(struct pipes_struct *p, + struct lsa_LookupPrivDisplayName *r) +{ + struct lsa_info *handle; + const char *description; + struct lsa_StringLarge *lsa_name; + NTSTATUS status; + + handle = find_policy_by_hnd(p, + r->in.handle, + LSA_HANDLE_POLICY_TYPE, + struct lsa_info, + &status); + if (!NT_STATUS_IS_OK(status)) { + return NT_STATUS_INVALID_HANDLE; + } + + /* check if the user has enough rights */ + + /* + * I don't know if it's the right one. not documented. + */ + if (!(handle->access & LSA_POLICY_VIEW_LOCAL_INFORMATION)) + return NT_STATUS_ACCESS_DENIED; + + DEBUG(10,("_lsa_LookupPrivDisplayName: name = %s\n", r->in.name->string)); + + description = get_privilege_dispname(r->in.name->string); + if (!description) { + DEBUG(10,("_lsa_LookupPrivDisplayName: doesn't exist\n")); + return NT_STATUS_NO_SUCH_PRIVILEGE; + } + + DEBUG(10,("_lsa_LookupPrivDisplayName: display name = %s\n", description)); + + lsa_name = talloc_zero(p->mem_ctx, struct lsa_StringLarge); + if (!lsa_name) { + return NT_STATUS_NO_MEMORY; + } + + init_lsa_StringLarge(lsa_name, description); + + *r->out.returned_language_id = r->in.language_id; + *r->out.disp_name = lsa_name; + + return NT_STATUS_OK; +} + +/*************************************************************************** + _lsa_EnumAccounts + ***************************************************************************/ + +NTSTATUS _lsa_EnumAccounts(struct pipes_struct *p, + struct lsa_EnumAccounts *r) +{ + struct lsa_info *handle; + struct dom_sid *sid_list; + int i, j, num_entries; + NTSTATUS status; + struct lsa_SidPtr *sids = NULL; + + handle = find_policy_by_hnd(p, + r->in.handle, + LSA_HANDLE_POLICY_TYPE, + struct lsa_info, + &status); + if (!NT_STATUS_IS_OK(status)) { + return NT_STATUS_INVALID_HANDLE; + } + + if (!(handle->access & LSA_POLICY_VIEW_LOCAL_INFORMATION)) + return NT_STATUS_ACCESS_DENIED; + + sid_list = NULL; + num_entries = 0; + + /* The only way we can currently find out all the SIDs that have been + privileged is to scan all privileges */ + + status = privilege_enumerate_accounts(&sid_list, &num_entries); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + if (*r->in.resume_handle >= num_entries) { + return NT_STATUS_NO_MORE_ENTRIES; + } + + if (num_entries - *r->in.resume_handle) { + sids = talloc_zero_array(p->mem_ctx, struct lsa_SidPtr, + num_entries - *r->in.resume_handle); + if (!sids) { + talloc_free(sid_list); + return NT_STATUS_NO_MEMORY; + } + + for (i = *r->in.resume_handle, j = 0; i < num_entries; i++, j++) { + sids[j].sid = dom_sid_dup(p->mem_ctx, &sid_list[i]); + if (!sids[j].sid) { + talloc_free(sid_list); + return NT_STATUS_NO_MEMORY; + } + } + } + + talloc_free(sid_list); + + *r->out.resume_handle = num_entries; + r->out.sids->num_sids = num_entries; + r->out.sids->sids = sids; + + return NT_STATUS_OK; +} + +/*************************************************************************** + _lsa_GetUserName + ***************************************************************************/ + +NTSTATUS _lsa_GetUserName(struct pipes_struct *p, + struct lsa_GetUserName *r) +{ + struct dcesrv_call_state *dce_call = p->dce_call; + struct auth_session_info *session_info = + dcesrv_call_session_info(dce_call); + const char *username, *domname; + struct lsa_String *account_name = NULL; + struct lsa_String *authority_name = NULL; + + if (p->transport != NCACN_NP && p->transport != NCALRPC) { + p->fault_state = DCERPC_FAULT_ACCESS_DENIED; + return NT_STATUS_ACCESS_DENIED; + } + + if (r->in.account_name && + *r->in.account_name) { + return NT_STATUS_INVALID_PARAMETER; + } + + if (r->in.authority_name && + *r->in.authority_name) { + return NT_STATUS_INVALID_PARAMETER; + } + + if (security_session_user_level(session_info, NULL) < SECURITY_USER) { + /* + * I'm 99% sure this is not the right place to do this, + * global_sid_Anonymous should probably be put into the token + * instead of the guest id -- vl + */ + if (!lookup_sid(p->mem_ctx, &global_sid_Anonymous, + &domname, &username, NULL)) { + return NT_STATUS_NO_MEMORY; + } + } else { + username = session_info->unix_info->sanitized_username; + domname = session_info->info->domain_name; + } + + account_name = talloc(p->mem_ctx, struct lsa_String); + if (!account_name) { + return NT_STATUS_NO_MEMORY; + } + init_lsa_String(account_name, username); + + if (r->out.authority_name) { + authority_name = talloc(p->mem_ctx, struct lsa_String); + if (!authority_name) { + return NT_STATUS_NO_MEMORY; + } + init_lsa_String(authority_name, domname); + } + + *r->out.account_name = account_name; + if (r->out.authority_name) { + *r->out.authority_name = authority_name; + } + + return NT_STATUS_OK; +} + +/*************************************************************************** + _lsa_CreateAccount + ***************************************************************************/ + +NTSTATUS _lsa_CreateAccount(struct pipes_struct *p, + struct lsa_CreateAccount *r) +{ + struct dcesrv_call_state *dce_call = p->dce_call; + struct auth_session_info *session_info = + dcesrv_call_session_info(dce_call); + NTSTATUS status; + struct lsa_info *handle; + uint32_t acc_granted; + struct security_descriptor *psd; + size_t sd_size; + uint32_t owner_access = (LSA_ACCOUNT_ALL_ACCESS & + ~(LSA_ACCOUNT_ADJUST_PRIVILEGES| + LSA_ACCOUNT_ADJUST_SYSTEM_ACCESS| + SEC_STD_DELETE)); + + /* find the connection policy handle. */ + handle = find_policy_by_hnd(p, + r->in.handle, + LSA_HANDLE_POLICY_TYPE, + struct lsa_info, + &status); + if (!NT_STATUS_IS_OK(status)) { + return NT_STATUS_INVALID_HANDLE; + } + + /* check if the user has enough rights */ + + if (!(handle->access & LSA_POLICY_CREATE_ACCOUNT)) { + return NT_STATUS_ACCESS_DENIED; + } + + /* Work out max allowed. */ + map_max_allowed_access(session_info->security_token, + session_info->unix_token, + &r->in.access_mask); + + /* map the generic bits to the lsa policy ones */ + se_map_generic(&r->in.access_mask, &lsa_account_mapping); + + status = make_lsa_object_sd(p->mem_ctx, &psd, &sd_size, + &lsa_account_mapping, + r->in.sid, owner_access); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + status = access_check_object(psd, session_info->security_token, + SEC_PRIV_INVALID, SEC_PRIV_INVALID, 0, r->in.access_mask, + &acc_granted, "_lsa_CreateAccount"); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + if ( is_privileged_sid( r->in.sid ) ) + return NT_STATUS_OBJECT_NAME_COLLISION; + + status = create_lsa_policy_handle(p->mem_ctx, p, + LSA_HANDLE_ACCOUNT_TYPE, + acc_granted, + r->in.sid, + NULL, + psd, + r->out.acct_handle); + if (!NT_STATUS_IS_OK(status)) { + return NT_STATUS_OBJECT_NAME_NOT_FOUND; + } + + return privilege_create_account(r->in.sid); +} + +/*************************************************************************** + _lsa_OpenAccount + ***************************************************************************/ + +NTSTATUS _lsa_OpenAccount(struct pipes_struct *p, + struct lsa_OpenAccount *r) +{ + struct dcesrv_call_state *dce_call = p->dce_call; + struct auth_session_info *session_info = + dcesrv_call_session_info(dce_call); + struct security_descriptor *psd = NULL; + size_t sd_size; + uint32_t des_access = r->in.access_mask; + uint32_t acc_granted; + uint32_t owner_access = (LSA_ACCOUNT_ALL_ACCESS & + ~(LSA_ACCOUNT_ADJUST_PRIVILEGES| + LSA_ACCOUNT_ADJUST_SYSTEM_ACCESS| + SEC_STD_DELETE)); + NTSTATUS status; + + /* find the connection policy handle. */ + (void)find_policy_by_hnd(p, + r->in.handle, + LSA_HANDLE_POLICY_TYPE, + struct lsa_info, + &status); + if (!NT_STATUS_IS_OK(status)) { + return NT_STATUS_INVALID_HANDLE; + } + + /* des_access is for the account here, not the policy + * handle - so don't check against policy handle. */ + + /* Work out max allowed. */ + map_max_allowed_access(session_info->security_token, + session_info->unix_token, + &des_access); + + /* map the generic bits to the lsa account ones */ + se_map_generic(&des_access, &lsa_account_mapping); + + /* get the generic lsa account SD until we store it */ + status = make_lsa_object_sd(p->mem_ctx, &psd, &sd_size, + &lsa_account_mapping, + r->in.sid, owner_access); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + status = access_check_object(psd, session_info->security_token, + SEC_PRIV_INVALID, SEC_PRIV_INVALID, 0, des_access, + &acc_granted, "_lsa_OpenAccount" ); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + /* TODO: Fis the parsing routine before reenabling this check! */ + #if 0 + if (!lookup_sid(&handle->sid, dom_name, name, &type)) + return NT_STATUS_ACCESS_DENIED; + #endif + + status = create_lsa_policy_handle(p->mem_ctx, p, + LSA_HANDLE_ACCOUNT_TYPE, + acc_granted, + r->in.sid, + NULL, + psd, + r->out.acct_handle); + if (!NT_STATUS_IS_OK(status)) { + return NT_STATUS_OBJECT_NAME_NOT_FOUND; + } + + return NT_STATUS_OK; +} + +/*************************************************************************** + _lsa_EnumPrivsAccount + For a given SID, enumerate all the privilege this account has. + ***************************************************************************/ + +NTSTATUS _lsa_EnumPrivsAccount(struct pipes_struct *p, + struct lsa_EnumPrivsAccount *r) +{ + NTSTATUS status = NT_STATUS_OK; + struct lsa_info *info=NULL; + PRIVILEGE_SET *privileges; + struct lsa_PrivilegeSet *priv_set = NULL; + struct dom_sid_buf buf; + + /* find the connection policy handle. */ + info = find_policy_by_hnd(p, + r->in.handle, + LSA_HANDLE_ACCOUNT_TYPE, + struct lsa_info, + &status); + if (!NT_STATUS_IS_OK(status)) { + return NT_STATUS_INVALID_HANDLE; + } + + if (!(info->access & LSA_ACCOUNT_VIEW)) + return NT_STATUS_ACCESS_DENIED; + + status = get_privileges_for_sid_as_set(p->mem_ctx, &privileges, &info->sid); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + *r->out.privs = priv_set = talloc_zero(p->mem_ctx, struct lsa_PrivilegeSet); + if (!priv_set) { + return NT_STATUS_NO_MEMORY; + } + + DEBUG(10,("_lsa_EnumPrivsAccount: %s has %d privileges\n", + dom_sid_str_buf(&info->sid, &buf), + privileges->count)); + + priv_set->count = privileges->count; + priv_set->unknown = 0; + priv_set->set = talloc_move(priv_set, &privileges->set); + + return status; +} + +/*************************************************************************** + _lsa_GetSystemAccessAccount + ***************************************************************************/ + +NTSTATUS _lsa_GetSystemAccessAccount(struct pipes_struct *p, + struct lsa_GetSystemAccessAccount *r) +{ + NTSTATUS status; + struct lsa_info *info = NULL; + struct lsa_EnumPrivsAccount e; + struct lsa_PrivilegeSet *privset; + + /* find the connection policy handle. */ + + info = find_policy_by_hnd(p, + r->in.handle, + LSA_HANDLE_ACCOUNT_TYPE, + struct lsa_info, + &status); + if (!NT_STATUS_IS_OK(status)) { + return NT_STATUS_INVALID_HANDLE; + } + + if (!(info->access & LSA_ACCOUNT_VIEW)) + return NT_STATUS_ACCESS_DENIED; + + privset = talloc_zero(p->mem_ctx, struct lsa_PrivilegeSet); + if (!privset) { + return NT_STATUS_NO_MEMORY; + } + + e.in.handle = r->in.handle; + e.out.privs = &privset; + + status = _lsa_EnumPrivsAccount(p, &e); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(10,("_lsa_GetSystemAccessAccount: " + "failed to call _lsa_EnumPrivsAccount(): %s\n", + nt_errstr(status))); + return status; + } + + /* Samba4 would iterate over the privset to merge the policy mode bits, + * not sure samba3 can do the same here, so just return what we did in + * the past - gd */ + + /* + 0x01 -> Log on locally + 0x02 -> Access this computer from network + 0x04 -> Log on as a batch job + 0x10 -> Log on as a service + + they can be ORed together + */ + + *r->out.access_mask = LSA_POLICY_MODE_INTERACTIVE | + LSA_POLICY_MODE_NETWORK; + + return NT_STATUS_OK; +} + +/*************************************************************************** + update the systemaccount information + ***************************************************************************/ + +NTSTATUS _lsa_SetSystemAccessAccount(struct pipes_struct *p, + struct lsa_SetSystemAccessAccount *r) +{ + struct lsa_info *info=NULL; + NTSTATUS status; + GROUP_MAP *map; + + /* find the connection policy handle. */ + info = find_policy_by_hnd(p, + r->in.handle, + LSA_HANDLE_ACCOUNT_TYPE, + struct lsa_info, + &status); + if (!NT_STATUS_IS_OK(status)) { + return NT_STATUS_INVALID_HANDLE; + } + + if (!(info->access & LSA_ACCOUNT_ADJUST_SYSTEM_ACCESS)) { + return NT_STATUS_ACCESS_DENIED; + } + + map = talloc_zero(p->mem_ctx, GROUP_MAP); + if (!map) { + return NT_STATUS_NO_MEMORY; + } + + if (!pdb_getgrsid(map, info->sid)) { + TALLOC_FREE(map); + return NT_STATUS_NO_SUCH_GROUP; + } + + status = pdb_update_group_mapping_entry(map); + TALLOC_FREE(map); + return status; +} + +/*************************************************************************** + _lsa_AddPrivilegesToAccount + For a given SID, add some privileges. + ***************************************************************************/ + +NTSTATUS _lsa_AddPrivilegesToAccount(struct pipes_struct *p, + struct lsa_AddPrivilegesToAccount *r) +{ + struct lsa_info *info = NULL; + struct lsa_PrivilegeSet *set = NULL; + NTSTATUS status; + + /* find the connection policy handle. */ + info = find_policy_by_hnd(p, + r->in.handle, + LSA_HANDLE_ACCOUNT_TYPE, + struct lsa_info, + &status); + if (!NT_STATUS_IS_OK(status)) { + return NT_STATUS_INVALID_HANDLE; + } + + if (!(info->access & LSA_ACCOUNT_ADJUST_PRIVILEGES)) { + return NT_STATUS_ACCESS_DENIED; + } + + set = r->in.privs; + + if ( !grant_privilege_set( &info->sid, set ) ) { + struct dom_sid_buf buf; + DEBUG(3,("_lsa_AddPrivilegesToAccount: grant_privilege_set(%s) failed!\n", + dom_sid_str_buf(&info->sid, &buf))); + return NT_STATUS_NO_SUCH_PRIVILEGE; + } + + return NT_STATUS_OK; +} + +/*************************************************************************** + _lsa_RemovePrivilegesFromAccount + For a given SID, remove some privileges. + ***************************************************************************/ + +NTSTATUS _lsa_RemovePrivilegesFromAccount(struct pipes_struct *p, + struct lsa_RemovePrivilegesFromAccount *r) +{ + struct lsa_info *info = NULL; + struct lsa_PrivilegeSet *set = NULL; + NTSTATUS status; + + /* find the connection policy handle. */ + info = find_policy_by_hnd(p, + r->in.handle, + LSA_HANDLE_ACCOUNT_TYPE, + struct lsa_info, + &status); + if (!NT_STATUS_IS_OK(status)) { + return NT_STATUS_INVALID_HANDLE; + } + + if (!(info->access & LSA_ACCOUNT_ADJUST_PRIVILEGES)) { + return NT_STATUS_ACCESS_DENIED; + } + + set = r->in.privs; + + if ( !revoke_privilege_set( &info->sid, set) ) { + struct dom_sid_buf buf; + DEBUG(3,("_lsa_RemovePrivilegesFromAccount: revoke_privilege(%s) failed!\n", + dom_sid_str_buf(&info->sid, &buf))); + return NT_STATUS_NO_SUCH_PRIVILEGE; + } + + return NT_STATUS_OK; +} + +/*************************************************************************** + _lsa_LookupPrivName + ***************************************************************************/ + +NTSTATUS _lsa_LookupPrivName(struct pipes_struct *p, + struct lsa_LookupPrivName *r) +{ + struct lsa_info *info = NULL; + const char *name; + struct lsa_StringLarge *lsa_name; + NTSTATUS status; + + /* find the connection policy handle. */ + info = find_policy_by_hnd(p, + r->in.handle, + LSA_HANDLE_POLICY_TYPE, + struct lsa_info, + &status); + if (!NT_STATUS_IS_OK(status)) { + return NT_STATUS_INVALID_HANDLE; + } + + if (!(info->access & LSA_POLICY_VIEW_LOCAL_INFORMATION)) { + return NT_STATUS_ACCESS_DENIED; + } + + if (r->in.luid->high != 0) { + return NT_STATUS_NO_SUCH_PRIVILEGE; + } + + name = sec_privilege_name(r->in.luid->low); + if (!name) { + return NT_STATUS_NO_SUCH_PRIVILEGE; + } + + lsa_name = talloc_zero(p->mem_ctx, struct lsa_StringLarge); + if (!lsa_name) { + return NT_STATUS_NO_MEMORY; + } + + lsa_name->string = talloc_strdup(lsa_name, name); + if (!lsa_name->string) { + TALLOC_FREE(lsa_name); + return NT_STATUS_NO_MEMORY; + } + + *r->out.name = lsa_name; + + return NT_STATUS_OK; +} + +/*************************************************************************** + _lsa_QuerySecurity + ***************************************************************************/ + +NTSTATUS _lsa_QuerySecurity(struct pipes_struct *p, + struct lsa_QuerySecurity *r) +{ + struct lsa_info *handle=NULL; + struct security_descriptor *psd = NULL; + size_t sd_size = 0; + NTSTATUS status; + + /* find the connection policy handle. */ + handle = find_policy_by_hnd(p, + r->in.handle, + DCESRV_HANDLE_ANY, + struct lsa_info, + &status); + if (!NT_STATUS_IS_OK(status)) { + return NT_STATUS_INVALID_HANDLE; + } + + switch (handle->type) { + case LSA_HANDLE_POLICY_TYPE: + case LSA_HANDLE_ACCOUNT_TYPE: + case LSA_HANDLE_TRUST_TYPE: + case LSA_HANDLE_SECRET_TYPE: + psd = handle->sd; + sd_size = ndr_size_security_descriptor(psd, 0); + status = NT_STATUS_OK; + break; + default: + status = NT_STATUS_INVALID_HANDLE; + break; + } + + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + *r->out.sdbuf = make_sec_desc_buf(p->mem_ctx, sd_size, psd); + if (!*r->out.sdbuf) { + return NT_STATUS_NO_MEMORY; + } + + return status; +} + +/*************************************************************************** + _lsa_AddAccountRights + ***************************************************************************/ + +NTSTATUS _lsa_AddAccountRights(struct pipes_struct *p, + struct lsa_AddAccountRights *r) +{ + struct dcesrv_call_state *dce_call = p->dce_call; + struct auth_session_info *session_info = + dcesrv_call_session_info(dce_call); + int i = 0; + uint32_t acc_granted = 0; + struct security_descriptor *psd = NULL; + size_t sd_size; + struct dom_sid sid; + NTSTATUS status; + + /* find the connection policy handle. */ + (void)find_policy_by_hnd(p, + r->in.handle, + LSA_HANDLE_POLICY_TYPE, + struct lsa_info, + &status); + if (!NT_STATUS_IS_OK(status)) { + return NT_STATUS_INVALID_HANDLE; + } + + /* get the generic lsa account SD for this SID until we store it */ + status = make_lsa_object_sd(p->mem_ctx, &psd, &sd_size, + &lsa_account_mapping, + NULL, 0); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + /* + * From the MS DOCs. If the sid doesn't exist, ask for LSA_POLICY_CREATE_ACCOUNT + * on the policy handle. If it does, ask for + * LSA_ACCOUNT_ADJUST_PRIVILEGES|LSA_ACCOUNT_ADJUST_SYSTEM_ACCESS|LSA_ACCOUNT_VIEW, + * on the account sid. We don't check here so just use the latter. JRA. + */ + + status = access_check_object(psd, session_info->security_token, + SEC_PRIV_INVALID, SEC_PRIV_INVALID, 0, + LSA_ACCOUNT_ADJUST_PRIVILEGES|LSA_ACCOUNT_ADJUST_SYSTEM_ACCESS|LSA_ACCOUNT_VIEW, + &acc_granted, "_lsa_AddAccountRights" ); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + /* according to an NT4 PDC, you can add privileges to SIDs even without + call_lsa_create_account() first. And you can use any arbitrary SID. */ + + sid_copy( &sid, r->in.sid ); + + for ( i=0; i < r->in.rights->count; i++ ) { + + const char *privname = r->in.rights->names[i].string; + + /* only try to add non-null strings */ + + if ( !privname ) + continue; + + if ( !grant_privilege_by_name( &sid, privname ) ) { + DEBUG(2,("_lsa_AddAccountRights: Failed to add privilege [%s]\n", + privname )); + return NT_STATUS_NO_SUCH_PRIVILEGE; + } + } + + return NT_STATUS_OK; +} + +/*************************************************************************** + _lsa_RemoveAccountRights + ***************************************************************************/ + +NTSTATUS _lsa_RemoveAccountRights(struct pipes_struct *p, + struct lsa_RemoveAccountRights *r) +{ + struct dcesrv_call_state *dce_call = p->dce_call; + struct auth_session_info *session_info = + dcesrv_call_session_info(dce_call); + int i = 0; + struct security_descriptor *psd = NULL; + size_t sd_size; + struct dom_sid sid; + const char *privname = NULL; + uint32_t acc_granted = 0; + NTSTATUS status; + + /* find the connection policy handle. */ + (void)find_policy_by_hnd(p, + r->in.handle, + LSA_HANDLE_POLICY_TYPE, + struct lsa_info, + &status); + if (!NT_STATUS_IS_OK(status)) { + return NT_STATUS_INVALID_HANDLE; + } + + /* get the generic lsa account SD for this SID until we store it */ + status = make_lsa_object_sd(p->mem_ctx, &psd, &sd_size, + &lsa_account_mapping, + NULL, 0); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + /* + * From the MS DOCs. We need + * LSA_ACCOUNT_ADJUST_PRIVILEGES|LSA_ACCOUNT_ADJUST_SYSTEM_ACCESS|LSA_ACCOUNT_VIEW + * and DELETE on the account sid. + */ + + status = access_check_object(psd, session_info->security_token, + SEC_PRIV_INVALID, SEC_PRIV_INVALID, 0, + LSA_ACCOUNT_ADJUST_PRIVILEGES|LSA_ACCOUNT_ADJUST_SYSTEM_ACCESS| + LSA_ACCOUNT_VIEW|SEC_STD_DELETE, + &acc_granted, "_lsa_RemoveAccountRights"); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + sid_copy( &sid, r->in.sid ); + + if ( r->in.remove_all ) { + if ( !revoke_all_privileges( &sid ) ) + return NT_STATUS_ACCESS_DENIED; + + return NT_STATUS_OK; + } + + for ( i=0; i < r->in.rights->count; i++ ) { + + privname = r->in.rights->names[i].string; + + /* only try to add non-null strings */ + + if ( !privname ) + continue; + + if ( !revoke_privilege_by_name( &sid, privname ) ) { + DEBUG(2,("_lsa_RemoveAccountRights: Failed to revoke privilege [%s]\n", + privname )); + return NT_STATUS_NO_SUCH_PRIVILEGE; + } + } + + return NT_STATUS_OK; +} + +/******************************************************************* +********************************************************************/ + +static NTSTATUS init_lsa_right_set(TALLOC_CTX *mem_ctx, + struct lsa_RightSet *r, + PRIVILEGE_SET *privileges) +{ + uint32_t i; + const char *privname; + const char **privname_array = NULL; + size_t num_priv = 0; + + for (i=0; icount; i++) { + if (privileges->set[i].luid.high) { + continue; + } + privname = sec_privilege_name(privileges->set[i].luid.low); + if (privname) { + if (!add_string_to_array(mem_ctx, privname, + &privname_array, &num_priv)) { + return NT_STATUS_NO_MEMORY; + } + } + } + + if (num_priv) { + + r->names = talloc_zero_array(mem_ctx, struct lsa_StringLarge, + num_priv); + if (!r->names) { + return NT_STATUS_NO_MEMORY; + } + + for (i=0; inames[i], privname_array[i]); + } + + r->count = num_priv; + } + + return NT_STATUS_OK; +} + +/*************************************************************************** + _lsa_EnumAccountRights + ***************************************************************************/ + +NTSTATUS _lsa_EnumAccountRights(struct pipes_struct *p, + struct lsa_EnumAccountRights *r) +{ + NTSTATUS status; + struct lsa_info *info = NULL; + PRIVILEGE_SET *privileges; + struct dom_sid_buf buf; + + /* find the connection policy handle. */ + + info = find_policy_by_hnd(p, + r->in.handle, + LSA_HANDLE_POLICY_TYPE, + struct lsa_info, + &status); + if (!NT_STATUS_IS_OK(status)) { + return NT_STATUS_INVALID_HANDLE; + } + + if (!(info->access & LSA_ACCOUNT_VIEW)) { + return NT_STATUS_ACCESS_DENIED; + } + + /* according to an NT4 PDC, you can add privileges to SIDs even without + call_lsa_create_account() first. And you can use any arbitrary SID. */ + + /* according to MS-LSAD 3.1.4.5.10 it is required to return + * NT_STATUS_OBJECT_NAME_NOT_FOUND if the account sid was not found in + * the lsa database */ + + status = get_privileges_for_sid_as_set(p->mem_ctx, &privileges, r->in.sid); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + DEBUG(10,("_lsa_EnumAccountRights: %s has %d privileges\n", + dom_sid_str_buf(r->in.sid, &buf), + privileges->count)); + + status = init_lsa_right_set(p->mem_ctx, r->out.rights, privileges); + + return status; +} + +/*************************************************************************** + _lsa_LookupPrivValue + ***************************************************************************/ + +NTSTATUS _lsa_LookupPrivValue(struct pipes_struct *p, + struct lsa_LookupPrivValue *r) +{ + struct lsa_info *info = NULL; + const char *name = NULL; + NTSTATUS status; + + /* find the connection policy handle. */ + + info = find_policy_by_hnd(p, + r->in.handle, + LSA_HANDLE_POLICY_TYPE, + struct lsa_info, + &status); + if (!NT_STATUS_IS_OK(status)) { + return NT_STATUS_INVALID_HANDLE; + } + + if (!(info->access & LSA_POLICY_LOOKUP_NAMES)) + return NT_STATUS_ACCESS_DENIED; + + name = r->in.name->string; + + DEBUG(10,("_lsa_lookup_priv_value: name = %s\n", name)); + + r->out.luid->low = sec_privilege_id(name); + r->out.luid->high = 0; + if (r->out.luid->low == SEC_PRIV_INVALID) { + return NT_STATUS_NO_SUCH_PRIVILEGE; + } + return NT_STATUS_OK; +} + +/*************************************************************************** + _lsa_EnumAccountsWithUserRight + ***************************************************************************/ + +NTSTATUS _lsa_EnumAccountsWithUserRight(struct pipes_struct *p, + struct lsa_EnumAccountsWithUserRight *r) +{ + NTSTATUS status; + struct lsa_info *info = NULL; + struct dom_sid *sids = NULL; + int num_sids = 0; + uint32_t i; + enum sec_privilege privilege; + + info = find_policy_by_hnd(p, + r->in.handle, + LSA_HANDLE_POLICY_TYPE, + struct lsa_info, + &status); + if (!NT_STATUS_IS_OK(status)) { + return NT_STATUS_INVALID_HANDLE; + } + + if (!(info->access & LSA_POLICY_LOOKUP_NAMES)) { + return NT_STATUS_ACCESS_DENIED; + } + + if (!r->in.name || !r->in.name->string) { + return NT_STATUS_NO_SUCH_PRIVILEGE; + } + + privilege = sec_privilege_id(r->in.name->string); + if (privilege == SEC_PRIV_INVALID) { + return NT_STATUS_NO_SUCH_PRIVILEGE; + } + + status = privilege_enum_sids(privilege, p->mem_ctx, + &sids, &num_sids); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + r->out.sids->num_sids = num_sids; + r->out.sids->sids = talloc_array(p->mem_ctx, struct lsa_SidPtr, + r->out.sids->num_sids); + + for (i=0; i < r->out.sids->num_sids; i++) { + r->out.sids->sids[i].sid = dom_sid_dup(r->out.sids->sids, + &sids[i]); + if (!r->out.sids->sids[i].sid) { + TALLOC_FREE(r->out.sids->sids); + r->out.sids->num_sids = 0; + return NT_STATUS_NO_MEMORY; + } + } + + return NT_STATUS_OK; +} + +/*************************************************************************** + _lsa_Delete + ***************************************************************************/ + +NTSTATUS _lsa_Delete(struct pipes_struct *p, + struct lsa_Delete *r) +{ + return NT_STATUS_NOT_SUPPORTED; +} + +static NTSTATUS info_ex_2_pdb_trusted_domain( + struct lsa_TrustDomainInfoInfoEx *info_ex, + struct pdb_trusted_domain *td) +{ + if (info_ex->domain_name.string == NULL || + info_ex->netbios_name.string == NULL || + info_ex->sid == NULL) { + return NT_STATUS_INVALID_PARAMETER; + } + + td->domain_name = talloc_strdup(td, info_ex->domain_name.string); + td->netbios_name = talloc_strdup(td, info_ex->netbios_name.string); + sid_copy(&td->security_identifier, info_ex->sid); + if (td->domain_name == NULL || + td->netbios_name == NULL || + is_null_sid(&td->security_identifier)) { + return NT_STATUS_NO_MEMORY; + } + td->trust_direction = info_ex->trust_direction; + td->trust_type = info_ex->trust_type; + td->trust_attributes = info_ex->trust_attributes; + + return NT_STATUS_OK; +} + +static NTSTATUS setInfoTrustedDomain_base(struct pipes_struct *p, + TALLOC_CTX *mem_ctx, + struct lsa_info *policy, + enum lsa_TrustDomInfoEnum level, + union lsa_TrustedDomainInfo *info) +{ + struct lsa_TrustDomainInfoAuthInfoInternal *auth_info_int = NULL; + DATA_BLOB auth_blob; + struct trustDomainPasswords auth_struct; + NTSTATUS nt_status; + + struct pdb_trusted_domain *td; + struct pdb_trusted_domain *orig_td; + + td = talloc_zero(mem_ctx, struct pdb_trusted_domain); + if (td == NULL) { + return NT_STATUS_NO_MEMORY; + } + + switch (level) { + case LSA_TRUSTED_DOMAIN_INFO_POSIX_OFFSET: + if (!(policy->access & LSA_TRUSTED_SET_POSIX)) { + return NT_STATUS_ACCESS_DENIED; + } + td->trust_posix_offset = &info->posix_offset.posix_offset; + break; + case LSA_TRUSTED_DOMAIN_INFO_INFO_EX: + if (!(policy->access & LSA_TRUSTED_SET_POSIX)) { + return NT_STATUS_ACCESS_DENIED; + } + nt_status = info_ex_2_pdb_trusted_domain(&info->info_ex, td); + if (!NT_STATUS_IS_OK(nt_status)) { + return nt_status; + } + break; + case LSA_TRUSTED_DOMAIN_INFO_AUTH_INFO: + if (!(policy->access & LSA_TRUSTED_SET_AUTH)) { + return NT_STATUS_ACCESS_DENIED; + } + nt_status = auth_info_2_auth_blob(td, &info->auth_info, + &td->trust_auth_incoming, + &td->trust_auth_outgoing); + if (!NT_STATUS_IS_OK(nt_status)) { + return nt_status; + } + break; + case LSA_TRUSTED_DOMAIN_INFO_FULL_INFO: + if (!(policy->access & (LSA_TRUSTED_SET_AUTH | LSA_TRUSTED_SET_POSIX))) { + return NT_STATUS_ACCESS_DENIED; + } + td->trust_posix_offset = &info->full_info.posix_offset.posix_offset; + nt_status = info_ex_2_pdb_trusted_domain(&info->full_info.info_ex, + td); + if (!NT_STATUS_IS_OK(nt_status)) { + return nt_status; + } + nt_status = auth_info_2_auth_blob(td, + &info->full_info.auth_info, + &td->trust_auth_incoming, + &td->trust_auth_outgoing); + if (!NT_STATUS_IS_OK(nt_status)) { + return nt_status; + } + break; + case LSA_TRUSTED_DOMAIN_INFO_AUTH_INFO_INTERNAL: + if (!(policy->access & LSA_TRUSTED_SET_AUTH)) { + return NT_STATUS_ACCESS_DENIED; + } + auth_info_int = &info->auth_info_internal; + break; + case LSA_TRUSTED_DOMAIN_INFO_FULL_INFO_INTERNAL: + if (!(policy->access & (LSA_TRUSTED_SET_AUTH | LSA_TRUSTED_SET_POSIX))) { + return NT_STATUS_ACCESS_DENIED; + } + td->trust_posix_offset = &info->full_info_internal.posix_offset.posix_offset; + nt_status = info_ex_2_pdb_trusted_domain(&info->full_info_internal.info_ex, + td); + if (!NT_STATUS_IS_OK(nt_status)) { + return nt_status; + } + auth_info_int = &info->full_info_internal.auth_info; + break; + case LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES: + if (!(policy->access & LSA_TRUSTED_SET_POSIX)) { + return NT_STATUS_ACCESS_DENIED; + } + td->supported_enc_type = &info->enc_types.enc_types; + break; + default: + return NT_STATUS_INVALID_PARAMETER; + } + + /* decode auth_info_int if set */ + if (auth_info_int) { + + /* now decrypt blob */ + auth_blob = data_blob_const(auth_info_int->auth_blob.data, + auth_info_int->auth_blob.size); + + nt_status = get_trustdom_auth_blob(p, mem_ctx, + &auth_blob, &auth_struct); + if (!NT_STATUS_IS_OK(nt_status)) { + return nt_status; + } + } else { + memset(&auth_struct, 0, sizeof(auth_struct)); + } + +/* TODO: verify only one object matches the dns/netbios/sid triplet and that + * this is the one we already have */ + +/* TODO: check if the trust direction is changed and we need to add or remove + * auth data */ + +/* TODO: check if trust type shall be changed and return an error in this case + * */ + nt_status = pdb_get_trusted_domain_by_sid(p->mem_ctx, &policy->sid, + &orig_td); + if (!NT_STATUS_IS_OK(nt_status)) { + return nt_status; + } + + + /* TODO: should we fetch previous values from the existing entry + * and append them ? */ + if (auth_struct.incoming.count) { + nt_status = get_trustauth_inout_blob(mem_ctx, + &auth_struct.incoming, + &td->trust_auth_incoming); + if (!NT_STATUS_IS_OK(nt_status)) { + return nt_status; + } + } else { + ZERO_STRUCT(td->trust_auth_incoming); + } + + if (auth_struct.outgoing.count) { + nt_status = get_trustauth_inout_blob(mem_ctx, + &auth_struct.outgoing, + &td->trust_auth_outgoing); + if (!NT_STATUS_IS_OK(nt_status)) { + return nt_status; + } + } else { + ZERO_STRUCT(td->trust_auth_outgoing); + } + + nt_status = pdb_set_trusted_domain(orig_td->domain_name, td); + if (!NT_STATUS_IS_OK(nt_status)) { + return nt_status; + } + + return NT_STATUS_OK; +} + +NTSTATUS _lsa_SetTrustedDomainInfo(struct pipes_struct *p, + struct lsa_SetTrustedDomainInfo *r) +{ + NTSTATUS status; + struct policy_handle trustdom_handle; + struct lsa_OpenTrustedDomain o; + struct lsa_SetInformationTrustedDomain s; + struct lsa_Close c; + + o.in.handle = r->in.handle; + o.in.sid = r->in.dom_sid; + o.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + o.out.trustdom_handle = &trustdom_handle; + + status = _lsa_OpenTrustedDomain(p, &o); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + s.in.trustdom_handle = &trustdom_handle; + s.in.level = r->in.level; + s.in.info = r->in.info; + + status = _lsa_SetInformationTrustedDomain(p, &s); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + c.in.handle = &trustdom_handle; + c.out.handle = &trustdom_handle; + + return _lsa_Close(p, &c); +} + +NTSTATUS _lsa_SetTrustedDomainInfoByName(struct pipes_struct *p, + struct lsa_SetTrustedDomainInfoByName *r) +{ + NTSTATUS status; + struct policy_handle trustdom_handle; + struct lsa_OpenTrustedDomainByName o; + struct lsa_SetInformationTrustedDomain s; + struct lsa_Close c; + + o.in.handle = r->in.handle; + o.in.name.string = r->in.trusted_domain->string; + o.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + o.out.trustdom_handle = &trustdom_handle; + + status = _lsa_OpenTrustedDomainByName(p, &o); + if (!NT_STATUS_IS_OK(status)) { + if (NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_DOMAIN)) { + return NT_STATUS_OBJECT_NAME_NOT_FOUND; + } + return status; + } + + s.in.trustdom_handle = &trustdom_handle; + s.in.level = r->in.level; + s.in.info = r->in.info; + + status = _lsa_SetInformationTrustedDomain(p, &s); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + c.in.handle = &trustdom_handle; + c.out.handle = &trustdom_handle; + + return _lsa_Close(p, &c); +} + +NTSTATUS _lsa_SetInformationTrustedDomain(struct pipes_struct *p, + struct lsa_SetInformationTrustedDomain *r) +{ + struct lsa_info *policy; + NTSTATUS status; + + policy = find_policy_by_hnd(p, + r->in.trustdom_handle, + LSA_HANDLE_TRUST_TYPE, + struct lsa_info, + &status); + if (!NT_STATUS_IS_OK(status)) { + return NT_STATUS_INVALID_HANDLE; + } + + return setInfoTrustedDomain_base(p, p->mem_ctx, policy, + r->in.level, r->in.info); +} + + +/* + * From here on the server routines are just dummy ones to make smbd link with + * librpc/gen_ndr/srv_lsa.c. These routines are actually never called, we are + * pulling the server stubs across one by one. + */ + +NTSTATUS _lsa_SetSecObj(struct pipes_struct *p, struct lsa_SetSecObj *r) +{ + p->fault_state = DCERPC_FAULT_OP_RNG_ERROR; + return NT_STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS _lsa_ChangePassword(struct pipes_struct *p, + struct lsa_ChangePassword *r) +{ + p->fault_state = DCERPC_FAULT_OP_RNG_ERROR; + return NT_STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS _lsa_SetInfoPolicy(struct pipes_struct *p, struct lsa_SetInfoPolicy *r) +{ + p->fault_state = DCERPC_FAULT_OP_RNG_ERROR; + return NT_STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS _lsa_ClearAuditLog(struct pipes_struct *p, struct lsa_ClearAuditLog *r) +{ + p->fault_state = DCERPC_FAULT_OP_RNG_ERROR; + return NT_STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS _lsa_GetQuotasForAccount(struct pipes_struct *p, + struct lsa_GetQuotasForAccount *r) +{ + p->fault_state = DCERPC_FAULT_OP_RNG_ERROR; + return NT_STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS _lsa_SetQuotasForAccount(struct pipes_struct *p, + struct lsa_SetQuotasForAccount *r) +{ + p->fault_state = DCERPC_FAULT_OP_RNG_ERROR; + return NT_STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS _lsa_StorePrivateData(struct pipes_struct *p, + struct lsa_StorePrivateData *r) +{ + p->fault_state = DCERPC_FAULT_OP_RNG_ERROR; + return NT_STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS _lsa_RetrievePrivateData(struct pipes_struct *p, + struct lsa_RetrievePrivateData *r) +{ + p->fault_state = DCERPC_FAULT_OP_RNG_ERROR; + return NT_STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS _lsa_SetInfoPolicy2(struct pipes_struct *p, + struct lsa_SetInfoPolicy2 *r) +{ + p->fault_state = DCERPC_FAULT_OP_RNG_ERROR; + return NT_STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS _lsa_EnumTrustedDomainsEx(struct pipes_struct *p, + struct lsa_EnumTrustedDomainsEx *r) +{ + struct lsa_info *info; + uint32_t count; + struct pdb_trusted_domain **domains; + struct lsa_TrustDomainInfoInfoEx *entries; + int i; + NTSTATUS nt_status; + + /* bail out early if pdb backend is not capable of ex trusted domains, + * if we don't do that, the client might not call + * _lsa_EnumTrustedDomains() afterwards - gd */ + + if (!(pdb_capabilities() & PDB_CAP_TRUSTED_DOMAINS_EX)) { + p->fault_state = DCERPC_FAULT_OP_RNG_ERROR; + return NT_STATUS_NOT_IMPLEMENTED; + } + + info = find_policy_by_hnd(p, + r->in.handle, + LSA_HANDLE_POLICY_TYPE, + struct lsa_info, + &nt_status); + if (!NT_STATUS_IS_OK(nt_status)) { + return NT_STATUS_INVALID_HANDLE; + } + + /* check if the user has enough rights */ + if (!(info->access & LSA_POLICY_VIEW_LOCAL_INFORMATION)) + return NT_STATUS_ACCESS_DENIED; + + become_root(); + nt_status = pdb_enum_trusted_domains(p->mem_ctx, &count, &domains); + unbecome_root(); + + if (!NT_STATUS_IS_OK(nt_status)) { + return nt_status; + } + + entries = talloc_zero_array(p->mem_ctx, struct lsa_TrustDomainInfoInfoEx, + count); + if (!entries) { + return NT_STATUS_NO_MEMORY; + } + + for (i=0; idomain_name); + init_lsa_StringLarge(&entries[i].netbios_name, + domains[i]->netbios_name); + entries[i].sid = &domains[i]->security_identifier; + entries[i].trust_direction = domains[i]->trust_direction; + entries[i].trust_type = domains[i]->trust_type; + entries[i].trust_attributes = domains[i]->trust_attributes; + } + + if (*r->in.resume_handle >= count) { + *r->out.resume_handle = -1; + TALLOC_FREE(entries); + return NT_STATUS_NO_MORE_ENTRIES; + } + + /* return the rest, limit by max_size. Note that we + use the w2k3 element size value of 60 */ + r->out.domains->count = count - *r->in.resume_handle; + r->out.domains->count = MIN(r->out.domains->count, + (r->in.max_size/LSA_ENUM_TRUST_DOMAIN_EX_MULTIPLIER)); + + r->out.domains->domains = entries + *r->in.resume_handle; + + if (r->out.domains->count < count - *r->in.resume_handle) { + *r->out.resume_handle = *r->in.resume_handle + r->out.domains->count; + return STATUS_MORE_ENTRIES; + } + + /* according to MS-LSAD 3.1.4.7.8 output resume handle MUST + * always be larger than the previous input resume handle, in + * particular when hitting the last query it is vital to set the + * resume handle correctly to avoid infinite client loops, as + * seen e.g. with Windows XP SP3 when resume handle is 0 and + * status is NT_STATUS_OK - gd */ + + *r->out.resume_handle = (uint32_t)-1; + + return NT_STATUS_OK; +} + +NTSTATUS _lsa_QueryDomainInformationPolicy(struct pipes_struct *p, + struct lsa_QueryDomainInformationPolicy *r) +{ + p->fault_state = DCERPC_FAULT_OP_RNG_ERROR; + return NT_STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS _lsa_SetDomainInformationPolicy(struct pipes_struct *p, + struct lsa_SetDomainInformationPolicy *r) +{ + p->fault_state = DCERPC_FAULT_OP_RNG_ERROR; + return NT_STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS _lsa_TestCall(struct pipes_struct *p, struct lsa_TestCall *r) +{ + p->fault_state = DCERPC_FAULT_OP_RNG_ERROR; + return NT_STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS _lsa_CREDRWRITE(struct pipes_struct *p, struct lsa_CREDRWRITE *r) +{ + p->fault_state = DCERPC_FAULT_OP_RNG_ERROR; + return NT_STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS _lsa_CREDRREAD(struct pipes_struct *p, struct lsa_CREDRREAD *r) +{ + p->fault_state = DCERPC_FAULT_OP_RNG_ERROR; + return NT_STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS _lsa_CREDRENUMERATE(struct pipes_struct *p, struct lsa_CREDRENUMERATE *r) +{ + p->fault_state = DCERPC_FAULT_OP_RNG_ERROR; + return NT_STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS _lsa_CREDRWRITEDOMAINCREDENTIALS(struct pipes_struct *p, + struct lsa_CREDRWRITEDOMAINCREDENTIALS *r) +{ + p->fault_state = DCERPC_FAULT_OP_RNG_ERROR; + return NT_STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS _lsa_CREDRREADDOMAINCREDENTIALS(struct pipes_struct *p, + struct lsa_CREDRREADDOMAINCREDENTIALS *r) +{ + p->fault_state = DCERPC_FAULT_OP_RNG_ERROR; + return NT_STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS _lsa_CREDRDELETE(struct pipes_struct *p, struct lsa_CREDRDELETE *r) +{ + p->fault_state = DCERPC_FAULT_OP_RNG_ERROR; + return NT_STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS _lsa_CREDRGETTARGETINFO(struct pipes_struct *p, + struct lsa_CREDRGETTARGETINFO *r) +{ + p->fault_state = DCERPC_FAULT_OP_RNG_ERROR; + return NT_STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS _lsa_CREDRPROFILELOADED(struct pipes_struct *p, + struct lsa_CREDRPROFILELOADED *r) +{ + p->fault_state = DCERPC_FAULT_OP_RNG_ERROR; + return NT_STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS _lsa_CREDRGETSESSIONTYPES(struct pipes_struct *p, + struct lsa_CREDRGETSESSIONTYPES *r) +{ + p->fault_state = DCERPC_FAULT_OP_RNG_ERROR; + return NT_STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS _lsa_LSARREGISTERAUDITEVENT(struct pipes_struct *p, + struct lsa_LSARREGISTERAUDITEVENT *r) +{ + p->fault_state = DCERPC_FAULT_OP_RNG_ERROR; + return NT_STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS _lsa_LSARGENAUDITEVENT(struct pipes_struct *p, + struct lsa_LSARGENAUDITEVENT *r) +{ + p->fault_state = DCERPC_FAULT_OP_RNG_ERROR; + return NT_STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS _lsa_LSARUNREGISTERAUDITEVENT(struct pipes_struct *p, + struct lsa_LSARUNREGISTERAUDITEVENT *r) +{ + p->fault_state = DCERPC_FAULT_OP_RNG_ERROR; + return NT_STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS _lsa_lsaRQueryForestTrustInformation(struct pipes_struct *p, + struct lsa_lsaRQueryForestTrustInformation *r) +{ + p->fault_state = DCERPC_FAULT_OP_RNG_ERROR; + return NT_STATUS_NOT_IMPLEMENTED; +} + +#define DNS_CMP_MATCH 0 +#define DNS_CMP_FIRST_IS_CHILD 1 +#define DNS_CMP_SECOND_IS_CHILD 2 +#define DNS_CMP_NO_MATCH 3 + +/* this function assumes names are well formed DNS names. + * it doesn't validate them */ +static int dns_cmp(const char *s1, size_t l1, + const char *s2, size_t l2) +{ + const char *p1, *p2; + size_t t1, t2; + int cret; + + if (l1 == l2) { + if (strcasecmp_m(s1, s2) == 0) { + return DNS_CMP_MATCH; + } + return DNS_CMP_NO_MATCH; + } + + if (l1 > l2) { + p1 = s1; + p2 = s2; + t1 = l1; + t2 = l2; + cret = DNS_CMP_FIRST_IS_CHILD; + } else { + p1 = s2; + p2 = s1; + t1 = l2; + t2 = l1; + cret = DNS_CMP_SECOND_IS_CHILD; + } + + if (p1[t1 - t2 - 1] != '.') { + return DNS_CMP_NO_MATCH; + } + + if (strcasecmp_m(&p1[t1 - t2], p2) == 0) { + return cret; + } + + return DNS_CMP_NO_MATCH; +} + +static NTSTATUS make_ft_info(TALLOC_CTX *mem_ctx, + struct lsa_ForestTrustInformation *lfti, + struct ForestTrustInfo *fti) +{ + struct lsa_ForestTrustRecord *lrec; + struct ForestTrustInfoRecord *rec; + struct lsa_StringLarge *tln; + struct lsa_ForestTrustDomainInfo *info; + uint32_t i; + + fti->version = 1; + fti->count = lfti->count; + fti->records = talloc_array(mem_ctx, + struct ForestTrustInfoRecordArmor, + fti->count); + if (!fti->records) { + return NT_STATUS_NO_MEMORY; + } + for (i = 0; i < fti->count; i++) { + lrec = lfti->entries[i]; + rec = &fti->records[i].record; + + rec->flags = lrec->flags; + rec->timestamp = lrec->time; + rec->type = (enum ForestTrustInfoRecordType)lrec->type; + + switch (lrec->type) { + case LSA_FOREST_TRUST_TOP_LEVEL_NAME: + case LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX: + tln = &lrec->forest_trust_data.top_level_name; + rec->data.name.string = + talloc_strdup(mem_ctx, tln->string); + if (!rec->data.name.string) { + return NT_STATUS_NO_MEMORY; + } + rec->data.name.size = strlen(rec->data.name.string); + break; + case LSA_FOREST_TRUST_DOMAIN_INFO: + info = &lrec->forest_trust_data.domain_info; + rec->data.info.sid = *info->domain_sid; + rec->data.info.dns_name.string = + talloc_strdup(mem_ctx, + info->dns_domain_name.string); + if (!rec->data.info.dns_name.string) { + return NT_STATUS_NO_MEMORY; + } + rec->data.info.dns_name.size = + strlen(rec->data.info.dns_name.string); + rec->data.info.netbios_name.string = + talloc_strdup(mem_ctx, + info->netbios_domain_name.string); + if (!rec->data.info.netbios_name.string) { + return NT_STATUS_NO_MEMORY; + } + rec->data.info.netbios_name.size = + strlen(rec->data.info.netbios_name.string); + break; + default: + return NT_STATUS_INVALID_DOMAIN_STATE; + } + } + + return NT_STATUS_OK; +} + +static NTSTATUS add_collision(struct lsa_ForestTrustCollisionInfo *c_info, + uint32_t index, uint32_t collision_type, + uint32_t conflict_type, const char *tdo_name); + +static NTSTATUS check_ft_info(TALLOC_CTX *mem_ctx, + const char *tdo_name, + struct ForestTrustInfo *tdo_fti, + struct ForestTrustInfo *new_fti, + struct lsa_ForestTrustCollisionInfo *c_info) +{ + struct ForestTrustInfoRecord *nrec; + struct ForestTrustInfoRecord *trec; + const char *dns_name; + const char *nb_name = NULL; + struct dom_sid *sid = NULL; + const char *tname = NULL; + size_t dns_len = 0; + size_t tlen = 0; + uint32_t new_fti_idx; + uint32_t i; + /* use always TDO type, until we understand when Xref can be used */ + uint32_t collision_type = LSA_FOREST_TRUST_COLLISION_TDO; + bool tln_conflict; + bool sid_conflict; + bool nb_conflict; + bool exclusion; + bool ex_rule = false; + int ret; + + for (new_fti_idx = 0; new_fti_idx < new_fti->count; new_fti_idx++) { + + nrec = &new_fti->records[new_fti_idx].record; + dns_name = NULL; + tln_conflict = false; + sid_conflict = false; + nb_conflict = false; + exclusion = false; + + switch (nrec->type) { + case LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX: + /* exclusions do not conflict by definition */ + break; + + case FOREST_TRUST_TOP_LEVEL_NAME: + dns_name = nrec->data.name.string; + dns_len = nrec->data.name.size; + break; + + case LSA_FOREST_TRUST_DOMAIN_INFO: + dns_name = nrec->data.info.dns_name.string; + dns_len = nrec->data.info.dns_name.size; + nb_name = nrec->data.info.netbios_name.string; + sid = &nrec->data.info.sid; + break; + } + + if (!dns_name) continue; + + /* check if this is already taken and not excluded */ + for (i = 0; i < tdo_fti->count; i++) { + trec = &tdo_fti->records[i].record; + + switch (trec->type) { + case FOREST_TRUST_TOP_LEVEL_NAME: + ex_rule = false; + tname = trec->data.name.string; + tlen = trec->data.name.size; + break; + case FOREST_TRUST_TOP_LEVEL_NAME_EX: + ex_rule = true; + tname = trec->data.name.string; + tlen = trec->data.name.size; + break; + case FOREST_TRUST_DOMAIN_INFO: + ex_rule = false; + tname = trec->data.info.dns_name.string; + tlen = trec->data.info.dns_name.size; + break; + default: + return NT_STATUS_INVALID_PARAMETER; + } + ret = dns_cmp(dns_name, dns_len, tname, tlen); + switch (ret) { + case DNS_CMP_MATCH: + /* if it matches exclusion, + * it doesn't conflict */ + if (ex_rule) { + exclusion = true; + break; + } + + FALL_THROUGH; + case DNS_CMP_FIRST_IS_CHILD: + case DNS_CMP_SECOND_IS_CHILD: + tln_conflict = true; + + FALL_THROUGH; + default: + break; + } + + /* explicit exclusion, no dns name conflict here */ + if (exclusion) { + tln_conflict = false; + } + + if (trec->type != FOREST_TRUST_DOMAIN_INFO) { + continue; + } + + /* also test for domain info */ + if (!(trec->flags & LSA_SID_DISABLED_ADMIN) && + dom_sid_compare(&trec->data.info.sid, sid) == 0) { + sid_conflict = true; + } + if (!(trec->flags & LSA_NB_DISABLED_ADMIN) && + strcasecmp_m(trec->data.info.netbios_name.string, + nb_name) == 0) { + nb_conflict = true; + } + } + + if (tln_conflict) { + (void)add_collision(c_info, new_fti_idx, + collision_type, + LSA_TLN_DISABLED_CONFLICT, + tdo_name); + } + if (sid_conflict) { + (void)add_collision(c_info, new_fti_idx, + collision_type, + LSA_SID_DISABLED_CONFLICT, + tdo_name); + } + if (nb_conflict) { + (void)add_collision(c_info, new_fti_idx, + collision_type, + LSA_NB_DISABLED_CONFLICT, + tdo_name); + } + } + + return NT_STATUS_OK; +} + +static NTSTATUS add_collision(struct lsa_ForestTrustCollisionInfo *c_info, + uint32_t idx, uint32_t collision_type, + uint32_t conflict_type, const char *tdo_name) +{ + struct lsa_ForestTrustCollisionRecord **es; + uint32_t i = c_info->count; + + es = talloc_realloc(c_info, c_info->entries, + struct lsa_ForestTrustCollisionRecord *, i + 1); + if (!es) { + return NT_STATUS_NO_MEMORY; + } + c_info->entries = es; + c_info->count = i + 1; + + es[i] = talloc(es, struct lsa_ForestTrustCollisionRecord); + if (!es[i]) { + return NT_STATUS_NO_MEMORY; + } + + es[i]->index = idx; + es[i]->type = collision_type; + es[i]->flags = conflict_type; + es[i]->name.string = talloc_strdup(es[i], tdo_name); + if (!es[i]->name.string) { + return NT_STATUS_NO_MEMORY; + } + es[i]->name.size = strlen(es[i]->name.string); + + return NT_STATUS_OK; +} + +static NTSTATUS get_ft_info(TALLOC_CTX *mem_ctx, + struct pdb_trusted_domain *td, + struct ForestTrustInfo *info) +{ + enum ndr_err_code ndr_err; + + if (td->trust_forest_trust_info.length == 0 || + td->trust_forest_trust_info.data == NULL) { + return NT_STATUS_OBJECT_NAME_NOT_FOUND; + } + ndr_err = ndr_pull_struct_blob_all(&td->trust_forest_trust_info, mem_ctx, + info, + (ndr_pull_flags_fn_t)ndr_pull_ForestTrustInfo); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + return NT_STATUS_INVALID_DOMAIN_STATE; + } + + return NT_STATUS_OK; +} + +static NTSTATUS own_ft_info(struct pdb_domain_info *dom_info, + struct ForestTrustInfo *fti) +{ + struct ForestTrustDataDomainInfo *info; + struct ForestTrustInfoRecord *rec; + + fti->version = 1; + fti->count = 2; + fti->records = talloc_array(fti, + struct ForestTrustInfoRecordArmor, 2); + if (!fti->records) { + return NT_STATUS_NO_MEMORY; + } + + /* TLN info */ + rec = &fti->records[0].record; + + rec->flags = 0; + rec->timestamp = 0; + rec->type = FOREST_TRUST_TOP_LEVEL_NAME; + + rec->data.name.string = talloc_strdup(fti, dom_info->dns_forest); + if (!rec->data.name.string) { + return NT_STATUS_NO_MEMORY; + } + rec->data.name.size = strlen(rec->data.name.string); + + /* DOMAIN info */ + rec = &fti->records[1].record; + + rec->flags = 0; + rec->timestamp = 0; + rec->type = FOREST_TRUST_DOMAIN_INFO; + + info = &rec->data.info; + + info->sid = dom_info->sid; + info->dns_name.string = talloc_strdup(fti, dom_info->dns_domain); + if (!info->dns_name.string) { + return NT_STATUS_NO_MEMORY; + } + info->dns_name.size = strlen(info->dns_name.string); + info->netbios_name.string = talloc_strdup(fti, dom_info->name); + if (!info->netbios_name.string) { + return NT_STATUS_NO_MEMORY; + } + info->netbios_name.size = strlen(info->netbios_name.string); + + return NT_STATUS_OK; +} + +NTSTATUS _lsa_lsaRSetForestTrustInformation(struct pipes_struct *p, + struct lsa_lsaRSetForestTrustInformation *r) +{ + NTSTATUS status; + int i; + int j; + struct lsa_info *handle; + uint32_t num_domains; + struct pdb_trusted_domain **domains; + struct ForestTrustInfo *nfti; + struct ForestTrustInfo *fti; + struct lsa_ForestTrustCollisionInfo *c_info; + struct pdb_domain_info *dom_info; + enum ndr_err_code ndr_err; + + if (!IS_DC) { + return NT_STATUS_NOT_SUPPORTED; + } + + handle = find_policy_by_hnd(p, + r->in.handle, + LSA_HANDLE_TRUST_TYPE, + struct lsa_info, + &status); + if (!NT_STATUS_IS_OK(status)) { + return NT_STATUS_INVALID_HANDLE; + } + + if (!(handle->access & LSA_TRUSTED_SET_AUTH)) { + return NT_STATUS_ACCESS_DENIED; + } + + status = pdb_enum_trusted_domains(p->mem_ctx, &num_domains, &domains); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + if (num_domains == 0) { + return NT_STATUS_NO_SUCH_DOMAIN; + } + + for (i = 0; i < num_domains; i++) { + if (domains[i]->domain_name == NULL) { + return NT_STATUS_INVALID_DOMAIN_STATE; + } + if (strcasecmp_m(domains[i]->domain_name, + r->in.trusted_domain_name->string) == 0) { + break; + } + } + if (i >= num_domains) { + return NT_STATUS_NO_SUCH_DOMAIN; + } + + if (!(domains[i]->trust_attributes & + LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE)) { + return NT_STATUS_INVALID_PARAMETER; + } + + if (r->in.highest_record_type >= LSA_FOREST_TRUST_RECORD_TYPE_LAST) { + return NT_STATUS_INVALID_PARAMETER; + } + + /* The following section until COPY_END is a copy from + * source4/rpmc_server/lsa/scesrc_lsa.c */ + nfti = talloc(p->mem_ctx, struct ForestTrustInfo); + if (!nfti) { + return NT_STATUS_NO_MEMORY; + } + + status = make_ft_info(nfti, r->in.forest_trust_info, nfti); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + c_info = talloc_zero(r->out.collision_info, + struct lsa_ForestTrustCollisionInfo); + if (!c_info) { + return NT_STATUS_NO_MEMORY; + } + + /* first check own info, then other domains */ + fti = talloc(p->mem_ctx, struct ForestTrustInfo); + if (!fti) { + return NT_STATUS_NO_MEMORY; + } + + dom_info = pdb_get_domain_info(p->mem_ctx); + + status = own_ft_info(dom_info, fti); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + status = check_ft_info(c_info, dom_info->dns_domain, fti, nfti, c_info); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + for (j = 0; j < num_domains; j++) { + fti = talloc(p->mem_ctx, struct ForestTrustInfo); + if (!fti) { + return NT_STATUS_NO_MEMORY; + } + + status = get_ft_info(p->mem_ctx, domains[j], fti); + if (!NT_STATUS_IS_OK(status)) { + if (NT_STATUS_EQUAL(status, + NT_STATUS_OBJECT_NAME_NOT_FOUND)) { + continue; + } + return status; + } + + if (domains[j]->domain_name == NULL) { + return NT_STATUS_INVALID_DOMAIN_STATE; + } + + status = check_ft_info(c_info, domains[j]->domain_name, + fti, nfti, c_info); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + } + + if (c_info->count != 0) { + *r->out.collision_info = c_info; + } + + if (r->in.check_only != 0) { + return NT_STATUS_OK; + } + + /* COPY_END */ + + ndr_err = ndr_push_struct_blob(&domains[i]->trust_forest_trust_info, + p->mem_ctx, nfti, + (ndr_push_flags_fn_t)ndr_push_ForestTrustInfo); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + return NT_STATUS_INVALID_PARAMETER; + } + + status = pdb_set_trusted_domain(domains[i]->domain_name, domains[i]); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + return NT_STATUS_OK; +} + +NTSTATUS _lsa_CREDRRENAME(struct pipes_struct *p, + struct lsa_CREDRRENAME *r) +{ + p->fault_state = DCERPC_FAULT_OP_RNG_ERROR; + return NT_STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS _lsa_LSAROPENPOLICYSCE(struct pipes_struct *p, + struct lsa_LSAROPENPOLICYSCE *r) +{ + p->fault_state = DCERPC_FAULT_OP_RNG_ERROR; + return NT_STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS _lsa_LSARADTREGISTERSECURITYEVENTSOURCE(struct pipes_struct *p, + struct lsa_LSARADTREGISTERSECURITYEVENTSOURCE *r) +{ + p->fault_state = DCERPC_FAULT_OP_RNG_ERROR; + return NT_STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS _lsa_LSARADTUNREGISTERSECURITYEVENTSOURCE(struct pipes_struct *p, + struct lsa_LSARADTUNREGISTERSECURITYEVENTSOURCE *r) +{ + p->fault_state = DCERPC_FAULT_OP_RNG_ERROR; + return NT_STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS _lsa_LSARADTREPORTSECURITYEVENT(struct pipes_struct *p, + struct lsa_LSARADTREPORTSECURITYEVENT *r) +{ + p->fault_state = DCERPC_FAULT_OP_RNG_ERROR; + return NT_STATUS_NOT_IMPLEMENTED; +} + +void _lsa_Opnum82NotUsedOnWire(struct pipes_struct *p, + struct lsa_Opnum82NotUsedOnWire *r) +{ + p->fault_state = DCERPC_FAULT_OP_RNG_ERROR; +} + +void _lsa_Opnum83NotUsedOnWire(struct pipes_struct *p, + struct lsa_Opnum83NotUsedOnWire *r) +{ + p->fault_state = DCERPC_FAULT_OP_RNG_ERROR; +} + +void _lsa_Opnum84NotUsedOnWire(struct pipes_struct *p, + struct lsa_Opnum84NotUsedOnWire *r) +{ + p->fault_state = DCERPC_FAULT_OP_RNG_ERROR; +} + +void _lsa_Opnum85NotUsedOnWire(struct pipes_struct *p, + struct lsa_Opnum85NotUsedOnWire *r) +{ + p->fault_state = DCERPC_FAULT_OP_RNG_ERROR; +} + +void _lsa_Opnum86NotUsedOnWire(struct pipes_struct *p, + struct lsa_Opnum86NotUsedOnWire *r) +{ + p->fault_state = DCERPC_FAULT_OP_RNG_ERROR; +} + +void _lsa_Opnum87NotUsedOnWire(struct pipes_struct *p, + struct lsa_Opnum87NotUsedOnWire *r) +{ + p->fault_state = DCERPC_FAULT_OP_RNG_ERROR; +} + +void _lsa_Opnum88NotUsedOnWire(struct pipes_struct *p, + struct lsa_Opnum88NotUsedOnWire *r) +{ + p->fault_state = DCERPC_FAULT_OP_RNG_ERROR; +} + +void _lsa_Opnum89NotUsedOnWire(struct pipes_struct *p, + struct lsa_Opnum89NotUsedOnWire *r) +{ + p->fault_state = DCERPC_FAULT_OP_RNG_ERROR; +} + +void _lsa_Opnum90NotUsedOnWire(struct pipes_struct *p, + struct lsa_Opnum90NotUsedOnWire *r) +{ + p->fault_state = DCERPC_FAULT_OP_RNG_ERROR; +} + +void _lsa_Opnum91NotUsedOnWire(struct pipes_struct *p, + struct lsa_Opnum91NotUsedOnWire *r) +{ + p->fault_state = DCERPC_FAULT_OP_RNG_ERROR; +} + +void _lsa_Opnum92NotUsedOnWire(struct pipes_struct *p, + struct lsa_Opnum92NotUsedOnWire *r) +{ + p->fault_state = DCERPC_FAULT_OP_RNG_ERROR; +} + +void _lsa_Opnum93NotUsedOnWire(struct pipes_struct *p, + struct lsa_Opnum93NotUsedOnWire *r) +{ + p->fault_state = DCERPC_FAULT_OP_RNG_ERROR; +} + +void _lsa_Opnum94NotUsedOnWire(struct pipes_struct *p, + struct lsa_Opnum94NotUsedOnWire *r) +{ + p->fault_state = DCERPC_FAULT_OP_RNG_ERROR; +} + +void _lsa_Opnum95NotUsedOnWire(struct pipes_struct *p, + struct lsa_Opnum95NotUsedOnWire *r) +{ + p->fault_state = DCERPC_FAULT_OP_RNG_ERROR; +} + +void _lsa_Opnum96NotUsedOnWire(struct pipes_struct *p, + struct lsa_Opnum96NotUsedOnWire *r) +{ + p->fault_state = DCERPC_FAULT_OP_RNG_ERROR; +} + +void _lsa_Opnum97NotUsedOnWire(struct pipes_struct *p, + struct lsa_Opnum97NotUsedOnWire *r) +{ + p->fault_state = DCERPC_FAULT_OP_RNG_ERROR; +} + +void _lsa_Opnum98NotUsedOnWire(struct pipes_struct *p, + struct lsa_Opnum98NotUsedOnWire *r) +{ + p->fault_state = DCERPC_FAULT_OP_RNG_ERROR; +} + +void _lsa_Opnum99NotUsedOnWire(struct pipes_struct *p, + struct lsa_Opnum99NotUsedOnWire *r) +{ + p->fault_state = DCERPC_FAULT_OP_RNG_ERROR; +} + +void _lsa_Opnum100NotUsedOnWire(struct pipes_struct *p, + struct lsa_Opnum100NotUsedOnWire *r) +{ + p->fault_state = DCERPC_FAULT_OP_RNG_ERROR; +} + +void _lsa_Opnum101NotUsedOnWire(struct pipes_struct *p, + struct lsa_Opnum101NotUsedOnWire *r) +{ + p->fault_state = DCERPC_FAULT_OP_RNG_ERROR; +} + +void _lsa_Opnum102NotUsedOnWire(struct pipes_struct *p, + struct lsa_Opnum102NotUsedOnWire *r) +{ + p->fault_state = DCERPC_FAULT_OP_RNG_ERROR; +} + +void _lsa_Opnum103NotUsedOnWire(struct pipes_struct *p, + struct lsa_Opnum103NotUsedOnWire *r) +{ + p->fault_state = DCERPC_FAULT_OP_RNG_ERROR; +} + +void _lsa_Opnum104NotUsedOnWire(struct pipes_struct *p, + struct lsa_Opnum104NotUsedOnWire *r) +{ + p->fault_state = DCERPC_FAULT_OP_RNG_ERROR; +} + +void _lsa_Opnum105NotUsedOnWire(struct pipes_struct *p, + struct lsa_Opnum105NotUsedOnWire *r) +{ + p->fault_state = DCERPC_FAULT_OP_RNG_ERROR; +} + +void _lsa_Opnum106NotUsedOnWire(struct pipes_struct *p, + struct lsa_Opnum106NotUsedOnWire *r) +{ + p->fault_state = DCERPC_FAULT_OP_RNG_ERROR; +} + +void _lsa_Opnum107NotUsedOnWire(struct pipes_struct *p, + struct lsa_Opnum107NotUsedOnWire *r) +{ + p->fault_state = DCERPC_FAULT_OP_RNG_ERROR; +} + +void _lsa_Opnum108NotUsedOnWire(struct pipes_struct *p, + struct lsa_Opnum108NotUsedOnWire *r) +{ + p->fault_state = DCERPC_FAULT_OP_RNG_ERROR; +} + +void _lsa_Opnum109NotUsedOnWire(struct pipes_struct *p, + struct lsa_Opnum109NotUsedOnWire *r) +{ + p->fault_state = DCERPC_FAULT_OP_RNG_ERROR; +} + +void _lsa_Opnum110NotUsedOnWire(struct pipes_struct *p, + struct lsa_Opnum110NotUsedOnWire *r) +{ + p->fault_state = DCERPC_FAULT_OP_RNG_ERROR; +} + +void _lsa_Opnum111NotUsedOnWire(struct pipes_struct *p, + struct lsa_Opnum111NotUsedOnWire *r) +{ + p->fault_state = DCERPC_FAULT_OP_RNG_ERROR; +} + +void _lsa_Opnum112NotUsedOnWire(struct pipes_struct *p, + struct lsa_Opnum112NotUsedOnWire *r) +{ + p->fault_state = DCERPC_FAULT_OP_RNG_ERROR; +} + +void _lsa_Opnum113NotUsedOnWire(struct pipes_struct *p, + struct lsa_Opnum113NotUsedOnWire *r) +{ + p->fault_state = DCERPC_FAULT_OP_RNG_ERROR; +} + +void _lsa_Opnum114NotUsedOnWire(struct pipes_struct *p, + struct lsa_Opnum114NotUsedOnWire *r) +{ + p->fault_state = DCERPC_FAULT_OP_RNG_ERROR; +} + +void _lsa_Opnum115NotUsedOnWire(struct pipes_struct *p, + struct lsa_Opnum115NotUsedOnWire *r) +{ + p->fault_state = DCERPC_FAULT_OP_RNG_ERROR; +} + +void _lsa_Opnum116NotUsedOnWire(struct pipes_struct *p, + struct lsa_Opnum116NotUsedOnWire *r) +{ + p->fault_state = DCERPC_FAULT_OP_RNG_ERROR; +} + +void _lsa_Opnum117NotUsedOnWire(struct pipes_struct *p, + struct lsa_Opnum117NotUsedOnWire *r) +{ + p->fault_state = DCERPC_FAULT_OP_RNG_ERROR; +} + +void _lsa_Opnum118NotUsedOnWire(struct pipes_struct *p, + struct lsa_Opnum118NotUsedOnWire *r) +{ + p->fault_state = DCERPC_FAULT_OP_RNG_ERROR; +} + +void _lsa_Opnum119NotUsedOnWire(struct pipes_struct *p, + struct lsa_Opnum119NotUsedOnWire *r) +{ + p->fault_state = DCERPC_FAULT_OP_RNG_ERROR; +} + +void _lsa_Opnum120NotUsedOnWire(struct pipes_struct *p, + struct lsa_Opnum120NotUsedOnWire *r) +{ + p->fault_state = DCERPC_FAULT_OP_RNG_ERROR; +} + +void _lsa_Opnum121NotUsedOnWire(struct pipes_struct *p, + struct lsa_Opnum121NotUsedOnWire *r) +{ + p->fault_state = DCERPC_FAULT_OP_RNG_ERROR; +} + +void _lsa_Opnum122NotUsedOnWire(struct pipes_struct *p, + struct lsa_Opnum122NotUsedOnWire *r) +{ + p->fault_state = DCERPC_FAULT_OP_RNG_ERROR; +} + +void _lsa_Opnum123NotUsedOnWire(struct pipes_struct *p, + struct lsa_Opnum123NotUsedOnWire *r) +{ + p->fault_state = DCERPC_FAULT_OP_RNG_ERROR; +} + +void _lsa_Opnum124NotUsedOnWire(struct pipes_struct *p, + struct lsa_Opnum124NotUsedOnWire *r) +{ + p->fault_state = DCERPC_FAULT_OP_RNG_ERROR; +} + +void _lsa_Opnum125NotUsedOnWire(struct pipes_struct *p, + struct lsa_Opnum125NotUsedOnWire *r) +{ + p->fault_state = DCERPC_FAULT_OP_RNG_ERROR; +} + +void _lsa_Opnum126NotUsedOnWire(struct pipes_struct *p, + struct lsa_Opnum126NotUsedOnWire *r) +{ + p->fault_state = DCERPC_FAULT_OP_RNG_ERROR; +} + +void _lsa_Opnum127NotUsedOnWire(struct pipes_struct *p, + struct lsa_Opnum127NotUsedOnWire *r) +{ + p->fault_state = DCERPC_FAULT_OP_RNG_ERROR; +} + +void _lsa_Opnum128NotUsedOnWire(struct pipes_struct *p, + struct lsa_Opnum128NotUsedOnWire *r) +{ + p->fault_state = DCERPC_FAULT_OP_RNG_ERROR; +} + +/*************************************************************************** + _lsa_CreateTrustedDomainEx3 + ***************************************************************************/ + +NTSTATUS _lsa_CreateTrustedDomainEx3(struct pipes_struct *p, + struct lsa_CreateTrustedDomainEx3 *r) +{ + p->fault_state = DCERPC_FAULT_OP_RNG_ERROR; + return NT_STATUS_NOT_IMPLEMENTED; +} + +/*************************************************************************** + _lsa_OpenPolicy3 + ***************************************************************************/ + +NTSTATUS _lsa_OpenPolicy3(struct pipes_struct *p, + struct lsa_OpenPolicy3 *r) +{ + struct dcesrv_call_state *dce_call = p->dce_call; + struct auth_session_info *session_info = + dcesrv_call_session_info(dce_call); + struct security_descriptor *psd = NULL; + size_t sd_size; + uint32_t des_access = r->in.access_mask; + uint32_t acc_granted; + NTSTATUS status; + + if (p->transport != NCACN_NP && p->transport != NCALRPC) { + p->fault_state = DCERPC_FAULT_ACCESS_DENIED; + return NT_STATUS_ACCESS_DENIED; + } + + ZERO_STRUCTP(r->out.handle); + + /* + * The attributes have no effect and MUST be ignored, except the + * root_dir which MUST be NULL. + */ + if (r->in.attr != NULL && r->in.attr->root_dir != NULL) { + return NT_STATUS_INVALID_PARAMETER; + } + + switch (r->in.in_version) { + case 1: + *r->out.out_version = 1; + + r->out.out_revision_info->info1.revision = 1; + /* TODO: Enable as soon as we support it */ +#if 0 + r->out.out_revision_info->info1.supported_features = + LSA_FEATURE_TDO_AUTH_INFO_AES_CIPHER; +#endif + + break; + default: + return NT_STATUS_NOT_SUPPORTED; + } + + /* Work out max allowed. */ + map_max_allowed_access(session_info->security_token, + session_info->unix_token, + &des_access); + + /* map the generic bits to the lsa policy ones */ + se_map_generic(&des_access, &lsa_policy_mapping); + + /* get the generic lsa policy SD until we store it */ + status = make_lsa_object_sd(p->mem_ctx, + &psd, + &sd_size, + &lsa_policy_mapping, + NULL, + 0); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + status = access_check_object(psd, + session_info->security_token, + SEC_PRIV_INVALID, + SEC_PRIV_INVALID, + 0, + des_access, + &acc_granted, + "_lsa_OpenPolicy2"); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + status = create_lsa_policy_handle(p->mem_ctx, + p, + LSA_HANDLE_POLICY_TYPE, + acc_granted, + get_global_sam_sid(), + NULL, + psd, + r->out.handle); + if (!NT_STATUS_IS_OK(status)) { + return NT_STATUS_OBJECT_NAME_NOT_FOUND; + } + + return NT_STATUS_OK; +} + +void _lsa_Opnum131NotUsedOnWire(struct pipes_struct *p, + struct lsa_Opnum131NotUsedOnWire *r) +{ + p->fault_state = DCERPC_FAULT_OP_RNG_ERROR; +} + +NTSTATUS _lsa_lsaRQueryForestTrustInformation2(struct pipes_struct *p, + struct lsa_lsaRQueryForestTrustInformation2 *r) +{ + p->fault_state = DCERPC_FAULT_OP_RNG_ERROR; + return NT_STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS _lsa_lsaRSetForestTrustInformation2(struct pipes_struct *p, + struct lsa_lsaRSetForestTrustInformation2 *r) +{ + p->fault_state = DCERPC_FAULT_OP_RNG_ERROR; + return NT_STATUS_NOT_IMPLEMENTED; +} + +#include "librpc/rpc/dcesrv_core.h" + +#define DCESRV_INTERFACE_LSARPC_BIND(context, iface) \ + dcesrv_interface_lsarpc_bind(context, iface) + +static NTSTATUS dcesrv_interface_lsarpc_bind( + struct dcesrv_connection_context *context, + const struct dcesrv_interface *iface) +{ + return dcesrv_interface_bind_reject_connect(context, iface); +} + +static NTSTATUS lsarpc__op_init_server(struct dcesrv_context *dce_ctx, + const struct dcesrv_endpoint_server *ep_server); +static const struct dcesrv_interface dcesrv_lsarpc_interface; + +#define NCACN_NP_PIPE_NETLOGON "ncacn_np:[\\pipe\\netlogon]" +#define NCACN_NP_PIPE_LSASS "ncacn_np:[\\pipe\\lsass]" + +#define DCESRV_INTERFACE_LSARPC_NCACN_NP_SECONDARY_ENDPOINT \ + NCACN_NP_PIPE_LSASS + +#define DCESRV_INTERFACE_LSARPC_INIT_SERVER \ + dcesrv_interface_lsarpc_init_server + +static NTSTATUS dcesrv_interface_lsarpc_init_server( + struct dcesrv_context *dce_ctx, + const struct dcesrv_endpoint_server *ep_server) +{ + NTSTATUS ret = dcesrv_interface_register(dce_ctx, + NCACN_NP_PIPE_NETLOGON, + NCACN_NP_PIPE_LSASS, + &dcesrv_lsarpc_interface, + NULL); + if (!NT_STATUS_IS_OK(ret)) { + DBG_ERR("Failed to register endpoint " + "'\\pipe\\netlogon'\n"); + return ret; + } + + return lsarpc__op_init_server(dce_ctx, ep_server); +} + +/* include the generated boilerplate */ +#include "librpc/gen_ndr/ndr_lsa_scompat.c" -- cgit v1.2.3