s3-winbindd: Listen on IRPC and do forwarded DNS updates on an RODC
authorAndrew Bartlett <abartlet@samba.org>
Tue, 6 May 2014 05:00:09 +0000 (17:00 +1200)
committerAndrew Bartlett <abartlet@samba.org>
Sat, 24 May 2014 11:26:21 +0000 (23:26 +1200)
Change-Id: Ib87933c318f510d95f7008e122216d73803ede68
Signed-off-by: Andrew Bartlett <abartlet@samba.org>
libcli/auth/netlogon_creds_cli.c
libcli/auth/netlogon_creds_cli.h
source3/librpc/idl/wbint.idl
source3/winbindd/winbindd.c
source3/winbindd/winbindd_dual_srv.c
source3/winbindd/winbindd_proto.h
source3/winbindd/winbindd_update_rodc_dns.c [new file with mode: 0644]
source3/wscript_build

index 472a45272c7dd2fd910ec5f503da23345a23acec..05a30da98cf4f93687f5d74d6322e3546382373e 100644 (file)
@@ -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;
+}
index 90d01826d06fa0233c8c92932c0698fd80b0b096..a910259a88bbdc9799205a675ba20bcc571c4a93 100644 (file)
@@ -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 */
index f05107a0a8b509b82ae6df612c185a37bad8bfc4..e91ef072fcc54d59bc0434a38a6fc383b1fd7b0a 100644 (file)
@@ -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
+       );
 }
index 69169792c96f32f839ef596ad576b77abd0014e5..9859b8715c06ee704419f3ca6f1e90d0b258dc25 100644 (file)
@@ -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 {
index f064467bf234da862b140dca7e1c51815e985afd..721d293c4d01adad1a2ac63873a7646f2ea31449 100644 (file)
@@ -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;
+}
index 642aadd440d7349b905596a458cde6a332a266d3..7f3eb92e0cbb807088a3e18d5395b018aec8c9cc 100644 (file)
@@ -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 (file)
index 0000000..f809dc6
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+*/
+
+#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);
+}
index 994f9fe6274e2e5d1fedceccea101df97422d1ba..6e6afe33310e2ae6fd387943279c4c040d3e2a11 100755 (executable)
@@ -950,6 +950,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