s4:winbind Make the 'no SID found' message even more detailed
[abartlet/samba.git/.git] / source4 / winbind / wb_server.c
index a66bc0832f14c001a56e3613494e89aa0bcd92ec..306c8e2add63073e1e9473bf7a467a5de9df55bd 100644 (file)
@@ -2,12 +2,13 @@
    Unix SMB/CIFS implementation.
    Main winbindd server routines
 
-   Copyright (C) Stefan Metzmacher     2005
+   Copyright (C) Stefan Metzmacher     2005-2008
    Copyright (C) Andrew Tridgell       2005
+   Copyright (C) Andrew Bartlett <abartlet@samba.org> 2010
    
    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 2 of the License, or
+   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,
    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, write to the Free Software
-   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
 #include "includes.h"
-#include "lib/socket/socket.h"
-#include "system/dir.h"
-#include "system/filesys.h"
-#include "dlinklist.h"
-#include "lib/events/events.h"
-#include "smbd/service_task.h"
-#include "smbd/service_stream.h"
-#include "nsswitch/winbind_nss_config.h"
-#include "nsswitch/winbindd_nss.h"
+#include "smbd/process_model.h"
 #include "winbind/wb_server.h"
+#include "lib/stream/packet.h"
+#include "lib/tsocket/tsocket.h"
+#include "libcli/util/tstream.h"
+#include "param/param.h"
+#include "param/secrets.h"
 
 void wbsrv_terminate_connection(struct wbsrv_connection *wbconn, const char *reason)
 {
        stream_terminate_connection(wbconn->conn, reason);
 }
 
