X-Git-Url: http://git.samba.org/samba.git/?a=blobdiff_plain;f=source4%2Frpc_server%2Fnetlogon%2Fdcerpc_netlogon.c;h=e96cd08ce2db209fdb4a30114ded11e30e545f2a;hb=2099add0657126e4a5427ec2db0fe8025478b355;hp=c140ee8e162614b339dbe307635cc2c5a5b6186e;hpb=efc335a03062740f51a6edd09d765a8b77e239c5;p=garming%2Fsamba-autobuild%2F.git diff --git a/source4/rpc_server/netlogon/dcerpc_netlogon.c b/source4/rpc_server/netlogon/dcerpc_netlogon.c index c140ee8e162..e96cd08ce2d 100644 --- a/source4/rpc_server/netlogon/dcerpc_netlogon.c +++ b/source4/rpc_server/netlogon/dcerpc_netlogon.c @@ -111,8 +111,10 @@ static NTSTATUS dcesrv_netr_ServerReqChallenge(struct dcesrv_call_state *dce_cal */ static NTSTATUS dcesrv_netr_ServerAuthenticate3_helper( struct dcesrv_call_state *dce_call, - TALLOC_CTX *mem_ctx, + TALLOC_CTX *mem_ctx, struct netr_ServerAuthenticate3 *r, + const char **trust_account_for_search, + const char **trust_account_in_db, struct dom_sid **sid) { struct netlogon_server_pipe_state *pipe_state = @@ -128,8 +130,7 @@ static NTSTATUS dcesrv_netr_ServerAuthenticate3_helper( struct ldb_message **msgs; NTSTATUS nt_status; const char *attrs[] = {"unicodePwd", "userAccountControl", - "objectSid", NULL}; - const char *account_name; + "objectSid", "samAccountName", NULL}; uint32_t server_flags = 0; uint32_t negotiate_flags = 0; bool allow_nt4_crypto = lpcfg_allow_nt4_crypto(dce_call->conn->dce_ctx->lp_ctx); @@ -270,7 +271,8 @@ static NTSTATUS dcesrv_netr_ServerAuthenticate3_helper( /* 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)); + __func__, + log_escape(mem_ctx, r->in.account_name))); return NT_STATUS_ACCESS_DENIED; } @@ -289,8 +291,12 @@ static NTSTATUS dcesrv_netr_ServerAuthenticate3_helper( 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); + 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), + dce_call->conn->remote_address, + 0); if (sam_ctx == NULL) { return NT_STATUS_INVALID_SYSTEM_SERVICE; } @@ -346,7 +352,8 @@ static NTSTATUS dcesrv_netr_ServerAuthenticate3_helper( if (NT_STATUS_EQUAL(nt_status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) { DEBUG(2, ("Client asked for a trusted domain secure channel, " "but there's no tdo for [%s] => [%s] \n", - r->in.account_name, encoded_name)); + log_escape(mem_ctx, r->in.account_name), + encoded_name)); return NT_STATUS_NO_TRUST_SAM_ACCOUNT; } if (!NT_STATUS_IS_OK(nt_status)) { @@ -368,34 +375,47 @@ static NTSTATUS dcesrv_netr_ServerAuthenticate3_helper( return NT_STATUS_NO_TRUST_SAM_ACCOUNT; } - account_name = talloc_asprintf(mem_ctx, "%s$", flatname); - if (account_name == NULL) { + *trust_account_for_search = talloc_asprintf(mem_ctx, "%s$", flatname); + if (*trust_account_for_search == NULL) { return NT_STATUS_NO_MEMORY; } } else { - account_name = r->in.account_name; + *trust_account_for_search = r->in.account_name; } /* pull the user attributes */ num_records = gendb_search(sam_ctx, mem_ctx, NULL, &msgs, attrs, "(&(sAMAccountName=%s)(objectclass=user))", - ldb_binary_encode_string(mem_ctx, account_name)); + ldb_binary_encode_string(mem_ctx, + *trust_account_for_search)); if (num_records == 0) { DEBUG(3,("Couldn't find user [%s] in samdb.\n", - r->in.account_name)); + log_escape(mem_ctx, r->in.account_name))); return NT_STATUS_NO_TRUST_SAM_ACCOUNT; } if (num_records > 1) { - DEBUG(0,("Found %d records matching user [%s]\n", num_records, r->in.account_name)); + DEBUG(0,("Found %d records matching user [%s]\n", + num_records, + log_escape(mem_ctx, r->in.account_name))); return NT_STATUS_INTERNAL_DB_CORRUPTION; } + *trust_account_in_db = ldb_msg_find_attr_as_string(msgs[0], + "samAccountName", + NULL); + if (*trust_account_in_db == NULL) { + DEBUG(0,("No samAccountName returned in record matching user [%s]\n", + r->in.account_name)); + return NT_STATUS_INTERNAL_DB_CORRUPTION; + } + user_account_control = ldb_msg_find_attr_as_uint(msgs[0], "userAccountControl", 0); if (user_account_control & UF_ACCOUNTDISABLE) { - DEBUG(1, ("Account [%s] is disabled\n", r->in.account_name)); + DEBUG(1, ("Account [%s] is disabled\n", + log_escape(mem_ctx, r->in.account_name))); return NT_STATUS_NO_TRUST_SAM_ACCOUNT; } @@ -442,8 +462,8 @@ static NTSTATUS dcesrv_netr_ServerAuthenticate3_helper( if (!challenge_valid) { DEBUG(1, ("No challenge requested by client [%s/%s], " "cannot authenticate\n", - r->in.computer_name, - r->in.account_name)); + log_escape(mem_ctx, r->in.computer_name), + log_escape(mem_ctx, r->in.account_name))); return NT_STATUS_ACCESS_DENIED; } @@ -507,6 +527,8 @@ static NTSTATUS dcesrv_netr_ServerAuthenticate3( { NTSTATUS status; struct dom_sid *sid = NULL; + const char *trust_account_for_search = NULL; + const char *trust_account_in_db = NULL; struct auth_usersupplied_info ui = { .local_host = dce_call->conn->local_address, .remote_host = dce_call->conn->remote_address, @@ -518,27 +540,28 @@ static NTSTATUS dcesrv_netr_ServerAuthenticate3( .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, + &trust_account_for_search, + &trust_account_in_db, &sid); ui.netlogon_trust_account.sid = sid; + ui.netlogon_trust_account.account_name = trust_account_in_db; + ui.mapped.account_name = trust_account_for_search; log_authentication_event( dce_call->conn->msg_ctx, dce_call->conn->dce_ctx->lp_ctx, + NULL, &ui, status, lpcfg_workgroup(dce_call->conn->dce_ctx->lp_ctx), - r->in.account_name, + trust_account_in_db, NULL, sid); @@ -681,7 +704,12 @@ static NTSTATUS dcesrv_netr_ServerPasswordSet(struct dcesrv_call_state *dce_call &creds); NT_STATUS_NOT_OK_RETURN(nt_status); - 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); + 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), + dce_call->conn->remote_address, + 0); if (sam_ctx == NULL) { return NT_STATUS_INVALID_SYSTEM_SERVICE; } @@ -741,7 +769,12 @@ static NTSTATUS dcesrv_netr_ServerPasswordSet2(struct dcesrv_call_state *dce_cal &creds); NT_STATUS_NOT_OK_RETURN(nt_status); - 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); + 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), + dce_call->conn->remote_address, + 0); if (sam_ctx == NULL) { return NT_STATUS_INVALID_SYSTEM_SERVICE; } @@ -831,7 +864,8 @@ static WERROR dcesrv_netr_LogonUasLogoff(struct dcesrv_call_state *dce_call, TAL } -static NTSTATUS dcesrv_netr_LogonSamLogon_check(const struct netr_LogonSamLogonEx *r) +static NTSTATUS dcesrv_netr_LogonSamLogon_check(struct dcesrv_call_state *dce_call, + const struct netr_LogonSamLogonEx *r) { switch (r->in.logon_level) { case NetlogonInteractiveInformation: @@ -887,9 +921,45 @@ static NTSTATUS dcesrv_netr_LogonSamLogon_check(const struct netr_LogonSamLogonE return NT_STATUS_INVALID_PARAMETER; } + switch (r->in.validation_level) { + case NetlogonValidationSamInfo4: /* 6 */ + if (dce_call->conn->auth_state.auth_level < DCERPC_AUTH_LEVEL_PRIVACY) { + return NT_STATUS_INVALID_PARAMETER; + } + break; + + default: + break; + } + return NT_STATUS_OK; } +struct dcesrv_netr_LogonSamLogon_base_state { + struct dcesrv_call_state *dce_call; + + TALLOC_CTX *mem_ctx; + + struct netlogon_creds_CredentialState *creds; + + struct netr_LogonSamLogonEx r; + + uint32_t _ignored_flags; + + struct { + struct netr_LogonSamLogon *lsl; + struct netr_LogonSamLogonWithFlags *lslwf; + struct netr_LogonSamLogonEx *lslex; + } _r; + + struct kdc_check_generic_kerberos kr; +}; + +static void dcesrv_netr_LogonSamLogon_base_auth_done(struct tevent_req *subreq); +static void dcesrv_netr_LogonSamLogon_base_krb5_done(struct tevent_req *subreq); +static void dcesrv_netr_LogonSamLogon_base_reply( + struct dcesrv_netr_LogonSamLogon_base_state *state); + /* netr_LogonSamLogon_base @@ -898,18 +968,18 @@ static NTSTATUS dcesrv_netr_LogonSamLogon_check(const struct netr_LogonSamLogonE 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) +static NTSTATUS dcesrv_netr_LogonSamLogon_base_call(struct dcesrv_netr_LogonSamLogon_base_state *state) { + struct dcesrv_call_state *dce_call = state->dce_call; + TALLOC_CTX *mem_ctx = state->mem_ctx; + struct netr_LogonSamLogonEx *r = &state->r; + struct netlogon_creds_CredentialState *creds = state->creds; 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_SamInfo2 *sam2 = NULL; - struct netr_SamInfo3 *sam3 = NULL; - struct netr_SamInfo6 *sam6 = NULL; + struct tevent_req *subreq = NULL; *r->out.authoritative = 1; @@ -1048,15 +1118,9 @@ static NTSTATUS dcesrv_netr_LogonSamLogon_base(struct dcesrv_call_state *dce_cal } if (strcmp(r->in.logon->generic->package_name.string, "Kerberos") == 0) { - NTSTATUS status; struct dcerpc_binding_handle *irpc_handle; - struct kdc_check_generic_kerberos check; struct netr_GenericInfo2 *generic = talloc_zero(mem_ctx, struct netr_GenericInfo2); NT_STATUS_HAVE_NO_MEMORY(generic); - *r->out.authoritative = 1; - - /* TODO: Describe and deal with these flags */ - *r->out.flags = 0; r->out.validation->generic = generic; @@ -1068,24 +1132,24 @@ static NTSTATUS dcesrv_netr_LogonSamLogon_base(struct dcesrv_call_state *dce_cal return NT_STATUS_NO_LOGON_SERVERS; } - check.in.generic_request = + state->kr.in.generic_request = data_blob_const(r->in.logon->generic->data, r->in.logon->generic->length); /* - * TODO: make this async and avoid - * dcerpc_binding_handle_set_sync_ev() + * 60 seconds should be enough */ - dcerpc_binding_handle_set_sync_ev(irpc_handle, - dce_call->event_ctx); - status = dcerpc_kdc_check_generic_kerberos_r(irpc_handle, - mem_ctx, - &check); - if (!NT_STATUS_IS_OK(status)) { - return status; + dcerpc_binding_handle_set_timeout(irpc_handle, 60); + subreq = dcerpc_kdc_check_generic_kerberos_r_send(state, + state->dce_call->event_ctx, + irpc_handle, &state->kr); + if (subreq == NULL) { + return NT_STATUS_NO_MEMORY; } - generic->length = check.out.generic_reply.length; - generic->data = check.out.generic_reply.data; + state->dce_call->state_flags |= DCESRV_CALL_STATE_FLAG_ASYNC; + tevent_req_set_callback(subreq, + dcesrv_netr_LogonSamLogon_base_krb5_done, + state); return NT_STATUS_OK; } @@ -1096,16 +1160,48 @@ static NTSTATUS dcesrv_netr_LogonSamLogon_base(struct dcesrv_call_state *dce_cal return NT_STATUS_INVALID_PARAMETER; } - nt_status = auth_check_password(auth_context, mem_ctx, user_info, - &user_info_dc, r->out.authoritative); - NT_STATUS_NOT_OK_RETURN(nt_status); + subreq = auth_check_password_send(state, state->dce_call->event_ctx, + auth_context, user_info); + state->dce_call->state_flags |= DCESRV_CALL_STATE_FLAG_ASYNC; + tevent_req_set_callback(subreq, + dcesrv_netr_LogonSamLogon_base_auth_done, + state); + return NT_STATUS_OK; +} + +static void dcesrv_netr_LogonSamLogon_base_auth_done(struct tevent_req *subreq) +{ + struct dcesrv_netr_LogonSamLogon_base_state *state = + tevent_req_callback_data(subreq, + struct dcesrv_netr_LogonSamLogon_base_state); + TALLOC_CTX *mem_ctx = state->mem_ctx; + struct netr_LogonSamLogonEx *r = &state->r; + struct auth_user_info_dc *user_info_dc = NULL; + struct netr_SamInfo2 *sam2 = NULL; + struct netr_SamInfo3 *sam3 = NULL; + struct netr_SamInfo6 *sam6 = NULL; + NTSTATUS nt_status; + + nt_status = auth_check_password_recv(subreq, mem_ctx, + &user_info_dc, + r->out.authoritative); + TALLOC_FREE(subreq); + if (!NT_STATUS_IS_OK(nt_status)) { + r->out.result = nt_status; + dcesrv_netr_LogonSamLogon_base_reply(state); + return; + } switch (r->in.validation_level) { case 2: nt_status = auth_convert_user_info_dc_saminfo2(mem_ctx, user_info_dc, &sam2); - NT_STATUS_NOT_OK_RETURN(nt_status); + if (!NT_STATUS_IS_OK(nt_status)) { + r->out.result = nt_status; + dcesrv_netr_LogonSamLogon_base_reply(state); + return; + } r->out.validation->sam2 = sam2; break; @@ -1114,54 +1210,140 @@ static NTSTATUS dcesrv_netr_LogonSamLogon_base(struct dcesrv_call_state *dce_cal nt_status = auth_convert_user_info_dc_saminfo3(mem_ctx, user_info_dc, &sam3); - NT_STATUS_NOT_OK_RETURN(nt_status); + if (!NT_STATUS_IS_OK(nt_status)) { + r->out.result = nt_status; + dcesrv_netr_LogonSamLogon_base_reply(state); + return; + } r->out.validation->sam3 = sam3; break; case 6: - if (dce_call->conn->auth_state.auth_level < DCERPC_AUTH_LEVEL_PRIVACY) { - return NT_STATUS_INVALID_PARAMETER; - } - nt_status = auth_convert_user_info_dc_saminfo6(mem_ctx, user_info_dc, &sam6); - NT_STATUS_NOT_OK_RETURN(nt_status); + if (!NT_STATUS_IS_OK(nt_status)) { + r->out.result = nt_status; + dcesrv_netr_LogonSamLogon_base_reply(state); + return; + } r->out.validation->sam6 = sam6; break; default: - return NT_STATUS_INVALID_INFO_CLASS; + if (!NT_STATUS_IS_OK(nt_status)) { + r->out.result = NT_STATUS_INVALID_INFO_CLASS; + dcesrv_netr_LogonSamLogon_base_reply(state); + return; + } + } + + /* TODO: Describe and deal with these flags */ + *r->out.flags = 0; + + r->out.result = NT_STATUS_OK; + + dcesrv_netr_LogonSamLogon_base_reply(state); +} + +static void dcesrv_netr_LogonSamLogon_base_krb5_done(struct tevent_req *subreq) +{ + struct dcesrv_netr_LogonSamLogon_base_state *state = + tevent_req_callback_data(subreq, + struct dcesrv_netr_LogonSamLogon_base_state); + TALLOC_CTX *mem_ctx = state->mem_ctx; + struct netr_LogonSamLogonEx *r = &state->r; + struct netr_GenericInfo2 *generic = NULL; + NTSTATUS status; + + status = dcerpc_kdc_check_generic_kerberos_r_recv(subreq, mem_ctx); + TALLOC_FREE(subreq); + if (!NT_STATUS_IS_OK(status)) { + r->out.result = status; + dcesrv_netr_LogonSamLogon_base_reply(state); + return; } - netlogon_creds_encrypt_samlogon_validation(creds, - r->in.validation_level, - r->out.validation); + generic = r->out.validation->generic; + generic->length = state->kr.out.generic_reply.length; + generic->data = state->kr.out.generic_reply.data; /* TODO: Describe and deal with these flags */ *r->out.flags = 0; - return NT_STATUS_OK; + r->out.result = NT_STATUS_OK; + + dcesrv_netr_LogonSamLogon_base_reply(state); +} + +static void dcesrv_netr_LogonSamLogon_base_reply( + struct dcesrv_netr_LogonSamLogon_base_state *state) +{ + struct netr_LogonSamLogonEx *r = &state->r; + NTSTATUS status; + + if (NT_STATUS_IS_OK(r->out.result)) { + netlogon_creds_encrypt_samlogon_validation(state->creds, + r->in.validation_level, + r->out.validation); + } + + if (state->_r.lslex != NULL) { + struct netr_LogonSamLogonEx *_r = state->_r.lslex; + _r->out.result = r->out.result; + } else if (state->_r.lslwf != NULL) { + struct netr_LogonSamLogonWithFlags *_r = state->_r.lslwf; + _r->out.result = r->out.result; + } else if (state->_r.lsl != NULL) { + struct netr_LogonSamLogon *_r = state->_r.lsl; + _r->out.result = r->out.result; + } + + status = dcesrv_reply(state->dce_call); + if (!NT_STATUS_IS_OK(status)) { + DBG_ERR("dcesrv_reply() failed - %s\n", + nt_errstr(status)); + } } static NTSTATUS dcesrv_netr_LogonSamLogonEx(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct netr_LogonSamLogonEx *r) { + struct dcesrv_netr_LogonSamLogon_base_state *state; NTSTATUS nt_status; - struct netlogon_creds_CredentialState *creds; *r->out.authoritative = 1; - nt_status = dcesrv_netr_LogonSamLogon_check(r); + state = talloc_zero(mem_ctx, struct dcesrv_netr_LogonSamLogon_base_state); + if (state == NULL) { + return NT_STATUS_NO_MEMORY; + } + + state->dce_call = dce_call; + state->mem_ctx = mem_ctx; + + state->r.in.server_name = r->in.server_name; + state->r.in.computer_name = r->in.computer_name; + state->r.in.logon_level = r->in.logon_level; + state->r.in.logon = r->in.logon; + state->r.in.validation_level = r->in.validation_level; + state->r.in.flags = r->in.flags; + state->r.out.validation = r->out.validation; + state->r.out.authoritative = r->out.authoritative; + state->r.out.flags = r->out.flags; + + state->_r.lslex = r; + + nt_status = dcesrv_netr_LogonSamLogon_check(dce_call, &state->r); if (!NT_STATUS_IS_OK(nt_status)) { return nt_status; } nt_status = schannel_get_creds_state(mem_ctx, dce_call->conn->dce_ctx->lp_ctx, - r->in.computer_name, &creds); + r->in.computer_name, &state->creds); if (!NT_STATUS_IS_OK(nt_status)) { return nt_status; } @@ -1169,7 +1351,14 @@ static NTSTATUS dcesrv_netr_LogonSamLogonEx(struct dcesrv_call_state *dce_call, 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); + + nt_status = dcesrv_netr_LogonSamLogon_base_call(state); + + if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) { + return nt_status; + } + + return nt_status; } /* @@ -1179,44 +1368,57 @@ static NTSTATUS dcesrv_netr_LogonSamLogonEx(struct dcesrv_call_state *dce_call, static NTSTATUS dcesrv_netr_LogonSamLogonWithFlags(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct netr_LogonSamLogonWithFlags *r) { + struct dcesrv_netr_LogonSamLogon_base_state *state; NTSTATUS nt_status; - struct netlogon_creds_CredentialState *creds; - struct netr_LogonSamLogonEx r2; - struct netr_Authenticator *return_authenticator; + *r->out.authoritative = 1; - ZERO_STRUCT(r2); + state = talloc_zero(mem_ctx, struct dcesrv_netr_LogonSamLogon_base_state); + if (state == NULL) { + return NT_STATUS_NO_MEMORY; + } + + state->dce_call = dce_call; + state->mem_ctx = mem_ctx; - r2.in.server_name = r->in.server_name; - r2.in.computer_name = r->in.computer_name; - r2.in.logon_level = r->in.logon_level; - r2.in.logon = r->in.logon; - r2.in.validation_level = r->in.validation_level; - r2.in.flags = r->in.flags; - r2.out.validation = r->out.validation; - r2.out.authoritative = r->out.authoritative; - r2.out.flags = r->out.flags; + state->r.in.server_name = r->in.server_name; + state->r.in.computer_name = r->in.computer_name; + state->r.in.logon_level = r->in.logon_level; + state->r.in.logon = r->in.logon; + state->r.in.validation_level = r->in.validation_level; + state->r.in.flags = r->in.flags; + state->r.out.validation = r->out.validation; + state->r.out.authoritative = r->out.authoritative; + state->r.out.flags = r->out.flags; - *r->out.authoritative = 1; + state->_r.lslwf = r; - nt_status = dcesrv_netr_LogonSamLogon_check(&r2); + nt_status = dcesrv_netr_LogonSamLogon_check(dce_call, &state->r); if (!NT_STATUS_IS_OK(nt_status)) { return nt_status; } - return_authenticator = talloc(mem_ctx, struct netr_Authenticator); - NT_STATUS_HAVE_NO_MEMORY(return_authenticator); + r->out.return_authenticator = talloc_zero(mem_ctx, + struct netr_Authenticator); + if (r->out.return_authenticator == NULL) { + return NT_STATUS_NO_MEMORY; + } nt_status = dcesrv_netr_creds_server_step_check(dce_call, mem_ctx, r->in.computer_name, - r->in.credential, return_authenticator, - &creds); - NT_STATUS_NOT_OK_RETURN(nt_status); + r->in.credential, + r->out.return_authenticator, + &state->creds); + if (!NT_STATUS_IS_OK(nt_status)) { + return nt_status; + } - nt_status = dcesrv_netr_LogonSamLogon_base(dce_call, mem_ctx, &r2, creds); + nt_status = dcesrv_netr_LogonSamLogon_base_call(state); - r->out.return_authenticator = return_authenticator; + if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) { + return nt_status; + } return nt_status; } @@ -1227,29 +1429,59 @@ static NTSTATUS dcesrv_netr_LogonSamLogonWithFlags(struct dcesrv_call_state *dce static NTSTATUS dcesrv_netr_LogonSamLogon(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct netr_LogonSamLogon *r) { - struct netr_LogonSamLogonWithFlags r2; - uint32_t flags = 0; - NTSTATUS status; + struct dcesrv_netr_LogonSamLogon_base_state *state; + NTSTATUS nt_status; - ZERO_STRUCT(r2); + *r->out.authoritative = 1; - r2.in.server_name = r->in.server_name; - r2.in.computer_name = r->in.computer_name; - r2.in.credential = r->in.credential; - r2.in.return_authenticator = r->in.return_authenticator; - r2.in.logon_level = r->in.logon_level; - r2.in.logon = r->in.logon; - r2.in.validation_level = r->in.validation_level; - r2.in.flags = &flags; - r2.out.validation = r->out.validation; - r2.out.authoritative = r->out.authoritative; - r2.out.flags = &flags; - - status = dcesrv_netr_LogonSamLogonWithFlags(dce_call, mem_ctx, &r2); + state = talloc_zero(mem_ctx, struct dcesrv_netr_LogonSamLogon_base_state); + if (state == NULL) { + return NT_STATUS_NO_MEMORY; + } - r->out.return_authenticator = r2.out.return_authenticator; + state->dce_call = dce_call; + state->mem_ctx = mem_ctx; - return status; + state->r.in.server_name = r->in.server_name; + state->r.in.computer_name = r->in.computer_name; + state->r.in.logon_level = r->in.logon_level; + state->r.in.logon = r->in.logon; + state->r.in.validation_level = r->in.validation_level; + state->r.in.flags = &state->_ignored_flags; + state->r.out.validation = r->out.validation; + state->r.out.authoritative = r->out.authoritative; + state->r.out.flags = &state->_ignored_flags; + + state->_r.lsl = r; + + nt_status = dcesrv_netr_LogonSamLogon_check(dce_call, &state->r); + if (!NT_STATUS_IS_OK(nt_status)) { + return nt_status; + } + + r->out.return_authenticator = talloc_zero(mem_ctx, + struct netr_Authenticator); + if (r->out.return_authenticator == NULL) { + return NT_STATUS_NO_MEMORY; + } + + nt_status = dcesrv_netr_creds_server_step_check(dce_call, + mem_ctx, + r->in.computer_name, + r->in.credential, + r->out.return_authenticator, + &state->creds); + if (!NT_STATUS_IS_OK(nt_status)) { + return nt_status; + } + + nt_status = dcesrv_netr_LogonSamLogon_base_call(state); + + if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) { + return nt_status; + } + + return nt_status; } @@ -1366,9 +1598,12 @@ static WERROR dcesrv_netr_GetDcName(struct dcesrv_call_state *dce_call, TALLOC_C */ } - sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx, + sam_ctx = samdb_connect(mem_ctx, + dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx, - dce_call->conn->auth_state.session_info, 0); + dce_call->conn->auth_state.session_info, + dce_call->conn->remote_address, + 0); if (sam_ctx == NULL) { return WERR_DS_UNAVAILABLE; } @@ -1568,8 +1803,13 @@ static WERROR dcesrv_netr_LogonControl_base_call(struct dcesrv_netr_LogonControl if (!ok) { struct ldb_context *sam_ctx; - sam_ctx = samdb_connect(state, state->dce_call->event_ctx, - lp_ctx, system_session(lp_ctx), 0); + sam_ctx = samdb_connect( + state, + state->dce_call->event_ctx, + lp_ctx, + system_session(lp_ctx), + state->dce_call->conn->remote_address, + 0); if (sam_ctx == NULL) { return WERR_DS_UNAVAILABLE; } @@ -1779,8 +2019,12 @@ static WERROR dcesrv_netr_GetAnyDCName(struct dcesrv_call_state *dce_call, TALLO r->in.domainname = lpcfg_workgroup(lp_ctx); } - sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx, lp_ctx, - dce_call->conn->auth_state.session_info, 0); + sam_ctx = samdb_connect(mem_ctx, + dce_call->event_ctx, + lp_ctx, + dce_call->conn->auth_state.session_info, + dce_call->conn->remote_address, + 0); if (sam_ctx == NULL) { return WERR_DS_UNAVAILABLE; } @@ -1925,8 +2169,12 @@ static WERROR dcesrv_netr_DsRGetSiteName(struct dcesrv_call_state *dce_call, TAL struct ldb_context *sam_ctx; struct loadparm_context *lp_ctx = dce_call->conn->dce_ctx->lp_ctx; - sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx, lp_ctx, - dce_call->conn->auth_state.session_info, 0); + sam_ctx = samdb_connect(mem_ctx, + dce_call->event_ctx, + lp_ctx, + dce_call->conn->auth_state.session_info, + dce_call->conn->remote_address, + 0); if (sam_ctx == NULL) { return WERR_DS_UNAVAILABLE; } @@ -1944,58 +2192,123 @@ static WERROR dcesrv_netr_DsRGetSiteName(struct dcesrv_call_state *dce_call, TAL /* - fill in a netr_OneDomainInfo from a ldb search result + fill in a netr_OneDomainInfo from our own domain/forest */ -static NTSTATUS fill_one_domain_info(TALLOC_CTX *mem_ctx, - struct loadparm_context *lp_ctx, - struct ldb_context *sam_ctx, - struct ldb_message *res, - struct netr_OneDomainInfo *info, - bool is_local, bool is_trust_list) +static NTSTATUS fill_our_one_domain_info(TALLOC_CTX *mem_ctx, + const struct lsa_TrustDomainInfoInfoEx *our_tdo, + struct GUID domain_guid, + struct netr_OneDomainInfo *info, + bool is_trust_list) { ZERO_STRUCTP(info); if (is_trust_list) { + struct netr_trust_extension *tei = NULL; + /* w2k8 only fills this on trusted domains */ - info->trust_extension.info = talloc_zero(mem_ctx, struct netr_trust_extension); - info->trust_extension.length = 16; - info->trust_extension.info->flags = - NETR_TRUST_FLAG_TREEROOT | - NETR_TRUST_FLAG_IN_FOREST | - NETR_TRUST_FLAG_PRIMARY | - NETR_TRUST_FLAG_NATIVE; + tei = talloc_zero(mem_ctx, struct netr_trust_extension); + if (tei == NULL) { + return NT_STATUS_NO_MEMORY; + } + tei->flags |= NETR_TRUST_FLAG_PRIMARY; + + /* + * We're always within a native forest + */ + tei->flags |= NETR_TRUST_FLAG_IN_FOREST; + tei->flags |= NETR_TRUST_FLAG_NATIVE; + + /* For now we assume we're always the tree root */ + tei->flags |= NETR_TRUST_FLAG_TREEROOT; + tei->parent_index = 0; + + tei->trust_type = our_tdo->trust_type; + /* + * This needs to be 0 instead of our_tdo->trust_attributes + * It means LSA_TRUST_ATTRIBUTE_WITHIN_FOREST won't + * be set, while NETR_TRUST_FLAG_IN_FOREST is set above. + */ + tei->trust_attributes = 0; - info->trust_extension.info->parent_index = 0; /* should be index into array - of parent */ - info->trust_extension.info->trust_type = LSA_TRUST_TYPE_UPLEVEL; /* should be based on ldb search for trusts */ - info->trust_extension.info->trust_attributes = 0; /* TODO: base on ldb search? */ + info->trust_extension.info = tei; + info->trust_extension.length = 16; } if (is_trust_list) { + info->dns_domainname.string = our_tdo->domain_name.string; + /* MS-NRPC 3.5.4.3.9 - must be set to NULL for trust list */ info->dns_forestname.string = NULL; } else { - info->dns_forestname.string = samdb_forest_name(sam_ctx, mem_ctx); - NT_STATUS_HAVE_NO_MEMORY(info->dns_forestname.string); - info->dns_forestname.string = talloc_asprintf(mem_ctx, "%s.", info->dns_forestname.string); - NT_STATUS_HAVE_NO_MEMORY(info->dns_forestname.string); + info->dns_domainname.string = talloc_asprintf(mem_ctx, "%s.", + our_tdo->domain_name.string); + if (info->dns_domainname.string == NULL) { + return NT_STATUS_NO_MEMORY; + } + + info->dns_forestname.string = info->dns_domainname.string; } - if (is_local) { - info->domainname.string = lpcfg_workgroup(lp_ctx); - info->dns_domainname.string = lpcfg_dnsdomain(lp_ctx); - info->domain_guid = samdb_result_guid(res, "objectGUID"); - info->domain_sid = samdb_result_dom_sid(mem_ctx, res, "objectSid"); - } else { - info->domainname.string = ldb_msg_find_attr_as_string(res, "flatName", NULL); - info->dns_domainname.string = ldb_msg_find_attr_as_string(res, "trustPartner", NULL); - info->domain_guid = samdb_result_guid(res, "objectGUID"); - info->domain_sid = samdb_result_dom_sid(mem_ctx, res, "securityIdentifier"); + info->domainname.string = our_tdo->netbios_name.string; + info->domain_sid = our_tdo->sid; + info->domain_guid = domain_guid; + + return NT_STATUS_OK; +} + +/* + fill in a netr_OneDomainInfo from a trust tdo +*/ +static NTSTATUS fill_trust_one_domain_info(TALLOC_CTX *mem_ctx, + struct GUID domain_guid, + const struct lsa_TrustDomainInfoInfoEx *tdo, + struct netr_OneDomainInfo *info) +{ + struct netr_trust_extension *tei = NULL; + + ZERO_STRUCTP(info); + + /* w2k8 only fills this on trusted domains */ + tei = talloc_zero(mem_ctx, struct netr_trust_extension); + if (tei == NULL) { + return NT_STATUS_NO_MEMORY; + } + + if (tdo->trust_direction & LSA_TRUST_DIRECTION_INBOUND) { + tei->flags |= NETR_TRUST_FLAG_INBOUND; + } + if (tdo->trust_direction & LSA_TRUST_DIRECTION_OUTBOUND) { + tei->flags |= NETR_TRUST_FLAG_OUTBOUND; } - if (!is_trust_list) { - info->dns_domainname.string = talloc_asprintf(mem_ctx, "%s.", info->dns_domainname.string); + if (tdo->trust_attributes & LSA_TRUST_ATTRIBUTE_WITHIN_FOREST) { + tei->flags |= NETR_TRUST_FLAG_IN_FOREST; } + /* + * TODO: once we support multiple domains within our forest, + * we need to fill this correct (or let the caller do it + * for all domains marked with NETR_TRUST_FLAG_IN_FOREST). + */ + tei->parent_index = 0; + + tei->trust_type = tdo->trust_type; + tei->trust_attributes = tdo->trust_attributes; + + info->trust_extension.info = tei; + info->trust_extension.length = 16; + + info->domainname.string = tdo->netbios_name.string; + if (tdo->trust_type != LSA_TRUST_TYPE_DOWNLEVEL) { + info->dns_domainname.string = tdo->domain_name.string; + } else { + info->dns_domainname.string = NULL; + } + info->domain_sid = tdo->sid; + info->domain_guid = domain_guid; + + /* MS-NRPC 3.5.4.3.9 - must be set to NULL for trust list */ + info->dns_forestname.string = NULL; + return NT_STATUS_OK; } @@ -2010,19 +2323,29 @@ static NTSTATUS dcesrv_netr_LogonGetDomainInfo(struct dcesrv_call_state *dce_cal TALLOC_CTX *mem_ctx, struct netr_LogonGetDomainInfo *r) { struct netlogon_creds_CredentialState *creds; - const char * const attrs[] = { "objectSid", "objectGUID", "flatName", - "securityIdentifier", "trustPartner", NULL }; + const char * const trusts_attrs[] = { + "securityIdentifier", + "flatName", + "trustPartner", + "trustAttributes", + "trustDirection", + "trustType", + NULL + }; const char * const attrs2[] = { "sAMAccountName", "dNSHostName", "msDS-SupportedEncryptionTypes", NULL }; const char *sam_account_name, *old_dns_hostname, *prefix1, *prefix2; struct ldb_context *sam_ctx; - struct ldb_message **res1, **res2, **res3, *new_msg; + const struct GUID *our_domain_guid = NULL; + struct lsa_TrustDomainInfoInfoEx *our_tdo = NULL; + struct ldb_message **res1, *new_msg; + struct ldb_result *trusts_res = NULL; struct ldb_dn *workstation_dn; struct netr_DomainInformation *domain_info; struct netr_LsaPolicyInformation *lsa_policy_info; uint32_t default_supported_enc_types = 0xFFFFFFFF; bool update_dns_hostname = true; - int ret, ret3, i; + int ret, i; NTSTATUS status; status = dcesrv_netr_creds_server_step_check(dce_call, @@ -2048,9 +2371,12 @@ static NTSTATUS dcesrv_netr_LogonGetDomainInfo(struct dcesrv_call_state *dce_cal } NT_STATUS_NOT_OK_RETURN(status); - sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx, + 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); + system_session(dce_call->conn->dce_ctx->lp_ctx), + dce_call->conn->remote_address, + 0); if (sam_ctx == NULL) { return NT_STATUS_INVALID_SYSTEM_SERVICE; } @@ -2231,21 +2557,23 @@ static NTSTATUS dcesrv_netr_LogonGetDomainInfo(struct dcesrv_call_state *dce_cal /* Writes back the domain information */ - /* We need to do two searches. The first will pull our primary - domain and the second will pull any trusted domains. Our - primary domain is also a "trusted" domain, so we need to - put the primary domain into the lists of returned trusts as - well. */ - ret = gendb_search_dn(sam_ctx, mem_ctx, ldb_get_default_basedn(sam_ctx), - &res2, attrs); - if (ret != 1) { + our_domain_guid = samdb_domain_guid(sam_ctx); + if (our_domain_guid == NULL) { return NT_STATUS_INTERNAL_DB_CORRUPTION; } - ret3 = gendb_search(sam_ctx, mem_ctx, NULL, &res3, attrs, - "(objectClass=trustedDomain)"); - if (ret3 == -1) { - return NT_STATUS_INTERNAL_DB_CORRUPTION; + status = dsdb_trust_local_tdo_info(mem_ctx, sam_ctx, &our_tdo); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + status = dsdb_trust_search_tdos(sam_ctx, + NULL, /* exclude */ + trusts_attrs, + mem_ctx, + &trusts_res); + if (!NT_STATUS_IS_OK(status)) { + return status; } domain_info = talloc(mem_ctx, struct netr_DomainInformation); @@ -2255,31 +2583,52 @@ static NTSTATUS dcesrv_netr_LogonGetDomainInfo(struct dcesrv_call_state *dce_cal /* Informations about the local and trusted domains */ - status = fill_one_domain_info(mem_ctx, - dce_call->conn->dce_ctx->lp_ctx, - sam_ctx, res2[0], &domain_info->primary_domain, - true, false); - NT_STATUS_NOT_OK_RETURN(status); + status = fill_our_one_domain_info(mem_ctx, + our_tdo, + *our_domain_guid, + &domain_info->primary_domain, + false); + if (!NT_STATUS_IS_OK(status)) { + return status; + } - domain_info->trusted_domain_count = ret3 + 1; - domain_info->trusted_domains = talloc_array(mem_ctx, + domain_info->trusted_domain_count = trusts_res->count + 1; + domain_info->trusted_domains = talloc_zero_array(mem_ctx, struct netr_OneDomainInfo, domain_info->trusted_domain_count); NT_STATUS_HAVE_NO_MEMORY(domain_info->trusted_domains); - for (i=0;iconn->dce_ctx->lp_ctx, - sam_ctx, res3[i], - &domain_info->trusted_domains[i], - false, true); - NT_STATUS_NOT_OK_RETURN(status); + for (i=0; i < trusts_res->count; i++) { + struct netr_OneDomainInfo *o = + &domain_info->trusted_domains[i]; + /* we can't know the guid of trusts outside our forest */ + struct GUID trust_domain_guid = GUID_zero(); + struct lsa_TrustDomainInfoInfoEx *tdo = NULL; + + status = dsdb_trust_parse_tdo_info(mem_ctx, + trusts_res->msgs[i], + &tdo); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + status = fill_trust_one_domain_info(mem_ctx, + trust_domain_guid, + tdo, + o); + if (!NT_STATUS_IS_OK(status)) { + return status; + } } - status = fill_one_domain_info(mem_ctx, - dce_call->conn->dce_ctx->lp_ctx, sam_ctx, res2[0], - &domain_info->trusted_domains[i], true, true); - NT_STATUS_NOT_OK_RETURN(status); + status = fill_our_one_domain_info(mem_ctx, + our_tdo, + *our_domain_guid, + &domain_info->trusted_domains[i], + true); + if (!NT_STATUS_IS_OK(status)) { + return status; + } /* Sets the supported encryption types */ domain_info->supported_enc_types = ldb_msg_find_attr_as_uint(res1[0], @@ -2445,9 +2794,12 @@ static NTSTATUS dcesrv_netr_NetrLogonSendToSam(struct dcesrv_call_state *dce_cal return NT_STATUS_INVALID_PARAMETER; } - sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx, + 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); + system_session(dce_call->conn->dce_ctx->lp_ctx), + dce_call->conn->remote_address, + 0); if (sam_ctx == NULL) { return NT_STATUS_INVALID_SYSTEM_SERVICE; } @@ -2532,14 +2884,27 @@ static NTSTATUS dcesrv_netr_NetrLogonSendToSam(struct dcesrv_call_state *dce_cal return NT_STATUS_OK; } +struct dcesrv_netr_DsRGetDCName_base_state { + struct dcesrv_call_state *dce_call; + TALLOC_CTX *mem_ctx; -/* - netr_DsRGetDCNameEx2 -*/ -static WERROR dcesrv_netr_DsRGetDCNameEx2(struct dcesrv_call_state *dce_call, - TALLOC_CTX *mem_ctx, - struct netr_DsRGetDCNameEx2 *r) + struct netr_DsRGetDCNameEx2 r; + const char *client_site; + + struct { + struct netr_DsRGetDCName *dc; + struct netr_DsRGetDCNameEx *dcex; + struct netr_DsRGetDCNameEx2 *dcex2; + } _r; +}; + +static void dcesrv_netr_DsRGetDCName_base_done(struct tevent_req *subreq); + +static WERROR dcesrv_netr_DsRGetDCName_base_call(struct dcesrv_netr_DsRGetDCName_base_state *state) { + struct dcesrv_call_state *dce_call = state->dce_call; + TALLOC_CTX *mem_ctx = state->mem_ctx; + struct netr_DsRGetDCNameEx2 *r = &state->r; struct ldb_context *sam_ctx; struct netr_DsRGetDCNameInfo *info; struct loadparm_context *lp_ctx = dce_call->conn->dce_ctx->lp_ctx; @@ -2554,24 +2919,29 @@ static WERROR dcesrv_netr_DsRGetDCNameEx2(struct dcesrv_call_state *dce_call, const char *dc_name = NULL; const char *domain_name = NULL; const char *pdc_ip; + bool different_domain = true; ZERO_STRUCTP(r->out.info); - sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx, lp_ctx, - dce_call->conn->auth_state.session_info, 0); + sam_ctx = samdb_connect(state, + dce_call->event_ctx, + lp_ctx, + dce_call->conn->auth_state.session_info, + dce_call->conn->remote_address, + 0); if (sam_ctx == NULL) { return WERR_DS_UNAVAILABLE; } local_address = dcesrv_connection_get_local_address(dce_call->conn); if (tsocket_address_is_inet(local_address, "ip")) { - local_addr = tsocket_address_inet_addr_string(local_address, mem_ctx); + local_addr = tsocket_address_inet_addr_string(local_address, state); W_ERROR_HAVE_NO_MEMORY(local_addr); } remote_address = dcesrv_connection_get_remote_address(dce_call->conn); if (tsocket_address_is_inet(remote_address, "ip")) { - remote_addr = tsocket_address_inet_addr_string(remote_address, mem_ctx); + remote_addr = tsocket_address_inet_addr_string(remote_address, state); W_ERROR_HAVE_NO_MEMORY(remote_addr); } @@ -2614,16 +2984,104 @@ static WERROR dcesrv_netr_DsRGetDCNameEx2(struct dcesrv_call_state *dce_call, return WERR_INVALID_FLAGS; } + /* + * If we send an all-zero GUID, we should ignore it as winbind actually + * checks it with a DNS query. Windows also appears to ignore it. + */ + if (r->in.domain_guid != NULL && GUID_all_zero(r->in.domain_guid)) { + r->in.domain_guid = NULL; + } + + /* Attempt winbind search only if we suspect the domain is incorrect */ + if (r->in.domain_name != NULL && strcmp("", r->in.domain_name) != 0) { + if (r->in.flags & DS_IS_FLAT_NAME) { + if (strcasecmp_m(r->in.domain_name, + lpcfg_sam_name(lp_ctx)) == 0) { + different_domain = false; + } + } else if (r->in.flags & DS_IS_DNS_NAME) { + if (strcasecmp_m(r->in.domain_name, + lpcfg_dnsdomain(lp_ctx)) == 0) { + different_domain = false; + } + } else { + if (strcasecmp_m(r->in.domain_name, + lpcfg_sam_name(lp_ctx)) == 0 || + strcasecmp_m(r->in.domain_name, + lpcfg_dnsdomain(lp_ctx)) == 0) { + different_domain = false; + } + } + } else { + /* + * We need to be able to handle empty domain names, where we + * revert to our domain by default. + */ + different_domain = false; + } + /* Proof server site parameter "site_name" if it was specified */ - server_site_name = samdb_server_site_name(sam_ctx, mem_ctx); + server_site_name = samdb_server_site_name(sam_ctx, state); W_ERROR_HAVE_NO_MEMORY(server_site_name); - if ((r->in.site_name != NULL) && (strcasecmp(r->in.site_name, - server_site_name) != 0)) { - return WERR_NO_SUCH_DOMAIN; + if (different_domain || (r->in.site_name != NULL && + (strcasecmp_m(r->in.site_name, + server_site_name) != 0))) { + + struct dcerpc_binding_handle *irpc_handle = NULL; + struct tevent_req *subreq = NULL; + + /* + * Retrieve the client site to override the winbind response. + * + * DO NOT use Windows fallback for client site. + * In the case of multiple domains, this is plainly wrong. + * + * Note: It's possible that the client may belong to multiple + * subnets across domains. It's not clear what this would mean, + * but here we only return what this domain knows. + */ + state->client_site = samdb_client_site_name(sam_ctx, + state, + remote_addr, + NULL, + false); + + irpc_handle = irpc_binding_handle_by_name(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")); + dce_call->fault_code = DCERPC_FAULT_CANT_PERFORM; + return WERR_SERVICE_NOT_FOUND; + } + + dcerpc_binding_handle_set_timeout(irpc_handle, 60); + + dce_call->state_flags |= DCESRV_CALL_STATE_FLAG_ASYNC; + + subreq = dcerpc_wbint_DsGetDcName_send(state, + dce_call->event_ctx, + irpc_handle, + r->in.domain_name, + r->in.domain_guid, + r->in.site_name, + r->in.flags, + r->out.info); + if (subreq == NULL) { + return WERR_NOT_ENOUGH_MEMORY; + } + + tevent_req_set_callback(subreq, + dcesrv_netr_DsRGetDCName_base_done, + state); + + return WERR_OK; } guid_str = r->in.domain_guid != NULL ? - GUID_string(mem_ctx, r->in.domain_guid) : NULL; + GUID_string(state, r->in.domain_guid) : NULL; status = fill_netlogon_samlogon_response(sam_ctx, mem_ctx, r->in.domain_name, @@ -2710,55 +3168,194 @@ static WERROR dcesrv_netr_DsRGetDCNameEx2(struct dcesrv_call_state *dce_call, return WERR_OK; } +static void dcesrv_netr_DsRGetDCName_base_done(struct tevent_req *subreq) +{ + struct dcesrv_netr_DsRGetDCName_base_state *state = + tevent_req_callback_data(subreq, + struct dcesrv_netr_DsRGetDCName_base_state); + struct dcesrv_call_state *dce_call = state->dce_call; + NTSTATUS result, status; + + status = dcerpc_wbint_DsGetDcName_recv(subreq, + state->mem_ctx, + &result); + TALLOC_FREE(subreq); + + if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) { + state->r.out.result = WERR_TIMEOUT; + goto finished; + } + + if (!NT_STATUS_IS_OK(status)) { + DBG_ERR(__location__ ": IRPC callback failed %s\n", + nt_errstr(status)); + state->r.out.result = WERR_GEN_FAILURE; + goto finished; + } + + if (!NT_STATUS_IS_OK(result)) { + DBG_NOTICE("DC location via winbind failed - %s\n", + nt_errstr(result)); + state->r.out.result = WERR_NO_SUCH_DOMAIN; + goto finished; + } + + if (state->r.out.info == NULL || state->r.out.info[0] == NULL) { + DBG_ERR("DC location via winbind returned no results\n"); + state->r.out.result = WERR_GEN_FAILURE; + goto finished; + } + + if (state->r.out.info[0]->dc_unc == NULL) { + DBG_ERR("DC location via winbind returned no DC unc\n"); + state->r.out.result = WERR_GEN_FAILURE; + goto finished; + } + + /* + * Either the supplied site name is NULL (possibly via + * TRY_NEXT_CLOSEST_SITE) or the resulting site name matches + * the input match name. + * + * TODO: Currently this means that requests with NETBIOS domain + * names can fail because they do not return the site name. + */ + if (state->r.in.site_name == NULL || + strcasecmp_m("", state->r.in.site_name) == 0 || + (state->r.out.info[0]->dc_site_name != NULL && + strcasecmp_m(state->r.out.info[0]->dc_site_name, + state->r.in.site_name) == 0)) { + + state->r.out.info[0]->client_site_name = + talloc_move(state->mem_ctx, &state->client_site); + + /* + * Make sure to return our DC UNC with // prefix. + * Winbind currently doesn't send the leading slashes + * for some reason. + */ + if (strlen(state->r.out.info[0]->dc_unc) > 2 && + strncmp("\\\\", state->r.out.info[0]->dc_unc, 2) != 0) { + const char *dc_unc = NULL; + + dc_unc = talloc_asprintf(state->mem_ctx, + "\\\\%s", + state->r.out.info[0]->dc_unc); + state->r.out.info[0]->dc_unc = dc_unc; + } + + state->r.out.result = WERR_OK; + } else { + state->r.out.info = NULL; + state->r.out.result = WERR_NO_SUCH_DOMAIN; + } + +finished: + if (state->_r.dcex2 != NULL) { + struct netr_DsRGetDCNameEx2 *r = state->_r.dcex2; + r->out.result = state->r.out.result; + } else if (state->_r.dcex != NULL) { + struct netr_DsRGetDCNameEx *r = state->_r.dcex; + r->out.result = state->r.out.result; + } else if (state->_r.dc != NULL) { + struct netr_DsRGetDCName *r = state->_r.dc; + r->out.result = state->r.out.result; + } + + 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))); + } +} + +/* + netr_DsRGetDCNameEx2 +*/ +static WERROR dcesrv_netr_DsRGetDCNameEx2(struct dcesrv_call_state *dce_call, + TALLOC_CTX *mem_ctx, + struct netr_DsRGetDCNameEx2 *r) +{ + struct dcesrv_netr_DsRGetDCName_base_state *state; + + state = talloc_zero(mem_ctx, struct dcesrv_netr_DsRGetDCName_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.dcex2 = r; + + return dcesrv_netr_DsRGetDCName_base_call(state); +} + /* netr_DsRGetDCNameEx */ static WERROR dcesrv_netr_DsRGetDCNameEx(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct netr_DsRGetDCNameEx *r) { - struct netr_DsRGetDCNameEx2 r2; - WERROR werr; + struct dcesrv_netr_DsRGetDCName_base_state *state; - ZERO_STRUCT(r2); + state = talloc_zero(mem_ctx, struct dcesrv_netr_DsRGetDCName_base_state); + if (state == NULL) { + return WERR_NOT_ENOUGH_MEMORY; + } + + state->dce_call = dce_call; + state->mem_ctx = mem_ctx; - r2.in.server_unc = r->in.server_unc; - r2.in.client_account = NULL; - r2.in.mask = 0; - r2.in.domain_guid = r->in.domain_guid; - r2.in.domain_name = r->in.domain_name; - r2.in.site_name = r->in.site_name; - r2.in.flags = r->in.flags; - r2.out.info = r->out.info; + state->r.in.server_unc = r->in.server_unc; + state->r.in.client_account = NULL; + state->r.in.mask = 0; + state->r.in.domain_guid = r->in.domain_guid; + state->r.in.domain_name = r->in.domain_name; + state->r.in.site_name = r->in.site_name; + state->r.in.flags = r->in.flags; + state->r.out.info = r->out.info; - werr = dcesrv_netr_DsRGetDCNameEx2(dce_call, mem_ctx, &r2); + state->_r.dcex = r; - return werr; + return dcesrv_netr_DsRGetDCName_base_call(state); } /* - netr_DsRGetDCName -*/ + * netr_DsRGetDCName + * + * This function is a predecessor to DsrGetDcNameEx2 according to [MS-NRPC]. + * Although it has a site-guid parameter, the documentation 3.5.4.3.3 DsrGetDcName + * insists that it be ignored. + */ static WERROR dcesrv_netr_DsRGetDCName(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, - struct netr_DsRGetDCName *r) + struct netr_DsRGetDCName *r) { - struct netr_DsRGetDCNameEx2 r2; - WERROR werr; + struct dcesrv_netr_DsRGetDCName_base_state *state; - ZERO_STRUCT(r2); + state = talloc_zero(mem_ctx, struct dcesrv_netr_DsRGetDCName_base_state); + if (state == NULL) { + return WERR_NOT_ENOUGH_MEMORY; + } - r2.in.server_unc = r->in.server_unc; - r2.in.client_account = NULL; - r2.in.mask = 0; - r2.in.domain_name = r->in.domain_name; - r2.in.domain_guid = r->in.domain_guid; + state->dce_call = dce_call; + state->mem_ctx = mem_ctx; - r2.in.site_name = NULL; /* this is correct, we should ignore site GUID */ - r2.in.flags = r->in.flags; - r2.out.info = r->out.info; + state->r.in.server_unc = r->in.server_unc; + state->r.in.client_account = NULL; + state->r.in.mask = 0; + state->r.in.domain_name = r->in.domain_name; + state->r.in.domain_guid = r->in.domain_guid; - werr = dcesrv_netr_DsRGetDCNameEx2(dce_call, mem_ctx, &r2); + state->r.in.site_name = NULL; /* this is correct, we should ignore site GUID */ + state->r.in.flags = r->in.flags; + state->r.out.info = r->out.info; - return werr; + state->_r.dc = r; + + return dcesrv_netr_DsRGetDCName_base_call(state); } /* netr_NETRLOGONGETTIMESERVICEPARENTDOMAIN @@ -2801,8 +3398,12 @@ static WERROR dcesrv_netr_DsRAddressToSitenamesExW(struct dcesrv_call_state *dce const char *res; uint32_t i; - sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx, lp_ctx, - dce_call->conn->auth_state.session_info, 0); + sam_ctx = samdb_connect(mem_ctx, + dce_call->event_ctx, + lp_ctx, + dce_call->conn->auth_state.session_info, + dce_call->conn->remote_address, + 0); if (sam_ctx == NULL) { return WERR_DS_UNAVAILABLE; } @@ -2859,7 +3460,8 @@ static WERROR dcesrv_netr_DsRAddressToSitenamesExW(struct dcesrv_call_state *dce ctr->sitename[i].string = samdb_client_site_name(sam_ctx, mem_ctx, addr_str, - &subnet_name); + &subnet_name, + true); W_ERROR_HAVE_NO_MEMORY(ctr->sitename[i].string); ctr->subnetname[i].string = subnet_name; } @@ -2917,8 +3519,12 @@ static WERROR dcesrv_netr_DsrGetDcSiteCoverageW(struct dcesrv_call_state *dce_ca struct DcSitesCtr *ctr; struct loadparm_context *lp_ctx = dce_call->conn->dce_ctx->lp_ctx; - sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx, lp_ctx, - dce_call->conn->auth_state.session_info, 0); + sam_ctx = samdb_connect(mem_ctx, + dce_call->event_ctx, + lp_ctx, + dce_call->conn->auth_state.session_info, + dce_call->conn->remote_address, + 0); if (sam_ctx == NULL) { return WERR_DS_UNAVAILABLE; } @@ -3085,8 +3691,12 @@ static WERROR dcesrv_netr_DsrEnumerateDomainTrusts(struct dcesrv_call_state *dce trusts->count = 0; r->out.trusts = trusts; - sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx, lp_ctx, - dce_call->conn->auth_state.session_info, 0); + sam_ctx = samdb_connect(mem_ctx, + dce_call->event_ctx, + lp_ctx, + dce_call->conn->auth_state.session_info, + dce_call->conn->remote_address, + 0); if (sam_ctx == NULL) { return WERR_GEN_FAILURE; } @@ -3218,8 +3828,12 @@ static WERROR dcesrv_netr_DsRGetForestTrustInformation(struct dcesrv_call_state return WERR_INVALID_FLAGS; } - sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx, lp_ctx, - dce_call->conn->auth_state.session_info, 0); + sam_ctx = samdb_connect(mem_ctx, + dce_call->event_ctx, + lp_ctx, + dce_call->conn->auth_state.session_info, + dce_call->conn->remote_address, + 0); if (sam_ctx == NULL) { return WERR_GEN_FAILURE; } @@ -3370,8 +3984,12 @@ static NTSTATUS dcesrv_netr_GetForestTrustInformation(struct dcesrv_call_state * return NT_STATUS_NOT_IMPLEMENTED; } - sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx, lp_ctx, - dce_call->conn->auth_state.session_info, 0); + sam_ctx = samdb_connect(mem_ctx, + dce_call->event_ctx, + lp_ctx, + dce_call->conn->auth_state.session_info, + dce_call->conn->remote_address, + 0); if (sam_ctx == NULL) { return NT_STATUS_INTERNAL_ERROR; } @@ -3465,8 +4083,12 @@ static NTSTATUS dcesrv_netr_ServerGetTrustInfo(struct dcesrv_call_state *dce_cal return NT_STATUS_INVALID_PARAMETER; } - sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx, - lp_ctx, system_session(lp_ctx), 0); + sam_ctx = samdb_connect(mem_ctx, + dce_call->event_ctx, + lp_ctx, + system_session(lp_ctx), + dce_call->conn->remote_address, + 0); if (sam_ctx == NULL) { return NT_STATUS_INVALID_SYSTEM_SERVICE; }