From 223fbdaf3872fe71a75fec62813b91612af73a2b Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Tue, 6 May 2014 17:00:09 +1200 Subject: s3-winbindd: Listen on IRPC and do forwarded DNS updates on an RODC Change-Id: Ib87933c318f510d95f7008e122216d73803ede68 Signed-off-by: Andrew Bartlett Reviewed-by: Andreas Schneider --- libcli/auth/netlogon_creds_cli.c | 265 ++++++++++++++++++++++++++++ libcli/auth/netlogon_creds_cli.h | 14 ++ source3/librpc/idl/wbint.idl | 6 + source3/winbindd/winbindd.c | 8 + source3/winbindd/winbindd_dual_srv.c | 39 ++++ source3/winbindd/winbindd_proto.h | 5 + source3/winbindd/winbindd_update_rodc_dns.c | 85 +++++++++ source3/wscript_build | 1 + 8 files changed, 423 insertions(+) create mode 100644 source3/winbindd/winbindd_update_rodc_dns.c diff --git a/libcli/auth/netlogon_creds_cli.c b/libcli/auth/netlogon_creds_cli.c index 472a45272c7..05a30da98cf 100644 --- a/libcli/auth/netlogon_creds_cli.c +++ b/libcli/auth/netlogon_creds_cli.c @@ -2568,3 +2568,268 @@ NTSTATUS netlogon_creds_cli_LogonSamLogon( TALLOC_FREE(frame); return status; } + +struct netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_state { + struct tevent_context *ev; + struct netlogon_creds_cli_context *context; + struct dcerpc_binding_handle *binding_handle; + + char *srv_name_slash; + enum dcerpc_AuthType auth_type; + enum dcerpc_AuthLevel auth_level; + + const char *site_name; + uint32_t dns_ttl; + struct NL_DNS_NAME_INFO_ARRAY *dns_names; + + struct netlogon_creds_CredentialState *creds; + struct netlogon_creds_CredentialState tmp_creds; + struct netr_Authenticator req_auth; + struct netr_Authenticator rep_auth; +}; + +static void netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_cleanup(struct tevent_req *req, + NTSTATUS status); +static void netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_locked(struct tevent_req *subreq); + +struct tevent_req *netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct netlogon_creds_cli_context *context, + struct dcerpc_binding_handle *b, + const char *site_name, + uint32_t dns_ttl, + struct NL_DNS_NAME_INFO_ARRAY *dns_names) +{ + struct tevent_req *req; + struct netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_state *state; + struct tevent_req *subreq; + bool ok; + + req = tevent_req_create(mem_ctx, &state, + struct netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_state); + if (req == NULL) { + return NULL; + } + + state->ev = ev; + state->context = context; + state->binding_handle = b; + + state->srv_name_slash = talloc_asprintf(state, "\\\\%s", + context->server.computer); + if (tevent_req_nomem(state->srv_name_slash, req)) { + return tevent_req_post(req, ev); + } + + state->site_name = site_name; + state->dns_ttl = dns_ttl; + state->dns_names = dns_names; + + dcerpc_binding_handle_auth_info(state->binding_handle, + &state->auth_type, + &state->auth_level); + + subreq = netlogon_creds_cli_lock_send(state, state->ev, + state->context); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + + tevent_req_set_callback(subreq, + netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_locked, + req); + + return req; +} + +static void netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_cleanup(struct tevent_req *req, + NTSTATUS status) +{ + struct netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_state *state = + tevent_req_data(req, + struct netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_state); + + if (state->creds == NULL) { + return; + } + + if (!NT_STATUS_EQUAL(status, NT_STATUS_NETWORK_ACCESS_DENIED) && + !NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) && + !NT_STATUS_EQUAL(status, NT_STATUS_DOWNGRADE_DETECTED) && + !NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED) && + !NT_STATUS_EQUAL(status, NT_STATUS_RPC_SEC_PKG_ERROR)) { + TALLOC_FREE(state->creds); + return; + } + + netlogon_creds_cli_delete(state->context, &state->creds); +} + +static void netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_done(struct tevent_req *subreq); + +static void netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_locked(struct tevent_req *subreq) +{ + struct tevent_req *req = + tevent_req_callback_data(subreq, + struct tevent_req); + struct netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_state *state = + tevent_req_data(req, + struct netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_state); + NTSTATUS status; + + status = netlogon_creds_cli_lock_recv(subreq, state, + &state->creds); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + return; + } + + if (state->auth_type == DCERPC_AUTH_TYPE_SCHANNEL) { + switch (state->auth_level) { + case DCERPC_AUTH_LEVEL_INTEGRITY: + case DCERPC_AUTH_LEVEL_PRIVACY: + break; + default: + tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_MIX); + return; + } + } else { + uint32_t tmp = state->creds->negotiate_flags; + + if (tmp & NETLOGON_NEG_AUTHENTICATED_RPC) { + /* + * if DCERPC_AUTH_TYPE_SCHANNEL is supported + * it should be used, which means + * we had a chance to verify no downgrade + * happened. + * + * This relies on netlogon_creds_cli_check* + * being called before, as first request after + * the DCERPC bind. + */ + tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_MIX); + return; + } + } + + /* + * we defer all callbacks in order to cleanup + * the database record. + */ + tevent_req_defer_callback(req, state->ev); + + state->tmp_creds = *state->creds; + netlogon_creds_client_authenticator(&state->tmp_creds, + &state->req_auth); + ZERO_STRUCT(state->rep_auth); + + subreq = dcerpc_netr_DsrUpdateReadOnlyServerDnsRecords_send(state, state->ev, + state->binding_handle, + state->srv_name_slash, + state->tmp_creds.computer_name, + &state->req_auth, + &state->rep_auth, + state->site_name, + state->dns_ttl, + state->dns_names); + if (tevent_req_nomem(subreq, req)) { + status = NT_STATUS_NO_MEMORY; + netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_cleanup(req, status); + return; + } + + tevent_req_set_callback(subreq, + netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_done, + req); +} + +static void netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_done(struct tevent_req *subreq) +{ + struct tevent_req *req = + tevent_req_callback_data(subreq, + struct tevent_req); + struct netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_state *state = + tevent_req_data(req, + struct netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_state); + NTSTATUS status; + NTSTATUS result; + bool ok; + + status = dcerpc_netr_DsrUpdateReadOnlyServerDnsRecords_recv(subreq, state, + &result); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_cleanup(req, status); + return; + } + + ok = netlogon_creds_client_check(&state->tmp_creds, + &state->rep_auth.cred); + if (!ok) { + status = NT_STATUS_ACCESS_DENIED; + tevent_req_nterror(req, status); + netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_cleanup(req, status); + return; + } + + if (tevent_req_nterror(req, result)) { + netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_cleanup(req, result); + return; + } + + *state->creds = state->tmp_creds; + status = netlogon_creds_cli_store(state->context, + &state->creds); + if (tevent_req_nterror(req, status)) { + netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_cleanup(req, status); + return; + } + + tevent_req_done(req); +} + +NTSTATUS netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_recv(struct tevent_req *req) +{ + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_cleanup(req, status); + tevent_req_received(req); + return status; + } + + tevent_req_received(req); + return NT_STATUS_OK; +} + +NTSTATUS netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords( + struct netlogon_creds_cli_context *context, + struct dcerpc_binding_handle *b, + const char *site_name, + uint32_t dns_ttl, + struct NL_DNS_NAME_INFO_ARRAY *dns_names) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct tevent_context *ev; + struct tevent_req *req; + NTSTATUS status = NT_STATUS_NO_MEMORY; + + ev = samba_tevent_context_init(frame); + if (ev == NULL) { + goto fail; + } + req = netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_send(frame, ev, context, b, + site_name, + dns_ttl, + dns_names); + if (req == NULL) { + goto fail; + } + if (!tevent_req_poll_ntstatus(req, ev, &status)) { + goto fail; + } + status = netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_recv(req); + fail: + TALLOC_FREE(frame); + return status; +} diff --git a/libcli/auth/netlogon_creds_cli.h b/libcli/auth/netlogon_creds_cli.h index 90d01826d06..a910259a88b 100644 --- a/libcli/auth/netlogon_creds_cli.h +++ b/libcli/auth/netlogon_creds_cli.h @@ -132,5 +132,19 @@ NTSTATUS netlogon_creds_cli_LogonSamLogon( union netr_Validation **validation, uint8_t *authoritative, uint32_t *flags); +struct tevent_req *netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct netlogon_creds_cli_context *context, + struct dcerpc_binding_handle *b, + const char *site_name, + uint32_t dns_ttl, + struct NL_DNS_NAME_INFO_ARRAY *dns_names); +NTSTATUS netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_recv(struct tevent_req *req); +NTSTATUS netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords( + struct netlogon_creds_cli_context *context, + struct dcerpc_binding_handle *b, + const char *site_name, + uint32_t dns_ttl, + struct NL_DNS_NAME_INFO_ARRAY *dns_names); #endif /* NETLOGON_CREDS_CLI_H */ diff --git a/source3/librpc/idl/wbint.idl b/source3/librpc/idl/wbint.idl index f05107a0a8b..e91ef072fcc 100644 --- a/source3/librpc/idl/wbint.idl +++ b/source3/librpc/idl/wbint.idl @@ -167,4 +167,10 @@ interface wbint NTSTATUS wbint_PingDc( [out,string,charset(UTF8)] char **dcname ); + + NTSTATUS wbint_DsrUpdateReadOnlyServerDnsRecords( + [in,unique] [string,charset(UTF16)] uint16 *site_name, + [in] uint32 dns_ttl, + [in,out,ref] NL_DNS_NAME_INFO_ARRAY *dns_names + ); } diff --git a/source3/winbindd/winbindd.c b/source3/winbindd/winbindd.c index 8a2c09e5158..153a400f2e8 100644 --- a/source3/winbindd/winbindd.c +++ b/source3/winbindd/winbindd.c @@ -42,6 +42,7 @@ #include "source4/lib/messaging/irpc.h" #include "source4/lib/messaging/messaging.h" #include "lib/param/param.h" +#include "source4/librpc/gen_ndr/ndr_winbind.h" #undef DBGC_CLASS #define DBGC_CLASS DBGC_WINBIND @@ -1147,6 +1148,7 @@ bool winbindd_use_cache(void) static void winbindd_register_handlers(struct messaging_context *msg_ctx, bool foreground) { + NTSTATUS status; /* Setup signal handlers */ if (!winbindd_setup_sig_term_handler(true)) @@ -1246,6 +1248,12 @@ static void winbindd_register_handlers(struct messaging_context *msg_ctx, } } + status = IRPC_REGISTER(winbind_imessaging_context(), winbind, WINBIND_DSRUPDATEREADONLYSERVERDNSRECORDS, + wb_irpc_DsrUpdateReadOnlyServerDnsRecords, NULL); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("Could not register IRPC handler for wb_irpc_DsrUpdateReadOnlyServerDnsRecords\n")); + exit(1); + } } struct winbindd_addrchanged_state { diff --git a/source3/winbindd/winbindd_dual_srv.c b/source3/winbindd/winbindd_dual_srv.c index f064467bf23..721d293c4d0 100644 --- a/source3/winbindd/winbindd_dual_srv.c +++ b/source3/winbindd/winbindd_dual_srv.c @@ -29,6 +29,7 @@ #include "../librpc/gen_ndr/ndr_netlogon_c.h" #include "idmap.h" #include "../libcli/security/security.h" +#include "../libcli/auth/netlogon_creds_cli.h" void _wbint_Ping(struct pipes_struct *p, struct wbint_Ping *r) { @@ -717,3 +718,41 @@ NTSTATUS _wbint_PingDc(struct pipes_struct *p, struct wbint_PingDc *r) DEBUG(5, ("winbindd_dual_ping_dc succeeded\n")); return NT_STATUS_OK; } + +NTSTATUS _wbint_DsrUpdateReadOnlyServerDnsRecords(struct pipes_struct *p, + struct wbint_DsrUpdateReadOnlyServerDnsRecords *r) +{ + struct winbindd_domain *domain; + NTSTATUS status; + struct rpc_pipe_client *netlogon_pipe; + + domain = wb_child_domain(); + if (domain == NULL) { + return NT_STATUS_REQUEST_NOT_ACCEPTED; + } + + status = cm_connect_netlogon(domain, &netlogon_pipe); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(3, ("could not open handle to NETLOGON pipe\n")); + goto done; + } + + status = netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords(domain->conn.netlogon_creds, + netlogon_pipe->binding_handle, + r->in.site_name, + r->in.dns_ttl, + r->in.dns_names); + + /* Pass back result code - zero for success, other values for + specific failures. */ + + DEBUG(3,("DNS records for domain %s %s\n", domain->name, + NT_STATUS_IS_OK(status) ? "changed" : "unchanged")); + + done: + DEBUG(NT_STATUS_IS_OK(status) ? 5 : 2, + ("Update of DNS records via RW DC %s returned %s\n", + domain->name, nt_errstr(status))); + + return status; +} diff --git a/source3/winbindd/winbindd_proto.h b/source3/winbindd/winbindd_proto.h index 642aadd440d..7f3eb92e0cb 100644 --- a/source3/winbindd/winbindd_proto.h +++ b/source3/winbindd/winbindd_proto.h @@ -908,4 +908,9 @@ NTSTATUS open_internal_samr_conn(TALLOC_CTX *mem_ctx, /* The following definitions come from winbindd/winbindd_ads.c */ ADS_STATUS ads_idmap_cached_connection(ADS_STRUCT **adsp, const char *dom_name); +/* The following definitions come from winbindd/winbindd_update_rodc_dns.c */ +struct irpc_message; +struct winbind_DsrUpdateReadOnlyServerDnsRecords; +NTSTATUS wb_irpc_DsrUpdateReadOnlyServerDnsRecords(struct irpc_message *msg, + struct winbind_DsrUpdateReadOnlyServerDnsRecords *req); #endif /* _WINBINDD_PROTO_H_ */ diff --git a/source3/winbindd/winbindd_update_rodc_dns.c b/source3/winbindd/winbindd_update_rodc_dns.c new file mode 100644 index 00000000000..f809dc615fc --- /dev/null +++ b/source3/winbindd/winbindd_update_rodc_dns.c @@ -0,0 +1,85 @@ +/* + Unix SMB/CIFS implementation. + async implementation of WINBINDD_CHANGE_MACHINE_ACCT + Copyright (C) Volker Lendecke 2009 + Copyright (C) Guenther Deschner 2009 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "winbindd.h" +#include "librpc/gen_ndr/ndr_wbint_c.h" +#include "librpc/gen_ndr/ndr_winbind_c.h" +#include "source4/lib/messaging/irpc.h" + +struct wb_irpc_DsrUpdateReadOnlyServerDnsRecords_state { + struct irpc_message *msg; + struct winbind_DsrUpdateReadOnlyServerDnsRecords *req; +}; + +static void wb_irpc_DsrUpdateReadOnlyServerDnsRecords_callback(struct tevent_req *subreq); + +NTSTATUS wb_irpc_DsrUpdateReadOnlyServerDnsRecords(struct irpc_message *msg, + struct winbind_DsrUpdateReadOnlyServerDnsRecords *req) +{ + struct wb_irpc_DsrUpdateReadOnlyServerDnsRecords_state *s; + struct tevent_req *subreq; + struct winbindd_domain *domain; + + DEBUG(5, ("wb_irpc_DsrUpdateReadOnlyServerDnsRecords called\n")); + + s = talloc(msg, struct wb_irpc_DsrUpdateReadOnlyServerDnsRecords_state); + NT_STATUS_HAVE_NO_MEMORY(s); + + s->msg = msg; + s->req = req; + + domain = find_our_domain(); + if (domain == NULL) { + return NT_STATUS_NO_SUCH_DOMAIN; + } + + subreq = dcerpc_wbint_DsrUpdateReadOnlyServerDnsRecords_send(s, winbind_event_context(), + dom_child_handle(domain), + req->in.site_name, + req->in.dns_ttl, + req->in.dns_names); + if (!subreq) { + return NT_STATUS_NO_MEMORY; + } + + tevent_req_set_callback(subreq, + wb_irpc_DsrUpdateReadOnlyServerDnsRecords_callback, + s); + + msg->defer_reply = true; + return NT_STATUS_OK; +} + +static void wb_irpc_DsrUpdateReadOnlyServerDnsRecords_callback(struct tevent_req *subreq) +{ + struct wb_irpc_DsrUpdateReadOnlyServerDnsRecords_state *s = + tevent_req_callback_data(subreq, + struct wb_irpc_DsrUpdateReadOnlyServerDnsRecords_state); + NTSTATUS status, result; + + DEBUG(5, ("wb_irpc_DsrUpdateReadOnlyServerDnsRecords_callback called\n")); + + status = dcerpc_wbint_DsrUpdateReadOnlyServerDnsRecords_recv(subreq, s, &result); + any_nt_status_not_ok(status, result, &status); + TALLOC_FREE(subreq); + + irpc_send_reply(s->msg, status); +} diff --git a/source3/wscript_build b/source3/wscript_build index 806a49712c2..a0080b32fcb 100755 --- a/source3/wscript_build +++ b/source3/wscript_build @@ -943,6 +943,7 @@ bld.SAMBA3_BINARY('winbindd/winbindd', winbindd/winbindd_list_groups.c winbindd/winbindd_check_machine_acct.c winbindd/winbindd_change_machine_acct.c + winbindd/winbindd_update_rodc_dns.c winbindd/winbindd_ping_dc.c winbindd/winbindd_pam_auth.c winbindd/winbindd_pam_logoff.c -- cgit