/* Unix SMB/CIFS implementation. endpoint server for the lsarpc pipe Copyright (C) Andrew Tridgell 2004 Copyright (C) Andrew Bartlett 2004-2007 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "rpc_server/lsa/lsa.h" #include "libds/common/roles.h" #include "libds/common/flag_mapping.h" #include "lib/messaging/irpc.h" #include "librpc/gen_ndr/ndr_lsa_c.h" struct dcesrv_lsa_TranslatedItem { enum lsa_SidType type; const struct dom_sid *sid; const char *name; const char *authority_name; const struct dom_sid *authority_sid; uint32_t flags; uint32_t wb_idx; bool done; struct { const char *domain; /* only $DOMAIN\ */ const char *namespace; /* $NAMESPACE\ or @$NAMESPACE */ const char *principal; /* \$PRINCIPAL or $PRIN@IPAL */ const char *sid; /* "S-1-5-21-9000-8000-7000-6000" */ const char *rid; /* "00001770" */ } hints; }; struct dcesrv_lsa_LookupSids_base_state; struct dcesrv_lsa_LookupNames_base_state; struct dcesrv_lsa_Lookup_view { const char *name; NTSTATUS (*lookup_sid)(struct dcesrv_lsa_LookupSids_base_state *state, struct dcesrv_lsa_TranslatedItem *item); NTSTATUS (*lookup_name)(struct dcesrv_lsa_LookupNames_base_state *state, struct dcesrv_lsa_TranslatedItem *item); }; struct dcesrv_lsa_Lookup_view_table { const char *name; size_t count; const struct dcesrv_lsa_Lookup_view **array; }; static const struct dcesrv_lsa_Lookup_view_table *dcesrv_lsa_view_table( enum lsa_LookupNamesLevel level); /* lookup a SID for 1 name */ static NTSTATUS dcesrv_lsa_lookup_name(struct lsa_policy_state *state, TALLOC_CTX *mem_ctx, const char *domain_name, const struct dom_sid *domain_sid, struct ldb_dn *domain_dn, const char *principal, const struct dom_sid **p_sid, enum lsa_SidType *p_type) { const char * const attrs[] = { "objectSid", "sAMAccountType", NULL}; struct ldb_message **res = NULL; const char *nt4_account = NULL; char *encoded_account = NULL; const char *at = NULL; NTSTATUS status; const struct dom_sid *sid = NULL; uint32_t atype; enum lsa_SidType type; bool match = false; int ret; if ((principal == NULL) || (principal[0] == '\0')) { return NT_STATUS_NONE_MAPPED; } at = strchr(principal, '@'); if (at != NULL) { const char *nt4_domain = NULL; status = crack_name_to_nt4_name(mem_ctx, state->sam_ldb, DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL, principal, &nt4_domain, &nt4_account); if (!NT_STATUS_IS_OK(status)) { DEBUG(3, ("Failed to crack name %s into an NT4 name: %s\n", principal, nt_errstr(status))); return status; } match = strequal(nt4_domain, domain_name); if (!match) { /* * TODO: handle multiple domains in a forest. */ return NT_STATUS_NONE_MAPPED; } } else { nt4_account = principal; } encoded_account = ldb_binary_encode_string(mem_ctx, nt4_account); if (encoded_account == NULL) { return NT_STATUS_NO_MEMORY; } ret = gendb_search(state->sam_ldb, mem_ctx, domain_dn, &res, attrs, "(&(sAMAccountName=%s)(objectSid=*))", encoded_account); TALLOC_FREE(encoded_account); if (ret < 0) { return NT_STATUS_INTERNAL_DB_ERROR; } if (ret == 0) { return NT_STATUS_NONE_MAPPED; } if (ret > 1) { status = NT_STATUS_INTERNAL_DB_CORRUPTION; DBG_ERR("nt4_account[%s] found %d times (principal[%s]) - %s\n", nt4_account, ret, principal, nt_errstr(status)); return status; } sid = samdb_result_dom_sid(mem_ctx, res[0], "objectSid"); if (sid == NULL) { return NT_STATUS_NO_MEMORY; } /* Check that this is in the domain */ match = dom_sid_in_domain(domain_sid, sid); if (!match) { return NT_STATUS_NONE_MAPPED; } atype = ldb_msg_find_attr_as_uint(res[0], "sAMAccountType", 0); type = ds_atype_map(atype); if (type == SID_NAME_UNKNOWN) { return NT_STATUS_NONE_MAPPED; } *p_sid = sid; *p_type = type; return NT_STATUS_OK; } /* add to the lsa_RefDomainList for LookupSids and LookupNames */ static NTSTATUS dcesrv_lsa_authority_list(const char *authority_name, const struct dom_sid *authority_sid, struct lsa_RefDomainList *domains, uint32_t *sid_index) { uint32_t i; *sid_index = UINT32_MAX; if (authority_name == NULL) { return NT_STATUS_OK; } /* see if we've already done this authority name */ for (i=0;icount;i++) { if (strcasecmp_m(authority_name, domains->domains[i].name.string) == 0) { *sid_index = i; return NT_STATUS_OK; } } domains->domains = talloc_realloc(domains, domains->domains, struct lsa_DomainInfo, domains->count+1); if (domains->domains == NULL) { return NT_STATUS_NO_MEMORY; } domains->domains[i].name.string = talloc_strdup(domains->domains, authority_name); if (domains->domains[i].name.string == NULL) { return NT_STATUS_NO_MEMORY; } domains->domains[i].sid = dom_sid_dup(domains->domains, authority_sid); if (domains->domains[i].sid == NULL) { return NT_STATUS_NO_MEMORY; } domains->count++; domains->max_size = LSA_REF_DOMAIN_LIST_MULTIPLIER * domains->count; *sid_index = i; return NT_STATUS_OK; } /* lookup a name for 1 SID */ static NTSTATUS dcesrv_lsa_lookup_sid(struct lsa_policy_state *state, TALLOC_CTX *mem_ctx, const char *domain_name, const struct dom_sid *domain_sid, struct ldb_dn *domain_dn, const struct dom_sid *sid, const char **p_name, enum lsa_SidType *p_type) { const char * const attrs[] = { "sAMAccountName", "sAMAccountType", NULL}; struct ldb_message **res = NULL; char *encoded_sid = NULL; const char *name = NULL; uint32_t atype; enum lsa_SidType type; int ret; encoded_sid = ldap_encode_ndr_dom_sid(mem_ctx, sid); if (encoded_sid == NULL) { return NT_STATUS_NO_MEMORY; } ret = gendb_search(state->sam_ldb, mem_ctx, domain_dn, &res, attrs, "(&(objectSid=%s)(sAMAccountName=*))", encoded_sid); TALLOC_FREE(encoded_sid); if (ret < 0) { return NT_STATUS_INTERNAL_DB_ERROR; } if (ret == 0) { return NT_STATUS_NONE_MAPPED; } if (ret > 1) { NTSTATUS status = NT_STATUS_INTERNAL_DB_CORRUPTION; DBG_ERR("sid[%s] found %d times - %s\n", dom_sid_string(mem_ctx, sid), ret, nt_errstr(status)); return status; } name = ldb_msg_find_attr_as_string(res[0], "sAMAccountName", NULL); if (name == NULL) { return NT_STATUS_INTERNAL_ERROR; } atype = ldb_msg_find_attr_as_uint(res[0], "sAMAccountType", 0); type = ds_atype_map(atype); if (type == SID_NAME_UNKNOWN) { return NT_STATUS_NONE_MAPPED; } *p_name = name; *p_type = type; return NT_STATUS_OK; } struct dcesrv_lsa_LookupSids_base_state { struct dcesrv_call_state *dce_call; TALLOC_CTX *mem_ctx; struct lsa_policy_state *policy_state; struct lsa_LookupSids3 r; const struct dcesrv_lsa_Lookup_view_table *view_table; struct dcesrv_lsa_TranslatedItem *items; struct dsdb_trust_routing_table *routing_table; struct { struct dcerpc_binding_handle *irpc_handle; struct lsa_SidArray sids; struct lsa_RefDomainList *domains; struct lsa_TransNameArray2 names; uint32_t count; NTSTATUS result; } wb; struct { struct lsa_LookupSids *l; struct lsa_LookupSids2 *l2; struct lsa_LookupSids3 *l3; } _r; }; static NTSTATUS dcesrv_lsa_LookupSids_base_finish( struct dcesrv_lsa_LookupSids_base_state *state); static void dcesrv_lsa_LookupSids_base_map( struct dcesrv_lsa_LookupSids_base_state *state); static void dcesrv_lsa_LookupSids_base_done(struct tevent_req *subreq); static NTSTATUS dcesrv_lsa_LookupSids_base_call(struct dcesrv_lsa_LookupSids_base_state *state) { struct lsa_LookupSids3 *r = &state->r; struct tevent_req *subreq = NULL; uint32_t v; uint32_t i; *r->out.domains = NULL; r->out.names->count = 0; r->out.names->names = NULL; *r->out.count = 0; state->view_table = dcesrv_lsa_view_table(r->in.level); if (state->view_table == NULL) { return NT_STATUS_INVALID_PARAMETER; } *r->out.domains = talloc_zero(r->out.domains, struct lsa_RefDomainList); if (*r->out.domains == NULL) { return NT_STATUS_NO_MEMORY; } r->out.names->names = talloc_zero_array(r->out.names, struct lsa_TranslatedName2, r->in.sids->num_sids); if (r->out.names->names == NULL) { return NT_STATUS_NO_MEMORY; } state->items = talloc_zero_array(state, struct dcesrv_lsa_TranslatedItem, r->in.sids->num_sids); if (state->items == NULL) { return NT_STATUS_NO_MEMORY; } for (i=0;iin.sids->num_sids;i++) { struct dcesrv_lsa_TranslatedItem *item = &state->items[i]; uint32_t rid = 0; if (r->in.sids->sids[i].sid == NULL) { return NT_STATUS_INVALID_PARAMETER; } item->type = SID_NAME_UNKNOWN; item->sid = r->in.sids->sids[i].sid; item->hints.sid = dom_sid_string(state->items, item->sid); if (item->hints.sid == NULL) { return NT_STATUS_NO_MEMORY; } dom_sid_split_rid(state->items, item->sid, NULL, &rid); item->hints.rid = talloc_asprintf(state->items, "%08X", (unsigned)rid); if (item->hints.rid == NULL) { return NT_STATUS_NO_MEMORY; } } for (v=0; v < state->view_table->count; v++) { const struct dcesrv_lsa_Lookup_view *view = state->view_table->array[v]; for (i=0; i < r->in.sids->num_sids; i++) { struct dcesrv_lsa_TranslatedItem *item = &state->items[i]; NTSTATUS status; if (item->done) { continue; } status = view->lookup_sid(state, item); if (NT_STATUS_IS_OK(status)) { item->done = true; } else if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) { status = NT_STATUS_OK; } else if (NT_STATUS_EQUAL(status, NT_STATUS_SOME_NOT_MAPPED)) { status = NT_STATUS_OK; } if (!NT_STATUS_IS_OK(status)) { return status; } } } if (state->wb.irpc_handle == NULL) { return dcesrv_lsa_LookupSids_base_finish(state); } state->wb.sids.sids = talloc_zero_array(state, struct lsa_SidPtr, r->in.sids->num_sids); if (state->wb.sids.sids == NULL) { return NT_STATUS_NO_MEMORY; } for (i=0; i < r->in.sids->num_sids; i++) { struct dcesrv_lsa_TranslatedItem *item = &state->items[i]; if (item->done) { continue; } item->wb_idx = state->wb.sids.num_sids; state->wb.sids.sids[item->wb_idx] = r->in.sids->sids[i]; state->wb.sids.num_sids++; } subreq = dcerpc_lsa_LookupSids3_send(state, state->dce_call->event_ctx, state->wb.irpc_handle, &state->wb.sids, &state->wb.domains, &state->wb.names, state->r.in.level, &state->wb.count, state->r.in.lookup_options, state->r.in.client_revision); if (subreq == NULL) { return NT_STATUS_NO_MEMORY;; } state->dce_call->state_flags |= DCESRV_CALL_STATE_FLAG_ASYNC; tevent_req_set_callback(subreq, dcesrv_lsa_LookupSids_base_done, state); return NT_STATUS_OK; } static NTSTATUS dcesrv_lsa_LookupSids_base_finish( struct dcesrv_lsa_LookupSids_base_state *state) { struct lsa_LookupSids3 *r = &state->r; uint32_t i; for (i=0;iin.sids->num_sids;i++) { struct dcesrv_lsa_TranslatedItem *item = &state->items[i]; NTSTATUS status; uint32_t sid_index = UINT32_MAX; status = dcesrv_lsa_authority_list(item->authority_name, item->authority_sid, *r->out.domains, &sid_index); if (!NT_STATUS_IS_OK(status)) { return status; } if (item->name == NULL && r->in.level == LSA_LOOKUP_NAMES_ALL) { if (sid_index == UINT32_MAX) { item->name = item->hints.sid; } else { item->name = item->hints.rid; } } r->out.names->names[i].sid_type = item->type; r->out.names->names[i].name.string = item->name; r->out.names->names[i].sid_index = sid_index; r->out.names->names[i].unknown = item->flags; r->out.names->count++; if (item->type != SID_NAME_UNKNOWN) { (*r->out.count)++; } } if (*r->out.count == 0) { return NT_STATUS_NONE_MAPPED; } if (*r->out.count != r->in.sids->num_sids) { return STATUS_SOME_UNMAPPED; } return NT_STATUS_OK; } static void dcesrv_lsa_LookupSids_base_map( struct dcesrv_lsa_LookupSids_base_state *state) { if (state->_r.l3 != NULL) { struct lsa_LookupSids3 *r = state->_r.l3; r->out.result = state->r.out.result; return; } if (state->_r.l2 != NULL) { struct lsa_LookupSids2 *r = state->_r.l2; r->out.result = state->r.out.result; return; } if (state->_r.l != NULL) { struct lsa_LookupSids *r = state->_r.l; uint32_t i; r->out.result = state->r.out.result; SMB_ASSERT(state->r.out.names->count <= r->in.sids->num_sids); for (i = 0; i < state->r.out.names->count; i++) { struct lsa_TranslatedName2 *n2 = &state->r.out.names->names[i]; struct lsa_TranslatedName *n = &r->out.names->names[i]; n->sid_type = n2->sid_type; n->name = n2->name; n->sid_index = n2->sid_index; } r->out.names->count = state->r.out.names->count; return; } } static void dcesrv_lsa_LookupSids_base_done(struct tevent_req *subreq) { struct dcesrv_lsa_LookupSids_base_state *state = tevent_req_callback_data(subreq, struct dcesrv_lsa_LookupSids_base_state); struct dcesrv_call_state *dce_call = state->dce_call; NTSTATUS status; uint32_t i; status = dcerpc_lsa_LookupSids3_recv(subreq, state->mem_ctx, &state->wb.result); TALLOC_FREE(subreq); TALLOC_FREE(state->wb.irpc_handle); if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) { DEBUG(0,(__location__ ": IRPC callback failed %s\n", nt_errstr(status))); goto finished; } else if (!NT_STATUS_IS_OK(status)) { state->dce_call->fault_code = DCERPC_FAULT_CANT_PERFORM; DEBUG(0,(__location__ ": IRPC callback failed %s\n", nt_errstr(status))); goto finished; } status = state->wb.result; if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) { status = NT_STATUS_OK; } else if (NT_STATUS_EQUAL(status, NT_STATUS_SOME_NOT_MAPPED)) { status = NT_STATUS_OK; } if (!NT_STATUS_IS_OK(status)) { goto finished; } for (i=0; i < state->r.in.sids->num_sids; i++) { struct dcesrv_lsa_TranslatedItem *item = &state->items[i]; struct lsa_TranslatedName2 *s2 = NULL; struct lsa_DomainInfo *d = NULL; if (item->done) { continue; } if (item->wb_idx >= state->wb.names.count) { status = NT_STATUS_INTERNAL_ERROR; goto finished; } s2 = &state->wb.names.names[item->wb_idx]; item->type = s2->sid_type; item->name = s2->name.string; item->flags = s2->unknown; if (s2->sid_index == UINT32_MAX) { continue; } if (state->wb.domains == NULL) { status = NT_STATUS_INTERNAL_ERROR; goto finished; } if (s2->sid_index >= state->wb.domains->count) { status = NT_STATUS_INTERNAL_ERROR; goto finished; } d = &state->wb.domains->domains[s2->sid_index]; item->authority_name = d->name.string; item->authority_sid = d->sid; } status = dcesrv_lsa_LookupSids_base_finish(state); finished: state->r.out.result = status; dcesrv_lsa_LookupSids_base_map(state); dcesrv_async_reply(dce_call); } /* lsa_LookupSids2 */ NTSTATUS dcesrv_lsa_LookupSids2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct lsa_LookupSids2 *r) { enum dcerpc_transport_t transport = dcerpc_binding_get_transport(dce_call->conn->endpoint->ep_description); struct dcesrv_lsa_LookupSids_base_state *state = NULL; struct dcesrv_handle *policy_handle = NULL; NTSTATUS status; if (transport != NCACN_NP && transport != NCALRPC) { DCESRV_FAULT(DCERPC_FAULT_ACCESS_DENIED); } DCESRV_PULL_HANDLE(policy_handle, r->in.handle, LSA_HANDLE_POLICY); *r->out.domains = NULL; r->out.names->count = 0; r->out.names->names = NULL; *r->out.count = 0; state = talloc_zero(mem_ctx, struct dcesrv_lsa_LookupSids_base_state); if (state == NULL) { return NT_STATUS_NO_MEMORY; } state->dce_call = dce_call; state->mem_ctx = mem_ctx; state->policy_state = policy_handle->data; state->r.in.sids = r->in.sids; state->r.in.level = r->in.level; state->r.in.lookup_options = r->in.lookup_options; state->r.in.client_revision = r->in.client_revision; state->r.in.names = r->in.names; state->r.in.count = r->in.count; state->r.out.domains = r->out.domains; state->r.out.names = r->out.names; state->r.out.count = r->out.count; state->_r.l2 = r; status = dcesrv_lsa_LookupSids_base_call(state); if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) { return status; } state->r.out.result = status; dcesrv_lsa_LookupSids_base_map(state); return status; } /* A random hexadecimal number (honest!) */ #define LSA_SERVER_IMPLICIT_POLICY_STATE_MAGIC 0xc0c99e00 /* Ensure we're operating on an schannel connection, and use a lsa_policy_state cache on the connection. */ static NTSTATUS schannel_call_setup(struct dcesrv_call_state *dce_call, struct lsa_policy_state **_policy_state) { struct lsa_policy_state *policy_state = NULL; enum dcerpc_transport_t transport = dcerpc_binding_get_transport(dce_call->conn->endpoint->ep_description); enum dcerpc_AuthType auth_type = DCERPC_AUTH_TYPE_NONE; if (transport != NCACN_IP_TCP) { /* We can't call DCESRV_FAULT() in the sub-function */ dce_call->fault_code = DCERPC_FAULT_ACCESS_DENIED; return NT_STATUS_ACCESS_DENIED; } /* * We don't have policy handles on this call. So this must be restricted * to crypto connections only. * * NB. gensec requires schannel connections to * have at least DCERPC_AUTH_LEVEL_INTEGRITY. */ dcesrv_call_auth_info(dce_call, &auth_type, NULL); if (auth_type != DCERPC_AUTH_TYPE_SCHANNEL) { /* We can't call DCESRV_FAULT() in the sub-function */ dce_call->fault_code = DCERPC_FAULT_ACCESS_DENIED; return NT_STATUS_ACCESS_DENIED; } /* * We don't have a policy handle on this call, so we want to * make a policy state and cache it for the life of the * connection, to avoid re-opening the DB each call. */ policy_state = dcesrv_iface_state_find_conn(dce_call, LSA_SERVER_IMPLICIT_POLICY_STATE_MAGIC, struct lsa_policy_state); if (policy_state == NULL) { NTSTATUS status = dcesrv_lsa_get_policy_state(dce_call, dce_call /* mem_ctx */, 0, /* we skip access checks */ &policy_state); if (!NT_STATUS_IS_OK(status)) { return status; } /* * This will talloc_steal() policy_state onto the * connection, which has longer lifetime than the * immediate caller requires */ status = dcesrv_iface_state_store_conn(dce_call, LSA_SERVER_IMPLICIT_POLICY_STATE_MAGIC, policy_state); if (!NT_STATUS_IS_OK(status)) { return status; } } *_policy_state = policy_state; return NT_STATUS_OK; } /* lsa_LookupSids3 Identical to LookupSids2, but doesn't take a policy handle */ NTSTATUS dcesrv_lsa_LookupSids3(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct lsa_LookupSids3 *r) { struct dcesrv_lsa_LookupSids_base_state *state = NULL; NTSTATUS status; *r->out.domains = NULL; r->out.names->count = 0; r->out.names->names = NULL; *r->out.count = 0; state = talloc_zero(mem_ctx, struct dcesrv_lsa_LookupSids_base_state); if (state == NULL) { return NT_STATUS_NO_MEMORY; } /* * We don't have a policy handle on this call, so we want to * make a policy state and cache it for the life of the * connection, to avoid re-opening the DB each call. * * This also enforces that this is only available over * ncacn_ip_tcp and with SCHANNEL. * * schannel_call_setup may also set the fault state. * * state->policy_state is shared between all calls on this * connection and is moved with talloc_steal() under the * connection, not dce_call or state. */ status = schannel_call_setup(dce_call, &state->policy_state); if (!NT_STATUS_IS_OK(status)) { return status; } state->dce_call = dce_call; state->mem_ctx = mem_ctx; state->r.in.sids = r->in.sids; state->r.in.level = r->in.level; state->r.in.lookup_options = r->in.lookup_options; state->r.in.client_revision = r->in.client_revision; state->r.in.names = r->in.names; state->r.in.count = r->in.count; state->r.out.domains = r->out.domains; state->r.out.names = r->out.names; state->r.out.count = r->out.count; state->_r.l3 = r; status = dcesrv_lsa_LookupSids_base_call(state); if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) { return status; } state->r.out.result = status; dcesrv_lsa_LookupSids_base_map(state); return status; } /* lsa_LookupSids */ NTSTATUS dcesrv_lsa_LookupSids(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct lsa_LookupSids *r) { enum dcerpc_transport_t transport = dcerpc_binding_get_transport(dce_call->conn->endpoint->ep_description); struct dcesrv_lsa_LookupSids_base_state *state = NULL; struct dcesrv_handle *policy_handle = NULL; NTSTATUS status; if (transport != NCACN_NP && transport != NCALRPC) { DCESRV_FAULT(DCERPC_FAULT_ACCESS_DENIED); } DCESRV_PULL_HANDLE(policy_handle, r->in.handle, LSA_HANDLE_POLICY); *r->out.domains = NULL; r->out.names->count = 0; r->out.names->names = NULL; *r->out.count = 0; r->out.names->names = talloc_zero_array(r->out.names, struct lsa_TranslatedName, r->in.sids->num_sids); if (r->out.names->names == NULL) { return NT_STATUS_NO_MEMORY; } state = talloc_zero(mem_ctx, struct dcesrv_lsa_LookupSids_base_state); if (state == NULL) { return NT_STATUS_NO_MEMORY; } state->dce_call = dce_call; state->mem_ctx = mem_ctx; state->policy_state = policy_handle->data; state->r.in.sids = r->in.sids; state->r.in.level = r->in.level; state->r.in.lookup_options = LSA_LOOKUP_OPTION_SEARCH_ISOLATED_NAMES; state->r.in.client_revision = LSA_CLIENT_REVISION_1; state->r.in.names = talloc_zero(state, struct lsa_TransNameArray2); if (state->r.in.names == NULL) { return NT_STATUS_NO_MEMORY; } state->r.in.count = r->in.count; state->r.out.domains = r->out.domains; state->r.out.names = talloc_zero(state, struct lsa_TransNameArray2); if (state->r.out.names == NULL) { return NT_STATUS_NO_MEMORY; } state->r.out.count = r->out.count; state->_r.l = r; status = dcesrv_lsa_LookupSids_base_call(state); if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) { return status; } state->r.out.result = status; dcesrv_lsa_LookupSids_base_map(state); return status; } struct dcesrv_lsa_LookupNames_base_state { struct dcesrv_call_state *dce_call; TALLOC_CTX *mem_ctx; struct lsa_policy_state *policy_state; struct lsa_LookupNames4 r; const struct dcesrv_lsa_Lookup_view_table *view_table; struct dcesrv_lsa_TranslatedItem *items; struct dsdb_trust_routing_table *routing_table; struct { struct dcerpc_binding_handle *irpc_handle; uint32_t num_names; struct lsa_String *names; struct lsa_RefDomainList *domains; struct lsa_TransSidArray3 sids; uint32_t count; NTSTATUS result; } wb; struct { struct lsa_LookupNames *l; struct lsa_LookupNames2 *l2; struct lsa_LookupNames3 *l3; struct lsa_LookupNames4 *l4; } _r; }; static NTSTATUS dcesrv_lsa_LookupNames_base_finish( struct dcesrv_lsa_LookupNames_base_state *state); static void dcesrv_lsa_LookupNames_base_map( struct dcesrv_lsa_LookupNames_base_state *state); static void dcesrv_lsa_LookupNames_base_done(struct tevent_req *subreq); static NTSTATUS dcesrv_lsa_LookupNames_base_call(struct dcesrv_lsa_LookupNames_base_state *state) { struct lsa_LookupNames4 *r = &state->r; enum lsa_LookupOptions invalid_lookup_options = 0; struct tevent_req *subreq = NULL; uint32_t v; uint32_t i; *r->out.domains = NULL; r->out.sids->count = 0; r->out.sids->sids = NULL; *r->out.count = 0; if (r->in.level != LSA_LOOKUP_NAMES_ALL) { invalid_lookup_options |= LSA_LOOKUP_OPTION_SEARCH_ISOLATED_NAMES_LOCAL; } if (r->in.lookup_options & invalid_lookup_options) { return NT_STATUS_INVALID_PARAMETER; } state->view_table = dcesrv_lsa_view_table(r->in.level); if (state->view_table == NULL) { return NT_STATUS_INVALID_PARAMETER; } *r->out.domains = talloc_zero(r->out.domains, struct lsa_RefDomainList); if (*r->out.domains == NULL) { return NT_STATUS_NO_MEMORY; } r->out.sids->sids = talloc_zero_array(r->out.sids, struct lsa_TranslatedSid3, r->in.num_names); if (r->out.sids->sids == NULL) { return NT_STATUS_NO_MEMORY; } state->items = talloc_zero_array(state, struct dcesrv_lsa_TranslatedItem, r->in.num_names); if (state->items == NULL) { return NT_STATUS_NO_MEMORY; } for (i=0;iin.num_names;i++) { struct dcesrv_lsa_TranslatedItem *item = &state->items[i]; char *p = NULL; item->type = SID_NAME_UNKNOWN; item->name = r->in.names[i].string; /* * Note: that item->name can be NULL! * * See test_LookupNames_NULL() in * source4/torture/rpc/lsa.c * * nt4 returns NT_STATUS_NONE_MAPPED with sid_type * SID_NAME_UNKNOWN, rid 0, and sid_index -1; * * w2k3/w2k8 return NT_STATUS_OK with sid_type * SID_NAME_DOMAIN, rid -1 and sid_index 0 and BUILTIN domain */ if (item->name == NULL) { continue; } item->hints.principal = item->name; p = strchr(item->name, '\\'); if (p != NULL && p != item->name) { item->hints.domain = talloc_strndup(state->items, item->name, p - item->name); if (item->hints.domain == NULL) { return NT_STATUS_NO_MEMORY; } item->hints.namespace = item->hints.domain; p++; if (p[0] == '\0') { /* * This is just 'BUILTIN\'. */ item->hints.principal = NULL; } else { item->hints.principal = p; } } if (item->hints.domain == NULL) { p = strchr(item->name, '@'); if (p != NULL && p != item->name && p[1] != '\0') { item->hints.namespace = p + 1; } } } for (v=0; v < state->view_table->count; v++) { const struct dcesrv_lsa_Lookup_view *view = state->view_table->array[v]; for (i=0; i < r->in.num_names; i++) { struct dcesrv_lsa_TranslatedItem *item = &state->items[i]; NTSTATUS status; if (item->done) { continue; } status = view->lookup_name(state, item); if (NT_STATUS_IS_OK(status)) { item->done = true; } else if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) { status = NT_STATUS_OK; } else if (NT_STATUS_EQUAL(status, NT_STATUS_SOME_NOT_MAPPED)) { status = NT_STATUS_OK; } if (!NT_STATUS_IS_OK(status)) { return status; } } } if (state->wb.irpc_handle == NULL) { return dcesrv_lsa_LookupNames_base_finish(state); } state->wb.names = talloc_zero_array(state, struct lsa_String, r->in.num_names); if (state->wb.names == NULL) { return NT_STATUS_NO_MEMORY; } for (i=0;iin.num_names;i++) { struct dcesrv_lsa_TranslatedItem *item = &state->items[i]; if (item->done) { continue; } item->wb_idx = state->wb.num_names; state->wb.names[item->wb_idx] = r->in.names[i]; state->wb.num_names++; } subreq = dcerpc_lsa_LookupNames4_send(state, state->dce_call->event_ctx, state->wb.irpc_handle, state->wb.num_names, state->wb.names, &state->wb.domains, &state->wb.sids, state->r.in.level, &state->wb.count, state->r.in.lookup_options, state->r.in.client_revision); if (subreq == NULL) { return NT_STATUS_NO_MEMORY; } state->dce_call->state_flags |= DCESRV_CALL_STATE_FLAG_ASYNC; tevent_req_set_callback(subreq, dcesrv_lsa_LookupNames_base_done, state); return NT_STATUS_OK; } static NTSTATUS dcesrv_lsa_LookupNames_base_finish( struct dcesrv_lsa_LookupNames_base_state *state) { struct lsa_LookupNames4 *r = &state->r; uint32_t i; for (i=0;iin.num_names;i++) { struct dcesrv_lsa_TranslatedItem *item = &state->items[i]; NTSTATUS status; uint32_t sid_index = UINT32_MAX; status = dcesrv_lsa_authority_list(item->authority_name, item->authority_sid, *r->out.domains, &sid_index); if (!NT_STATUS_IS_OK(status)) { return status; } r->out.sids->sids[i].sid_type = item->type; r->out.sids->sids[i].sid = discard_const_p(struct dom_sid, item->sid); r->out.sids->sids[i].sid_index = sid_index; r->out.sids->sids[i].flags = item->flags; r->out.sids->count++; if (item->type != SID_NAME_UNKNOWN) { (*r->out.count)++; } } if (*r->out.count == 0) { return NT_STATUS_NONE_MAPPED; } if (*r->out.count != r->in.num_names) { return STATUS_SOME_UNMAPPED; } return NT_STATUS_OK; } static void dcesrv_lsa_LookupNames_base_map( struct dcesrv_lsa_LookupNames_base_state *state) { if (state->_r.l4 != NULL) { struct lsa_LookupNames4 *r = state->_r.l4; r->out.result = state->r.out.result; return; } if (state->_r.l3 != NULL) { struct lsa_LookupNames3 *r = state->_r.l3; r->out.result = state->r.out.result; return; } if (state->_r.l2 != NULL) { struct lsa_LookupNames2 *r = state->_r.l2; uint32_t i; r->out.result = state->r.out.result; SMB_ASSERT(state->r.out.sids->count <= r->in.num_names); for (i = 0; i < state->r.out.sids->count; i++) { const struct lsa_TranslatedSid3 *s3 = &state->r.out.sids->sids[i]; struct lsa_TranslatedSid2 *s2 = &r->out.sids->sids[i]; s2->sid_type = s3->sid_type; if (s3->sid_type == SID_NAME_DOMAIN) { s2->rid = UINT32_MAX; } else if (s3->flags & 0x00000004) { s2->rid = UINT32_MAX; } else if (s3->sid == NULL) { /* * MS-LSAT 3.1.4.7 - rid zero is considered * equivalent to sid NULL - so we should return * 0 rid for unmapped entries */ s2->rid = 0; } else { s2->rid = 0; dom_sid_split_rid(NULL, s3->sid, NULL, &s2->rid); } s2->sid_index = s3->sid_index; s2->unknown = s3->flags; } r->out.sids->count = state->r.out.sids->count; return; } if (state->_r.l != NULL) { struct lsa_LookupNames *r = state->_r.l; uint32_t i; r->out.result = state->r.out.result; SMB_ASSERT(state->r.out.sids->count <= r->in.num_names); for (i = 0; i < state->r.out.sids->count; i++) { struct lsa_TranslatedSid3 *s3 = &state->r.out.sids->sids[i]; struct lsa_TranslatedSid *s = &r->out.sids->sids[i]; s->sid_type = s3->sid_type; if (s3->sid_type == SID_NAME_DOMAIN) { s->rid = UINT32_MAX; } else if (s3->flags & 0x00000004) { s->rid = UINT32_MAX; } else if (s3->sid == NULL) { /* * MS-LSAT 3.1.4.7 - rid zero is considered * equivalent to sid NULL - so we should return * 0 rid for unmapped entries */ s->rid = 0; } else { s->rid = 0; dom_sid_split_rid(NULL, s3->sid, NULL, &s->rid); } s->sid_index = s3->sid_index; } r->out.sids->count = state->r.out.sids->count; return; } } static void dcesrv_lsa_LookupNames_base_done(struct tevent_req *subreq) { struct dcesrv_lsa_LookupNames_base_state *state = tevent_req_callback_data(subreq, struct dcesrv_lsa_LookupNames_base_state); struct dcesrv_call_state *dce_call = state->dce_call; NTSTATUS status; uint32_t i; status = dcerpc_lsa_LookupNames4_recv(subreq, state->mem_ctx, &state->wb.result); TALLOC_FREE(subreq); TALLOC_FREE(state->wb.irpc_handle); if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) { DEBUG(0,(__location__ ": IRPC callback failed %s\n", nt_errstr(status))); goto finished; } else if (!NT_STATUS_IS_OK(status)) { state->dce_call->fault_code = DCERPC_FAULT_CANT_PERFORM; DEBUG(0,(__location__ ": IRPC callback failed %s\n", nt_errstr(status))); goto finished; } status = state->wb.result; if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) { status = NT_STATUS_OK; } else if (NT_STATUS_EQUAL(status, NT_STATUS_SOME_NOT_MAPPED)) { status = NT_STATUS_OK; } if (!NT_STATUS_IS_OK(status)) { goto finished; } for (i=0; i < state->r.in.num_names;i++) { struct dcesrv_lsa_TranslatedItem *item = &state->items[i]; struct lsa_TranslatedSid3 *s3 = NULL; struct lsa_DomainInfo *d = NULL; if (item->done) { continue; } if (item->wb_idx >= state->wb.sids.count) { status = NT_STATUS_INTERNAL_ERROR; goto finished; } s3 = &state->wb.sids.sids[item->wb_idx]; item->type = s3->sid_type; item->sid = s3->sid; item->flags = s3->flags; if (s3->sid_index == UINT32_MAX) { continue; } if (state->wb.domains == NULL) { status = NT_STATUS_INTERNAL_ERROR; goto finished; } if (s3->sid_index >= state->wb.domains->count) { status = NT_STATUS_INTERNAL_ERROR; goto finished; } d = &state->wb.domains->domains[s3->sid_index]; item->authority_name = d->name.string; item->authority_sid = d->sid; } status = dcesrv_lsa_LookupNames_base_finish(state); finished: state->r.out.result = status; dcesrv_lsa_LookupNames_base_map(state); dcesrv_async_reply(dce_call); } /* lsa_LookupNames3 */ NTSTATUS dcesrv_lsa_LookupNames3(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct lsa_LookupNames3 *r) { enum dcerpc_transport_t transport = dcerpc_binding_get_transport(dce_call->conn->endpoint->ep_description); struct dcesrv_lsa_LookupNames_base_state *state = NULL; struct dcesrv_handle *policy_handle = NULL; NTSTATUS status; if (transport != NCACN_NP && transport != NCALRPC) { DCESRV_FAULT(DCERPC_FAULT_ACCESS_DENIED); } DCESRV_PULL_HANDLE(policy_handle, r->in.handle, LSA_HANDLE_POLICY); *r->out.domains = NULL; r->out.sids->count = 0; r->out.sids->sids = NULL; *r->out.count = 0; state = talloc_zero(mem_ctx, struct dcesrv_lsa_LookupNames_base_state); if (state == NULL) { return NT_STATUS_NO_MEMORY; } state->dce_call = dce_call; state->mem_ctx = mem_ctx; state->policy_state = policy_handle->data; state->r.in.num_names = r->in.num_names; state->r.in.names = r->in.names; state->r.in.level = r->in.level; state->r.in.lookup_options = r->in.lookup_options; state->r.in.client_revision = r->in.client_revision; state->r.in.sids = r->in.sids; state->r.in.count = r->in.count; state->r.out.domains = r->out.domains; state->r.out.sids = r->out.sids; state->r.out.count = r->out.count; state->_r.l3 = r; status = dcesrv_lsa_LookupNames_base_call(state); if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) { return status; } state->r.out.result = status; dcesrv_lsa_LookupNames_base_map(state); return status; } /* lsa_LookupNames4 Identical to LookupNames3, but doesn't take a policy handle */ NTSTATUS dcesrv_lsa_LookupNames4(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct lsa_LookupNames4 *r) { struct dcesrv_lsa_LookupNames_base_state *state = NULL; NTSTATUS status; *r->out.domains = NULL; r->out.sids->count = 0; r->out.sids->sids = NULL; *r->out.count = 0; state = talloc_zero(mem_ctx, struct dcesrv_lsa_LookupNames_base_state); if (state == NULL) { return NT_STATUS_NO_MEMORY; } state->dce_call = dce_call; state->mem_ctx = mem_ctx; /* * We don't have a policy handle on this call, so we want to * make a policy state and cache it for the life of the * connection, to avoid re-opening the DB each call. * * This also enforces that this is only available over * ncacn_ip_tcp and with SCHANNEL. * * schannel_call_setup may also set the fault state. * * state->policy_state is shared between all calls on this * connection and is moved with talloc_steal() under the * connection, not dce_call or state. */ status = schannel_call_setup(dce_call, &state->policy_state); if (!NT_STATUS_IS_OK(status)) { return status; } state->r.in.num_names = r->in.num_names; state->r.in.names = r->in.names; state->r.in.level = r->in.level; state->r.in.lookup_options = r->in.lookup_options; state->r.in.client_revision = r->in.client_revision; state->r.in.sids = r->in.sids; state->r.in.count = r->in.count; state->r.out.domains = r->out.domains; state->r.out.sids = r->out.sids; state->r.out.count = r->out.count; state->_r.l4 = r; status = dcesrv_lsa_LookupNames_base_call(state); if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) { return status; } state->r.out.result = status; dcesrv_lsa_LookupNames_base_map(state); return status; } /* lsa_LookupNames2 */ NTSTATUS dcesrv_lsa_LookupNames2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct lsa_LookupNames2 *r) { enum dcerpc_transport_t transport = dcerpc_binding_get_transport(dce_call->conn->endpoint->ep_description); struct dcesrv_lsa_LookupNames_base_state *state = NULL; struct dcesrv_handle *policy_handle = NULL; NTSTATUS status; if (transport != NCACN_NP && transport != NCALRPC) { DCESRV_FAULT(DCERPC_FAULT_ACCESS_DENIED); } DCESRV_PULL_HANDLE(policy_handle, r->in.handle, LSA_HANDLE_POLICY); *r->out.domains = NULL; r->out.sids->count = 0; r->out.sids->sids = NULL; *r->out.count = 0; r->out.sids->sids = talloc_zero_array(r->out.sids, struct lsa_TranslatedSid2, r->in.num_names); if (r->out.sids->sids == NULL) { return NT_STATUS_NO_MEMORY; } state = talloc_zero(mem_ctx, struct dcesrv_lsa_LookupNames_base_state); if (state == NULL) { return NT_STATUS_NO_MEMORY; } state->dce_call = dce_call; state->mem_ctx = mem_ctx; state->policy_state = policy_handle->data; state->r.in.num_names = r->in.num_names; state->r.in.names = r->in.names; state->r.in.level = r->in.level; /* * MS-LSAT 3.1.4.7: * * The LookupOptions and ClientRevision parameters MUST be ignored. * Message processing MUST happen as if LookupOptions is set to * 0x00000000 and ClientRevision is set to 0x00000002. */ state->r.in.lookup_options = LSA_LOOKUP_OPTION_SEARCH_ISOLATED_NAMES; state->r.in.client_revision = LSA_CLIENT_REVISION_2; state->r.in.sids = talloc_zero(state, struct lsa_TransSidArray3); if (state->r.in.sids == NULL) { return NT_STATUS_NO_MEMORY; } state->r.in.count = r->in.count; state->r.out.domains = r->out.domains; state->r.out.sids = talloc_zero(state, struct lsa_TransSidArray3); if (state->r.out.sids == NULL) { return NT_STATUS_NO_MEMORY; } state->r.out.count = r->out.count; state->_r.l2 = r; status = dcesrv_lsa_LookupNames_base_call(state); if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) { return status; } state->r.out.result = status; dcesrv_lsa_LookupNames_base_map(state); return status; } /* lsa_LookupNames */ NTSTATUS dcesrv_lsa_LookupNames(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct lsa_LookupNames *r) { enum dcerpc_transport_t transport = dcerpc_binding_get_transport(dce_call->conn->endpoint->ep_description); struct dcesrv_lsa_LookupNames_base_state *state = NULL; struct dcesrv_handle *policy_handle = NULL; NTSTATUS status; if (transport != NCACN_NP && transport != NCALRPC) { DCESRV_FAULT(DCERPC_FAULT_ACCESS_DENIED); } DCESRV_PULL_HANDLE(policy_handle, r->in.handle, LSA_HANDLE_POLICY); *r->out.domains = NULL; r->out.sids->count = 0; r->out.sids->sids = NULL; *r->out.count = 0; r->out.sids->sids = talloc_zero_array(r->out.sids, struct lsa_TranslatedSid, r->in.num_names); if (r->out.sids->sids == NULL) { return NT_STATUS_NO_MEMORY; } state = talloc_zero(mem_ctx, struct dcesrv_lsa_LookupNames_base_state); if (state == NULL) { return NT_STATUS_NO_MEMORY; } state->dce_call = dce_call; state->mem_ctx = mem_ctx; state->policy_state = policy_handle->data; state->r.in.num_names = r->in.num_names; state->r.in.names = r->in.names; state->r.in.level = r->in.level; state->r.in.lookup_options = LSA_LOOKUP_OPTION_SEARCH_ISOLATED_NAMES; state->r.in.client_revision = LSA_CLIENT_REVISION_1; state->r.in.sids = talloc_zero(state, struct lsa_TransSidArray3); if (state->r.in.sids == NULL) { return NT_STATUS_NO_MEMORY; } state->r.in.count = r->in.count; state->r.out.domains = r->out.domains; state->r.out.sids = talloc_zero(state, struct lsa_TransSidArray3); if (state->r.out.sids == NULL) { return NT_STATUS_NO_MEMORY; } state->r.out.count = r->out.count; state->_r.l = r; status = dcesrv_lsa_LookupNames_base_call(state); if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) { return status; } state->r.out.result = status; dcesrv_lsa_LookupNames_base_map(state); return status; } static NTSTATUS dcesrv_lsa_lookup_name_predefined( struct dcesrv_lsa_LookupNames_base_state *state, struct dcesrv_lsa_TranslatedItem *item) { NTSTATUS status; status = dom_sid_lookup_predefined_name(item->name, &item->sid, &item->type, &item->authority_sid, &item->authority_name); if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) { return status; } if (!NT_STATUS_IS_OK(status)) { return status; } return NT_STATUS_OK; } static NTSTATUS dcesrv_lsa_lookup_sid_predefined( struct dcesrv_lsa_LookupSids_base_state *state, struct dcesrv_lsa_TranslatedItem *item) { NTSTATUS status; status = dom_sid_lookup_predefined_sid(item->sid, &item->name, &item->type, &item->authority_sid, &item->authority_name); if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) { return status; } if (!NT_STATUS_IS_OK(status)) { return status; } return NT_STATUS_OK; } static const struct dcesrv_lsa_Lookup_view view_predefined = { .name = "Predefined", .lookup_sid = dcesrv_lsa_lookup_sid_predefined, .lookup_name = dcesrv_lsa_lookup_name_predefined, }; static NTSTATUS dcesrv_lsa_lookup_name_builtin( struct dcesrv_lsa_LookupNames_base_state *state, struct dcesrv_lsa_TranslatedItem *item) { struct lsa_policy_state *policy_state = state->policy_state; NTSTATUS status; bool is_builtin = false; if (item->name == NULL) { /* * This should not be mapped. */ return NT_STATUS_OK; } /* * The predefined view already handled the BUILTIN domain. * * Now we just need to find the principal. * * We only allow 'BUILTIN\something' and * not 'something@BUILTIN. * * And we try out best for just 'something'. */ is_builtin = strequal(item->hints.domain, NAME_BUILTIN); if (!is_builtin && item->hints.domain != NULL) { return NT_STATUS_NONE_MAPPED; } status = dcesrv_lsa_lookup_name(state->policy_state, state->mem_ctx, NAME_BUILTIN, policy_state->builtin_sid, policy_state->builtin_dn, item->hints.principal, &item->sid, &item->type); if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) { if (!is_builtin) { return NT_STATUS_NONE_MAPPED; } /* * We know we're authoritative */ status = NT_STATUS_OK; } if (!NT_STATUS_IS_OK(status)) { return status; } item->authority_name = NAME_BUILTIN; item->authority_sid = policy_state->builtin_sid; return NT_STATUS_OK; } static NTSTATUS dcesrv_lsa_lookup_sid_builtin( struct dcesrv_lsa_LookupSids_base_state *state, struct dcesrv_lsa_TranslatedItem *item) { struct lsa_policy_state *policy_state = state->policy_state; NTSTATUS status; bool is_builtin = false; /* * The predefined view already handled the BUILTIN domain. * * Now we just need to find the principal. */ is_builtin = dom_sid_in_domain(policy_state->builtin_sid, item->sid); if (!is_builtin) { return NT_STATUS_NONE_MAPPED; } status = dcesrv_lsa_lookup_sid(state->policy_state, state->mem_ctx, NAME_BUILTIN, policy_state->builtin_sid, policy_state->builtin_dn, item->sid, &item->name, &item->type); if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) { /* * We know we're authoritative */ status = NT_STATUS_OK; } if (!NT_STATUS_IS_OK(status)) { return status; } item->authority_name = NAME_BUILTIN; item->authority_sid = policy_state->builtin_sid; return NT_STATUS_OK; } static const struct dcesrv_lsa_Lookup_view view_builtin = { .name = "Builtin", .lookup_sid = dcesrv_lsa_lookup_sid_builtin, .lookup_name = dcesrv_lsa_lookup_name_builtin, }; static NTSTATUS dcesrv_lsa_lookup_name_account( struct dcesrv_lsa_LookupNames_base_state *state, struct dcesrv_lsa_TranslatedItem *item) { struct lsa_policy_state *policy_state = state->policy_state; struct loadparm_context *lp_ctx = state->dce_call->conn->dce_ctx->lp_ctx; struct lsa_LookupNames4 *r = &state->r; NTSTATUS status; int role; bool (*is_local_match_fn)(struct loadparm_context *, const char *) = NULL; bool is_domain = false; bool try_lookup = false; const char *check_domain_name = NULL; role = lpcfg_server_role(lp_ctx); if (role == ROLE_ACTIVE_DIRECTORY_DC) { is_local_match_fn = lpcfg_is_my_domain_or_realm; } else { is_local_match_fn = lpcfg_is_myname; } if (item->name == NULL) { /* * This should not be mapped. */ return NT_STATUS_OK; } if (item->hints.domain != NULL && item->hints.principal == NULL) { /* * This is 'DOMAIN\'. */ check_domain_name = item->hints.domain; } else { /* * This is just 'DOMAIN'. */ check_domain_name = item->name; } is_domain = is_local_match_fn(lp_ctx, check_domain_name); if (is_domain) { item->type = SID_NAME_DOMAIN; item->sid = policy_state->domain_sid; item->authority_name = policy_state->domain_name; item->authority_sid = policy_state->domain_sid; return NT_STATUS_OK; } if (r->in.lookup_options & LSA_LOOKUP_OPTION_SEARCH_ISOLATED_NAMES_LOCAL) { if (item->hints.domain != item->hints.namespace) { /* * This means the client asked for an UPN, * and it should not be mapped. */ return NT_STATUS_OK; } } if (item->hints.namespace != NULL) { is_domain = is_local_match_fn(lp_ctx, item->hints.namespace); try_lookup = is_domain; } else { try_lookup = true; } if (!try_lookup) { struct dcesrv_lsa_TranslatedItem tmp; tmp = *item; status = dom_sid_lookup_predefined_name(item->hints.namespace, &tmp.sid, &tmp.type, &tmp.authority_sid, &tmp.authority_name); if (NT_STATUS_IS_OK(status)) { /* * It should not be handled by us. */ return NT_STATUS_NONE_MAPPED; } if (!NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) { return status; } } if (!try_lookup) { const struct lsa_TrustDomainInfoInfoEx *tdo = NULL; const struct lsa_ForestTrustDomainInfo *di = NULL; if (state->routing_table == NULL) { status = dsdb_trust_routing_table_load(policy_state->sam_ldb, state, &state->routing_table); if (!NT_STATUS_IS_OK(status)) { return status; } } tdo = dsdb_trust_domain_by_name(state->routing_table, item->hints.namespace, &di); if (tdo == NULL) { /* * The name is not resolvable at all... */ return NT_STATUS_OK; } if (!(tdo->trust_attributes & LSA_TRUST_ATTRIBUTE_WITHIN_FOREST)) { /* * The name is not resolvable here */ return NT_STATUS_NONE_MAPPED; } /* * TODO: handle multiple domains in a forest together with * LSA_LOOKUP_NAMES_PRIMARY_DOMAIN_ONLY */ is_domain = true; try_lookup = true; } if (!try_lookup) { /* * It should not be handled by us. */ return NT_STATUS_NONE_MAPPED; } /* * TODO: handle multiple domains in our forest. */ status = dcesrv_lsa_lookup_name(state->policy_state, state->mem_ctx, policy_state->domain_name, policy_state->domain_sid, policy_state->domain_dn, item->hints.principal, &item->sid, &item->type); if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) { if (!is_domain) { return NT_STATUS_NONE_MAPPED; } /* * We know we're authoritative */ status = NT_STATUS_OK; } if (!NT_STATUS_IS_OK(status)) { return status; } item->authority_name = policy_state->domain_name; item->authority_sid = policy_state->domain_sid; return NT_STATUS_OK; } static NTSTATUS dcesrv_lsa_lookup_sid_account( struct dcesrv_lsa_LookupSids_base_state *state, struct dcesrv_lsa_TranslatedItem *item) { struct lsa_policy_state *policy_state = state->policy_state; NTSTATUS status; bool is_domain; is_domain = dom_sid_equal(policy_state->domain_sid, item->sid); if (is_domain) { item->type = SID_NAME_DOMAIN; item->name = policy_state->domain_name; item->authority_name = policy_state->domain_name; item->authority_sid = policy_state->domain_sid; return NT_STATUS_OK; } is_domain = dom_sid_in_domain(policy_state->domain_sid, item->sid); if (!is_domain) { return NT_STATUS_NONE_MAPPED; } status = dcesrv_lsa_lookup_sid(state->policy_state, state->mem_ctx, policy_state->domain_name, policy_state->domain_sid, policy_state->domain_dn, item->sid, &item->name, &item->type); if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) { /* * We know we're authoritative */ status = NT_STATUS_OK; } if (!NT_STATUS_IS_OK(status)) { return status; } item->authority_name = policy_state->domain_name; item->authority_sid = policy_state->domain_sid; return NT_STATUS_OK; } static const struct dcesrv_lsa_Lookup_view view_account = { .name = "Account", .lookup_sid = dcesrv_lsa_lookup_sid_account, .lookup_name = dcesrv_lsa_lookup_name_account, }; static NTSTATUS dcesrv_lsa_lookup_name_winbind( struct dcesrv_lsa_LookupNames_base_state *state, struct dcesrv_lsa_TranslatedItem *item) { struct lsa_LookupNames4 *r = &state->r; const struct lsa_TrustDomainInfoInfoEx *tdo = NULL; const struct lsa_ForestTrustDomainInfo *di = NULL; NTSTATUS status; const char *check_domain_name = NULL; bool expect_domain = false; struct imessaging_context *imsg_ctx = dcesrv_imessaging_context(state->dce_call->conn); if (item->name == NULL) { /* * This should not be mapped. */ return NT_STATUS_OK; } if (item->hints.domain != NULL && item->hints.principal == NULL) { /* * This is 'DOMAIN\'. */ check_domain_name = item->hints.domain; expect_domain = true; } else if (item->hints.namespace != NULL) { /* * This is 'DOMAIN\someone' * or 'someone@DOMAIN' */ check_domain_name = item->hints.namespace; } else { /* * This is just 'DOMAIN'. */ check_domain_name = item->name; expect_domain = true; } if (state->routing_table == NULL) { struct lsa_policy_state *policy_state = state->policy_state; status = dsdb_trust_routing_table_load(policy_state->sam_ldb, state, &state->routing_table); if (!NT_STATUS_IS_OK(status)) { return status; } } tdo = dsdb_trust_domain_by_name(state->routing_table, check_domain_name, &di); if (tdo == NULL) { /* * The name is not resolvable at all... * * And for now we don't send unqualified names * to winbindd, as we don't handle them * there yet. * * TODO: how should that work within * winbindd? */ return NT_STATUS_OK; } if (tdo->trust_attributes & LSA_TRUST_ATTRIBUTE_WITHIN_FOREST) { /* * The name should have been resolved in the account view. * * TODO: handle multiple domains in a forest... */ return NT_STATUS_OK; } if (expect_domain) { const char *name = NULL; const struct dom_sid *sid = NULL; name = talloc_strdup(state->mem_ctx, di->netbios_domain_name.string); if (name == NULL) { return NT_STATUS_NO_MEMORY; } sid = dom_sid_dup(state->mem_ctx, di->domain_sid); if (sid == NULL) { return NT_STATUS_NO_MEMORY; } item->type = SID_NAME_DOMAIN; item->sid = sid; item->authority_name = name; item->authority_sid = sid; return NT_STATUS_OK; } if (r->in.lookup_options & LSA_LOOKUP_OPTION_SEARCH_ISOLATED_NAMES_LOCAL) { if (item->hints.namespace == NULL) { /* * We should not try to resolve isolated names * remotely. */ return NT_STATUS_OK; } } /* * We know at least the domain part of the name exists. * * For now the rest handled within winbindd. * * In future we can optimize it based on * r->in.level. * * We can also try to resolve SID_NAME_DOMAIN * just based on the routing table. */ if (state->wb.irpc_handle != NULL) { /* * already called... */ return NT_STATUS_NONE_MAPPED; } state->wb.irpc_handle = irpc_binding_handle_by_name(state, imsg_ctx, "winbind_server", &ndr_table_lsarpc); if (state->wb.irpc_handle == NULL) { DEBUG(0,("Failed to get binding_handle for winbind_server task\n")); state->dce_call->fault_code = DCERPC_FAULT_CANT_PERFORM; return NT_STATUS_INVALID_SYSTEM_SERVICE; } /* * 60 seconds timeout should be enough */ dcerpc_binding_handle_set_timeout(state->wb.irpc_handle, 60); return NT_STATUS_NONE_MAPPED; } static NTSTATUS dcesrv_lsa_lookup_sid_winbind( struct dcesrv_lsa_LookupSids_base_state *state, struct dcesrv_lsa_TranslatedItem *item) { const struct lsa_TrustDomainInfoInfoEx *tdo = NULL; const struct lsa_ForestTrustDomainInfo *di = NULL; struct dcesrv_lsa_TranslatedItem tmp; struct dom_sid domain_sid = {0,}; NTSTATUS status; bool match; struct imessaging_context *imsg_ctx = dcesrv_imessaging_context(state->dce_call->conn); /* * Verify the sid is not INVALID. */ tmp = *item; status = dom_sid_lookup_predefined_sid(tmp.sid, &tmp.name, &tmp.type, &tmp.authority_sid, &tmp.authority_name); if (NT_STATUS_IS_OK(status)) { status = NT_STATUS_NONE_MAPPED; } if (!NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) { /* * Typically INVALID_SID */ return status; } if (state->routing_table == NULL) { struct lsa_policy_state *policy_state = state->policy_state; status = dsdb_trust_routing_table_load(policy_state->sam_ldb, state, &state->routing_table); if (!NT_STATUS_IS_OK(status)) { return status; } } domain_sid = *item->sid; if (domain_sid.num_auths == 5) { sid_split_rid(&domain_sid, NULL); } tdo = dsdb_trust_domain_by_sid(state->routing_table, &domain_sid, &di); if (tdo == NULL) { /* * The sid is not resolvable at all... */ return NT_STATUS_OK; } if (tdo->trust_attributes & LSA_TRUST_ATTRIBUTE_WITHIN_FOREST) { /* * The name should have been resolved in the account view. * * TODO: handle multiple domains in a forest... */ return NT_STATUS_OK; } match = dom_sid_equal(di->domain_sid, item->sid); if (match) { const char *name = NULL; name = talloc_strdup(state->mem_ctx, di->netbios_domain_name.string); if (name == NULL) { return NT_STATUS_NO_MEMORY; } item->type = SID_NAME_DOMAIN; item->name = name; item->authority_name = name; item->authority_sid = item->sid; return NT_STATUS_OK; } /* * We know at least the domain part of the sid exists. * * For now the rest handled within winbindd. * * In future we can optimize it based on * r->in.level. * * We can also try to resolve SID_NAME_DOMAIN * just based on the routing table. */ if (state->wb.irpc_handle != NULL) { /* * already called... */ return NT_STATUS_NONE_MAPPED; } state->wb.irpc_handle = irpc_binding_handle_by_name(state, imsg_ctx, "winbind_server", &ndr_table_lsarpc); if (state->wb.irpc_handle == NULL) { DEBUG(0,("Failed to get binding_handle for winbind_server task\n")); state->dce_call->fault_code = DCERPC_FAULT_CANT_PERFORM; return NT_STATUS_INVALID_SYSTEM_SERVICE; } /* * 60 seconds timeout should be enough */ dcerpc_binding_handle_set_timeout(state->wb.irpc_handle, 60); return NT_STATUS_NONE_MAPPED; } static const struct dcesrv_lsa_Lookup_view view_winbind = { .name = "Winbind", .lookup_sid = dcesrv_lsa_lookup_sid_winbind, .lookup_name = dcesrv_lsa_lookup_name_winbind, }; static const struct dcesrv_lsa_Lookup_view *table_all_views[] = { &view_predefined, &view_builtin, &view_account, &view_winbind, }; static const struct dcesrv_lsa_Lookup_view_table table_all = { .name = "LSA_LOOKUP_NAMES_ALL", .count = ARRAY_SIZE(table_all_views), .array = table_all_views, }; static const struct dcesrv_lsa_Lookup_view *table_domains_views[] = { &view_account, &view_winbind, }; static const struct dcesrv_lsa_Lookup_view_table table_domains = { .name = "LSA_LOOKUP_NAMES_DOMAINS_ONLY", .count = ARRAY_SIZE(table_domains_views), .array = table_domains_views, }; static const struct dcesrv_lsa_Lookup_view *table_primary_views[] = { &view_account, }; static const struct dcesrv_lsa_Lookup_view_table table_primary = { .name = "LSA_LOOKUP_NAMES_PRIMARY_DOMAIN_ONLY", .count = ARRAY_SIZE(table_primary_views), .array = table_primary_views, }; static const struct dcesrv_lsa_Lookup_view *table_remote_views[] = { &view_winbind, }; static const struct dcesrv_lsa_Lookup_view_table table_gc = { .name = "LSA_LOOKUP_NAMES_UPLEVEL_TRUSTS_ONLY", .count = ARRAY_SIZE(table_domains_views), .array = table_domains_views, }; static const struct dcesrv_lsa_Lookup_view_table table_xreferral = { .name = "LSA_LOOKUP_NAMES_FOREST_TRUSTS_ONLY", .count = ARRAY_SIZE(table_remote_views), .array = table_remote_views, }; static const struct dcesrv_lsa_Lookup_view_table table_xresolve = { .name = "LSA_LOOKUP_NAMES_UPLEVEL_TRUSTS_ONLY2", .count = ARRAY_SIZE(table_domains_views), .array = table_domains_views, }; static const struct dcesrv_lsa_Lookup_view_table table_rodc = { .name = "LSA_LOOKUP_NAMES_RODC_REFERRAL_TO_FULL_DC", .count = ARRAY_SIZE(table_remote_views), .array = table_remote_views, }; static const struct dcesrv_lsa_Lookup_view_table *dcesrv_lsa_view_table( enum lsa_LookupNamesLevel level) { switch (level) { case LSA_LOOKUP_NAMES_ALL: return &table_all; case LSA_LOOKUP_NAMES_DOMAINS_ONLY: return &table_domains; case LSA_LOOKUP_NAMES_PRIMARY_DOMAIN_ONLY: return &table_primary; case LSA_LOOKUP_NAMES_UPLEVEL_TRUSTS_ONLY: return &table_gc; case LSA_LOOKUP_NAMES_FOREST_TRUSTS_ONLY: return &table_xreferral; case LSA_LOOKUP_NAMES_UPLEVEL_TRUSTS_ONLY2: return &table_xresolve; case LSA_LOOKUP_NAMES_RODC_REFERRAL_TO_FULL_DC: return &table_rodc; } return NULL; }