s3:nmbd: implement a MAILSLOT => CLDAP proxy for NETLOGON_SAMLOGON requests
[ira/wip.git] / source3 / nmbd / nmbd_processlogon.c
index 59a2ca405eab7e736c839db495f4946f64628085..8173337da07ca74fe2cfcbea203f131ddf78806e 100644 (file)
@@ -24,6 +24,9 @@
 */
 
 #include "includes.h"
 */
 
 #include "includes.h"
+#include "../libcli/netlogon.h"
+#include "../libcli/cldap/cldap.h"
+#include "../lib/tsocket/tsocket.h"
 
 struct sam_database_info {
         uint32 index;
 
 struct sam_database_info {
         uint32 index;
@@ -65,6 +68,235 @@ static void delayed_init_logon_handler(struct event_context *event_ctx,
        TALLOC_FREE(te);
 }
 
        TALLOC_FREE(te);
 }
 
+struct nmbd_proxy_logon_context {
+       struct cldap_socket *cldap_sock;
+};
+
+static struct nmbd_proxy_logon_context *global_nmbd_proxy_logon;
+
+bool initialize_nmbd_proxy_logon(void)
+{
+       const char *cldap_server = lp_parm_const_string(-1, "nmbd_proxy_logon",
+                                                       "cldap_server", NULL);
+       struct nmbd_proxy_logon_context *ctx;
+       NTSTATUS status;
+       struct in_addr addr;
+       char addrstr[INET_ADDRSTRLEN];
+       const char *server_str;
+       int ret;
+       struct tsocket_address *server_addr;
+
+       if (!cldap_server) {
+               return true;
+       }
+
+       addr = interpret_addr2(cldap_server);
+       server_str = inet_ntop(AF_INET, &addr,
+                            addrstr, sizeof(addrstr));
+       if (!server_str || strcmp("0.0.0.0", server_str) == 0) {
+               DEBUG(0,("Failed to resolve[%s] for nmbd_proxy_logon\n",
+                        cldap_server));
+               return false;
+       }
+
+       ctx = talloc_zero(nmbd_event_context(),
+                         struct nmbd_proxy_logon_context);
+       if (!ctx) {
+               return false;
+       }
+
+       ret = tsocket_address_inet_from_strings(ctx, "ipv4",
+                                               server_str, LDAP_PORT,
+                                               &server_addr);
+       if (ret != 0) {
+               TALLOC_FREE(ctx);
+               status = map_nt_error_from_unix(errno);
+               DEBUG(0,("Failed to create cldap tsocket_address for %s - %s\n",
+                        server_str, nt_errstr(status)));
+               return false;
+       }
+
+       /* we create a connected udp socket */
+       status = cldap_socket_init(ctx, nmbd_event_context(), NULL,
+                                  server_addr, &ctx->cldap_sock);
+       TALLOC_FREE(server_addr);
+       if (!NT_STATUS_IS_OK(status)) {
+               TALLOC_FREE(ctx);
+               DEBUG(0,("failed to create cldap socket for %s: %s\n",
+                       server_str, nt_errstr(status)));
+               return false;
+       }
+
+       global_nmbd_proxy_logon = ctx;
+       return true;
+}
+
+struct nmbd_proxy_logon_state {
+       struct in_addr local_ip;
+       struct packet_struct *p;
+       const char *remote_name;
+       uint8_t remote_name_type;
+       const char *remote_mailslot;
+       struct nbt_netlogon_packet req;
+       struct nbt_netlogon_response resp;
+       struct cldap_netlogon io;
+};
+
+static int nmbd_proxy_logon_state_destructor(struct nmbd_proxy_logon_state *s)
+{
+       s->p->locked = false;
+       free_packet(s->p);
+       return 0;
+}
+
+static void nmbd_proxy_logon_done(struct tevent_req *subreq);
+
+static void nmbd_proxy_logon(struct nmbd_proxy_logon_context *ctx,
+                            struct in_addr local_ip,
+                            struct packet_struct *p,
+                            uint8_t *buf,
+                            uint32_t len)
+{
+       struct nmbd_proxy_logon_state *state;
+       enum ndr_err_code ndr_err;
+       DATA_BLOB blob = data_blob_const(buf, len);
+       const char *computer_name = NULL;
+       const char *mailslot_name = NULL;
+       const char *user_name = NULL;
+       const char *domain_sid = NULL;
+       uint32_t acct_control = 0;
+       uint32_t nt_version = 0;
+       struct tevent_req *subreq;
+       fstring source_name;
+       struct dgram_packet *dgram = &p->packet.dgram;
+
+       state = TALLOC_ZERO_P(ctx, struct nmbd_proxy_logon_state);
+       if (!state) {
+               DEBUG(0,("failed to allocate nmbd_proxy_logon_state\n"));
+               return;
+       }
+
+       pull_ascii_nstring(source_name, sizeof(source_name), dgram->source_name.name);
+       state->remote_name = talloc_strdup(state, source_name);
+       state->remote_name_type = dgram->source_name.name_type,
+       state->local_ip = local_ip;
+       state->p = p;
+
+       ndr_err = ndr_pull_struct_blob(
+               &blob, state, NULL, &state->req,
+               (ndr_pull_flags_fn_t)ndr_pull_nbt_netlogon_packet);
+       if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+               NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
+               DEBUG(0,("failed parse nbt_letlogon_packet: %s\n",
+                       nt_errstr(status)));
+               TALLOC_FREE(state);
+               return;
+       }
+
+       if (DEBUGLEVEL >= 10) {
+               DEBUG(10, ("nmbd_proxy_logon:\n"));
+               NDR_PRINT_DEBUG(nbt_netlogon_packet, &state->req);
+       }
+
+       switch (state->req.command) {
+       case LOGON_SAM_LOGON_REQUEST:
+               computer_name   = state->req.req.logon.computer_name;
+               user_name       = state->req.req.logon.user_name;
+               mailslot_name   = state->req.req.logon.mailslot_name;
+               acct_control    = state->req.req.logon.acct_control;
+               if (state->req.req.logon.sid_size > 0) {
+                       domain_sid = dom_sid_string(state,
+                                                   &state->req.req.logon.sid);
+                       if (!domain_sid) {
+                               DEBUG(0,("failed to get a string for sid\n"));
+                               TALLOC_FREE(state);
+                               return;
+                       }
+               }
+               nt_version      = state->req.req.logon.nt_version;
+               break;
+
+       default:
+               /* this can't happen as the caller already checks the command */
+               break;
+       }
+
+       state->remote_mailslot = mailslot_name;
+
+       if (user_name && strlen(user_name) == 0) {
+               user_name = NULL;
+       }
+
+       if (computer_name && strlen(computer_name) == 0) {
+               computer_name = NULL;
+       }
+
+       /*
+        * as the socket is connected,
+        * we don't need to specify the destination
+        */
+       state->io.in.dest_address       = NULL;
+       state->io.in.dest_port          = 0;
+       state->io.in.realm              = NULL;
+       state->io.in.host               = computer_name;
+       state->io.in.user               = user_name;
+       state->io.in.domain_guid        = NULL;
+       state->io.in.domain_sid         = domain_sid;
+       state->io.in.acct_control       = acct_control;
+       state->io.in.version            = nt_version;
+       state->io.in.map_response       = false;
+
+       subreq = cldap_netlogon_send(state,
+                                    ctx->cldap_sock,
+                                    &state->io);
+       if (!subreq) {
+               DEBUG(0,("failed to send cldap netlogon call\n"));
+               TALLOC_FREE(state);
+               return;
+       }
+       tevent_req_set_callback(subreq, nmbd_proxy_logon_done, state);
+
+       /* we reply async */
+       state->p->locked = true;
+       talloc_set_destructor(state, nmbd_proxy_logon_state_destructor);
+}
+
+static void nmbd_proxy_logon_done(struct tevent_req *subreq)
+{
+       struct nmbd_proxy_logon_state *state =
+               tevent_req_callback_data(subreq,
+               struct nmbd_proxy_logon_state);
+       NTSTATUS status;
+       DATA_BLOB response;
+
+       status = cldap_netlogon_recv(subreq, NULL, state, &state->io);
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(0,("failed to recv cldap netlogon call: %s\n",
+                       nt_errstr(status)));
+               TALLOC_FREE(state);
+               return;
+       }
+
+       status = push_netlogon_samlogon_response(&response, state, NULL,
+                                                &state->io.out.netlogon);
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(0,("failed to push netlogon_samlogon_response: %s\n",
+                       nt_errstr(status)));
+               TALLOC_FREE(state);
+               return;
+       }
+
+       send_mailslot(true, state->remote_mailslot,
+                     (char *)response.data, response.length,
+                     global_myname(), 0x0,
+                     state->remote_name,
+                     state->remote_name_type,
+                     state->p->ip,
+                     state->local_ip,
+                     state->p->port);
+       TALLOC_FREE(state);
+}
+
 /****************************************************************************
 Process a domain logon packet
 **************************************************************************/
 /****************************************************************************
 Process a domain logon packet
 **************************************************************************/
@@ -318,6 +550,12 @@ reporting %s domain %s 0x%x ntversion=%x lm_nt token=%x lm_20 token=%x\n",
                                char *q = buf + 2;
                                fstring asccomp;
 
                                char *q = buf + 2;
                                fstring asccomp;
 
+                               if (global_nmbd_proxy_logon) {
+                                       nmbd_proxy_logon(global_nmbd_proxy_logon,
+                                                        ip, p, (uint8_t *)buf, len);
+                                       return;
+                               }
+
                                q += 2;
 
                                if (PTR_DIFF(q, buf) >= len) {
                                q += 2;
 
                                if (PTR_DIFF(q, buf) >= len) {