s4-rpcserver: added shared association groups
authorAndrew Tridgell <tridge@samba.org>
Tue, 22 Sep 2009 04:36:54 +0000 (21:36 -0700)
committerAndrew Tridgell <tridge@samba.org>
Wed, 23 Sep 2009 00:10:05 +0000 (17:10 -0700)
This patch allows us to share association groups and their rpc handles
between connections. This is needed for some DRSUAPI behaviour when
recent windows clients connect.

source4/rpc_server/dcerpc_server.c
source4/rpc_server/dcerpc_server.h
source4/rpc_server/handles.c

index 97ed0a0d53cefe273adf3fcbc50d24d66cf9ce0c..a70c920f75ba66981bce42b12ded25885cfaec92 100644 (file)
 #include "libcli/security/security.h"
 #include "param/param.h"
 
-#define SAMBA_ASSOC_GROUP 0x12345678
+/* this is only used when the client asks for an unknown interface */
+#define DUMMY_ASSOC_GROUP 0x0FFFFFFF
 
 extern const struct dcesrv_interface dcesrv_mgmt_interface;
 
+
+/*
+  find an association group given a assoc_group_id
+ */
+static struct dcesrv_assoc_group *dcesrv_assoc_group_find(struct dcesrv_context *dce_ctx,
+                                                         uint32_t id)
+{
+       void *id_ptr;
+
+       id_ptr = idr_find(dce_ctx->assoc_groups_idr, id);
+       if (id_ptr == NULL) {
+               return NULL;
+       }
+       return talloc_get_type_abort(id_ptr, struct dcesrv_assoc_group);
+}
+
+/*
+  take a reference to an existing association group
+ */
+static struct dcesrv_assoc_group *dcesrv_assoc_group_reference(TALLOC_CTX *mem_ctx,
+                                                              struct dcesrv_context *dce_ctx,
+                                                              uint32_t id)
+{
+       struct dcesrv_assoc_group *assoc_group;
+
+       assoc_group = dcesrv_assoc_group_find(dce_ctx, id);
+       if (assoc_group == NULL) {
+               DEBUG(0,(__location__ ": Failed to find assoc_group 0x%08x\n", id));
+               return NULL;
+       }
+       return talloc_reference(mem_ctx, assoc_group);
+}
+
+/*
+  allocate a new association group
+ */
+static struct dcesrv_assoc_group *dcesrv_assoc_group_new(TALLOC_CTX *mem_ctx,
+                                                        struct dcesrv_context *dce_ctx)
+{
+       struct dcesrv_assoc_group *assoc_group;
+       int id;
+
+       assoc_group = talloc_zero(mem_ctx, struct dcesrv_assoc_group);
+       if (assoc_group == NULL) {
+               return NULL;
+       }
+       
+       id = idr_get_new_random(dce_ctx->assoc_groups_idr, assoc_group, UINT16_MAX);
+       if (id == -1) {
+               talloc_free(assoc_group);
+               DEBUG(0,(__location__ ": Out of association groups!\n"));
+               return NULL;
+       }
+
+       assoc_group->id = id;
+       return assoc_group;
+}
+
+
 /*
   see if two endpoints match
 */
@@ -510,18 +570,11 @@ static NTSTATUS dcesrv_bind(struct dcesrv_call_state *call)
        uint32_t extra_flags = 0;
 
        /*
-        * Association groups allow policy handles to be shared across
-        * multiple client connections.  We don't implement this yet.
-        *
-        * So we just allow 0 if the client wants to create a new
-        * association group.
-        *
-        * And we allow the 0x12345678 value, we give away as
-        * assoc_group_id back to the clients
+         if provided, check the assoc_group is valid
         */
        if (call->pkt.u.bind.assoc_group_id != 0 &&
            lp_parm_bool(call->conn->dce_ctx->lp_ctx, NULL, "dcesrv","assoc group checking", true) &&
-           call->pkt.u.bind.assoc_group_id != SAMBA_ASSOC_GROUP) {
+           dcesrv_assoc_group_find(call->conn->dce_ctx, call->pkt.u.bind.assoc_group_id) == NULL) {
                return dcesrv_bind_nak(call, 0);        
        }
 
