1
0
mirror of https://github.com/samba-team/samba.git synced 2025-01-08 21:18:16 +03:00
samba-mirror/source4/rpc_server/lsa/lsa_lookup.c
Andreas Schneider a49eb60e04 s4-lsarpc: DCERPC_FAULT_ACCESS_DENIED for tcp
Autobuild-User(master): Andreas Schneider <asn@cryptomilk.org>
Autobuild-Date(master): Fri Jul  6 11:50:40 CEST 2012 on sn-devel-104
2012-07-06 11:50:40 +02:00

1095 lines
28 KiB
C

/*
Unix SMB/CIFS implementation.
endpoint server for the lsarpc pipe
Copyright (C) Andrew Tridgell 2004
Copyright (C) Andrew Bartlett <abartlet@samba.org> 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 <http://www.gnu.org/licenses/>.
*/
#include "rpc_server/lsa/lsa.h"
#include "libds/common/flag_mapping.h"
static const struct {
const char *domain;
const char *name;
const char *sid;
enum lsa_SidType rtype;
} well_known[] = {
{
.name = "EVERYONE",
.sid = SID_WORLD,
.rtype = SID_NAME_WKN_GRP,
},
{
.name = "CREATOR OWNER",
.sid = SID_CREATOR_OWNER,
.rtype = SID_NAME_WKN_GRP,
},
{
.name = "CREATOR GROUP",
.sid = SID_CREATOR_GROUP,
.rtype = SID_NAME_WKN_GRP,
},
{
.name = "Owner Rights",
.sid = SID_OWNER_RIGHTS,
.rtype = SID_NAME_WKN_GRP,
},
{
.domain = "NT AUTHORITY",
.name = "Dialup",
.sid = SID_NT_DIALUP,
.rtype = SID_NAME_WKN_GRP,
},
{
.domain = "NT AUTHORITY",
.name = "Network",
.sid = SID_NT_NETWORK,
.rtype = SID_NAME_WKN_GRP,
},
{
.domain = "NT AUTHORITY",
.name = "Batch",
.sid = SID_NT_BATCH,
.rtype = SID_NAME_WKN_GRP,
},
{
.domain = "NT AUTHORITY",
.name = "Interactive",
.sid = SID_NT_INTERACTIVE,
.rtype = SID_NAME_WKN_GRP,
},
{
.domain = "NT AUTHORITY",
.name = "Service",
.sid = SID_NT_SERVICE,
.rtype = SID_NAME_WKN_GRP,
},
{
.domain = "NT AUTHORITY",
.name = "ANONYMOUS LOGON",
.sid = SID_NT_ANONYMOUS,
.rtype = SID_NAME_WKN_GRP,
},
{
.domain = "NT AUTHORITY",
.name = "Proxy",
.sid = SID_NT_PROXY,
.rtype = SID_NAME_WKN_GRP,
},
{
.domain = "NT AUTHORITY",
.name = "ServerLogon",
.sid = SID_NT_ENTERPRISE_DCS,
.rtype = SID_NAME_WKN_GRP,
},
{
.domain = "NT AUTHORITY",
.name = "Self",
.sid = SID_NT_SELF,
.rtype = SID_NAME_WKN_GRP,
},
{
.domain = "NT AUTHORITY",
.name = "Authenticated Users",
.sid = SID_NT_AUTHENTICATED_USERS,
.rtype = SID_NAME_WKN_GRP,
},
{
.domain = "NT AUTHORITY",
.name = "Restricted",
.sid = SID_NT_RESTRICTED,
.rtype = SID_NAME_WKN_GRP,
},
{
.domain = "NT AUTHORITY",
.name = "Terminal Server User",
.sid = SID_NT_TERMINAL_SERVER_USERS,
.rtype = SID_NAME_WKN_GRP,
},
{
.domain = "NT AUTHORITY",
.name = "Remote Interactive Logon",
.sid = SID_NT_REMOTE_INTERACTIVE,
.rtype = SID_NAME_WKN_GRP,
},
{
.domain = "NT AUTHORITY",
.name = "This Organization",
.sid = SID_NT_THIS_ORGANISATION,
.rtype = SID_NAME_WKN_GRP,
},
{
.domain = "NT AUTHORITY",
.name = "SYSTEM",
.sid = SID_NT_SYSTEM,
.rtype = SID_NAME_WKN_GRP,
},
{
.domain = "NT AUTHORITY",
.name = "Local Service",
.sid = SID_NT_LOCAL_SERVICE,
.rtype = SID_NAME_WKN_GRP,
},
{
.domain = "NT AUTHORITY",
.name = "Network Service",
.sid = SID_NT_NETWORK_SERVICE,
.rtype = SID_NAME_WKN_GRP,
},
{
.domain = "NT AUTHORITY",
.name = "Digest Authentication",
.sid = SID_NT_DIGEST_AUTHENTICATION,
.rtype = SID_NAME_WKN_GRP,
},
{
.domain = "NT AUTHORITY",
.name = "Enterprise Domain Controllers",
.sid = SID_NT_ENTERPRISE_DCS,
.rtype = SID_NAME_WKN_GRP,
},
{
.domain = "NT AUTHORITY",
.name = "NTLM Authentication",
.sid = SID_NT_NTLM_AUTHENTICATION,
.rtype = SID_NAME_WKN_GRP,
},
{
.domain = "NT AUTHORITY",
.name = "Other Organization",
.sid = SID_NT_OTHER_ORGANISATION,
.rtype = SID_NAME_WKN_GRP,
},
{
.domain = "NT AUTHORITY",
.name = "SChannel Authentication",
.sid = SID_NT_SCHANNEL_AUTHENTICATION,
.rtype = SID_NAME_WKN_GRP,
},
{
.domain = "NT AUTHORITY",
.name = "IUSR",
.sid = SID_NT_IUSR,
.rtype = SID_NAME_WKN_GRP,
},
{
.sid = NULL,
}
};
static NTSTATUS lookup_well_known_names(TALLOC_CTX *mem_ctx, const char *domain,
const char *name, const char **authority_name,
struct dom_sid **sid, enum lsa_SidType *rtype)
{
unsigned int i;
for (i=0; well_known[i].sid; i++) {
if (domain) {
if (strcasecmp_m(domain, well_known[i].domain) == 0
&& strcasecmp_m(name, well_known[i].name) == 0) {
*authority_name = well_known[i].domain;
*sid = dom_sid_parse_talloc(mem_ctx, well_known[i].sid);
*rtype = well_known[i].rtype;
return NT_STATUS_OK;
}
} else {
if (strcasecmp_m(name, well_known[i].name) == 0) {
*authority_name = well_known[i].domain;
*sid = dom_sid_parse_talloc(mem_ctx, well_known[i].sid);
*rtype = well_known[i].rtype;
return NT_STATUS_OK;
}
}
}
return NT_STATUS_NOT_FOUND;
}
static NTSTATUS lookup_well_known_sids(TALLOC_CTX *mem_ctx,
const char *sid_str, const char **authority_name,
const char **name, enum lsa_SidType *rtype)
{
unsigned int i;
for (i=0; well_known[i].sid; i++) {
if (strcasecmp_m(sid_str, well_known[i].sid) == 0) {
*authority_name = well_known[i].domain;
*name = well_known[i].name;
*rtype = well_known[i].rtype;
return NT_STATUS_OK;
}
}
return NT_STATUS_NOT_FOUND;
}
/*
lookup a SID for 1 name
*/
static NTSTATUS dcesrv_lsa_lookup_name(struct tevent_context *ev_ctx,
struct loadparm_context *lp_ctx,
struct lsa_policy_state *state, TALLOC_CTX *mem_ctx,
const char *name, const char **authority_name,
struct dom_sid **sid, enum lsa_SidType *rtype,
uint32_t *rid)
{
int ret, i;
uint32_t atype;
struct ldb_message **res;
const char * const attrs[] = { "objectSid", "sAMAccountType", NULL};
const char *p;
const char *domain;
const char *username;
struct ldb_dn *domain_dn;
struct dom_sid *domain_sid;
NTSTATUS status;
p = strchr_m(name, '\\');
if (p != NULL) {
domain = talloc_strndup(mem_ctx, name, p-name);
if (!domain) {
return NT_STATUS_NO_MEMORY;
}
username = p + 1;
} else if (strchr_m(name, '@')) {
status = crack_name_to_nt4_name(mem_ctx, ev_ctx, lp_ctx, DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL, name, &domain, &username);
if (!NT_STATUS_IS_OK(status)) {
DEBUG(3, ("Failed to crack name %s into an NT4 name: %s\n", name, nt_errstr(status)));
return status;
}
} else {
domain = NULL;
username = name;
}
if (!domain) {
/* Look up table of well known names */
status = lookup_well_known_names(mem_ctx, NULL, username, authority_name, sid, rtype);
if (NT_STATUS_IS_OK(status)) {
dom_sid_split_rid(NULL, *sid, NULL, rid);
return NT_STATUS_OK;
}
if (username == NULL) {
*authority_name = NAME_BUILTIN;
*sid = dom_sid_parse_talloc(mem_ctx, SID_BUILTIN);
*rtype = SID_NAME_DOMAIN;
*rid = 0xFFFFFFFF;
return NT_STATUS_OK;
}
if (strcasecmp_m(username, NAME_NT_AUTHORITY) == 0) {
*authority_name = NAME_NT_AUTHORITY;
*sid = dom_sid_parse_talloc(mem_ctx, SID_NT_AUTHORITY);
*rtype = SID_NAME_DOMAIN;
dom_sid_split_rid(NULL, *sid, NULL, rid);
return NT_STATUS_OK;
}
if (strcasecmp_m(username, NAME_BUILTIN) == 0) {
*authority_name = NAME_BUILTIN;
*sid = dom_sid_parse_talloc(mem_ctx, SID_BUILTIN);
*rtype = SID_NAME_DOMAIN;
*rid = 0xFFFFFFFF;
return NT_STATUS_OK;
}
if (strcasecmp_m(username, state->domain_dns) == 0) {
*authority_name = state->domain_name;
*sid = state->domain_sid;
*rtype = SID_NAME_DOMAIN;
*rid = 0xFFFFFFFF;
return NT_STATUS_OK;
}
if (strcasecmp_m(username, state->domain_name) == 0) {
*authority_name = state->domain_name;
*sid = state->domain_sid;
*rtype = SID_NAME_DOMAIN;
*rid = 0xFFFFFFFF;
return NT_STATUS_OK;
}
/* Perhaps this is a well known user? */
name = talloc_asprintf(mem_ctx, "%s\\%s", NAME_NT_AUTHORITY, username);
if (!name) {
return NT_STATUS_NO_MEMORY;
}
status = dcesrv_lsa_lookup_name(ev_ctx, lp_ctx, state, mem_ctx, name, authority_name, sid, rtype, rid);
if (NT_STATUS_IS_OK(status)) {
return status;
}
/* Perhaps this is a BUILTIN user? */
name = talloc_asprintf(mem_ctx, "%s\\%s", NAME_BUILTIN, username);
if (!name) {
return NT_STATUS_NO_MEMORY;
}
status = dcesrv_lsa_lookup_name(ev_ctx, lp_ctx, state, mem_ctx, name, authority_name, sid, rtype, rid);
if (NT_STATUS_IS_OK(status)) {
return status;
}
/* OK, I give up - perhaps we need to assume the user is in our domain? */
name = talloc_asprintf(mem_ctx, "%s\\%s", state->domain_name, username);
if (!name) {
return NT_STATUS_NO_MEMORY;
}
status = dcesrv_lsa_lookup_name(ev_ctx, lp_ctx, state, mem_ctx, name, authority_name, sid, rtype, rid);
if (NT_STATUS_IS_OK(status)) {
return status;
}
return STATUS_SOME_UNMAPPED;
} else if (strcasecmp_m(domain, NAME_NT_AUTHORITY) == 0) {
if (!*username) {
*authority_name = NAME_NT_AUTHORITY;
*sid = dom_sid_parse_talloc(mem_ctx, SID_NT_AUTHORITY);
*rtype = SID_NAME_DOMAIN;
dom_sid_split_rid(NULL, *sid, NULL, rid);
return NT_STATUS_OK;
}
/* Look up table of well known names */
status = lookup_well_known_names(mem_ctx, domain, username, authority_name,
sid, rtype);
if (NT_STATUS_IS_OK(status)) {
dom_sid_split_rid(NULL, *sid, NULL, rid);
}
return status;
} else if (strcasecmp_m(domain, NAME_BUILTIN) == 0) {
*authority_name = NAME_BUILTIN;
domain_dn = state->builtin_dn;
} else if (strcasecmp_m(domain, state->domain_dns) == 0) {
*authority_name = state->domain_name;
domain_dn = state->domain_dn;
} else if (strcasecmp_m(domain, state->domain_name) == 0) {
*authority_name = state->domain_name;
domain_dn = state->domain_dn;
} else {
/* Not local, need to ask winbind in future */
return STATUS_SOME_UNMAPPED;
}
ret = gendb_search_dn(state->sam_ldb, mem_ctx, domain_dn, &res, attrs);
if (ret != 1) {
return NT_STATUS_INTERNAL_DB_CORRUPTION;
}
domain_sid = samdb_result_dom_sid(mem_ctx, res[0], "objectSid");
if (domain_sid == NULL) {
return NT_STATUS_INVALID_SID;
}
if (!*username) {
*sid = domain_sid;
*rtype = SID_NAME_DOMAIN;
*rid = 0xFFFFFFFF;
return NT_STATUS_OK;
}
ret = gendb_search(state->sam_ldb, mem_ctx, domain_dn, &res, attrs,
"(&(sAMAccountName=%s)(objectSid=*))",
ldb_binary_encode_string(mem_ctx, username));
if (ret < 0) {
return NT_STATUS_INTERNAL_DB_CORRUPTION;
}
for (i=0; i < ret; i++) {
*sid = samdb_result_dom_sid(mem_ctx, res[i], "objectSid");
if (*sid == NULL) {
return NT_STATUS_INVALID_SID;
}
/* Check that this is in the domain */
if (!dom_sid_in_domain(domain_sid, *sid)) {
continue;
}
atype = ldb_msg_find_attr_as_uint(res[i], "sAMAccountType", 0);
*rtype = ds_atype_map(atype);
if (*rtype == SID_NAME_UNKNOWN) {
return STATUS_SOME_UNMAPPED;
}
dom_sid_split_rid(NULL, *sid, NULL, rid);
return NT_STATUS_OK;
}
/* need to check for an allocated sid */
return NT_STATUS_INVALID_SID;
}
/*
add to the lsa_RefDomainList for LookupSids and LookupNames
*/
static NTSTATUS dcesrv_lsa_authority_list(struct lsa_policy_state *state, TALLOC_CTX *mem_ctx,
enum lsa_SidType rtype,
const char *authority_name,
struct dom_sid *sid,
struct lsa_RefDomainList *domains,
uint32_t *sid_index)
{
struct dom_sid *authority_sid;
uint32_t i;
if (rtype != SID_NAME_DOMAIN) {
authority_sid = dom_sid_dup(mem_ctx, sid);
if (authority_sid == NULL) {
return NT_STATUS_NO_MEMORY;
}
authority_sid->num_auths--;
} else {
authority_sid = sid;
}
/* see if we've already done this authority name */
for (i=0;i<domains->count;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 = authority_name;
domains->domains[i].sid = authority_sid;
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,
struct dom_sid *sid, const char *sid_str,
const char **authority_name,
const char **name, enum lsa_SidType *rtype)
{
NTSTATUS status;
int ret;
uint32_t atype;
struct ldb_message **res;
struct ldb_dn *domain_dn;
const char * const attrs[] = { "sAMAccountName", "sAMAccountType", "cn", NULL};
status = lookup_well_known_sids(mem_ctx, sid_str, authority_name, name, rtype);
if (NT_STATUS_IS_OK(status)) {
return status;
}
if (dom_sid_in_domain(state->domain_sid, sid)) {
*authority_name = state->domain_name;
domain_dn = state->domain_dn;
} else if (dom_sid_in_domain(state->builtin_sid, sid)) {
*authority_name = NAME_BUILTIN;
domain_dn = state->builtin_dn;
} else {
/* Not well known, our domain or built in */
/* In future, we must look at SID histories, and at trusted domains via winbind */
return NT_STATUS_NOT_FOUND;
}
/* need to re-add a check for an allocated sid */
ret = gendb_search(state->sam_ldb, mem_ctx, domain_dn, &res, attrs,
"objectSid=%s", ldap_encode_ndr_dom_sid(mem_ctx, sid));
if ((ret < 0) || (ret > 1)) {
return NT_STATUS_INTERNAL_DB_CORRUPTION;
}
if (ret == 0) {
return NT_STATUS_NOT_FOUND;
}
*name = ldb_msg_find_attr_as_string(res[0], "sAMAccountName", NULL);
if (!*name) {
*name = ldb_msg_find_attr_as_string(res[0], "cn", NULL);
if (!*name) {
*name = talloc_strdup(mem_ctx, sid_str);
NT_STATUS_HAVE_NO_MEMORY(*name);
}
}
atype = ldb_msg_find_attr_as_uint(res[0], "sAMAccountType", 0);
*rtype = ds_atype_map(atype);
return NT_STATUS_OK;
}
static NTSTATUS dcesrv_lsa_LookupSids_common(struct dcesrv_call_state *dce_call,
TALLOC_CTX *mem_ctx,
struct lsa_policy_state *state,
struct lsa_LookupSids2 *r)
{
struct lsa_RefDomainList *domains = NULL;
NTSTATUS status = NT_STATUS_OK;
uint32_t i;
if (r->in.level < LSA_LOOKUP_NAMES_ALL ||
r->in.level > LSA_LOOKUP_NAMES_RODC_REFERRAL_TO_FULL_DC) {
return NT_STATUS_INVALID_PARAMETER;
}
*r->out.domains = NULL;
/* NOTE: the WSPP test suite tries SIDs with invalid revision numbers,
and expects NT_STATUS_INVALID_PARAMETER back - we just treat it as
an unknown SID. We could add a SID validator here. (tridge)
MS-DTYP 2.4.2
*/
domains = talloc_zero(r->out.domains, struct lsa_RefDomainList);
if (domains == NULL) {
return NT_STATUS_NO_MEMORY;
}
*r->out.domains = domains;
r->out.names = talloc_zero(mem_ctx, struct lsa_TransNameArray2);
if (r->out.names == NULL) {
return NT_STATUS_NO_MEMORY;
}
*r->out.count = 0;
r->out.names->names = talloc_array(r->out.names, struct lsa_TranslatedName2,
r->in.sids->num_sids);
if (r->out.names->names == NULL) {
return NT_STATUS_NO_MEMORY;
}
for (i=0;i<r->in.sids->num_sids;i++) {
struct dom_sid *sid = r->in.sids->sids[i].sid;
char *sid_str = dom_sid_string(mem_ctx, sid);
const char *name, *authority_name;
enum lsa_SidType rtype;
uint32_t sid_index;
NTSTATUS status2;
r->out.names->count++;
r->out.names->names[i].sid_type = SID_NAME_UNKNOWN;
r->out.names->names[i].name.string = sid_str;
r->out.names->names[i].sid_index = 0xFFFFFFFF;
r->out.names->names[i].unknown = 0;
if (sid_str == NULL) {
r->out.names->names[i].name.string = "(SIDERROR)";
status = STATUS_SOME_UNMAPPED;
continue;
}
status2 = dcesrv_lsa_lookup_sid(state, mem_ctx, sid, sid_str,
&authority_name, &name, &rtype);
if (!NT_STATUS_IS_OK(status2)) {
status = STATUS_SOME_UNMAPPED;
continue;
}
/* set up the authority table */
status2 = dcesrv_lsa_authority_list(state, mem_ctx, rtype,
authority_name, sid,
domains, &sid_index);
if (!NT_STATUS_IS_OK(status2)) {
continue;
}
r->out.names->names[i].sid_type = rtype;
r->out.names->names[i].name.string = name;
r->out.names->names[i].sid_index = sid_index;
r->out.names->names[i].unknown = 0;
(*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;
}
/*
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 = dce_call->conn->endpoint->ep_description->transport;
struct lsa_policy_state *state;
struct dcesrv_handle *h;
if (transport != NCACN_NP && transport != NCALRPC) {
DCESRV_FAULT(DCERPC_FAULT_ACCESS_DENIED);
}
DCESRV_PULL_HANDLE(h, r->in.handle, LSA_HANDLE_POLICY);
state = h->data;
return dcesrv_lsa_LookupSids_common(dce_call,
mem_ctx,
state,
r);
}
/*
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)
{
enum dcerpc_transport_t transport = dce_call->conn->endpoint->ep_description->transport;
struct dcerpc_auth *auth_info = dce_call->conn->auth_state.auth_info;
struct lsa_policy_state *policy_state;
struct lsa_LookupSids2 q;
NTSTATUS status;
if (transport != NCACN_IP_TCP) {
DCESRV_FAULT(DCERPC_FAULT_ACCESS_DENIED);
}
/*
* We don't have policy handles on this call. So this must be restricted
* to crypto connections only.
*/
if (auth_info->auth_type != DCERPC_AUTH_TYPE_SCHANNEL ||
auth_info->auth_level < DCERPC_AUTH_LEVEL_INTEGRITY) {
DCESRV_FAULT(DCERPC_FAULT_ACCESS_DENIED);
}
status = dcesrv_lsa_get_policy_state(dce_call, mem_ctx, &policy_state);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
ZERO_STRUCT(q);
q.in.handle = NULL;
q.in.sids = r->in.sids;
q.in.names = r->in.names;
q.in.level = r->in.level;
q.in.count = r->in.count;
q.in.lookup_options = r->in.lookup_options;
q.in.client_revision = r->in.client_revision;
q.out.count = r->out.count;
q.out.names = r->out.names;
q.out.domains = r->out.domains;
status = dcesrv_lsa_LookupSids_common(dce_call,
mem_ctx,
policy_state,
&q);
talloc_free(policy_state);
r->out.count = q.out.count;
r->out.names = q.out.names;
r->out.domains = q.out.domains;
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 = dce_call->conn->endpoint->ep_description->transport;
struct lsa_LookupSids2 r2;
NTSTATUS status;
uint32_t i;
if (transport != NCACN_NP && transport != NCALRPC) {
DCESRV_FAULT(DCERPC_FAULT_ACCESS_DENIED);
}
ZERO_STRUCT(r2);
r2.in.handle = r->in.handle;
r2.in.sids = r->in.sids;
r2.in.names = NULL;
r2.in.level = r->in.level;
r2.in.count = r->in.count;
r2.in.lookup_options = 0;
r2.in.client_revision = 0;
r2.out.count = r->out.count;
r2.out.names = NULL;
r2.out.domains = r->out.domains;
status = dcesrv_lsa_LookupSids2(dce_call, mem_ctx, &r2);
/* we deliberately don't check for error from the above,
as even on error we are supposed to return the names */
r->out.domains = r2.out.domains;
if (!r2.out.names) {
r->out.names = NULL;
return status;
}
r->out.names = talloc(mem_ctx, struct lsa_TransNameArray);
if (r->out.names == NULL) {
return NT_STATUS_NO_MEMORY;
}
r->out.names->count = r2.out.names->count;
r->out.names->names = talloc_array(r->out.names, struct lsa_TranslatedName,
r->out.names->count);
if (r->out.names->names == NULL) {
return NT_STATUS_NO_MEMORY;
}
for (i=0;i<r->out.names->count;i++) {
r->out.names->names[i].sid_type = r2.out.names->names[i].sid_type;
r->out.names->names[i].name.string = r2.out.names->names[i].name.string;
r->out.names->names[i].sid_index = r2.out.names->names[i].sid_index;
}
return status;
}
static NTSTATUS dcesrv_lsa_LookupNames_common(struct dcesrv_call_state *dce_call,
TALLOC_CTX *mem_ctx,
struct lsa_policy_state *policy_state,
struct lsa_LookupNames3 *r)
{
struct loadparm_context *lp_ctx = dce_call->conn->dce_ctx->lp_ctx;
struct lsa_RefDomainList *domains;
uint32_t i;
if (r->in.level < LSA_LOOKUP_NAMES_ALL ||
r->in.level > LSA_LOOKUP_NAMES_RODC_REFERRAL_TO_FULL_DC) {
return NT_STATUS_INVALID_PARAMETER;
}
*r->out.domains = NULL;
domains = talloc_zero(mem_ctx, struct lsa_RefDomainList);
if (domains == NULL) {
return NT_STATUS_NO_MEMORY;
}
*r->out.domains = domains;
r->out.sids = talloc_zero(mem_ctx, struct lsa_TransSidArray3);
if (r->out.sids == NULL) {
return NT_STATUS_NO_MEMORY;
}
*r->out.count = 0;
r->out.sids->sids = talloc_array(r->out.sids, struct lsa_TranslatedSid3,
r->in.num_names);
if (r->out.sids->sids == NULL) {
return NT_STATUS_NO_MEMORY;
}
for (i=0;i<r->in.num_names;i++) {
const char *name = r->in.names[i].string;
const char *authority_name;
struct dom_sid *sid;
uint32_t sid_index, rid;
enum lsa_SidType rtype;
NTSTATUS status2;
r->out.sids->count++;
r->out.sids->sids[i].sid_type = SID_NAME_UNKNOWN;
r->out.sids->sids[i].sid = NULL;
r->out.sids->sids[i].sid_index = 0xFFFFFFFF;
r->out.sids->sids[i].flags = 0;
status2 = dcesrv_lsa_lookup_name(dce_call->event_ctx, lp_ctx, policy_state, mem_ctx, name,
&authority_name, &sid, &rtype, &rid);
if (!NT_STATUS_IS_OK(status2) || sid->num_auths == 0) {
continue;
}
status2 = dcesrv_lsa_authority_list(policy_state, mem_ctx, rtype, authority_name,
sid, domains, &sid_index);
if (!NT_STATUS_IS_OK(status2)) {
continue;
}
r->out.sids->sids[i].sid_type = rtype;
r->out.sids->sids[i].sid = sid;
r->out.sids->sids[i].sid_index = sid_index;
r->out.sids->sids[i].flags = 0;
(*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;
}
/*
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 = dce_call->conn->endpoint->ep_description->transport;
struct lsa_policy_state *policy_state;
struct dcesrv_handle *policy_handle;
if (transport != NCACN_NP && transport != NCALRPC) {
DCESRV_FAULT(DCERPC_FAULT_ACCESS_DENIED);
}
DCESRV_PULL_HANDLE(policy_handle, r->in.handle, LSA_HANDLE_POLICY);
policy_state = policy_handle->data;
return dcesrv_lsa_LookupNames_common(dce_call,
mem_ctx,
policy_state,
r);
}
/*
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)
{
enum dcerpc_transport_t transport = dce_call->conn->endpoint->ep_description->transport;
struct dcerpc_auth *auth_info = dce_call->conn->auth_state.auth_info;
struct lsa_policy_state *policy_state;
struct lsa_LookupNames3 q;
NTSTATUS status;
if (transport != NCACN_IP_TCP) {
DCESRV_FAULT(DCERPC_FAULT_ACCESS_DENIED);
}
/*
* We don't have policy handles on this call. So this must be restricted
* to crypto connections only.
*/
if (auth_info->auth_type != DCERPC_AUTH_TYPE_SCHANNEL ||
auth_info->auth_level < DCERPC_AUTH_LEVEL_INTEGRITY) {
DCESRV_FAULT(DCERPC_FAULT_ACCESS_DENIED);
}
status = dcesrv_lsa_get_policy_state(dce_call, mem_ctx, &policy_state);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
ZERO_STRUCT(q);
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.sids = r->in.sids;
q.in.count = r->in.count;
q.in.lookup_options = r->in.lookup_options;
q.in.client_revision = r->in.client_revision;
q.out.count = r->out.count;
q.out.sids = r->out.sids;
q.out.domains = r->out.domains;
status = dcesrv_lsa_LookupNames_common(dce_call,
mem_ctx,
policy_state,
&q);
talloc_free(policy_state);
r->out.count = q.out.count;
r->out.sids = q.out.sids;
r->out.domains = q.out.domains;
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 = dce_call->conn->endpoint->ep_description->transport;
struct lsa_policy_state *state;
struct dcesrv_handle *h;
uint32_t i;
struct loadparm_context *lp_ctx = dce_call->conn->dce_ctx->lp_ctx;
struct lsa_RefDomainList *domains;
if (transport != NCACN_NP && transport != NCALRPC) {
DCESRV_FAULT(DCERPC_FAULT_ACCESS_DENIED);
}
*r->out.domains = NULL;
DCESRV_PULL_HANDLE(h, r->in.handle, LSA_HANDLE_POLICY);
if (r->in.level < LSA_LOOKUP_NAMES_ALL ||
r->in.level > LSA_LOOKUP_NAMES_RODC_REFERRAL_TO_FULL_DC) {
return NT_STATUS_INVALID_PARAMETER;
}
state = h->data;
domains = talloc_zero(mem_ctx, struct lsa_RefDomainList);
if (domains == NULL) {
return NT_STATUS_NO_MEMORY;
}
*r->out.domains = domains;
r->out.sids = talloc_zero(mem_ctx, struct lsa_TransSidArray2);
if (r->out.sids == NULL) {
return NT_STATUS_NO_MEMORY;
}
*r->out.count = 0;
r->out.sids->sids = talloc_array(r->out.sids, struct lsa_TranslatedSid2,
r->in.num_names);
if (r->out.sids->sids == NULL) {
return NT_STATUS_NO_MEMORY;
}
for (i=0;i<r->in.num_names;i++) {
const char *name = r->in.names[i].string;
const char *authority_name;
struct dom_sid *sid;
uint32_t sid_index, rid=0;
enum lsa_SidType rtype;
NTSTATUS status2;
r->out.sids->count++;
r->out.sids->sids[i].sid_type = SID_NAME_UNKNOWN;
/* MS-LSAT 3.1.4.7 - rid zero is considered equivalent
to sid NULL - so we should return 0 rid for
unmapped entries */
r->out.sids->sids[i].rid = 0;
r->out.sids->sids[i].sid_index = 0xFFFFFFFF;
r->out.sids->sids[i].unknown = 0;
status2 = dcesrv_lsa_lookup_name(dce_call->event_ctx, lp_ctx, state, mem_ctx, name,
&authority_name, &sid, &rtype, &rid);
if (!NT_STATUS_IS_OK(status2)) {
continue;
}
status2 = dcesrv_lsa_authority_list(state, mem_ctx, rtype, authority_name,
sid, domains, &sid_index);
if (!NT_STATUS_IS_OK(status2)) {
continue;
}
r->out.sids->sids[i].sid_type = rtype;
r->out.sids->sids[i].rid = rid;
r->out.sids->sids[i].sid_index = sid_index;
r->out.sids->sids[i].unknown = 0;
(*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;
}
/*
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 = dce_call->conn->endpoint->ep_description->transport;
struct lsa_LookupNames2 r2;
NTSTATUS status;
uint32_t i;
if (transport != NCACN_NP && transport != NCALRPC) {
DCESRV_FAULT(DCERPC_FAULT_ACCESS_DENIED);
}
ZERO_STRUCT(r2);
r2.in.handle = r->in.handle;
r2.in.num_names = r->in.num_names;
r2.in.names = r->in.names;
r2.in.sids = NULL;
r2.in.level = r->in.level;
r2.in.count = r->in.count;
r2.in.lookup_options = 0;
r2.in.client_revision = 0;
r2.out.count = r->out.count;
r2.out.domains = r->out.domains;
status = dcesrv_lsa_LookupNames2(dce_call, mem_ctx, &r2);
if (r2.out.sids == NULL) {
return status;
}
r->out.sids = talloc(mem_ctx, struct lsa_TransSidArray);
if (r->out.sids == NULL) {
return NT_STATUS_NO_MEMORY;
}
r->out.sids->count = r2.out.sids->count;
r->out.sids->sids = talloc_array(r->out.sids, struct lsa_TranslatedSid,
r->out.sids->count);
if (r->out.sids->sids == NULL) {
return NT_STATUS_NO_MEMORY;
}
for (i=0;i<r->out.sids->count;i++) {
r->out.sids->sids[i].sid_type = r2.out.sids->sids[i].sid_type;
r->out.sids->sids[i].rid = r2.out.sids->sids[i].rid;
r->out.sids->sids[i].sid_index = r2.out.sids->sids[i].sid_index;
}
return status;
}