#include "auth/auth_sam_reply.h"
#include "dsdb/samdb/samdb.h"
#include "../lib/util/util_ldb.h"
-#include "../lib/util/memcache.h"
#include "../libcli/auth/schannel.h"
#include "libcli/security/security.h"
#include "param/param.h"
#include "librpc/gen_ndr/ndr_lsa.h"
#include "librpc/gen_ndr/ndr_samr.h"
#include "librpc/gen_ndr/ndr_irpc.h"
+#include "librpc/gen_ndr/ndr_winbind.h"
+#include "librpc/gen_ndr/ndr_winbind_c.h"
#include "lib/socket/netif.h"
+#include "rpc_server/common/sid_helper.h"
+#include "lib/util/util_str_escape.h"
-static struct memcache *global_challenge_table;
+#define DCESRV_INTERFACE_NETLOGON_BIND(call, iface) \
+ dcesrv_interface_netlogon_bind(call, iface)
+
+/*
+ * This #define allows the netlogon interface to accept invalid
+ * association groups, because association groups are to coordinate
+ * handles, and handles are not used in NETLOGON. This in turn avoids
+ * the need to coordinate these across multiple possible NETLOGON
+ * processes
+ */
+#define DCESRV_INTERFACE_NETLOGON_FLAGS DCESRV_INTERFACE_FLAGS_HANDLES_NOT_USED
+
+static NTSTATUS dcesrv_interface_netlogon_bind(struct dcesrv_call_state *dce_call,
+ const struct dcesrv_interface *iface)
+{
+ return dcesrv_interface_bind_reject_connect(dce_call, iface);
+}
struct netlogon_server_pipe_state {
struct netr_Credential client_challenge;
{
struct netlogon_server_pipe_state *pipe_state =
talloc_get_type(dce_call->context->private_data, struct netlogon_server_pipe_state);
- DATA_BLOB key, val;
+ NTSTATUS ntstatus;
ZERO_STRUCTP(r->out.return_credentials);
- if (global_challenge_table == NULL) {
- /*
- * We maintain a global challenge table
- * with a fixed size (8k)
- *
- * This is required for the strange clients
- * which use different connections for
- * netr_ServerReqChallenge() and netr_ServerAuthenticate3()
- *
- */
- global_challenge_table = memcache_init(talloc_autofree_context(),
- 8192);
- if (global_challenge_table == NULL) {
- return NT_STATUS_NO_MEMORY;
- }
- }
-
- /* destroyed on pipe shutdown */
-
if (pipe_state) {
talloc_free(pipe_state);
dce_call->context->private_data = NULL;
dce_call->context->private_data = pipe_state;
- key = data_blob_string_const(r->in.computer_name);
- val = data_blob_const(pipe_state, sizeof(*pipe_state));
-
- memcache_add(global_challenge_table, SINGLETON_CACHE, key, val);
+ ntstatus = schannel_save_challenge(dce_call->conn->dce_ctx->lp_ctx,
+ &pipe_state->client_challenge,
+ &pipe_state->server_challenge,
+ r->in.computer_name);
+ if (!NT_STATUS_IS_OK(ntstatus)) {
+ return ntstatus;
+ }
return NT_STATUS_OK;
}
-static NTSTATUS dcesrv_netr_ServerAuthenticate3(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
- struct netr_ServerAuthenticate3 *r)
+/*
+ * Do the actual processing of a netr_ServerAuthenticate3 message.
+ * called from dcesrv_netr_ServerAuthenticate3, which handles the logging.
+ */
+static NTSTATUS dcesrv_netr_ServerAuthenticate3_helper(
+ struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct netr_ServerAuthenticate3 *r,
+ struct dom_sid **sid)
{
struct netlogon_server_pipe_state *pipe_state =
talloc_get_type(dce_call->context->private_data, struct netlogon_server_pipe_state);
- DATA_BLOB challenge_key;
bool challenge_valid = false;
struct netlogon_server_pipe_state challenge;
struct netlogon_creds_CredentialState *creds;
bool allow_nt4_crypto = lpcfg_allow_nt4_crypto(dce_call->conn->dce_ctx->lp_ctx);
bool reject_des_client = !allow_nt4_crypto;
bool reject_md5_client = lpcfg_reject_md5_clients(dce_call->conn->dce_ctx->lp_ctx);
+ int schannel = lpcfg_server_schannel(dce_call->conn->dce_ctx->lp_ctx);
+ bool reject_none_rpc = (schannel == true);
ZERO_STRUCTP(r->out.return_credentials);
*r->out.rid = 0;
- challenge_key = data_blob_string_const(r->in.computer_name);
if (pipe_state != NULL) {
dce_call->context->private_data = NULL;
* netr_ServerAuthenticate3() on the same dcerpc connection.
*/
challenge = *pipe_state;
- TALLOC_FREE(pipe_state);
+
challenge_valid = true;
+
} else {
- DATA_BLOB val;
- bool ok;
+ NTSTATUS ntstatus;
/*
* Fallback and try to get the challenge from
*
* If too many clients are using this code path,
* they may destroy their cache entries as the
- * global_challenge_table memcache has a fixed size.
+ * TDB has a fixed size limited via a lossy hash
+ *
+ * The TDB used is the schannel store, which is
+ * initialised at startup.
+ *
+ * NOTE: The challenge is deleted from the DB as soon as it is
+ * fetched, to prevent reuse.
*
- * Note: this handles global_challenge_table == NULL fine
*/
- ok = memcache_lookup(global_challenge_table, SINGLETON_CACHE,
- challenge_key, &val);
- if (ok && val.length == sizeof(challenge)) {
- memcpy(&challenge, val.data, sizeof(challenge));
- challenge_valid = true;
- } else {
+
+ ntstatus = schannel_get_challenge(dce_call->conn->dce_ctx->lp_ctx,
+ &challenge.client_challenge,
+ &challenge.server_challenge,
+ r->in.computer_name);
+
+ if (!NT_STATUS_IS_OK(ntstatus)) {
ZERO_STRUCT(challenge);
+ } else {
+ challenge_valid = true;
}
}
- /*
- * At this point we can cleanup the cache entry,
- * if we fail the client needs to call netr_ServerReqChallenge
- * again.
- *
- * Note: this handles global_challenge_table == NULL
- * and also a non existing record just fine.
- */
- memcache_delete(global_challenge_table,
- SINGLETON_CACHE, challenge_key);
-
server_flags = NETLOGON_NEG_ACCOUNT_LOCKOUT |
NETLOGON_NEG_PERSISTENT_SAMREPL |
NETLOGON_NEG_ARCFOUR |
negotiate_flags = *r->in.negotiate_flags & server_flags;
+ if (negotiate_flags & NETLOGON_NEG_AUTHENTICATED_RPC) {
+ reject_none_rpc = false;
+ }
+
if (negotiate_flags & NETLOGON_NEG_STRONG_KEYS) {
reject_des_client = false;
}
return NT_STATUS_DOWNGRADE_DETECTED;
}
+ /*
+ * This talloc_free is important to prevent re-use of the
+ * challenge. We have to delay it this far due to NETApp
+ * servers per:
+ * https://bugzilla.samba.org/show_bug.cgi?id=11291
+ */
+ TALLOC_FREE(pipe_state);
+
+ /*
+ * At this point we must also cleanup the TDB cache
+ * entry, if we fail the client needs to call
+ * netr_ServerReqChallenge again.
+ *
+ * Note: this handles a non existing record just fine,
+ * the r->in.computer_name might not be the one used
+ * in netr_ServerReqChallenge(), but we are trying to
+ * just tidy up the normal case to prevent re-use.
+ */
+ schannel_delete_challenge(dce_call->conn->dce_ctx->lp_ctx,
+ r->in.computer_name);
+
/*
* According to Microsoft (see bugid #6099)
* Windows 7 looks at the negotiate_flags
*/
*r->out.negotiate_flags = negotiate_flags;
+ if (reject_none_rpc) {
+ /* schannel must be used, but client did not offer it. */
+ DEBUG(0,("%s: schannel required but client failed "
+ "to offer it. Client was %s\n",
+ __func__, r->in.account_name));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
switch (r->in.secure_channel_type) {
case SEC_CHAN_WKSTA:
case SEC_CHAN_DNS_DOMAIN:
r->out.return_credentials,
negotiate_flags);
}
+
if (creds == NULL) {
return NT_STATUS_ACCESS_DENIED;
}
-
creds->sid = samdb_result_dom_sid(creds, msgs[0], "objectSid");
+ *sid = talloc_memdup(mem_ctx, creds->sid, sizeof(struct dom_sid));
nt_status = schannel_save_creds_state(mem_ctx,
dce_call->conn->dce_ctx->lp_ctx,
return NT_STATUS_OK;
}
+/*
+ * Log a netr_ServerAuthenticate3 request, and then invoke
+ * dcesrv_netr_ServerAuthenticate3_helper to perform the actual processing
+ */
+static NTSTATUS dcesrv_netr_ServerAuthenticate3(
+ struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct netr_ServerAuthenticate3 *r)
+{
+ NTSTATUS status;
+ struct dom_sid *sid = NULL;
+ struct auth_usersupplied_info ui = {
+ .local_host = dce_call->conn->local_address,
+ .remote_host = dce_call->conn->remote_address,
+ .client = {
+ .account_name = r->in.account_name,
+ .domain_name = lpcfg_workgroup(dce_call->conn->dce_ctx->lp_ctx),
+ },
+ .service_description = "NETLOGON",
+ .auth_description = "ServerAuthenticate",
+ .netlogon_trust_account = {
+ .computer_name = r->in.computer_name,
+ .account_name = r->in.account_name,
+ .negotiate_flags = *r->in.negotiate_flags,
+ .secure_channel_type = r->in.secure_channel_type,
+ },
+ .mapped = {
+ .account_name = r->in.account_name,
+ }
+ };
+
+ status = dcesrv_netr_ServerAuthenticate3_helper(dce_call,
+ mem_ctx,
+ r,
+ &sid);
+ ui.netlogon_trust_account.sid = sid;
+ log_authentication_event(
+ dce_call->conn->msg_ctx,
+ dce_call->conn->dce_ctx->lp_ctx,
+ &ui,
+ status,
+ lpcfg_workgroup(dce_call->conn->dce_ctx->lp_ctx),
+ r->in.account_name,
+ NULL,
+ sid);
+
+ return status;
+}
static NTSTATUS dcesrv_netr_ServerAuthenticate(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
struct netr_ServerAuthenticate *r)
{
/*
* If schannel is required for this call test that it actually is available.
*/
-static NTSTATUS schannel_check_required(struct dcerpc_auth *auth_info,
+static NTSTATUS schannel_check_required(const struct dcesrv_auth *auth_info,
const char *computer_name,
bool integrity, bool privacy)
{
struct netlogon_creds_CredentialState **creds_out)
{
NTSTATUS nt_status;
- struct dcerpc_auth *auth_info = dce_call->conn->auth_state.auth_info;
- bool schannel_global_required = false; /* Should be lpcfg_schannel_server() == true */
+ int schannel = lpcfg_server_schannel(dce_call->conn->dce_ctx->lp_ctx);
+ bool schannel_global_required = (schannel == true);
if (schannel_global_required) {
- nt_status = schannel_check_required(auth_info,
+ nt_status = schannel_check_required(&dce_call->conn->auth_state,
computer_name,
true, false);
if (!NT_STATUS_IS_OK(nt_status)) {
This version of the function allows other wrappers to say 'do not check the credentials'
- We can't do the traditional 'wrapping' format completly, as this function must only run under schannel
+ We can't do the traditional 'wrapping' format completely, as this
+ function must only run under schannel
*/
static NTSTATUS dcesrv_netr_LogonSamLogon_base(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
struct netr_LogonSamLogonEx *r, struct netlogon_creds_CredentialState *creds)
{
- struct auth4_context *auth_context;
- struct auth_usersupplied_info *user_info;
- struct auth_user_info_dc *user_info_dc;
+ struct loadparm_context *lp_ctx = dce_call->conn->dce_ctx->lp_ctx;
+ const char *workgroup = lpcfg_workgroup(lp_ctx);
+ struct auth4_context *auth_context = NULL;
+ struct auth_usersupplied_info *user_info = NULL;
+ struct auth_user_info_dc *user_info_dc = NULL;
NTSTATUS nt_status;
- struct netr_SamBaseInfo *sam;
- struct netr_SamInfo2 *sam2;
- struct netr_SamInfo3 *sam3;
- struct netr_SamInfo6 *sam6;
+ struct netr_SamInfo2 *sam2 = NULL;
+ struct netr_SamInfo3 *sam3 = NULL;
+ struct netr_SamInfo6 *sam6 = NULL;
*r->out.authoritative = 1;
+ if (*r->in.flags & NETLOGON_SAMLOGON_FLAG_PASS_TO_FOREST_ROOT) {
+ /*
+ * Currently we're always the forest root ourself.
+ */
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ if (*r->in.flags & NETLOGON_SAMLOGON_FLAG_PASS_CROSS_FOREST_HOP) {
+ /*
+ * Currently we don't support trusts correctly yet.
+ */
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
user_info = talloc_zero(mem_ctx, struct auth_usersupplied_info);
NT_STATUS_HAVE_NO_MEMORY(user_info);
+ user_info->service_description = "SamLogon";
+
netlogon_creds_decrypt_samlogon_logon(creds,
r->in.logon_level,
r->in.logon);
case NetlogonServiceInformation:
case NetlogonInteractiveTransitiveInformation:
case NetlogonServiceTransitiveInformation:
+ case NetlogonNetworkInformation:
+ case NetlogonNetworkTransitiveInformation:
- /* TODO: we need to deny anonymous access here */
- nt_status = auth_context_create(mem_ctx,
- dce_call->event_ctx, dce_call->msg_ctx,
- dce_call->conn->dce_ctx->lp_ctx,
- &auth_context);
+ nt_status = auth_context_create_for_netlogon(mem_ctx,
+ dce_call->event_ctx, dce_call->msg_ctx,
+ dce_call->conn->dce_ctx->lp_ctx,
+ &auth_context);
NT_STATUS_NOT_OK_RETURN(nt_status);
- user_info->logon_parameters = r->in.logon->password->identity_info.parameter_control;
- user_info->client.account_name = r->in.logon->password->identity_info.account_name.string;
- user_info->client.domain_name = r->in.logon->password->identity_info.domain_name.string;
- user_info->workstation_name = r->in.logon->password->identity_info.workstation.string;
+ user_info->remote_host = dce_call->conn->remote_address;
+ user_info->local_host = dce_call->conn->local_address;
+
+ user_info->netlogon_trust_account.secure_channel_type
+ = creds->secure_channel_type;
+ user_info->netlogon_trust_account.negotiate_flags
+ = creds->negotiate_flags;
+
+ /*
+ * These two can be unrelated when the account is
+ * actually that of a trusted domain, so we want to
+ * know which DC in that trusted domain contacted
+ * us
+ */
+ user_info->netlogon_trust_account.computer_name
+ = creds->computer_name;
+ user_info->netlogon_trust_account.account_name
+ = creds->account_name;
+ user_info->netlogon_trust_account.sid
+ = creds->sid;
+
+ default:
+ /* We do not need to set up the user_info in this case */
+ break;
+ }
+ switch (r->in.logon_level) {
+ case NetlogonInteractiveInformation:
+ case NetlogonServiceInformation:
+ case NetlogonInteractiveTransitiveInformation:
+ case NetlogonServiceTransitiveInformation:
+ user_info->auth_description = "interactive";
+
+ user_info->logon_parameters
+ = r->in.logon->password->identity_info.parameter_control;
+ user_info->client.account_name
+ = r->in.logon->password->identity_info.account_name.string;
+ user_info->client.domain_name
+ = r->in.logon->password->identity_info.domain_name.string;
+ user_info->workstation_name
+ = r->in.logon->password->identity_info.workstation.string;
user_info->flags |= USER_INFO_INTERACTIVE_LOGON;
user_info->password_state = AUTH_PASSWORD_HASH;
break;
case NetlogonNetworkInformation:
case NetlogonNetworkTransitiveInformation:
+ user_info->auth_description = "network";
- /* TODO: we need to deny anonymous access here */
- nt_status = auth_context_create(mem_ctx,
- dce_call->event_ctx, dce_call->msg_ctx,
- dce_call->conn->dce_ctx->lp_ctx,
- &auth_context);
+ nt_status = auth_context_set_challenge(
+ auth_context,
+ r->in.logon->network->challenge,
+ "netr_LogonSamLogonWithFlags");
NT_STATUS_NOT_OK_RETURN(nt_status);
- nt_status = auth_context_set_challenge(auth_context, r->in.logon->network->challenge, "netr_LogonSamLogonWithFlags");
- NT_STATUS_NOT_OK_RETURN(nt_status);
-
- user_info->logon_parameters = r->in.logon->network->identity_info.parameter_control;
- user_info->client.account_name = r->in.logon->network->identity_info.account_name.string;
- user_info->client.domain_name = r->in.logon->network->identity_info.domain_name.string;
- user_info->workstation_name = r->in.logon->network->identity_info.workstation.string;
+ user_info->logon_parameters
+ = r->in.logon->network->identity_info.parameter_control;
+ user_info->client.account_name
+ = r->in.logon->network->identity_info.account_name.string;
+ user_info->client.domain_name
+ = r->in.logon->network->identity_info.domain_name.string;
+ user_info->workstation_name
+ = r->in.logon->network->identity_info.workstation.string;
user_info->password_state = AUTH_PASSWORD_RESPONSE;
user_info->password.response.lanman = data_blob_talloc(mem_ctx, r->in.logon->network->lm.data, r->in.logon->network->lm.length);
user_info->password.response.nt = data_blob_talloc(mem_ctx, r->in.logon->network->nt.data, r->in.logon->network->nt.length);
+ nt_status = NTLMv2_RESPONSE_verify_netlogon_creds(
+ user_info->client.account_name,
+ user_info->client.domain_name,
+ user_info->password.response.nt,
+ creds, workgroup);
+ NT_STATUS_NOT_OK_RETURN(nt_status);
+
break;
return NT_STATUS_INVALID_PARAMETER;
}
- nt_status = auth_check_password(auth_context, mem_ctx, user_info, &user_info_dc);
- /* TODO: set *r->out.authoritative = 0 on specific errors */
+ nt_status = auth_check_password(auth_context, mem_ctx, user_info,
+ &user_info_dc, r->out.authoritative);
NT_STATUS_NOT_OK_RETURN(nt_status);
switch (r->in.validation_level) {
case 2:
- nt_status = auth_convert_user_info_dc_sambaseinfo(mem_ctx, user_info_dc, &sam);
+ nt_status = auth_convert_user_info_dc_saminfo2(mem_ctx,
+ user_info_dc,
+ &sam2);
NT_STATUS_NOT_OK_RETURN(nt_status);
- sam2 = talloc_zero(mem_ctx, struct netr_SamInfo2);
- NT_STATUS_HAVE_NO_MEMORY(sam2);
- sam2->base = *sam;
-
- /* And put into the talloc tree */
- talloc_steal(sam2, sam);
r->out.validation->sam2 = sam2;
-
- sam = &sam2->base;
break;
case 3:
nt_status = auth_convert_user_info_dc_saminfo3(mem_ctx,
- user_info_dc,
- &sam3);
+ user_info_dc,
+ &sam3);
NT_STATUS_NOT_OK_RETURN(nt_status);
r->out.validation->sam3 = sam3;
-
- sam = &sam3->base;
break;
case 6:
- nt_status = auth_convert_user_info_dc_saminfo3(mem_ctx,
- user_info_dc,
- &sam3);
- NT_STATUS_NOT_OK_RETURN(nt_status);
-
- sam6 = talloc_zero(mem_ctx, struct netr_SamInfo6);
- NT_STATUS_HAVE_NO_MEMORY(sam6);
- sam6->base = sam3->base;
- sam = &sam6->base;
- sam6->sidcount = sam3->sidcount;
- sam6->sids = sam3->sids;
+ if (dce_call->conn->auth_state.auth_level < DCERPC_AUTH_LEVEL_PRIVACY) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
- sam6->dns_domainname.string = lpcfg_dnsdomain(dce_call->conn->dce_ctx->lp_ctx);
- sam6->principle.string = talloc_asprintf(mem_ctx, "%s@%s",
- sam->account_name.string, sam6->dns_domainname.string);
- NT_STATUS_HAVE_NO_MEMORY(sam6->principle.string);
- /* And put into the talloc tree */
- talloc_steal(sam6, sam3);
+ nt_status = auth_convert_user_info_dc_saminfo6(mem_ctx,
+ user_info_dc,
+ &sam6);
+ NT_STATUS_NOT_OK_RETURN(nt_status);
r->out.validation->sam6 = sam6;
break;
return nt_status;
}
- if (!dce_call->conn->auth_state.auth_info ||
- dce_call->conn->auth_state.auth_info->auth_type != DCERPC_AUTH_TYPE_SCHANNEL) {
+ if (dce_call->conn->auth_state.auth_type != DCERPC_AUTH_TYPE_SCHANNEL) {
return NT_STATUS_ACCESS_DENIED;
}
return dcesrv_netr_LogonSamLogon_base(dce_call, mem_ctx, r, creds);
size_t len = strlen(r->in.domainname);
if (dot || len > 15) {
- return WERR_DCNOTFOUND;
+ return WERR_NERR_DCNOTFOUND;
}
/*
return WERR_OK;
}
+struct dcesrv_netr_LogonControl_base_state {
+ struct dcesrv_call_state *dce_call;
-/*
- netr_LogonControl2Ex
-*/
-static WERROR dcesrv_netr_LogonControl2Ex(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
- struct netr_LogonControl2Ex *r)
+ TALLOC_CTX *mem_ctx;
+
+ struct netr_LogonControl2Ex r;
+
+ struct {
+ struct netr_LogonControl *l;
+ struct netr_LogonControl2 *l2;
+ struct netr_LogonControl2Ex *l2ex;
+ } _r;
+};
+
+static void dcesrv_netr_LogonControl_base_done(struct tevent_req *subreq);
+
+static WERROR dcesrv_netr_LogonControl_base_call(struct dcesrv_netr_LogonControl_base_state *state)
{
- return WERR_NOT_SUPPORTED;
+ struct dcesrv_connection *conn = state->dce_call->conn;
+ struct loadparm_context *lp_ctx = state->dce_call->conn->dce_ctx->lp_ctx;
+ struct auth_session_info *session_info = conn->auth_state.session_info;
+ enum security_user_level security_level;
+ struct dcerpc_binding_handle *irpc_handle;
+ struct tevent_req *subreq;
+ bool ok;
+
+ /* TODO: check for WERR_INVALID_COMPUTERNAME ? */
+
+ if (state->_r.l != NULL) {
+ /*
+ * netr_LogonControl
+ */
+ if (state->r.in.level == 0x00000002) {
+ return WERR_NOT_SUPPORTED;
+ } else if (state->r.in.level != 0x00000001) {
+ return WERR_INVALID_LEVEL;
+ }
+
+ switch (state->r.in.function_code) {
+ case NETLOGON_CONTROL_QUERY:
+ case NETLOGON_CONTROL_REPLICATE:
+ case NETLOGON_CONTROL_SYNCHRONIZE:
+ case NETLOGON_CONTROL_PDC_REPLICATE:
+ case NETLOGON_CONTROL_BREAKPOINT:
+ case NETLOGON_CONTROL_BACKUP_CHANGE_LOG:
+ case NETLOGON_CONTROL_TRUNCATE_LOG:
+ break;
+ default:
+ return WERR_NOT_SUPPORTED;
+ }
+ }
+
+ if (state->r.in.level < 0x00000001) {
+ return WERR_INVALID_LEVEL;
+ }
+
+ if (state->r.in.level > 0x00000004) {
+ return WERR_INVALID_LEVEL;
+ }
+
+ if (state->r.in.function_code == NETLOGON_CONTROL_QUERY) {
+ struct netr_NETLOGON_INFO_1 *info1 = NULL;
+ struct netr_NETLOGON_INFO_3 *info3 = NULL;
+
+ switch (state->r.in.level) {
+ case 0x00000001:
+ info1 = talloc_zero(state->mem_ctx,
+ struct netr_NETLOGON_INFO_1);
+ if (info1 == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ state->r.out.query->info1 = info1;
+ return WERR_OK;
+
+ case 0x00000003:
+ info3 = talloc_zero(state->mem_ctx,
+ struct netr_NETLOGON_INFO_3);
+ if (info3 == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ state->r.out.query->info3 = info3;
+ return WERR_OK;
+
+ default:
+ return WERR_INVALID_PARAMETER;
+ }
+ }
+
+ /*
+ * Some validations are done before the access check
+ * and some after the access check
+ */
+ security_level = security_session_user_level(session_info, NULL);
+ if (security_level < SECURITY_ADMINISTRATOR) {
+ return WERR_ACCESS_DENIED;
+ }
+
+ if (state->_r.l2 != NULL) {
+ /*
+ * netr_LogonControl2
+ */
+ if (state->r.in.level == 0x00000004) {
+ return WERR_INVALID_LEVEL;
+ }
+ }
+
+ switch (state->r.in.level) {
+ case 0x00000001:
+ break;
+
+ case 0x00000002:
+ switch (state->r.in.function_code) {
+ case NETLOGON_CONTROL_REDISCOVER:
+ case NETLOGON_CONTROL_TC_QUERY:
+ case NETLOGON_CONTROL_TC_VERIFY:
+ break;
+ default:
+ return WERR_INVALID_PARAMETER;
+ }
+
+ break;
+
+ case 0x00000003:
+ break;
+
+ case 0x00000004:
+ if (state->r.in.function_code != NETLOGON_CONTROL_FIND_USER) {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ break;
+
+ default:
+ return WERR_INVALID_LEVEL;
+ }
+
+ switch (state->r.in.function_code) {
+ case NETLOGON_CONTROL_REDISCOVER:
+ case NETLOGON_CONTROL_TC_QUERY:
+ case NETLOGON_CONTROL_TC_VERIFY:
+ if (state->r.in.level != 2) {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ if (state->r.in.data == NULL) {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ if (state->r.in.data->domain == NULL) {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ break;
+
+ case NETLOGON_CONTROL_CHANGE_PASSWORD:
+ if (state->r.in.level != 1) {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ if (state->r.in.data == NULL) {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ if (state->r.in.data->domain == NULL) {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ ok = lpcfg_is_my_domain_or_realm(lp_ctx,
+ state->r.in.data->domain);
+ if (!ok) {
+ struct ldb_context *sam_ctx;
+
+ sam_ctx = samdb_connect(state, state->dce_call->event_ctx,
+ lp_ctx, system_session(lp_ctx), 0);
+ if (sam_ctx == NULL) {
+ return WERR_DS_UNAVAILABLE;
+ }
+
+ /*
+ * Secrets for trusted domains can only be triggered on
+ * the PDC.
+ */
+ ok = samdb_is_pdc(sam_ctx);
+ TALLOC_FREE(sam_ctx);
+ if (!ok) {
+ return WERR_INVALID_DOMAIN_ROLE;
+ }
+ }
+
+ break;
+ default:
+ return WERR_NOT_SUPPORTED;
+ }
+
+ irpc_handle = irpc_binding_handle_by_name(state,
+ state->dce_call->msg_ctx,
+ "winbind_server",
+ &ndr_table_winbind);
+ if (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 WERR_SERVICE_NOT_FOUND;
+ }
+
+ /*
+ * 60 seconds timeout should be enough
+ */
+ dcerpc_binding_handle_set_timeout(irpc_handle, 60);
+
+ subreq = dcerpc_winbind_LogonControl_send(state,
+ state->dce_call->event_ctx,
+ irpc_handle,
+ state->r.in.function_code,
+ state->r.in.level,
+ state->r.in.data,
+ state->r.out.query);
+ if (subreq == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ state->dce_call->state_flags |= DCESRV_CALL_STATE_FLAG_ASYNC;
+ tevent_req_set_callback(subreq,
+ dcesrv_netr_LogonControl_base_done,
+ state);
+
+ return WERR_OK;
}
+static void dcesrv_netr_LogonControl_base_done(struct tevent_req *subreq)
+{
+ struct dcesrv_netr_LogonControl_base_state *state =
+ tevent_req_callback_data(subreq,
+ struct dcesrv_netr_LogonControl_base_state);
+ NTSTATUS status;
+
+ status = dcerpc_winbind_LogonControl_recv(subreq, state->mem_ctx,
+ &state->r.out.result);
+ TALLOC_FREE(subreq);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) {
+ state->r.out.result = WERR_TIMEOUT;
+ } 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)));
+ }
+
+ if (state->_r.l2ex != NULL) {
+ struct netr_LogonControl2Ex *r = state->_r.l2ex;
+ r->out.result = state->r.out.result;
+ } else if (state->_r.l2 != NULL) {
+ struct netr_LogonControl2 *r = state->_r.l2;
+ r->out.result = state->r.out.result;
+ } else if (state->_r.l != NULL) {
+ struct netr_LogonControl *r = state->_r.l;
+ r->out.result = state->r.out.result;
+ }
+
+ status = dcesrv_reply(state->dce_call);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,(__location__ ": dcesrv_reply() failed - %s\n", nt_errstr(status)));
+ }
+}
/*
netr_LogonControl
static WERROR dcesrv_netr_LogonControl(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
struct netr_LogonControl *r)
{
- struct netr_LogonControl2Ex r2;
+ struct dcesrv_netr_LogonControl_base_state *state;
WERROR werr;
- if (r->in.level == 0x00000001) {
- ZERO_STRUCT(r2);
+ state = talloc_zero(mem_ctx, struct dcesrv_netr_LogonControl_base_state);
+ if (state == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
- r2.in.logon_server = r->in.logon_server;
- r2.in.function_code = r->in.function_code;
- r2.in.level = r->in.level;
- r2.in.data = NULL;
- r2.out.query = r->out.query;
+ state->dce_call = dce_call;
+ state->mem_ctx = mem_ctx;
- werr = dcesrv_netr_LogonControl2Ex(dce_call, mem_ctx, &r2);
- } else if (r->in.level == 0x00000002) {
- werr = WERR_NOT_SUPPORTED;
- } else {
- werr = WERR_UNKNOWN_LEVEL;
+ state->r.in.logon_server = r->in.logon_server;
+ state->r.in.function_code = r->in.function_code;
+ state->r.in.level = r->in.level;
+ state->r.in.data = NULL;
+ state->r.out.query = r->out.query;
+
+ state->_r.l = r;
+
+ werr = dcesrv_netr_LogonControl_base_call(state);
+
+ if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
+ return werr;
}
return werr;
}
-
/*
netr_LogonControl2
*/
static WERROR dcesrv_netr_LogonControl2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
struct netr_LogonControl2 *r)
{
- struct netr_LogonControl2Ex r2;
+ struct dcesrv_netr_LogonControl_base_state *state;
WERROR werr;
- ZERO_STRUCT(r2);
+ state = talloc_zero(mem_ctx, struct dcesrv_netr_LogonControl_base_state);
+ if (state == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
- r2.in.logon_server = r->in.logon_server;
- r2.in.function_code = r->in.function_code;
- r2.in.level = r->in.level;
- r2.in.data = r->in.data;
- r2.out.query = r->out.query;
+ state->dce_call = dce_call;
+ state->mem_ctx = mem_ctx;
+
+ state->r.in.logon_server = r->in.logon_server;
+ state->r.in.function_code = r->in.function_code;
+ state->r.in.level = r->in.level;
+ state->r.in.data = r->in.data;
+ state->r.out.query = r->out.query;
+
+ state->_r.l2 = r;
- werr = dcesrv_netr_LogonControl2Ex(dce_call, mem_ctx, &r2);
+ werr = dcesrv_netr_LogonControl_base_call(state);
+
+ if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
+ return werr;
+ }
+
+ return werr;
+}
+
+/*
+ netr_LogonControl2Ex
+*/
+static WERROR dcesrv_netr_LogonControl2Ex(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_LogonControl2Ex *r)
+{
+ struct dcesrv_netr_LogonControl_base_state *state;
+ WERROR werr;
+
+ state = talloc_zero(mem_ctx, struct dcesrv_netr_LogonControl_base_state);
+ if (state == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ state->dce_call = dce_call;
+ state->mem_ctx = mem_ctx;
+
+ state->r = *r;
+ state->_r.l2ex = r;
+
+ werr = dcesrv_netr_LogonControl_base_call(state);
+
+ if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
+ return werr;
+ }
return werr;
}
r->out.return_authenticator,
&creds);
if (!NT_STATUS_IS_OK(status)) {
- DEBUG(0,(__location__ " Bad credentials - error\n"));
+ char* local = NULL;
+ char* remote = NULL;
+ TALLOC_CTX *frame = talloc_stackframe();
+ remote = tsocket_address_string(dce_call->conn->remote_address,
+ frame);
+ local = tsocket_address_string(dce_call->conn->local_address,
+ frame);
+ DBG_ERR(("Bad credentials - "
+ "computer[%s] remote[%s] local[%s]\n"),
+ log_escape(frame, r->in.computer_name),
+ remote,
+ local);
+ talloc_free(frame);
}
NT_STATUS_NOT_OK_RETURN(status);
DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
}
+static bool sam_rodc_access_check(struct ldb_context *sam_ctx,
+ TALLOC_CTX *mem_ctx,
+ struct dom_sid *user_sid,
+ struct ldb_dn *obj_dn)
+{
+ const char *rodc_attrs[] = { "msDS-KrbTgtLink", "msDS-NeverRevealGroup", "msDS-RevealOnDemandGroup", "objectGUID", NULL };
+ const char *obj_attrs[] = { "tokenGroups", "objectSid", "UserAccountControl", "msDS-KrbTgtLinkBL", NULL };
+ struct ldb_dn *rodc_dn;
+ int ret;
+ struct ldb_result *rodc_res = NULL, *obj_res = NULL;
+ const struct dom_sid *additional_sids[] = { NULL, NULL };
+ WERROR werr;
+ struct dom_sid *object_sid;
+ const struct dom_sid **never_reveal_sids, **reveal_sids, **token_sids;
+
+ rodc_dn = ldb_dn_new_fmt(mem_ctx, sam_ctx, "<SID=%s>",
+ dom_sid_string(mem_ctx, user_sid));
+ if (!ldb_dn_validate(rodc_dn)) goto denied;
+
+ /* do the two searches we need */
+ ret = dsdb_search_dn(sam_ctx, mem_ctx, &rodc_res, rodc_dn, rodc_attrs,
+ DSDB_SEARCH_SHOW_EXTENDED_DN);
+ if (ret != LDB_SUCCESS || rodc_res->count != 1) goto denied;
+
+ ret = dsdb_search_dn(sam_ctx, mem_ctx, &obj_res, obj_dn, obj_attrs, 0);
+ if (ret != LDB_SUCCESS || obj_res->count != 1) goto denied;
+
+ object_sid = samdb_result_dom_sid(mem_ctx, obj_res->msgs[0], "objectSid");
+
+ additional_sids[0] = object_sid;
+
+ werr = samdb_result_sid_array_dn(sam_ctx, rodc_res->msgs[0],
+ mem_ctx, "msDS-NeverRevealGroup", &never_reveal_sids);
+ if (!W_ERROR_IS_OK(werr)) {
+ goto denied;
+ }
+
+ werr = samdb_result_sid_array_dn(sam_ctx, rodc_res->msgs[0],
+ mem_ctx, "msDS-RevealOnDemandGroup", &reveal_sids);
+ if (!W_ERROR_IS_OK(werr)) {
+ goto denied;
+ }
+
+ /*
+ * The SID list needs to include itself as well as the tokenGroups.
+ *
+ * TODO determine if sIDHistory is required for this check
+ */
+ werr = samdb_result_sid_array_ndr(sam_ctx, obj_res->msgs[0],
+ mem_ctx, "tokenGroups", &token_sids,
+ additional_sids, 1);
+ if (!W_ERROR_IS_OK(werr) || token_sids==NULL) {
+ goto denied;
+ }
+
+ if (never_reveal_sids &&
+ sid_list_match(token_sids, never_reveal_sids)) {
+ goto denied;
+ }
+
+ if (reveal_sids &&
+ sid_list_match(token_sids, reveal_sids)) {
+ goto allowed;
+ }
+
+denied:
+ return false;
+allowed:
+ return true;
+
+}
/*
- netr_NETRLOGONSENDTOSAM
+ netr_NetrLogonSendToSam
*/
-static WERROR dcesrv_netr_NETRLOGONSENDTOSAM(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
- struct netr_NETRLOGONSENDTOSAM *r)
+static NTSTATUS dcesrv_netr_NetrLogonSendToSam(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_NetrLogonSendToSam *r)
{
- DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+ struct netlogon_creds_CredentialState *creds;
+ struct ldb_context *sam_ctx;
+ NTSTATUS nt_status;
+ DATA_BLOB decrypted_blob;
+ enum ndr_err_code ndr_err;
+ struct netr_SendToSamBase base_msg = { 0 };
+
+ nt_status = dcesrv_netr_creds_server_step_check(dce_call,
+ mem_ctx,
+ r->in.computer_name,
+ r->in.credential,
+ r->out.return_authenticator,
+ &creds);
+
+ NT_STATUS_NOT_OK_RETURN(nt_status);
+
+ switch (creds->secure_channel_type) {
+ case SEC_CHAN_BDC:
+ case SEC_CHAN_RODC:
+ break;
+ case SEC_CHAN_WKSTA:
+ case SEC_CHAN_DNS_DOMAIN:
+ case SEC_CHAN_DOMAIN:
+ case SEC_CHAN_NULL:
+ return NT_STATUS_INVALID_PARAMETER;
+ default:
+ DEBUG(1, ("Client asked for an invalid secure channel type: %d\n",
+ creds->secure_channel_type));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx,
+ dce_call->conn->dce_ctx->lp_ctx,
+ system_session(dce_call->conn->dce_ctx->lp_ctx), 0);
+ if (sam_ctx == NULL) {
+ return NT_STATUS_INVALID_SYSTEM_SERVICE;
+ }
+
+ /* Buffer is meant to be 16-bit aligned */
+ if (creds->negotiate_flags & NETLOGON_NEG_SUPPORTS_AES) {
+ netlogon_creds_aes_decrypt(creds, r->in.opaque_buffer, r->in.buffer_len);
+ } else {
+ netlogon_creds_arcfour_crypt(creds, r->in.opaque_buffer, r->in.buffer_len);
+ }
+
+ decrypted_blob.data = r->in.opaque_buffer;
+ decrypted_blob.length = r->in.buffer_len;
+
+ ndr_err = ndr_pull_struct_blob(&decrypted_blob, mem_ctx, &base_msg,
+ (ndr_pull_flags_fn_t)ndr_pull_netr_SendToSamBase);
+
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ /* We only partially implement SendToSam */
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ /* Now 'send' to SAM */
+ switch (base_msg.message_type) {
+ case SendToSamResetBadPasswordCount:
+ {
+ struct ldb_message *msg = ldb_msg_new(mem_ctx);
+ struct ldb_dn *dn = NULL;
+ int ret = 0;
+
+
+ ret = ldb_transaction_start(sam_ctx);
+ if (ret != LDB_SUCCESS) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ ret = dsdb_find_dn_by_guid(sam_ctx,
+ mem_ctx,
+ &base_msg.message.reset_bad_password.guid,
+ 0,
+ &dn);
+ if (ret != LDB_SUCCESS) {
+ ldb_transaction_cancel(sam_ctx);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (creds->secure_channel_type == SEC_CHAN_RODC &&
+ !sam_rodc_access_check(sam_ctx, mem_ctx, creds->sid, dn)) {
+ DEBUG(1, ("Client asked to reset bad password on "
+ "an arbitrary user: %s\n",
+ ldb_dn_get_linearized(dn)));
+ ldb_transaction_cancel(sam_ctx);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ msg->dn = dn;
+
+ ret = samdb_msg_add_int(sam_ctx, mem_ctx, msg, "badPwdCount", 0);
+ if (ret != LDB_SUCCESS) {
+ ldb_transaction_cancel(sam_ctx);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ ret = dsdb_replace(sam_ctx, msg, 0);
+ if (ret != LDB_SUCCESS) {
+ ldb_transaction_cancel(sam_ctx);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ ret = ldb_transaction_commit(sam_ctx);
+ if (ret != LDB_SUCCESS) {
+ ldb_transaction_cancel(sam_ctx);
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ break;
+ }
+ default:
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ return NT_STATUS_OK;
}
ldb_get_default_basedn(sam_ctx),
"(&(objectClass=container)(cn=System))");
if (!system_dn) {
- return WERR_GENERAL_FAILURE;
+ return WERR_GEN_FAILURE;
}
ret = gendb_search(sam_ctx, mem_ctx, system_dn,
sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx, lp_ctx,
dce_call->conn->auth_state.session_info, 0);
if (sam_ctx == NULL) {
- return WERR_GENERAL_FAILURE;
+ return WERR_GEN_FAILURE;
}
if ((r->in.trust_flags & NETR_TRUST_FLAG_INBOUND) ||
ret = gendb_search_dn(sam_ctx, mem_ctx, NULL,
&dom_res, dom_attrs);
if (ret != 1) {
- return WERR_GENERAL_FAILURE;
+ return WERR_GEN_FAILURE;
}
trusts->count = n + 1;
return status;
}
-
-static WERROR fill_forest_trust_array(TALLOC_CTX *mem_ctx,
- struct ldb_context *sam_ctx,
- struct loadparm_context *lp_ctx,
- struct lsa_ForestTrustInformation *info)
-{
- struct lsa_ForestTrustDomainInfo *domain_info;
- struct lsa_ForestTrustRecord *e;
- struct ldb_message **dom_res;
- const char * const dom_attrs[] = { "objectSid", NULL };
- int ret;
-
- /* we need to provide 2 entries:
- * 1. the Root Forest name
- * 2. the Domain Information
- */
-
- info->count = 2;
- info->entries = talloc_array(info, struct lsa_ForestTrustRecord *, 2);
- W_ERROR_HAVE_NO_MEMORY(info->entries);
-
- /* Forest root info */
- e = talloc(info, struct lsa_ForestTrustRecord);
- W_ERROR_HAVE_NO_MEMORY(e);
-
- e->flags = 0;
- e->type = LSA_FOREST_TRUST_TOP_LEVEL_NAME;
- e->time = 0; /* so far always 0 in trces. */
- e->forest_trust_data.top_level_name.string = samdb_forest_name(sam_ctx,
- mem_ctx);
- W_ERROR_HAVE_NO_MEMORY(e->forest_trust_data.top_level_name.string);
-
- info->entries[0] = e;
-
- /* Domain info */
- e = talloc(info, struct lsa_ForestTrustRecord);
- W_ERROR_HAVE_NO_MEMORY(e);
-
- /* get our own domain info */
- ret = gendb_search_dn(sam_ctx, mem_ctx, NULL, &dom_res, dom_attrs);
- if (ret != 1) {
- return WERR_GENERAL_FAILURE;
- }
-
- /* TODO: check if disabled and set flags accordingly */
- e->flags = 0;
- e->type = LSA_FOREST_TRUST_DOMAIN_INFO;
- e->time = 0; /* so far always 0 in traces. */
-
- domain_info = &e->forest_trust_data.domain_info;
- domain_info->domain_sid = samdb_result_dom_sid(info, dom_res[0],
- "objectSid");
- domain_info->dns_domain_name.string = lpcfg_dnsdomain(lp_ctx);
- domain_info->netbios_domain_name.string = lpcfg_workgroup(lp_ctx);
-
- info->entries[1] = e;
-
- talloc_free(dom_res);
-
- return WERR_OK;
-}
-
/*
netr_DsRGetForestTrustInformation
*/
+struct dcesrv_netr_DsRGetForestTrustInformation_state {
+ struct dcesrv_call_state *dce_call;
+ TALLOC_CTX *mem_ctx;
+ struct netr_DsRGetForestTrustInformation *r;
+};
+
+static void dcesrv_netr_DsRGetForestTrustInformation_done(struct tevent_req *subreq);
+
static WERROR dcesrv_netr_DsRGetForestTrustInformation(struct dcesrv_call_state *dce_call,
TALLOC_CTX *mem_ctx,
struct netr_DsRGetForestTrustInformation *r)
{
struct loadparm_context *lp_ctx = dce_call->conn->dce_ctx->lp_ctx;
- struct lsa_ForestTrustInformation *info, **info_ptr;
- struct ldb_context *sam_ctx;
- WERROR werr;
+ struct dcesrv_connection *conn = dce_call->conn;
+ struct auth_session_info *session_info = conn->auth_state.session_info;
+ enum security_user_level security_level;
+ struct ldb_context *sam_ctx = NULL;
+ struct dcesrv_netr_DsRGetForestTrustInformation_state *state = NULL;
+ struct dcerpc_binding_handle *irpc_handle = NULL;
+ struct tevent_req *subreq = NULL;
+ struct ldb_dn *domain_dn = NULL;
+ struct ldb_dn *forest_dn = NULL;
+ int cmp;
+ int forest_level;
+
+ security_level = security_session_user_level(session_info, NULL);
+ if (security_level < SECURITY_USER) {
+ return WERR_ACCESS_DENIED;
+ }
if (r->in.flags & 0xFFFFFFFE) {
return WERR_INVALID_FLAGS;
sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx, lp_ctx,
dce_call->conn->auth_state.session_info, 0);
if (sam_ctx == NULL) {
- return WERR_GENERAL_FAILURE;
+ return WERR_GEN_FAILURE;
+ }
+
+ domain_dn = ldb_get_default_basedn(sam_ctx);
+ if (domain_dn == NULL) {
+ return WERR_GEN_FAILURE;
+ }
+
+ forest_dn = ldb_get_root_basedn(sam_ctx);
+ if (forest_dn == NULL) {
+ return WERR_GEN_FAILURE;
+ }
+
+ cmp = ldb_dn_compare(domain_dn, forest_dn);
+ if (cmp != 0) {
+ return WERR_NERR_ACFNOTLOADED;
+ }
+
+ forest_level = dsdb_forest_functional_level(sam_ctx);
+ if (forest_level < DS_DOMAIN_FUNCTION_2003) {
+ return WERR_INVALID_FUNCTION;
}
if (r->in.flags & DS_GFTI_UPDATE_TDO) {
if (r->in.trusted_domain_name == NULL) {
return WERR_INVALID_FLAGS;
}
+ }
- /* TODO: establish an schannel connection with
- * r->in.trusted_domain_name and perform a
- * netr_GetForestTrustInformation call against it */
+ if (r->in.trusted_domain_name == NULL) {
+ NTSTATUS status;
- /* for now return not implementd */
- return WERR_CALL_NOT_IMPLEMENTED;
- }
+ /*
+ * information about our own domain
+ */
+ status = dsdb_trust_xref_forest_info(mem_ctx, sam_ctx,
+ r->out.forest_trust_info);
+ if (!NT_STATUS_IS_OK(status)) {
+ return ntstatus_to_werror(status);
+ }
- /* TODO: check r->in.server_name is our name */
+ return WERR_OK;
+ }
- info_ptr = talloc(mem_ctx, struct lsa_ForestTrustInformation *);
- W_ERROR_HAVE_NO_MEMORY(info_ptr);
+ /*
+ * Forward the request to winbindd
+ */
- info = talloc_zero(info_ptr, struct lsa_ForestTrustInformation);
- W_ERROR_HAVE_NO_MEMORY(info);
+ state = talloc_zero(mem_ctx,
+ struct dcesrv_netr_DsRGetForestTrustInformation_state);
+ if (state == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ state->dce_call = dce_call;
+ state->mem_ctx = mem_ctx;
+ state->r = r;
- werr = fill_forest_trust_array(mem_ctx, sam_ctx, lp_ctx, info);
- W_ERROR_NOT_OK_RETURN(werr);
+ irpc_handle = irpc_binding_handle_by_name(state,
+ state->dce_call->msg_ctx,
+ "winbind_server",
+ &ndr_table_winbind);
+ if (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 WERR_SERVICE_NOT_FOUND;
+ }
- *info_ptr = info;
- r->out.forest_trust_info = info_ptr;
+ /*
+ * 60 seconds timeout should be enough
+ */
+ dcerpc_binding_handle_set_timeout(irpc_handle, 60);
+
+ subreq = dcerpc_winbind_GetForestTrustInformation_send(state,
+ state->dce_call->event_ctx,
+ irpc_handle,
+ r->in.trusted_domain_name,
+ r->in.flags,
+ r->out.forest_trust_info);
+ if (subreq == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ state->dce_call->state_flags |= DCESRV_CALL_STATE_FLAG_ASYNC;
+ tevent_req_set_callback(subreq,
+ dcesrv_netr_DsRGetForestTrustInformation_done,
+ state);
return WERR_OK;
}
+static void dcesrv_netr_DsRGetForestTrustInformation_done(struct tevent_req *subreq)
+{
+ struct dcesrv_netr_DsRGetForestTrustInformation_state *state =
+ tevent_req_callback_data(subreq,
+ struct dcesrv_netr_DsRGetForestTrustInformation_state);
+ NTSTATUS status;
+
+ status = dcerpc_winbind_GetForestTrustInformation_recv(subreq,
+ state->mem_ctx,
+ &state->r->out.result);
+ TALLOC_FREE(subreq);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) {
+ state->r->out.result = WERR_TIMEOUT;
+ } 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)));
+ }
+
+ status = dcesrv_reply(state->dce_call);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,(__location__ ": dcesrv_reply() failed - %s\n", nt_errstr(status)));
+ }
+}
/*
netr_GetForestTrustInformation
struct netr_GetForestTrustInformation *r)
{
struct loadparm_context *lp_ctx = dce_call->conn->dce_ctx->lp_ctx;
- struct netlogon_creds_CredentialState *creds;
- struct lsa_ForestTrustInformation *info, **info_ptr;
- struct ldb_context *sam_ctx;
+ struct netlogon_creds_CredentialState *creds = NULL;
+ struct ldb_context *sam_ctx = NULL;
+ struct ldb_dn *domain_dn = NULL;
+ struct ldb_dn *forest_dn = NULL;
+ int cmp;
+ int forest_level;
NTSTATUS status;
- WERROR werr;
status = dcesrv_netr_creds_server_step_check(dce_call,
mem_ctx,
/* TODO: check r->in.server_name is our name */
- info_ptr = talloc(mem_ctx, struct lsa_ForestTrustInformation *);
- if (!info_ptr) {
- return NT_STATUS_NO_MEMORY;
+ domain_dn = ldb_get_default_basedn(sam_ctx);
+ if (domain_dn == NULL) {
+ return NT_STATUS_INTERNAL_ERROR;
}
- info = talloc_zero(info_ptr, struct lsa_ForestTrustInformation);
- if (!info) {
- return NT_STATUS_NO_MEMORY;
+
+ forest_dn = ldb_get_root_basedn(sam_ctx);
+ if (forest_dn == NULL) {
+ return NT_STATUS_INTERNAL_ERROR;
}
- werr = fill_forest_trust_array(mem_ctx, sam_ctx, lp_ctx, info);
- if (!W_ERROR_IS_OK(werr)) {
- return werror_to_ntstatus(werr);
+ cmp = ldb_dn_compare(domain_dn, forest_dn);
+ if (cmp != 0) {
+ return NT_STATUS_INVALID_DOMAIN_STATE;
+ }
+
+ forest_level = dsdb_forest_functional_level(sam_ctx);
+ if (forest_level < DS_DOMAIN_FUNCTION_2003) {
+ return NT_STATUS_INVALID_DOMAIN_STATE;
}
- *info_ptr = info;
- r->out.forest_trust_info = info_ptr;
+ status = dsdb_trust_xref_forest_info(mem_ctx, sam_ctx,
+ r->out.forest_trust_info);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
return NT_STATUS_OK;
}