@@ -572,13 +625,18 @@ static NTSTATUS dcesrv_bind(struct dcesrv_call_state *call)
                context->conn = call->conn;
                context->iface = iface;
                context->context_id = context_id;
-               /*
-                * we need to send a non zero assoc_group_id here to make longhorn happy,
-                * it also matches samba3
-                */
-               context->assoc_group_id = SAMBA_ASSOC_GROUP;
+               if (call->pkt.u.bind.assoc_group_id != 0) {
+                       context->assoc_group = dcesrv_assoc_group_reference(context,
+                                                                           call->conn->dce_ctx, 
+                                                                           call->pkt.u.bind.assoc_group_id);
+               } else {
+                       context->assoc_group = dcesrv_assoc_group_new(context, call->conn->dce_ctx);
+               }
+               if (context->assoc_group == NULL) {
+                       talloc_free(context);
+                       return dcesrv_bind_nak(call, 0);
+               }
                context->private_data = NULL;
-               context->handles = NULL;
                DLIST_ADD(call->conn->contexts, context);
                call->context = context;
                talloc_set_destructor(context, dcesrv_connection_context_destructor);
@@ -630,12 +688,9 @@ static NTSTATUS dcesrv_bind(struct dcesrv_call_state *call)
          metze
        */
        if (call->context) {
-               pkt.u.bind_ack.assoc_group_id = call->context->assoc_group_id;
+               pkt.u.bind_ack.assoc_group_id = call->context->assoc_group->id;
        } else {
-               /* we better pick something - this chosen so as to
-                * send a non zero assoc_group_id (matching windows),
-                * it also matches samba3 */
-               pkt.u.bind_ack.assoc_group_id = SAMBA_ASSOC_GROUP;
+               pkt.u.bind_ack.assoc_group_id = DUMMY_ASSOC_GROUP;
        }
 
        if (iface) {
@@ -748,9 +803,19 @@ static NTSTATUS dcesrv_alter_new_context(struct dcesrv_call_state *call, uint32_
        context->conn = call->conn;
        context->iface = iface;
        context->context_id = context_id;
-       context->assoc_group_id = SAMBA_ASSOC_GROUP;
+       if (call->pkt.u.alter.assoc_group_id != 0) {
+               context->assoc_group = dcesrv_assoc_group_reference(context,
+                                                                   call->conn->dce_ctx, 
+                                                                   call->pkt.u.alter.assoc_group_id);
+       } else {
+               context->assoc_group = dcesrv_assoc_group_new(context, call->conn->dce_ctx);
+       }
+       if (context->assoc_group == NULL) {
+               talloc_free(context);
+               call->context = NULL;
+               return NT_STATUS_NO_MEMORY;
+       }
        context->private_data = NULL;
-       context->handles = NULL;
        DLIST_ADD(call->conn->contexts, context);
        call->context = context;
        talloc_set_destructor(context, dcesrv_connection_context_destructor);
@@ -803,8 +868,10 @@ static NTSTATUS dcesrv_alter(struct dcesrv_call_state *call)
        if (result == 0 &&
            call->pkt.u.alter.assoc_group_id != 0 &&
            lp_parm_bool(call->conn->dce_ctx->lp_ctx, NULL, "dcesrv","assoc group checking", true) &&
-           call->pkt.u.alter.assoc_group_id != call->context->assoc_group_id) {
-               /* TODO: work out what to return here */
+           call->pkt.u.alter.assoc_group_id != call->context->assoc_group->id) {
+               DEBUG(0,(__location__ ": Failed attempt to use new assoc_group in alter context (0x%08x 0x%08x)\n",
+                        call->context->assoc_group->id, call->pkt.u.alter.assoc_group_id));
+               /* TODO: can they ask for a new association group? */
                result = DCERPC_BIND_PROVIDER_REJECT;
                reason = DCERPC_BIND_REASON_ASYNTAX;
        }
@@ -818,7 +885,7 @@ static NTSTATUS dcesrv_alter(struct dcesrv_call_state *call)
        pkt.u.alter_resp.max_xmit_frag = 0x2000;
        pkt.u.alter_resp.max_recv_frag = 0x2000;
        if (result == 0) {
-               pkt.u.alter_resp.assoc_group_id = call->context->assoc_group_id;
+               pkt.u.alter_resp.assoc_group_id = call->context->assoc_group->id;
        } else {
                pkt.u.alter_resp.assoc_group_id = 0;
        }
@@ -1205,6 +1272,8 @@ _PUBLIC_ NTSTATUS dcesrv_init_context(TALLOC_CTX *mem_ctx,
        NT_STATUS_HAVE_NO_MEMORY(dce_ctx);
        dce_ctx->endpoint_list  = NULL;
        dce_ctx->lp_ctx = lp_ctx;
+       dce_ctx->assoc_groups_idr = idr_init(dce_ctx);
+       NT_STATUS_HAVE_NO_MEMORY(dce_ctx->assoc_groups_idr);
 
        for (i=0;endpoint_servers[i];i++) {
                const struct dcesrv_endpoint_server *ep_server;
index 0c4d4d413d7bc76676c56ed6c6c070860d779cc5..a64b01fdc59c72b0506dc9e9976e9ae16d3b4f99 100644 (file)
@@ -153,7 +153,7 @@ struct dcesrv_connection_context {
        struct dcesrv_connection_context *next, *prev;
        uint32_t context_id;
 
-       uint32_t assoc_group_id;
+       struct dcesrv_assoc_group *assoc_group;
 
        /* the connection this is on */
        struct dcesrv_connection *conn;
@@ -163,10 +163,6 @@ struct dcesrv_connection_context {
 
        /* private data for the interface implementation */
        void *private_data;
-
-       /* current rpc handles - this is really the wrong scope for
-          them, but it will do for now */
-       struct dcesrv_handle *handles;
 };
 
 
@@ -252,6 +248,15 @@ struct dcesrv_endpoint_server {
 };
 
 
+/* one association groups */
+struct dcesrv_assoc_group {
+       /* the wire id */
+       uint32_t id;
+       
+       /* list of handles in this association group */
+       struct dcesrv_handle *handles;
+};
+
 /* server-wide context information for the dcerpc server */
 struct dcesrv_context {
        /* the list of endpoints that have registered 
@@ -272,6 +277,8 @@ struct dcesrv_context {
 
        /* loadparm context to use for this connection */
        struct loadparm_context *lp_ctx;
+
+       struct idr_context *assoc_groups_idr;
 };
 
 /* this structure is used by modules to determine the size of some critical types */
index 284354feb484380ac9dc24bf75010efd3ad268b9..74b0ddc82e6f3f61ecd5f7ad13478bced084b9af 100644 (file)
@@ -28,7 +28,7 @@
 */
 static int dcesrv_handle_destructor(struct dcesrv_handle *h)
 {
-       DLIST_REMOVE(h->context->handles, h);
+       DLIST_REMOVE(h->context->assoc_group->handles, h);
        return 0;
 }
 
@@ -51,7 +51,7 @@ _PUBLIC_ struct dcesrv_handle *dcesrv_handle_new(struct dcesrv_connection_contex
        h->wire_handle.handle_type = handle_type;
        h->wire_handle.uuid = GUID_random();
        
-       DLIST_ADD(context->handles, h);
+       DLIST_ADD(context->assoc_group->handles, h);
 
        talloc_set_destructor(h, dcesrv_handle_destructor);
 
@@ -70,10 +70,11 @@ _PUBLIC_ struct dcesrv_handle *dcesrv_handle_fetch(
        struct dcesrv_handle *h;
 
        if (policy_handle_empty(p)) {
+               /* TODO: we should probably return a NULL handle here */
                return dcesrv_handle_new(context, handle_type);
        }
 
-       for (h=context->handles; h; h=h->next) {
+       for (h=context->assoc_group->handles; h; h=h->next) {
                if (h->wire_handle.handle_type == p->handle_type &&
                    GUID_equal(&p->uuid, &h->wire_handle.uuid)) {
                        if (handle_type != DCESRV_HANDLE_ANY &&