r25446: Merge some changes I made on the way home from SFO:
[jelmer/samba4-debian.git] / source / winbind / wb_server.c
index a835067dd01cc8a2eb09133d0aeca44531709c88..3e27f31429130ff3054738ba1af3a0934d56a51e 100644 (file)
@@ -7,7 +7,7 @@
    
    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/util/dlinklist.h"
 #include "lib/events/events.h"
 #include "smbd/service_task.h"
+#include "smbd/process_model.h"
 #include "smbd/service_stream.h"
 #include "nsswitch/winbind_nss_config.h"
-#include "nsswitch/winbindd_nss.h"
 #include "winbind/wb_server.h"
+#include "lib/stream/packet.h"
+#include "smbd/service.h"
+#include "param/secrets.h"
+#include "param/param.h"
 
 void wbsrv_terminate_connection(struct wbsrv_connection *wbconn, const char *reason)
 {
@@ -38,167 +39,53 @@ void wbsrv_terminate_connection(struct wbsrv_connection *wbconn, const char *rea
 }
 
 /*
-  called when we get a new connection
+  called on a tcp recv error
 */
+static void wbsrv_recv_error(void *private, NTSTATUS status)
+{
+       struct wbsrv_connection *wbconn = talloc_get_type(private, struct wbsrv_connection);
+       wbsrv_terminate_connection(wbconn, nt_errstr(status));
+}
+
 static void wbsrv_accept(struct stream_connection *conn)
 {
-       struct wbsrv_listen_socket *listen_socket =
-               talloc_get_type(conn->private, struct wbsrv_listen_socket);
+       struct wbsrv_listen_socket *listen_socket = talloc_get_type(conn->private, 
+                                                                   struct wbsrv_listen_socket);
        struct wbsrv_connection *wbconn;
 
        wbconn = talloc_zero(conn, struct wbsrv_connection);
        if (!wbconn) {
-               stream_terminate_connection(conn,
-                                           "wbsrv_accept: out of memory");
+               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);
-               return;
-       }
-
-       /* 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);
-               return;
-       }
-
-       if (wbconn->partial.length == 0) {
-               wbconn->partial = data_blob_talloc(wbconn, NULL, 4);
-               if (!wbconn->partial.data) goto nomem;
-
-               wbconn->partial_read = 0;
-       }
-
-       /* read in the packet length */
-       if (wbconn->partial_read < 4) {
-               uint32_t packet_length;
-
-               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;
-
-               wbconn->partial_read += nread;
-               if (wbconn->partial_read != 4) return;
-
-               packet_length = ops->packet_length(wbconn->partial);
-
-               wbconn->partial.data =
-                       talloc_realloc(wbconn, wbconn->partial.data, uint8_t,
-                                      packet_length);
-               if (!wbconn->partial.data) goto nomem;
-
-               wbconn->partial.length = packet_length;
-       }
-
-       /* 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;
-
-       wbconn->partial_read += nread;
-       if (wbconn->partial_read != wbconn->partial.length) return;
+       wbconn->conn          = conn;
+       wbconn->listen_socket = listen_socket;
+       conn->private         = wbconn;
 
-       /* 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;
-
-       /*
-        * 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
-        */
-       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) {
+       wbconn->packet = packet_init(wbconn);
+       if (wbconn->packet == NULL) {
+               wbsrv_terminate_connection(wbconn, "wbsrv_accept: out of memory");
                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));
+       packet_set_private(wbconn->packet, wbconn);
+       packet_set_socket(wbconn->packet, conn->socket);
+       packet_set_callback(wbconn->packet, wbsrv_samba3_process);
+       packet_set_full_request(wbconn->packet, wbsrv_samba3_packet_full_request);
+       packet_set_error_handler(wbconn->packet, wbsrv_recv_error);
+       packet_set_event_context(wbconn->packet, conn->event.ctx);
+       packet_set_fde(wbconn->packet, conn->event.fde);
+       packet_set_serialise(wbconn->packet);
 }
 
 /*
- * 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)
+  receive some data on a winbind connection
+*/
+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);
+       struct wbsrv_connection *wbconn = talloc_get_type(conn->private, 
+                                                         struct wbsrv_connection);
+       packet_recv(wbconn->packet);
 
-       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;
 }
 
 /*
@@ -206,74 +93,18 @@ 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 *wbconn = talloc_get_type(conn->private, 
+                                                         struct wbsrv_connection);
+       packet_queue_run(wbconn->packet);
 }
 
 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
-};
-
-static NTSTATUS init_my_domain(TALLOC_CTX *mem_ctx,
-                              struct wbsrv_domain **domain)
-{
-       struct wbsrv_domain *result;
-
-       result = talloc_zero(mem_ctx, struct wbsrv_domain);
-       NT_STATUS_HAVE_NO_MEMORY(result);
-
-       result->name = talloc_strdup(result, lp_workgroup());
-       if (result->name == NULL) {
-               talloc_free(result);
-               return NT_STATUS_NO_MEMORY;
-       }
-
-       result->sid = secrets_get_domain_sid(result, lp_workgroup());
-       if (result->sid == NULL) {
-               talloc_free(result);
-               return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
-       }
-
-       result->initialized = False;
-
-       *domain = result;
-       return NT_STATUS_OK;
-}
-
 /*
   startup the winbind task
 */
@@ -285,6 +116,8 @@ static void winbind_task_init(struct task_server *task)
        struct wbsrv_service *service;
        struct wbsrv_listen_socket *listen_socket;
 
+       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. */
@@ -295,31 +128,34 @@ static void winbind_task_init(struct task_server *task)
                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_socket_directory(global_loadparm), geteuid(), 0755)) {
+               task_server_terminate(task,
+                                     "Cannot create winbindd pipe directory");
+               return;
        }
 
        service = talloc_zero(task, struct wbsrv_service);
        if (!service) goto nomem;
        service->task   = task;
 
-       status = init_my_domain(service, &service->domains);
-       if (!NT_STATUS_IS_OK(status)) {
-               DEBUG(0, ("Could not init my domain: %s\n",
-                         nt_errstr(status)));
-               task_server_terminate(task, nt_errstr(status));
+       service->primary_sid = secrets_get_domain_sid(service,
+                                                     lp_workgroup(global_loadparm));
+       if (service->primary_sid == NULL) {
+               task_server_terminate(
+                       task, nt_errstr(NT_STATUS_CANT_ACCESS_DOMAIN_INFO));
                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(global_loadparm), 
+                                                         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,
                                     &wbsrv_ops, "unix",
                                     listen_socket->socket_path, &port,
@@ -330,18 +166,20 @@ static void winbind_task_init(struct task_server *task)
        listen_socket = talloc(service, struct wbsrv_listen_socket);
        if (!listen_socket) goto nomem;
        listen_socket->socket_path      =
-               smbd_tmp_path(listen_socket,
+               smbd_tmp_path(listen_socket, global_loadparm, 
                              WINBINDD_SAMBA3_PRIVILEGED_SOCKET);
        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,
                                     &wbsrv_ops, "unix",
                                     listen_socket->socket_path, &port,
                                     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:
@@ -349,6 +187,11 @@ listen_failed:
                 listen_socket->socket_path, nt_errstr(status)));
        task_server_terminate(task, nt_errstr(status));
        return;
+irpc_failed:
+       DEBUG(0,("wbsrv_init_irpc() failed - %s\n",
+                nt_errstr(status)));
+       task_server_terminate(task, nt_errstr(status));
+       return;
 nomem:
        task_server_terminate(task, nt_errstr(NT_STATUS_NO_MEMORY));
        return;