s4:rpc_server: fix crash bugs in 26200f4fb1db81be7a9da51f317e46405351b170
[kai/samba.git] / source4 / rpc_server / dcerpc_server.c
index 1962a97d5b49d3863e283a5ee1a15c156e06f7d8..063e3ff3bdbbe53e0709b31cd072bb797c5a09a2 100644 (file)
@@ -24,7 +24,7 @@
 #include "librpc/gen_ndr/ndr_dcerpc.h"
 #include "auth/auth.h"
 #include "auth/gensec/gensec.h"
-#include "lib/util/dlinklist.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"
@@ -36,6 +36,8 @@
 #include "libcli/security/security.h"
 #include "param/param.h"
 
+#define SAMBA_ASSOC_GROUP 0x12345678
+
 extern const struct dcesrv_interface dcesrv_mgmt_interface;
 
 /*
@@ -249,8 +251,8 @@ _PUBLIC_ NTSTATUS dcesrv_interface_register(struct dcesrv_context *dce_ctx,
        return NT_STATUS_OK;
 }
 
-static NTSTATUS dcesrv_inherited_session_key(struct dcesrv_connection *p,
-                                             DATA_BLOB *session_key)
+NTSTATUS dcesrv_inherited_session_key(struct dcesrv_connection *p,
+                                     DATA_BLOB *session_key)
 {
        if (p->auth_state.session_info->session_key.length) {
                *session_key = p->auth_state.session_info->session_key;
@@ -270,11 +272,20 @@ 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 
 */
 _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;
 }
 
 
@@ -328,6 +339,7 @@ _PUBLIC_ NTSTATUS dcesrv_endpoint_connect(struct dcesrv_context *dce_ctx,
        p->endpoint = ep;
        p->contexts = NULL;
        p->call_list = NULL;
+       p->packet_log_dir = lp_lockdir(dce_ctx->lp_ctx);
        p->incoming_fragmented_call_list = NULL;
        p->pending_call_list = NULL;
        p->cli_max_recv_frag = 0;
@@ -445,6 +457,7 @@ 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 */
@@ -458,6 +471,9 @@ 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;
@@ -529,8 +545,21 @@ static NTSTATUS dcesrv_bind(struct dcesrv_call_state *call)
        uint32_t result=0, reason=0;
        uint32_t context_id;
        const struct dcesrv_interface *iface;
-
-       if (call->pkt.u.bind.assoc_group_id != 0) {
+       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 &&
+           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) {
                return dcesrv_bind_nak(call, 0);        
        }
 
@@ -581,6 +610,11 @@ 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;
                context->private = NULL;
                context->handles = NULL;
                DLIST_ADD(call->conn->contexts, context);
@@ -591,6 +625,12 @@ 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)) {
                return dcesrv_bind_nak(call, DCERPC_BIND_REASON_INVALID_AUTH_TYPE);
@@ -601,11 +641,10 @@ static NTSTATUS dcesrv_bind(struct dcesrv_call_state *call)
        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;
-       /* 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 = 0x12345678;
+       pkt.u.bind_ack.assoc_group_id = call->context->assoc_group_id;
        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);
@@ -638,6 +677,9 @@ static NTSTATUS dcesrv_bind(struct dcesrv_call_state *call)
                }
        }
 
+       /* the iface->bind() might change the assoc_group_id */
+       pkt.u.bind_ack.assoc_group_id = call->context->assoc_group_id;
+
        rep = talloc(call, struct data_blob_list_item);
        if (!rep) {
                return NT_STATUS_NO_MEMORY;
@@ -684,6 +726,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;
@@ -712,11 +755,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;
        context->private = NULL;
        context->handles = NULL;
        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;
 }
 
@@ -742,15 +793,26 @@ static NTSTATUS dcesrv_alter(struct dcesrv_call_state *call)
        context_id = call->pkt.u.alter.ctx_list[0].context_id;
 
        /* see if they are asking for a new interface */
-       if (result == 0 &&
-           dcesrv_find_context(call->conn, context_id) == NULL) {
-               status = dcesrv_alter_new_context(call, context_id);
-               if (!NT_STATUS_IS_OK(status)) {
-                       result = DCERPC_BIND_PROVIDER_REJECT;
-                       reason = DCERPC_BIND_REASON_ASYNTAX;            
+       if (result == 0) {
+               call->context = dcesrv_find_context(call->conn, context_id);
+               if (!call->context) {
+                       status = dcesrv_alter_new_context(call, context_id);
+                       if (!NT_STATUS_IS_OK(status)) {
+                               result = DCERPC_BIND_PROVIDER_REJECT;
+                               reason = DCERPC_BIND_REASON_ASYNTAX;
+                       }
                }
        }
 
+       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 */
+               result = DCERPC_BIND_PROVIDER_REJECT;
+               reason = DCERPC_BIND_REASON_ASYNTAX;
+       }
+
        /* setup a alter_resp */
        dcesrv_init_hdr(&pkt, lp_rpc_big_endian(call->conn->dce_ctx->lp_ctx));
        pkt.auth_length = 0;
@@ -759,7 +821,11 @@ static NTSTATUS dcesrv_alter(struct dcesrv_call_state *call)
        pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
        pkt.u.alter_resp.max_xmit_frag = 0x2000;
        pkt.u.alter_resp.max_recv_frag = 0x2000;
-       pkt.u.alter_resp.assoc_group_id = call->pkt.u.alter.assoc_group_id;
+       if (result == 0) {
+               pkt.u.alter_resp.assoc_group_id = call->context->assoc_group_id;
+       } else {
+               pkt.u.alter_resp.assoc_group_id = 0;
+       }
        pkt.u.alter_resp.num_results = 1;
        pkt.u.alter_resp.ctx_list = talloc_array(call, struct dcerpc_ack_ctx, 1);
        if (!pkt.u.alter_resp.ctx_list) {
@@ -829,10 +895,6 @@ static NTSTATUS dcesrv_request(struct dcesrv_call_state *call)
        call->context   = context;
        call->ndr_pull  = pull;
 
-       if (call->pkt.pfc_flags & DCERPC_PFC_FLAG_OBJECT_UUID) {
-               pull->flags |= LIBNDR_FLAG_OBJECT_PRESENT;
-       }
-
        if (!(call->pkt.drep[0] & DCERPC_DREP_LE)) {
                pull->flags |= LIBNDR_FLAG_BIGENDIAN;
        }
@@ -874,8 +936,9 @@ _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);
@@ -905,6 +968,21 @@ _PUBLIC_ 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;
@@ -913,12 +991,7 @@ _PUBLIC_ 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, lp_rpc_big_endian(call->conn->dce_ctx->lp_ctx));
@@ -938,7 +1011,7 @@ _PUBLIC_ 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);          
                }
 
@@ -1060,6 +1133,10 @@ NTSTATUS dcesrv_input_process(struct dcesrv_connection *dce_conn)
                ndr->flags |= LIBNDR_FLAG_BIGENDIAN;
        }
 
+       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);