s4:rpc_server: correctly handle dcerpc requests with object uuids
[kai/samba.git] / source4 / rpc_server / dcerpc_server.c
index bd8c40e6fdeab9f4e01a7e249d78191c45d5aa57..893055d3b1ad70f078b2541edc38acccdaa174e2 100644 (file)
@@ -8,7 +8,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 "librpc/gen_ndr/ndr_dcerpc.h"
 #include "auth/auth.h"
-#include "dlinklist.h"
+#include "auth/gensec/gensec.h"
+#include "lib/util/dlinklist.h"
 #include "rpc_server/dcerpc_server.h"
+#include "rpc_server/dcerpc_server_proto.h"
+#include "librpc/rpc/dcerpc_proto.h"
 #include "lib/events/events.h"
+#include "smbd/service_task.h"
 #include "smbd/service_stream.h"
+#include "smbd/service.h"
 #include "system/filesys.h"
-#include "smb_build.h"
+#include "libcli/security/security.h"
+#include "param/param.h"
+
+#define SAMBA_ACCOC_GROUP 0x12345678
+
+extern const struct dcesrv_interface dcesrv_mgmt_interface;
 
 /*
   see if two endpoints match
 */
-static BOOL endpoints_match(const struct dcerpc_binding *ep1,
+static bool endpoints_match(const struct dcerpc_binding *ep1,
                            const struct dcerpc_binding *ep2)
 {
        if (ep1->transport != ep2->transport) {
-               return False;
+               return false;
        }
 
        if (!ep1->endpoint || !ep2->endpoint) {
@@ -46,9 +55,9 @@ static BOOL endpoints_match(const struct dcerpc_binding *ep1,
        }
 
        if (strcasecmp(ep1->endpoint, ep2->endpoint) != 0) 
-               return False;
+               return false;
 
-       return True;
+       return true;
 }
 
 /*
@@ -82,11 +91,11 @@ static struct dcesrv_connection_context *dcesrv_find_context(struct dcesrv_conne
 /*
   see if a uuid and if_version match to an interface
 */
-static BOOL interface_match(const struct dcesrv_interface *if1,
+static bool interface_match(const struct dcesrv_interface *if1,
                                                        const struct dcesrv_interface *if2)
 {
-       return (if1->if_version == if2->if_version && 
-                       GUID_equal(&if1->uuid, &if2->uuid));
+       return (if1->syntax_id.if_version == if2->syntax_id.if_version && 
+                       GUID_equal(&if1->syntax_id.uuid, &if2->syntax_id.uuid));
 }
 
 /*
@@ -107,10 +116,11 @@ static const struct dcesrv_interface *find_interface(const struct dcesrv_endpoin
 /*
   see if a uuid and if_version match to an interface
 */
-static BOOL interface_match_by_uuid(const struct dcesrv_interface *iface,
+static bool interface_match_by_uuid(const struct dcesrv_interface *iface,
                                    const struct GUID *uuid, uint32_t if_version)
 {
-       return (iface->if_version == if_version && GUID_equal(&iface->uuid, uuid));
+       return (iface->syntax_id.if_version == if_version && 
+                       GUID_equal(&iface->syntax_id.uuid, uuid));
 }
 
 /*
@@ -129,12 +139,12 @@ static const struct dcesrv_interface *find_interface_by_uuid(const struct dcesrv
 }
 
 /*
-  find a call that is pending in our call list
+  find the earlier parts of a fragmented call awaiting reassembily
 */
-static struct dcesrv_call_state *dcesrv_find_call(struct dcesrv_connection *dce_conn, uint16_t call_id)
+static struct dcesrv_call_state *dcesrv_find_fragmented_call(struct dcesrv_connection *dce_conn, uint16_t call_id)
 {
        struct dcesrv_call_state *c;
-       for (c=dce_conn->call_list;c;c=c->next) {
+       for (c=dce_conn->incoming_fragmented_call_list;c;c=c->next) {
                if (c->pkt.call_id == call_id) {
                        return c;
                }
@@ -145,7 +155,7 @@ static struct dcesrv_call_state *dcesrv_find_call(struct dcesrv_connection *dce_
 /*
   register an interface on an endpoint
 */
-NTSTATUS dcesrv_interface_register(struct dcesrv_context *dce_ctx,
+_PUBLIC_ NTSTATUS dcesrv_interface_register(struct dcesrv_context *dce_ctx,
                                   const char *ep_name,
                                   const struct dcesrv_interface *iface,
                                   const struct security_descriptor *sd)
@@ -153,7 +163,7 @@ NTSTATUS dcesrv_interface_register(struct dcesrv_context *dce_ctx,
        struct dcesrv_endpoint *ep;
        struct dcesrv_if_list *ifl;
        struct dcerpc_binding *binding;
-       BOOL add_ep = False;
+       bool add_ep = false;
        NTSTATUS status;
        
        status = dcerpc_parse_binding(dce_ctx, ep_name, &binding);
@@ -172,7 +182,18 @@ NTSTATUS dcesrv_interface_register(struct dcesrv_context *dce_ctx,
                }
                ZERO_STRUCTP(ep);
                ep->ep_description = talloc_reference(ep, binding);
-               add_ep = True;
+               add_ep = true;
+
+               /* add mgmt interface */
+               ifl = talloc(dce_ctx, struct dcesrv_if_list);
+               if (!ifl) {
+                       return NT_STATUS_NO_MEMORY;
+               }
+
+               memcpy(&(ifl->iface), &dcesrv_mgmt_interface, 
+                          sizeof(struct dcesrv_interface));
+
+               DLIST_ADD(ep->interface_list, ifl);
        }
 
        /* see if the interface is already registered on te endpoint */
@@ -251,21 +272,28 @@ NTSTATUS dcesrv_generic_session_key(struct dcesrv_connection *p,
 
 /*
   fetch the user session key - may be default (above) or the SMB session key
+
+  The key is always truncated to 16 bytes 
 */
-NTSTATUS dcesrv_fetch_session_key(struct dcesrv_connection *p,
+_PUBLIC_ NTSTATUS dcesrv_fetch_session_key(struct dcesrv_connection *p,
                                  DATA_BLOB *session_key)
 {
-       return p->auth_state.session_key(p, session_key);
+       NTSTATUS status = p->auth_state.session_key(p, session_key);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+
+       session_key->length = MIN(session_key->length, 16);
+
+       return NT_STATUS_OK;
 }
 
 
 /*
   destroy a link to an endpoint
 */
-static int dcesrv_endpoint_destructor(void *ptr)
+static int dcesrv_endpoint_destructor(struct dcesrv_connection *p)
 {
-       struct dcesrv_connection *p = ptr;
-
        while (p->contexts) {
                struct dcesrv_connection_context *c = p->contexts;
 
@@ -283,30 +311,48 @@ static int dcesrv_endpoint_destructor(void *ptr)
 /*
   connect to a dcerpc endpoint
 */
-NTSTATUS dcesrv_endpoint_connect(struct dcesrv_context *dce_ctx,
+_PUBLIC_ NTSTATUS dcesrv_endpoint_connect(struct dcesrv_context *dce_ctx,
                                 TALLOC_CTX *mem_ctx,
                                 const struct dcesrv_endpoint *ep,
-                                struct stream_connection *srv_conn,
+                                struct auth_session_info *session_info,
+                                struct event_context *event_ctx,
+                                struct messaging_context *msg_ctx,
+                                struct server_id server_id,
+                                uint32_t state_flags,
                                 struct dcesrv_connection **_p)
 {
        struct dcesrv_connection *p;
 
+       if (!session_info) {
+               return NT_STATUS_ACCESS_DENIED;
+       }
+
        p = talloc(mem_ctx, struct dcesrv_connection);
        NT_STATUS_HAVE_NO_MEMORY(p);
 
+       if (!talloc_reference(p, session_info)) {
+               talloc_free(p);
+               return NT_STATUS_NO_MEMORY;
+       }
+
        p->dce_ctx = dce_ctx;
        p->endpoint = ep;
        p->contexts = NULL;
        p->call_list = NULL;
+       p->incoming_fragmented_call_list = NULL;
        p->pending_call_list = NULL;
        p->cli_max_recv_frag = 0;
        p->partial_input = data_blob(NULL, 0);
        p->auth_state.auth_info = NULL;
        p->auth_state.gensec_security = NULL;
-       p->auth_state.session_info = NULL;
+       p->auth_state.session_info = session_info;
        p->auth_state.session_key = dcesrv_generic_session_key;
-       p->srv_conn = srv_conn;
-       p->processing = False;
+       p->event_ctx = event_ctx;
+       p->msg_ctx = msg_ctx;
+       p->server_id = server_id;
+       p->processing = false;
+       p->state_flags = state_flags;
+       ZERO_STRUCT(p->transport);
 
        talloc_set_destructor(p, dcesrv_endpoint_destructor);
 
@@ -317,11 +363,14 @@ NTSTATUS dcesrv_endpoint_connect(struct dcesrv_context *dce_ctx,
 /*
   search and connect to a dcerpc endpoint
 */
-NTSTATUS dcesrv_endpoint_search_connect(struct dcesrv_context *dce_ctx,
+_PUBLIC_ NTSTATUS dcesrv_endpoint_search_connect(struct dcesrv_context *dce_ctx,
                                        TALLOC_CTX *mem_ctx,
                                        const struct dcerpc_binding *ep_description,
                                        struct auth_session_info *session_info,
-                                       struct stream_connection *srv_conn,
+                                       struct event_context *event_ctx,
+                                       struct messaging_context *msg_ctx,
+                                       struct server_id server_id,
+                                       uint32_t state_flags,
                                        struct dcesrv_connection **dce_conn_p)
 {
        NTSTATUS status;
@@ -333,12 +382,11 @@ NTSTATUS dcesrv_endpoint_search_connect(struct dcesrv_context *dce_ctx,
                return NT_STATUS_OBJECT_NAME_NOT_FOUND;
        }
 
-       status = dcesrv_endpoint_connect(dce_ctx, mem_ctx, ep, srv_conn, dce_conn_p);
-       if (!NT_STATUS_IS_OK(status)) {
-               return status;
-       }
+       status = dcesrv_endpoint_connect(dce_ctx, mem_ctx, ep, session_info,
+                                        event_ctx, msg_ctx, server_id,
+                                        state_flags, dce_conn_p);
+       NT_STATUS_NOT_OK_RETURN(status);
 
-       (*dce_conn_p)->auth_state.session_info = talloc_reference((*dce_conn_p), session_info);
        (*dce_conn_p)->auth_state.session_key = dcesrv_inherited_session_key;
 
        /* TODO: check security descriptor of the endpoint here 
@@ -350,11 +398,11 @@ NTSTATUS dcesrv_endpoint_search_connect(struct dcesrv_context *dce_ctx,
 }
 
 
-static void dcesrv_init_hdr(struct ncacn_packet *pkt)
+static void dcesrv_init_hdr(struct ncacn_packet *pkt, bool bigendian)
 {
        pkt->rpc_vers = 5;
        pkt->rpc_vers_minor = 0;
-       if (lp_rpc_big_endian()) {
+       if (bigendian) {
                pkt->drep[0] = 0;
        } else {
                pkt->drep[0] = DCERPC_DREP_LE;
@@ -364,6 +412,43 @@ static void dcesrv_init_hdr(struct ncacn_packet *pkt)
        pkt->drep[3] = 0;
 }
 
+/*
+  move a call from an existing linked list to the specified list. This
+  prevents bugs where we forget to remove the call from a previous
+  list when moving it.
+ */
+static void dcesrv_call_set_list(struct dcesrv_call_state *call, 
+                                enum dcesrv_call_list list)
+{
+       switch (call->list) {
+       case DCESRV_LIST_NONE:
+               break;
+       case DCESRV_LIST_CALL_LIST:
+               DLIST_REMOVE(call->conn->call_list, call);
+               break;
+       case DCESRV_LIST_FRAGMENTED_CALL_LIST:
+               DLIST_REMOVE(call->conn->incoming_fragmented_call_list, call);
+               break;
+       case DCESRV_LIST_PENDING_CALL_LIST:
+               DLIST_REMOVE(call->conn->pending_call_list, call);
+               break;
+       }
+       call->list = list;
+       switch (list) {
+       case DCESRV_LIST_NONE:
+               break;
+       case DCESRV_LIST_CALL_LIST:
+               DLIST_ADD_END(call->conn->call_list, call, struct dcesrv_call_state *);
+               break;
+       case DCESRV_LIST_FRAGMENTED_CALL_LIST:
+               DLIST_ADD_END(call->conn->incoming_fragmented_call_list, call, struct dcesrv_call_state *);
+               break;
+       case DCESRV_LIST_PENDING_CALL_LIST:
+               DLIST_ADD_END(call->conn->pending_call_list, call, struct dcesrv_call_state *);
+               break;
+       }
+}
+
 /*
   return a dcerpc fault
 */
@@ -371,10 +456,11 @@ static NTSTATUS dcesrv_fault(struct dcesrv_call_state *call, uint32_t fault_code
 {
        struct ncacn_packet pkt;
        struct data_blob_list_item *rep;
+       uint8_t zeros[4];
        NTSTATUS status;
 
        /* setup a bind_ack */
-       dcesrv_init_hdr(&pkt);
+       dcesrv_init_hdr(&pkt, lp_rpc_big_endian(call->conn->dce_ctx->lp_ctx));
        pkt.auth_length = 0;
        pkt.call_id = call->pkt.call_id;
        pkt.ptype = DCERPC_PKT_FAULT;
@@ -384,12 +470,15 @@ static NTSTATUS dcesrv_fault(struct dcesrv_call_state *call, uint32_t fault_code
        pkt.u.fault.cancel_count = 0;
        pkt.u.fault.status = fault_code;
 
+       ZERO_STRUCT(zeros);
+       pkt.u.fault._pad = data_blob_const(zeros, sizeof(zeros));
+
        rep = talloc(call, struct data_blob_list_item);
        if (!rep) {
                return NT_STATUS_NO_MEMORY;
        }
 
-       status = ncacn_push_auth(&rep->blob, call, &pkt, NULL);
+       status = ncacn_push_auth(&rep->blob, call, lp_iconv_convenience(call->conn->dce_ctx->lp_ctx), &pkt, NULL);
        if (!NT_STATUS_IS_OK(status)) {
                return status;
        }
@@ -397,7 +486,7 @@ static NTSTATUS dcesrv_fault(struct dcesrv_call_state *call, uint32_t fault_code
        dcerpc_set_frag_length(&rep->blob, rep->blob.length);
 
        DLIST_ADD_END(call->replies, rep, struct data_blob_list_item *);
-       DLIST_ADD_END(call->conn->call_list, call, struct dcesrv_call_state *);
+       dcesrv_call_set_list(call, DCESRV_LIST_CALL_LIST);
 
        return NT_STATUS_OK;    
 }
@@ -413,20 +502,22 @@ static NTSTATUS dcesrv_bind_nak(struct dcesrv_call_state *call, uint32_t reason)
        NTSTATUS status;
 
        /* setup a bind_nak */
-       dcesrv_init_hdr(&pkt);
+       dcesrv_init_hdr(&pkt, lp_rpc_big_endian(call->conn->dce_ctx->lp_ctx));
        pkt.auth_length = 0;
        pkt.call_id = call->pkt.call_id;
        pkt.ptype = DCERPC_PKT_BIND_NAK;
        pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
        pkt.u.bind_nak.reject_reason = reason;
-       pkt.u.bind_nak.num_versions = 0;
+       if (pkt.u.bind_nak.reject_reason == DECRPC_BIND_PROTOCOL_VERSION_NOT_SUPPORTED) {
+               pkt.u.bind_nak.versions.v.num_versions = 0;
+       }
 
        rep = talloc(call, struct data_blob_list_item);
        if (!rep) {
                return NT_STATUS_NO_MEMORY;
        }
 
-       status = ncacn_push_auth(&rep->blob, call, &pkt, NULL);
+       status = ncacn_push_auth(&rep->blob, call, lp_iconv_convenience(call->conn->dce_ctx->lp_ctx), &pkt, NULL);
        if (!NT_STATUS_IS_OK(status)) {
                return status;
        }
@@ -434,7 +525,7 @@ static NTSTATUS dcesrv_bind_nak(struct dcesrv_call_state *call, uint32_t reason)
        dcerpc_set_frag_length(&rep->blob, rep->blob.length);
 
        DLIST_ADD_END(call->replies, rep, struct data_blob_list_item *);
-       DLIST_ADD_END(call->conn->call_list, call, struct dcesrv_call_state *);
+       dcesrv_call_set_list(call, DCESRV_LIST_CALL_LIST);
 
        return NT_STATUS_OK;    
 }
@@ -453,6 +544,22 @@ static NTSTATUS dcesrv_bind(struct dcesrv_call_state *call)
        uint32_t result=0, reason=0;
        uint32_t context_id;
        const struct dcesrv_interface *iface;
+       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 (call->pkt.u.bind.assoc_group_id != 0 &&
+           call->pkt.u.bind.assoc_group_id != SAMBA_ACCOC_GROUP) {
+               return dcesrv_bind_nak(call, 0);        
+       }
 
        if (call->pkt.u.bind.num_contexts < 1 ||
            call->pkt.u.bind.ctx_list[0].num_transfer_syntaxes < 1) {
@@ -511,21 +618,27 @@ static NTSTATUS dcesrv_bind(struct dcesrv_call_state *call)
                call->conn->cli_max_recv_frag = call->pkt.u.bind.max_recv_frag;
        }
 
+       if ((call->pkt.pfc_flags & DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN) &&
+           lp_parm_bool(call->conn->dce_ctx->lp_ctx, NULL, "dcesrv","header signing", false)) {
+               call->conn->state_flags |= DCESRV_CALL_STATE_FLAG_HEADER_SIGNING;
+               extra_flags |= DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN;
+       }
+
        /* handle any authentication that is being requested */
        if (!dcesrv_auth_bind(call)) {
-               /* TODO: work out the right reject code */
-               return dcesrv_bind_nak(call, 0);
+               return dcesrv_bind_nak(call, DCERPC_BIND_REASON_INVALID_AUTH_TYPE);
        }
 
        /* setup a bind_ack */
-       dcesrv_init_hdr(&pkt);
+       dcesrv_init_hdr(&pkt, lp_rpc_big_endian(call->conn->dce_ctx->lp_ctx));
        pkt.auth_length = 0;
        pkt.call_id = call->pkt.call_id;
        pkt.ptype = DCERPC_PKT_BIND_ACK;
-       pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
+       pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST | extra_flags;
        pkt.u.bind_ack.max_xmit_frag = 0x2000;
        pkt.u.bind_ack.max_recv_frag = 0x2000;
-       pkt.u.bind_ack.assoc_group_id = call->pkt.u.bind.assoc_group_id;
+       /* we need to send a non zero assoc_group_id here to make longhorn happy, it also matches samba3 */
+       pkt.u.bind_ack.assoc_group_id = SAMBA_ACCOC_GROUP;
        if (iface) {
                /* FIXME: Use pipe name as specified by endpoint instead of interface name */
                pkt.u.bind_ack.secondary_address = talloc_asprintf(call, "\\PIPE\\%s", iface->name);
@@ -542,7 +655,8 @@ static NTSTATUS dcesrv_bind(struct dcesrv_call_state *call)
        pkt.u.bind_ack.ctx_list[0].syntax = ndr_transfer_syntax;
        pkt.u.bind_ack.auth_info = data_blob(NULL, 0);
 
-       if (!dcesrv_auth_bind_ack(call, &pkt)) {
+       status = dcesrv_auth_bind_ack(call, &pkt);
+       if (!NT_STATUS_IS_OK(status)) {
                return dcesrv_bind_nak(call, 0);
        }
 
@@ -562,8 +676,7 @@ static NTSTATUS dcesrv_bind(struct dcesrv_call_state *call)
                return NT_STATUS_NO_MEMORY;
        }
 
-       status = ncacn_push_auth(&rep->blob, call, &pkt, 
-                                 call->conn->auth_state.auth_info);
+       status = ncacn_push_auth(&rep->blob, call, lp_iconv_convenience(call->conn->dce_ctx->lp_ctx), &pkt, call->conn->auth_state.auth_info);
        if (!NT_STATUS_IS_OK(status)) {
                return status;
        }
@@ -571,7 +684,7 @@ static NTSTATUS dcesrv_bind(struct dcesrv_call_state *call)
        dcerpc_set_frag_length(&rep->blob, rep->blob.length);
 
        DLIST_ADD_END(call->replies, rep, struct data_blob_list_item *);
-       DLIST_ADD_END(call->conn->call_list, call, struct dcesrv_call_state *);
+       dcesrv_call_set_list(call, DCESRV_LIST_CALL_LIST);
 
        return NT_STATUS_OK;
 }
@@ -604,6 +717,7 @@ static NTSTATUS dcesrv_alter_new_context(struct dcesrv_call_state *call, uint32_
        struct dcesrv_connection_context *context;
        const struct dcesrv_interface *iface;
        struct GUID uuid, *transfer_syntax_uuid;
+       NTSTATUS status;
 
        if_version = call->pkt.u.alter.ctx_list[0].abstract_syntax.if_version;
        uuid = call->pkt.u.alter.ctx_list[0].abstract_syntax.uuid;
@@ -637,6 +751,13 @@ static NTSTATUS dcesrv_alter_new_context(struct dcesrv_call_state *call, uint32_
        DLIST_ADD(call->conn->contexts, context);
        call->context = context;
 
+       if (iface) {
+               status = iface->bind(call, iface);
+               if (!NT_STATUS_IS_OK(status)) {
+                       return status;
+               }
+       }
+
        return NT_STATUS_OK;
 }
 
@@ -672,7 +793,7 @@ static NTSTATUS dcesrv_alter(struct dcesrv_call_state *call)
        }
 
        /* setup a alter_resp */
-       dcesrv_init_hdr(&pkt);
+       dcesrv_init_hdr(&pkt, lp_rpc_big_endian(call->conn->dce_ctx->lp_ctx));
        pkt.auth_length = 0;
        pkt.call_id = call->pkt.call_id;
        pkt.ptype = DCERPC_PKT_ALTER_RESP;
@@ -691,8 +812,15 @@ static NTSTATUS dcesrv_alter(struct dcesrv_call_state *call)
        pkt.u.alter_resp.auth_info = data_blob(NULL, 0);
        pkt.u.alter_resp.secondary_address = "";
 
-       if (!dcesrv_auth_alter_ack(call, &pkt)) {
-               return dcesrv_bind_nak(call, 0);
+       status = dcesrv_auth_alter_ack(call, &pkt);
+       if (!NT_STATUS_IS_OK(status)) {
+               if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)
+                   || NT_STATUS_EQUAL(status, NT_STATUS_LOGON_FAILURE)
+                   || NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_USER)
+                   || NT_STATUS_EQUAL(status, NT_STATUS_WRONG_PASSWORD)) {
+                       return dcesrv_fault(call, DCERPC_FAULT_ACCESS_DENIED);
+               }
+               return dcesrv_fault(call, 0);
        }
 
        rep = talloc(call, struct data_blob_list_item);
@@ -700,8 +828,7 @@ static NTSTATUS dcesrv_alter(struct dcesrv_call_state *call)
                return NT_STATUS_NO_MEMORY;
        }
 
-       status = ncacn_push_auth(&rep->blob, call, &pkt, 
-                                 call->conn->auth_state.auth_info);
+       status = ncacn_push_auth(&rep->blob, call, lp_iconv_convenience(call->conn->dce_ctx->lp_ctx), &pkt, call->conn->auth_state.auth_info);
        if (!NT_STATUS_IS_OK(status)) {
                return status;
        }
@@ -709,7 +836,7 @@ static NTSTATUS dcesrv_alter(struct dcesrv_call_state *call)
        dcerpc_set_frag_length(&rep->blob, rep->blob.length);
 
        DLIST_ADD_END(call->replies, rep, struct data_blob_list_item *);
-       DLIST_ADD_END(call->conn->call_list, call, struct dcesrv_call_state *);
+       dcesrv_call_set_list(call, DCESRV_LIST_CALL_LIST);
 
        return NT_STATUS_OK;
 }
@@ -723,10 +850,6 @@ static NTSTATUS dcesrv_request(struct dcesrv_call_state *call)
        NTSTATUS status;
        struct dcesrv_connection_context *context;
 
-       call->fault_code        = 0;
-       call->state_flags       = call->conn->dce_ctx->state_flags;
-       call->time              = timeval_current();
-
        /* if authenticated, and the mech we use can't do async replies, don't use them... */
        if (call->conn->auth_state.gensec_security && 
            !gensec_have_feature(call->conn->auth_state.gensec_security, GENSEC_FEATURE_ASYNC_REPLIES)) {
@@ -738,19 +861,15 @@ static NTSTATUS dcesrv_request(struct dcesrv_call_state *call)
                return dcesrv_fault(call, DCERPC_FAULT_UNK_IF);
        }
 
-       pull = ndr_pull_init_blob(&call->pkt.u.request.stub_and_verifier, call);
+       pull = ndr_pull_init_blob(&call->pkt.u.request.stub_and_verifier, call,
+                                 lp_iconv_convenience(call->conn->dce_ctx->lp_ctx));
        NT_STATUS_HAVE_NO_MEMORY(pull);
 
        pull->flags |= LIBNDR_FLAG_REF_ALLOC;
 
        call->context   = context;
-       call->event_ctx = context->conn->srv_conn->event.ctx;
        call->ndr_pull  = pull;
 
-       if (call->pkt.pfc_flags & DCERPC_PFC_FLAG_ORPC) {
-               pull->flags |= LIBNDR_FLAG_OBJECT_PRESENT;
-       }
-
        if (!(call->pkt.drep[0] & DCERPC_DREP_LE)) {
                pull->flags |= LIBNDR_FLAG_BIGENDIAN;
        }
@@ -778,7 +897,7 @@ static NTSTATUS dcesrv_request(struct dcesrv_call_state *call)
        }
 
        /* add the call to the pending list */
-       DLIST_ADD_END(call->conn->pending_call_list, call, struct dcesrv_call_state *);
+       dcesrv_call_set_list(call, DCESRV_LIST_PENDING_CALL_LIST);
 
        if (call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
                return NT_STATUS_OK;
@@ -787,13 +906,14 @@ static NTSTATUS dcesrv_request(struct dcesrv_call_state *call)
        return dcesrv_reply(call);
 }
 
-NTSTATUS dcesrv_reply(struct dcesrv_call_state *call)
+_PUBLIC_ NTSTATUS dcesrv_reply(struct dcesrv_call_state *call)
 {
        struct ndr_push *push;
        NTSTATUS status;
        DATA_BLOB stub;
-       uint32_t total_length;
+       uint32_t total_length, chunk_size;
        struct dcesrv_connection_context *context = call->context;
+       size_t sig_size = 0;
 
        /* call the reply function */
        status = context->iface->reply(call, call, call->r);
@@ -802,7 +922,7 @@ NTSTATUS dcesrv_reply(struct dcesrv_call_state *call)
        }
 
        /* form the reply NDR */
-       push = ndr_push_init_ctx(call);
+       push = ndr_push_init_ctx(call, lp_iconv_convenience(call->conn->dce_ctx->lp_ctx));
        NT_STATUS_HAVE_NO_MEMORY(push);
 
        /* carry over the pointer count to the reply in case we are
@@ -810,7 +930,7 @@ NTSTATUS dcesrv_reply(struct dcesrv_call_state *call)
           pointers */
        push->ptr_count = call->ndr_pull->ptr_count;
 
-       if (lp_rpc_big_endian()) {
+       if (lp_rpc_big_endian(call->conn->dce_ctx->lp_ctx)) {
                push->flags |= LIBNDR_FLAG_BIGENDIAN;
        }
 
@@ -823,6 +943,21 @@ NTSTATUS dcesrv_reply(struct dcesrv_call_state *call)
 
        total_length = stub.length;
 
+       /* we can write a full max_recv_frag size, minus the dcerpc
+          request header size */
+       chunk_size = call->conn->cli_max_recv_frag;
+       chunk_size -= DCERPC_REQUEST_LENGTH;
+       if (call->conn->auth_state.auth_info &&
+           call->conn->auth_state.gensec_security) {
+               sig_size = gensec_sig_size(call->conn->auth_state.gensec_security,
+                                          call->conn->cli_max_recv_frag);
+               if (sig_size) {
+                       chunk_size -= DCERPC_AUTH_TRAILER_LENGTH;
+                       chunk_size -= sig_size;
+               }
+       }
+       chunk_size -= (chunk_size % 16);
+
        do {
                uint32_t length;
                struct data_blob_list_item *rep;
@@ -831,15 +966,10 @@ NTSTATUS dcesrv_reply(struct dcesrv_call_state *call)
                rep = talloc(call, struct data_blob_list_item);
                NT_STATUS_HAVE_NO_MEMORY(rep);
 
-               length = stub.length;
-               if (length + DCERPC_RESPONSE_LENGTH > call->conn->cli_max_recv_frag) {
-                       /* the 32 is to cope with signing data */
-                       length = call->conn->cli_max_recv_frag - 
-                               (DCERPC_MAX_SIGN_SIZE+DCERPC_RESPONSE_LENGTH);
-               }
+               length = MIN(chunk_size, stub.length);
 
                /* form the dcerpc response packet */
-               dcesrv_init_hdr(&pkt);
+               dcesrv_init_hdr(&pkt, lp_rpc_big_endian(call->conn->dce_ctx->lp_ctx));
                pkt.auth_length = 0;
                pkt.call_id = call->pkt.call_id;
                pkt.ptype = DCERPC_PKT_RESPONSE;
@@ -856,7 +986,7 @@ NTSTATUS dcesrv_reply(struct dcesrv_call_state *call)
                pkt.u.response.stub_and_verifier.data = stub.data;
                pkt.u.response.stub_and_verifier.length = length;
 
-               if (!dcesrv_auth_response(call, &rep->blob, &pkt)) {
+               if (!dcesrv_auth_response(call, &rep->blob, sig_size, &pkt)) {
                        return dcesrv_fault(call, DCERPC_FAULT_OTHER);          
                }
 
@@ -869,32 +999,47 @@ NTSTATUS dcesrv_reply(struct dcesrv_call_state *call)
        } while (stub.length != 0);
 
        /* move the call from the pending to the finished calls list */
-       DLIST_REMOVE(call->conn->pending_call_list, call);
-       DLIST_ADD_END(call->conn->call_list, call, struct dcesrv_call_state *);
+       dcesrv_call_set_list(call, DCESRV_LIST_CALL_LIST);
 
        if (call->conn->call_list && call->conn->call_list->replies) {
-               if (call->conn->srv_conn &&
-                   call->conn->srv_conn->event.fde) {
-                       EVENT_FD_WRITEABLE(call->conn->srv_conn->event.fde);
+               if (call->conn->transport.report_output_data) {
+                       call->conn->transport.report_output_data(call->conn);
                }
        }
 
        return NT_STATUS_OK;
 }
 
+_PUBLIC_ struct socket_address *dcesrv_connection_get_my_addr(struct dcesrv_connection *conn, TALLOC_CTX *mem_ctx)
+{
+       if (!conn->transport.get_my_addr) {
+               return NULL;
+       }
+
+       return conn->transport.get_my_addr(conn, mem_ctx);
+}
+
+_PUBLIC_ struct socket_address *dcesrv_connection_get_peer_addr(struct dcesrv_connection *conn, TALLOC_CTX *mem_ctx)
+{
+       if (!conn->transport.get_peer_addr) {
+               return NULL;
+       }
+
+       return conn->transport.get_peer_addr(conn, mem_ctx);
+}
 
 /*
   work out if we have a full packet yet
 */
-static BOOL dce_full_packet(const DATA_BLOB *data)
+static bool dce_full_packet(const DATA_BLOB *data)
 {
        if (data->length < DCERPC_FRAG_LEN_OFFSET+2) {
-               return False;
+               return false;
        }
        if (dcerpc_get_frag_length(data) > data->length) {
-               return False;
+               return false;
        }
-       return True;
+       return true;
 }
 
 /*
@@ -915,30 +1060,44 @@ static void dce_partial_advance(struct dcesrv_connection *dce_conn, uint32_t off
        data_blob_free(&blob);
 }
 
+/*
+  remove the call from the right list when freed
+ */
+static int dcesrv_call_dequeue(struct dcesrv_call_state *call)
+{
+       dcesrv_call_set_list(call, DCESRV_LIST_NONE);
+       return 0;
+}
+
 /*
   process some input to a dcerpc endpoint server.
 */
 NTSTATUS dcesrv_input_process(struct dcesrv_connection *dce_conn)
 {
        struct ndr_pull *ndr;
+       enum ndr_err_code ndr_err;
        NTSTATUS status;
        struct dcesrv_call_state *call;
        DATA_BLOB blob;
 
-       call = talloc(dce_conn, struct dcesrv_call_state);
+       call = talloc_zero(dce_conn, struct dcesrv_call_state);
        if (!call) {
                talloc_free(dce_conn->partial_input.data);
                return NT_STATUS_NO_MEMORY;
        }
-       call->conn      = dce_conn;
-       call->replies   = NULL;
-       call->context   = NULL;
-       call->event_ctx = dce_conn->srv_conn->event.ctx;
+       call->conn              = dce_conn;
+       call->event_ctx         = dce_conn->event_ctx;
+       call->msg_ctx           = dce_conn->msg_ctx;
+       call->state_flags       = call->conn->state_flags;
+       call->time              = timeval_current();
+       call->list              = DCESRV_LIST_NONE;
+
+       talloc_set_destructor(call, dcesrv_call_dequeue);
 
        blob = dce_conn->partial_input;
        blob.length = dcerpc_get_frag_length(&blob);
 
-       ndr = ndr_pull_init_blob(&blob, call);
+       ndr = ndr_pull_init_blob(&blob, call, lp_iconv_convenience(call->conn->dce_ctx->lp_ctx));
        if (!ndr) {
                talloc_free(dce_conn->partial_input.data);
                talloc_free(call);
@@ -949,11 +1108,15 @@ NTSTATUS dcesrv_input_process(struct dcesrv_connection *dce_conn)
                ndr->flags |= LIBNDR_FLAG_BIGENDIAN;
        }
 
-       status = ndr_pull_ncacn_packet(ndr, NDR_SCALARS|NDR_BUFFERS, &call->pkt);
-       if (!NT_STATUS_IS_OK(status)) {
+       if (CVAL(blob.data, DCERPC_PFC_OFFSET) & DCERPC_PFC_FLAG_OBJECT_UUID) {
+               ndr->flags |= LIBNDR_FLAG_OBJECT_PRESENT;
+       }
+
+       ndr_err = ndr_pull_ncacn_packet(ndr, NDR_SCALARS|NDR_BUFFERS, &call->pkt);
+       if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
                talloc_free(dce_conn->partial_input.data);
                talloc_free(call);
-               return status;
+               return ndr_map_error2ntstatus(ndr_err);
        }
 
        /* we have to check the signing here, before combining the
@@ -979,7 +1142,7 @@ NTSTATUS dcesrv_input_process(struct dcesrv_connection *dce_conn)
 
                /* this is a continuation of an existing call - find the call then
                   tack it on the end */
-               call = dcesrv_find_call(dce_conn, call2->pkt.call_id);
+               call = dcesrv_find_fragmented_call(dce_conn, call2->pkt.call_id);
                if (!call) {
                        return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
                }
@@ -1015,12 +1178,15 @@ NTSTATUS dcesrv_input_process(struct dcesrv_connection *dce_conn)
        }
 
        /* this may not be the last pdu in the chain - if its isn't then
-          just put it on the call_list and wait for the rest */
+          just put it on the incoming_fragmented_call_list and wait for the rest */
        if (call->pkt.ptype == DCERPC_PKT_REQUEST &&
            !(call->pkt.pfc_flags & DCERPC_PFC_FLAG_LAST)) {
-               DLIST_ADD_END(dce_conn->call_list, call, struct dcesrv_call_state *);
+               dcesrv_call_set_list(call, DCESRV_LIST_FRAGMENTED_CALL_LIST);
                return NT_STATUS_OK;
-       }
+       } 
+       
+       /* This removes any fragments we may have had stashed away */
+       dcesrv_call_set_list(call, DCESRV_LIST_NONE);
 
        switch (call->pkt.ptype) {
        case DCERPC_PKT_BIND:
@@ -1055,7 +1221,7 @@ NTSTATUS dcesrv_input_process(struct dcesrv_connection *dce_conn)
   provide some input to a dcerpc endpoint server. This passes data
   from a dcerpc client into the server
 */
-NTSTATUS dcesrv_input(struct dcesrv_connection *dce_conn, const DATA_BLOB *data)
+_PUBLIC_ NTSTATUS dcesrv_input(struct dcesrv_connection *dce_conn, const DATA_BLOB *data)
 {
        NTSTATUS status;
 
@@ -1092,7 +1258,7 @@ NTSTATUS dcesrv_input(struct dcesrv_connection *dce_conn, const DATA_BLOB *data)
 
   from the current fragment
 */
-NTSTATUS dcesrv_output(struct dcesrv_connection *dce_conn, 
+_PUBLIC_ NTSTATUS dcesrv_output(struct dcesrv_connection *dce_conn, 
                       void *private_data,
                       NTSTATUS (*write_fn)(void *private_data, DATA_BLOB *output, size_t *nwritten))
 {
@@ -1127,14 +1293,16 @@ NTSTATUS dcesrv_output(struct dcesrv_connection *dce_conn,
 
        if (call->replies == NULL) {
                /* we're done with the whole call */
-               DLIST_REMOVE(dce_conn->call_list, call);
+               dcesrv_call_set_list(call, DCESRV_LIST_NONE);
                talloc_free(call);
        }
 
        return status;
 }
 
-static NTSTATUS dcesrv_init_context(TALLOC_CTX *mem_ctx, const char **endpoint_servers, uint32_t state_flags, struct dcesrv_context **_dce_ctx)
+_PUBLIC_ NTSTATUS dcesrv_init_context(TALLOC_CTX *mem_ctx, 
+                                     struct loadparm_context *lp_ctx,
+                                     const char **endpoint_servers, struct dcesrv_context **_dce_ctx)
 {
        NTSTATUS status;
        struct dcesrv_context *dce_ctx;
@@ -1148,7 +1316,7 @@ static NTSTATUS dcesrv_init_context(TALLOC_CTX *mem_ctx, const char **endpoint_s
        dce_ctx = talloc(mem_ctx, struct dcesrv_context);
        NT_STATUS_HAVE_NO_MEMORY(dce_ctx);
        dce_ctx->endpoint_list  = NULL;
-       dce_ctx->state_flags    = state_flags;
+       dce_ctx->lp_ctx = lp_ctx;
 
        for (i=0;endpoint_servers[i];i++) {
                const struct dcesrv_endpoint_server *ep_server;
@@ -1171,21 +1339,6 @@ static NTSTATUS dcesrv_init_context(TALLOC_CTX *mem_ctx, const char **endpoint_s
        return NT_STATUS_OK;
 }
 
-/*
-  initialise the dcerpc server context for ncacn_np based services
-*/
-NTSTATUS dcesrv_init_ipc_context(TALLOC_CTX *mem_ctx, struct dcesrv_context **_dce_ctx)
-{
-       NTSTATUS status;
-       struct dcesrv_context *dce_ctx;
-
-       status = dcesrv_init_context(mem_ctx, lp_dcerpc_endpoint_servers(), 0, &dce_ctx);
-       NT_STATUS_NOT_OK_RETURN(status);
-
-       *_dce_ctx = dce_ctx;
-       return NT_STATUS_OK;
-}
-
 /* the list of currently registered DCERPC endpoint servers.
  */
 static struct ep_server {
@@ -1201,7 +1354,7 @@ static int num_ep_servers;
 
   The 'type' is used to specify whether this is for a disk, printer or IPC$ share
 */
-NTSTATUS dcerpc_register_ep_server(const void *_ep_server)
+_PUBLIC_ NTSTATUS dcerpc_register_ep_server(const void *_ep_server)
 {
        const struct dcesrv_endpoint_server *ep_server = _ep_server;
        
@@ -1268,64 +1421,19 @@ const struct dcesrv_critical_sizes *dcerpc_module_version(void)
 }
 
 /*
-  initialise the dcerpc server context 
+  initialise the dcerpc server context for ncacn_np based services
 */
-static NTSTATUS dcesrv_init(struct event_context *event_context, const struct model_ops *model_ops)
+_PUBLIC_ NTSTATUS dcesrv_init_ipc_context(TALLOC_CTX *mem_ctx, struct loadparm_context *lp_ctx,
+                                         struct dcesrv_context **_dce_ctx)
 {
        NTSTATUS status;
        struct dcesrv_context *dce_ctx;
-       struct dcesrv_endpoint *e;
 
-       status = dcesrv_init_context(event_context,
-                                    lp_dcerpc_endpoint_servers(),
-                                    DCESRV_CALL_STATE_FLAG_MAY_ASYNC,
-                                    &dce_ctx);
+       status = dcesrv_init_context(mem_ctx, lp_ctx, lp_dcerpc_endpoint_servers(lp_ctx), &dce_ctx);
        NT_STATUS_NOT_OK_RETURN(status);
 
-       /* Make sure the directory for NCALRPC exists */
-       if (!directory_exist(lp_ncalrpc_dir())) {
-               mkdir(lp_ncalrpc_dir(), 0755);
-       }
-
-       for (e=dce_ctx->endpoint_list;e;e=e->next) {
-               switch (e->ep_description->transport) {
-               case NCACN_UNIX_STREAM:
-                       status = dcesrv_add_ep_unix(dce_ctx, e, event_context, model_ops);
-                       NT_STATUS_NOT_OK_RETURN(status);
-                       break;
-               
-               case NCALRPC:
-                       status = dcesrv_add_ep_ncalrpc(dce_ctx, e, event_context, model_ops);
-                       NT_STATUS_NOT_OK_RETURN(status);
-                       break;
-
-               case NCACN_IP_TCP:
-                       status = dcesrv_add_ep_tcp(dce_ctx, e, event_context, model_ops);
-                       NT_STATUS_NOT_OK_RETURN(status);
-                       break;
-                       
-               case NCACN_NP:
-/*                     FIXME: status = dcesrv_add_ep_np(dce_ctx, e, event_context, model_ops);
-                       NT_STATUS_NOT_OK_RETURN(status); */
-                       break;
-
-               default:
-                       return NT_STATUS_NOT_SUPPORTED;
-               }
-       }
-
-       return NT_STATUS_OK;    
+       *_dce_ctx = dce_ctx;
+       return NT_STATUS_OK;
 }
 
-NTSTATUS server_service_rpc_init(void)
-{
-       init_module_fn static_init[] = STATIC_DCERPC_MODULES;
-       init_module_fn *shared_init = load_samba_modules(NULL, "rpc_server");
 
-       run_init_functions(static_init);
-       run_init_functions(shared_init);
-
-       talloc_free(shared_init);
-       
-       return register_server_service("rpc", dcesrv_init);
-}