-/*
-  called when we get a new connection
-*/
-static void wbsrv_accept(struct stream_connection *conn)
+static void wbsrv_call_loop(struct tevent_req *subreq)
 {
-       struct wbsrv_listen_socket *listen_socket =
-               talloc_get_type(conn->private, struct wbsrv_listen_socket);
-       struct wbsrv_connection *wbconn;
+       struct wbsrv_connection *wbsrv_conn = tevent_req_callback_data(subreq,
+                                     struct wbsrv_connection);
+       struct wbsrv_samba3_call *call;
+       NTSTATUS status;
 
-       wbconn = talloc_zero(conn, struct wbsrv_connection);
-       if (!wbconn) {
-               stream_terminate_connection(conn,
-                                           "wbsrv_accept: out of memory");
-               return;
-       }
-       wbconn->conn            = conn;
-       wbconn->listen_socket   = listen_socket;
-       conn->private = wbconn;
-}
-
-/*
-  receive some data on a winbind connection
-*/
-static void wbsrv_recv(struct stream_connection *conn, uint16_t flags)
-{
-       struct wbsrv_connection *wbconn =
-               talloc_get_type(conn->private, struct wbsrv_connection);
-       const struct wbsrv_protocol_ops *ops = wbconn->listen_socket->ops;
-       struct wbsrv_call *call;
-       NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
-       size_t nread;
-
-       /* avoid recursion, because of half async code */
-       if (wbconn->processing) {
-               EVENT_FD_NOT_READABLE(conn->event.fde);
+       call = talloc_zero(wbsrv_conn, struct wbsrv_samba3_call);
+       if (call == NULL) {
+               wbsrv_terminate_connection(wbsrv_conn, "wbsrv_call_loop: "
+                               "no memory for wbsrv_samba3_call");
                return;
        }
+       call->wbconn = wbsrv_conn;
+
+       status = tstream_read_pdu_blob_recv(subreq,
+                                           call,
+                                           &call->in);
+       TALLOC_FREE(subreq);
+       if (!NT_STATUS_IS_OK(status)) {
+               const char *reason;
+
+               reason = talloc_asprintf(call, "wbsrv_call_loop: "
+                                        "tstream_read_pdu_blob_recv() - %s",
+                                        nt_errstr(status));
+               if (!reason) {
+                       reason = nt_errstr(status);
+               }
 
-       /* if the used protocol doesn't support pending requests disallow
-        * them */
-       if (wbconn->pending_calls && !ops->allow_pending_calls) {
-               EVENT_FD_NOT_READABLE(conn->event.fde);
+               wbsrv_terminate_connection(wbsrv_conn, reason);
                return;
        }
 
-       if (wbconn->partial.length == 0) {
-               wbconn->partial = data_blob_talloc(wbconn, NULL, 4);
-               if (!wbconn->partial.data) goto nomem;
-
-               wbconn->partial_read = 0;
-       }
+       DEBUG(10,("Received winbind TCP packet of length %lu from %s\n",
+                (long) call->in.length,
+                tsocket_address_string(wbsrv_conn->conn->remote_address, call)));
 
-       /* read in the packet length */
-       if (wbconn->partial_read < 4) {
-               uint32_t packet_length;
+       status = wbsrv_samba3_process(call);
+       if (!NT_STATUS_IS_OK(status)) {
+               const char *reason;
 
-               status = socket_recv(conn->socket, 
-                                    wbconn->partial.data+wbconn->partial_read,
-                                    4 - wbconn->partial_read,
-                                    &nread, 0);
-               if (NT_STATUS_IS_ERR(status)) goto failed;
-               if (!NT_STATUS_IS_OK(status)) return;
+               reason = talloc_asprintf(call, "wbsrv_call_loop: "
+                                        "tstream_read_pdu_blob_recv() - %s",
+                                        nt_errstr(status));
+               if (!reason) {
+                       reason = nt_errstr(status);
+               }
 
-               wbconn->partial_read += nread;
-               if (wbconn->partial_read != 4) return;
+               wbsrv_terminate_connection(wbsrv_conn, reason);
+               return;
+       }
 
-               packet_length = ops->packet_length(wbconn->partial);
+       /*
+        * The winbind pdu's has the length as 4 byte (initial_read_size),
+        * wbsrv_samba3_packet_full_request provides the pdu length then.
+        */
+       subreq = tstream_read_pdu_blob_send(wbsrv_conn,
+                                           wbsrv_conn->conn->event.ctx,
+                                           wbsrv_conn->tstream,
+                                           4, /* initial_read_size */
+                                           wbsrv_samba3_packet_full_request,
+                                           wbsrv_conn);
+       if (subreq == NULL) {
+               wbsrv_terminate_connection(wbsrv_conn, "wbsrv_call_loop: "
+                               "no memory for tstream_read_pdu_blob_send");
+               return;
+       }
+       tevent_req_set_callback(subreq, wbsrv_call_loop, wbsrv_conn);
+}
 
-               wbconn->partial.data =
-                       talloc_realloc(wbconn, wbconn->partial.data, uint8_t,
-                                      packet_length);
-               if (!wbconn->partial.data) goto nomem;
+static void wbsrv_accept(struct stream_connection *conn)
+{
+       struct wbsrv_listen_socket *wbsrv_socket = talloc_get_type(conn->private_data,
+                                                                  struct wbsrv_listen_socket);
+       struct wbsrv_connection *wbsrv_conn;
+       struct tevent_req *subreq;
+       int rc;
+
+       wbsrv_conn = talloc_zero(conn, struct wbsrv_connection);
+       if (wbsrv_conn == NULL) {
+               stream_terminate_connection(conn, "wbsrv_accept: out of memory");
+               return;
+       }
 
-               wbconn->partial.length = packet_length;
+       wbsrv_conn->send_queue = tevent_queue_create(conn, "wbsrv_accept");
+       if (wbsrv_conn->send_queue == NULL) {
+               stream_terminate_connection(conn,
+                               "wbsrv_accept: out of memory");
+               return;
        }
 
-       /* read in the body */
-       status = socket_recv(conn->socket, 
-                            wbconn->partial.data + wbconn->partial_read,
-                            wbconn->partial.length - wbconn->partial_read,
-                            &nread, 0);
-       if (NT_STATUS_IS_ERR(status)) goto failed;
-       if (!NT_STATUS_IS_OK(status)) return;
+       TALLOC_FREE(conn->event.fde);
 
-       wbconn->partial_read += nread;
-       if (wbconn->partial_read != wbconn->partial.length) return;
+       rc = tstream_bsd_existing_socket(wbsrv_conn->tstream,
+                       socket_get_fd(conn->socket),
+                       &wbsrv_conn->tstream);
+       if (rc < 0) {
+               stream_terminate_connection(conn,
+                               "wbsrv_accept: out of memory");
+               return;
+       }
 
-       /* we have a full request - parse it */
-       status = ops->pull_request(wbconn->partial, wbconn, &call);
-       if (!NT_STATUS_IS_OK(status)) goto failed;
-       call->wbconn    = wbconn;
-       call->event_ctx = conn->event.ctx;
+       wbsrv_conn->conn = conn;
+       wbsrv_conn->listen_socket = wbsrv_socket;
+       wbsrv_conn->lp_ctx = wbsrv_socket->service->task->lp_ctx;
+       conn->private_data = wbsrv_conn;
 
        /*
-        * we have parsed the request, so we can reset the
-        * wbconn->partial_read, maybe we could also free wbconn->partial, but
-        * for now we keep it, and overwrite it the next time
+        * The winbind pdu's has the length as 4 byte (initial_read_size),
+        * wbsrv_samba3_packet_full_request provides the pdu length then.
         */
-       wbconn->partial_read = 0;
-
-       /* actually process the request */
-       wbconn->pending_calls++;
-       wbconn->processing = True;
-       status = ops->handle_call(call);
-       wbconn->processing = False;
-       if (!NT_STATUS_IS_OK(status)) goto failed;
-
-       /* if the backend want to reply later just return here */
-       if (call->flags & WBSRV_CALL_FLAGS_REPLY_ASYNC) {
+       subreq = tstream_read_pdu_blob_send(wbsrv_conn,
+                                           wbsrv_conn->conn->event.ctx,
+                                           wbsrv_conn->tstream,
+                                           4, /* initial_read_size */
+                                           wbsrv_samba3_packet_full_request,
+                                           wbsrv_conn);
+       if (subreq == NULL) {
+               wbsrv_terminate_connection(wbsrv_conn, "wbsrv_accept: "
+                               "no memory for tstream_read_pdu_blob_send");
                return;
        }
-
-       /*
-        * and queue the reply, this implies talloc_free(call),
-        * and set the socket to readable again
-        */
-       status = wbsrv_send_reply(call);
-       if (!NT_STATUS_IS_OK(status)) goto failed;
-
-       return;
-nomem:
-       status = NT_STATUS_NO_MEMORY;
-failed:
-       wbsrv_terminate_connection(wbconn, nt_errstr(status));
+       tevent_req_set_callback(subreq, wbsrv_call_loop, wbsrv_conn);
 }
 
 /*
- * queue a wbsrv_call reply on a wbsrv_connection
- * NOTE: that this implies talloc_free(call),
- *       use talloc_reference(call) if you need it after
- *       calling wbsrv_queue_reply
- * NOTE: if this function desn't return NT_STATUS_OK,
- *       the caller needs to call
- *           wbsrv_terminate_connection(call->wbconn, "reason...");
- *           return;
- *       to drop the connection
- */
-NTSTATUS wbsrv_send_reply(struct wbsrv_call *call)
+  called on a tcp recv
+*/
+static void wbsrv_recv(struct stream_connection *conn, uint16_t flags)
 {
-       struct wbsrv_connection *wbconn = call->wbconn;
-       const struct wbsrv_protocol_ops *ops = wbconn->listen_socket->ops;
-       struct data_blob_list_item *rep;
-       NTSTATUS status;
-
-       /* and now encode the reply */
-       rep = talloc(wbconn, struct data_blob_list_item);
-       NT_STATUS_HAVE_NO_MEMORY(rep);
-
-       status = ops->push_reply(call, rep, &rep->blob);
-       NT_STATUS_NOT_OK_RETURN(status);
-
-       if (!wbconn->send_queue) {
-               EVENT_FD_WRITEABLE(wbconn->conn->event.fde);
-       }
-       DLIST_ADD_END(wbconn->send_queue, rep, struct data_blob_list_item *);
-
-       EVENT_FD_READABLE(wbconn->conn->event.fde);
-
-       /* the call isn't needed any more */
-       wbconn->pending_calls--;
-       talloc_free(call);
-       return NT_STATUS_OK;
+       struct wbsrv_connection *wbsrv_conn = talloc_get_type(conn->private_data,
+                                                       struct wbsrv_connection);
+       wbsrv_terminate_connection(wbsrv_conn, "wbsrv_recv: called");
 }
 
 /*
@@ -206,48 +174,19 @@ NTSTATUS wbsrv_send_reply(struct wbsrv_call *call)
 */
 static void wbsrv_send(struct stream_connection *conn, uint16_t flags)
 {
-       struct wbsrv_connection *wbconn = talloc_get_type(conn->private, struct wbsrv_connection);
-       NTSTATUS status;
-
-       while (wbconn->send_queue) {
-               struct data_blob_list_item *q = wbconn->send_queue;
-               size_t sendlen;
-
-               status = socket_send(conn->socket, &q->blob, &sendlen, 0);
-               if (NT_STATUS_IS_ERR(status)) goto failed;
-               if (!NT_STATUS_IS_OK(status)) return;
-
-               q->blob.length -= sendlen;
-               q->blob.data   += sendlen;
-
-               if (q->blob.length == 0) {
-                       DLIST_REMOVE(wbconn->send_queue, q);
-                       talloc_free(q);
-               }
-       }
-
-       EVENT_FD_NOT_WRITEABLE(conn->event.fde);
-       return;
-failed:
-       wbsrv_terminate_connection(wbconn, nt_errstr(status));
+       struct wbsrv_connection *wbsrv_conn = talloc_get_type(conn->private_data,
+                                                       struct wbsrv_connection);
+       /* this should never be triggered! */
+       wbsrv_terminate_connection(wbsrv_conn, "wbsrv_send: called");
 }
 
 static const struct stream_server_ops wbsrv_ops = {
-       .name                   = "winbind",
+       .name                   = "winbind samba3 protocol",
        .accept_connection      = wbsrv_accept,
        .recv_handler           = wbsrv_recv,
        .send_handler           = wbsrv_send
 };
 
-static const struct wbsrv_protocol_ops wbsrv_samba3_protocol_ops = {
-       .name                   = "winbind samba3 protocol",
-       .allow_pending_calls    = False,
-       .packet_length          = wbsrv_samba3_packet_length,
-       .pull_request           = wbsrv_samba3_pull_request,
-       .handle_call            = wbsrv_samba3_handle_call,
-       .push_reply             = wbsrv_samba3_push_reply
-};
-
 /*
   startup the winbind task
 */
@@ -258,89 +197,152 @@ static void winbind_task_init(struct task_server *task)
        NTSTATUS status;
        struct wbsrv_service *service;
        struct wbsrv_listen_socket *listen_socket;
+       char *errstring;
+       struct dom_sid *primary_sid;
+
+       task_server_set_title(task, "task[winbind]");
 
        /* within the winbind task we want to be a single process, so
           ask for the single process model ops and pass these to the
           stream_setup_socket() call. */
-       model_ops = process_model_byname("single");
+       model_ops = process_model_startup(task->event_ctx, "single");
        if (!model_ops) {
                task_server_terminate(task,
-                                     "Can't find 'single' process model_ops");
+                                     "Can't find 'single' process model_ops", true);
+               return;
+       }
+
+       /* Make sure the directory for the Samba3 socket exists, and is of the correct permissions */
+       if (!directory_create_or_exist(lp_winbindd_socket_directory(task->lp_ctx), geteuid(), 0755)) {
+               task_server_terminate(task,
+                                     "Cannot create winbindd pipe directory", true);
                return;
        }
 
-       /* Make sure the directory for NCALRPC exists */
-       if (!directory_exist(WINBINDD_DIR)) {
-               mkdir(WINBINDD_DIR, 0755);
+       /* Make sure the directory for the Samba3 socket exists, and is of the correct permissions */
+       if (!directory_create_or_exist(lp_winbindd_privileged_socket_directory(task->lp_ctx), geteuid(), 0750)) {
+               task_server_terminate(task,
+                                     "Cannot create winbindd privileged pipe directory", true);
+               return;
        }
 
        service = talloc_zero(task, struct wbsrv_service);
        if (!service) goto nomem;
        service->task   = task;
 
-       service->primary_sid = secrets_get_domain_sid(service,
-                                                     lp_workgroup());
-       if (service->primary_sid == NULL) {
-               task_server_terminate(
-                       task, nt_errstr(NT_STATUS_CANT_ACCESS_DOMAIN_INFO));
+
+       /* Find the primary SID, depending if we are a standalone
+        * server (what good is winbind in this case, but anyway...),
+        * or are in a domain as a member or a DC */
+       switch (lp_server_role(service->task->lp_ctx)) {
+       case ROLE_STANDALONE:
+               primary_sid = secrets_get_domain_sid(service,
+                                                    service->task->event_ctx,
+                                                    service->task->lp_ctx,
+                                                    lp_netbios_name(service->task->lp_ctx), &errstring);
+               if (!primary_sid) {
+                       char *message = talloc_asprintf(task, 
+                                                       "Cannot start Winbind (standalone configuration): %s: "
+                                                       "Have you provisioned this server (%s) or changed it's name?", 
+                                                       errstring, lp_netbios_name(service->task->lp_ctx));
+                       task_server_terminate(task, message, true);
+                       return;
+               }
+               break;
+       case ROLE_DOMAIN_MEMBER:
+               primary_sid = secrets_get_domain_sid(service,
+                                                    service->task->event_ctx,
+                                                    service->task->lp_ctx,
+                                                    lp_workgroup(service->task->lp_ctx), &errstring);
+               if (!primary_sid) {
+                       char *message = talloc_asprintf(task, "Cannot start Winbind (domain member): %s: "
+                                                       "Have you joined the %s domain?", 
+                                                       errstring, lp_workgroup(service->task->lp_ctx));
+                       task_server_terminate(task, message, true);
+                       return;
+               }
+               break;
+       case ROLE_DOMAIN_CONTROLLER:
+               primary_sid = secrets_get_domain_sid(service,
+                                                    service->task->event_ctx,
+                                                    service->task->lp_ctx,
+                                                    lp_workgroup(service->task->lp_ctx), &errstring);
+               if (!primary_sid) {
+                       char *message = talloc_asprintf(task, "Cannot start Winbind (domain controller): %s: "
+                                                       "Have you provisioned the %s domain?", 
+                                                       errstring, lp_workgroup(service->task->lp_ctx));
+                       task_server_terminate(task, message, true);
+                       return;
+               }
+               break;
+       }
+       service->primary_sid = primary_sid;
+
+       service->idmap_ctx = idmap_init(service, task->event_ctx, task->lp_ctx);
+       if (service->idmap_ctx == NULL) {
+               task_server_terminate(task, "Failed to load idmap database", true);
                return;
        }
 
        /* setup the unprivileged samba3 socket */
        listen_socket = talloc(service, struct wbsrv_listen_socket);
        if (!listen_socket) goto nomem;
-       listen_socket->socket_path      = WINBINDD_SAMBA3_SOCKET;
+       listen_socket->socket_path      = talloc_asprintf(listen_socket, "%s/%s", 
+                                                         lp_winbindd_socket_directory(task->lp_ctx), 
+                                                         WINBINDD_SAMBA3_SOCKET);
        if (!listen_socket->socket_path) goto nomem;
        listen_socket->service          = service;
-       listen_socket->privileged       = False;
-       listen_socket->ops              = &wbsrv_samba3_protocol_ops;
-       status = stream_setup_socket(task->event_ctx, model_ops,
+       listen_socket->privileged       = false;
+       status = stream_setup_socket(task->event_ctx, task->lp_ctx, model_ops,
                                     &wbsrv_ops, "unix",
                                     listen_socket->socket_path, &port,
+                                    lp_socket_options(task->lp_ctx), 
                                     listen_socket);
        if (!NT_STATUS_IS_OK(status)) goto listen_failed;
 
        /* setup the privileged samba3 socket */
        listen_socket = talloc(service, struct wbsrv_listen_socket);
        if (!listen_socket) goto nomem;
-       listen_socket->socket_path      =
-               smbd_tmp_path(listen_socket,
-                             WINBINDD_SAMBA3_PRIVILEGED_SOCKET);
+       listen_socket->socket_path 
+               = service->priv_socket_path 
+               = talloc_asprintf(listen_socket, "%s/%s", 
+                                                         lp_winbindd_privileged_socket_directory(task->lp_ctx), 
+                                                         WINBINDD_SAMBA3_SOCKET);
+       if (!listen_socket->socket_path) goto nomem;
        if (!listen_socket->socket_path) goto nomem;
        listen_socket->service          = service;
-       listen_socket->privileged       = True;
-       listen_socket->ops              = &wbsrv_samba3_protocol_ops;
-       status = stream_setup_socket(task->event_ctx, model_ops,
+       listen_socket->privileged       = true;
+       status = stream_setup_socket(task->event_ctx, task->lp_ctx, model_ops,
                                     &wbsrv_ops, "unix",
                                     listen_socket->socket_path, &port,
+                                    lp_socket_options(task->lp_ctx), 
                                     listen_socket);
        if (!NT_STATUS_IS_OK(status)) goto listen_failed;
 
+       status = wbsrv_init_irpc(service);
+       if (!NT_STATUS_IS_OK(status)) goto irpc_failed;
+
        return;
 
 listen_failed:
        DEBUG(0,("stream_setup_socket(path=%s) failed - %s\n",
                 listen_socket->socket_path, nt_errstr(status)));
-       task_server_terminate(task, nt_errstr(status));
+       task_server_terminate(task, nt_errstr(status), true);
+       return;
+irpc_failed:
+       DEBUG(0,("wbsrv_init_irpc() failed - %s\n",
+                nt_errstr(status)));
+       task_server_terminate(task, nt_errstr(status), true);
        return;
 nomem:
-       task_server_terminate(task, nt_errstr(NT_STATUS_NO_MEMORY));
+       task_server_terminate(task, nt_errstr(NT_STATUS_NO_MEMORY), true);
        return;
 }
 
-/*
-  initialise the winbind server
- */
-static NTSTATUS winbind_init(struct event_context *event_ctx,
-                            const struct model_ops *model_ops)
-{
-       return task_server_startup(event_ctx, model_ops, winbind_task_init);
-}
-
 /*
   register ourselves as a available server
 */
 NTSTATUS server_service_winbind_init(void)
 {
-       return register_server_service("winbind", winbind_init);
+       return register_server_service("winbind", winbind_task_init);
 }