1
0
mirror of https://github.com/samba-team/samba.git synced 2025-01-11 05:18:09 +03:00

s4:rpc_server/lsa: implement forwarding lsa_Lookup{Sids,Names}() requests to winbindd

This might not be perfect yet, but it's enough to allow names from trusted
forests/domain to be resolved, which is very important for samba based
domain members.

Bug: https://bugzilla.samba.org/show_bug.cgi?id=13286

Signed-off-by: Stefan Metzmacher <metze@samba.org>
Reviewed-by: Ralph Boehme <slow@samba.org>
This commit is contained in:
Stefan Metzmacher 2018-01-19 13:42:40 +01:00 committed by Ralph Boehme
parent 3ffebee3de
commit e9d5b8b6b4
2 changed files with 525 additions and 4 deletions

View File

@ -1,2 +0,0 @@
^samba4.blackbox.trust_ntlm.Test07.rpcclient.lookupnames.with.ADDOMAIN.*fl20
^samba4.blackbox.trust_ntlm.Test08.rpcclient.lookupnames.with.ADDOM.SAMBA.EXAMPLE.COM.*fl20

View File

@ -23,6 +23,8 @@
#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;
@ -31,6 +33,7 @@ struct dcesrv_lsa_TranslatedItem {
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\ */
@ -280,6 +283,15 @@ struct dcesrv_lsa_LookupSids_base_state {
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;
@ -291,10 +303,12 @@ 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;
@ -377,7 +391,47 @@ static NTSTATUS dcesrv_lsa_LookupSids_base_call(struct dcesrv_lsa_LookupSids_bas
}
}
return dcesrv_lsa_LookupSids_base_finish(state);
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(
@ -467,6 +521,91 @@ static void dcesrv_lsa_LookupSids_base_map(
}
}
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);
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);
TALLOC_FREE(state);
status = dcesrv_reply(dce_call);
if (!NT_STATUS_IS_OK(status)) {
DEBUG(0,(__location__ ": dcesrv_reply() failed - %s\n", nt_errstr(status)));
}
}
/*
lsa_LookupSids2
*/
@ -684,6 +823,16 @@ struct dcesrv_lsa_LookupNames_base_state {
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;
@ -696,11 +845,13 @@ 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;
@ -817,7 +968,48 @@ static NTSTATUS dcesrv_lsa_LookupNames_base_call(struct dcesrv_lsa_LookupNames_b
}
}
return dcesrv_lsa_LookupNames_base_finish(state);
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;i<r->in.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(
@ -952,6 +1144,91 @@ static void dcesrv_lsa_LookupNames_base_map(
}
}
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);
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);
TALLOC_FREE(state);
status = dcesrv_reply(dce_call);
if (!NT_STATUS_IS_OK(status)) {
DEBUG(0,(__location__ ": dcesrv_reply() failed - %s\n", nt_errstr(status)));
}
}
/*
lsa_LookupNames3
*/
@ -1599,6 +1876,142 @@ 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;
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,
state->dce_call->msg_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;
}
@ -1606,6 +2019,116 @@ 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;
/*
* 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,
state->dce_call->msg_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;
}