librpc/rpc: correct the chunk_size depending on the signature size
[tprouty/samba.git] / source4 / librpc / rpc / dcerpc.c
index a379398f19772cc7b72c64c9e1038d1aafe35a64..28b5cd65a9143a3f398bfc2146103a7739973308 100644 (file)
 #include "lib/util/dlinklist.h"
 #include "lib/events/events.h"
 #include "librpc/rpc/dcerpc.h"
+#include "librpc/rpc/dcerpc_proto.h"
 #include "librpc/gen_ndr/ndr_misc.h"
 #include "librpc/gen_ndr/ndr_dcerpc.h"
 #include "libcli/composite/composite.h"
 #include "auth/gensec/gensec.h"
 #include "param/param.h"
 
-NTSTATUS dcerpc_init(void)
+_PUBLIC_ NTSTATUS dcerpc_init(void)
 {
        gensec_init(global_loadparm);
 
@@ -66,22 +67,15 @@ static struct dcerpc_connection *dcerpc_connection_init(TALLOC_CTX *mem_ctx,
                return NULL;
        }
 
-       if (ev == NULL) {
-               ev = event_context_init(c);
-               if (ev == NULL) {
-                       talloc_free(c);
-                       return NULL;
-               }
-       }
-
        c->iconv_convenience = talloc_reference(c, ic);
 
-       c->event_ctx = ev;
-       
-       if (!talloc_reference(c, ev)) {
+       c->event_ctx = talloc_reference(c, ev);
+
+       if (c->event_ctx == NULL) {
                talloc_free(c);
                return NULL;
        }
+
        c->call_id = 1;
        c->security_state.auth_info = NULL;
        c->security_state.session_key = dcerpc_generic_session_key;
@@ -98,7 +92,7 @@ static struct dcerpc_connection *dcerpc_connection_init(TALLOC_CTX *mem_ctx,
 }
 
 /* initialise a dcerpc pipe. */
-struct dcerpc_pipe *dcerpc_pipe_init(TALLOC_CTX *mem_ctx, struct event_context *ev,
+_PUBLIC_ struct dcerpc_pipe *dcerpc_pipe_init(TALLOC_CTX *mem_ctx, struct event_context *ev,
                                     struct smb_iconv_convenience *ic)
 {
        struct dcerpc_pipe *p;
@@ -340,6 +334,7 @@ static NTSTATUS ncacn_pull_request_auth(struct dcerpc_connection *c, TALLOC_CTX
 */
 static NTSTATUS ncacn_push_request_sign(struct dcerpc_connection *c, 
                                         DATA_BLOB *blob, TALLOC_CTX *mem_ctx, 
+                                        size_t sig_size,
                                         struct ncacn_packet *pkt)
 {
        NTSTATUS status;
@@ -390,8 +385,7 @@ static NTSTATUS ncacn_push_request_sign(struct dcerpc_connection *c,
                 * GENSEC mech does AEAD signing of the packet
                 * headers */
                c->security_state.auth_info->credentials
-                       = data_blob_talloc(mem_ctx, NULL, gensec_sig_size(c->security_state.generic_state, 
-                                                                         payload_length));
+                       = data_blob_talloc(mem_ctx, NULL, sig_size);
                data_blob_clear(&c->security_state.auth_info->credentials);
                break;
 
@@ -664,6 +658,16 @@ static void dcerpc_bind_recv_handler(struct rpc_request *req,
        conn->srv_max_xmit_frag = pkt->u.bind_ack.max_xmit_frag;
        conn->srv_max_recv_frag = pkt->u.bind_ack.max_recv_frag;
 
+       if ((req->p->binding->flags & DCERPC_CONCURRENT_MULTIPLEX) &&
+           (pkt->pfc_flags & DCERPC_PFC_FLAG_CONC_MPX)) {
+               conn->flags |= DCERPC_CONCURRENT_MULTIPLEX;
+       }
+
+       if ((req->p->binding->flags & DCERPC_HEADER_SIGNING) &&
+           (pkt->pfc_flags & DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN)) {
+               conn->flags |= DCERPC_HEADER_SIGNING;
+       }
+
        /* the bind_ack might contain a reply set of credentials */
        if (conn->security_state.auth_info &&
            pkt->u.bind_ack.auth_info.length) {
@@ -737,6 +741,10 @@ struct composite_context *dcerpc_bind_send(struct dcerpc_pipe *p,
                pkt.pfc_flags |= DCERPC_PFC_FLAG_CONC_MPX;
        }
 
+       if (p->binding->flags & DCERPC_HEADER_SIGNING) {
+               pkt.pfc_flags |= DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN;
+       }
+
        pkt.u.bind.max_xmit_frag = 5840;
        pkt.u.bind.max_recv_frag = 5840;
        pkt.u.bind.assoc_group_id = p->binding->assoc_group_id;
@@ -796,30 +804,41 @@ NTSTATUS dcerpc_bind_recv(struct composite_context *ctx)
 /* 
    perform a continued bind (and auth3)
 */
-NTSTATUS dcerpc_auth3(struct dcerpc_connection *c, 
+NTSTATUS dcerpc_auth3(struct dcerpc_pipe *p,
                      TALLOC_CTX *mem_ctx)
 {
        struct ncacn_packet pkt;
        NTSTATUS status;
        DATA_BLOB blob;
 
-       init_ncacn_hdr(c, &pkt);
+       init_ncacn_hdr(p->conn, &pkt);
 
        pkt.ptype = DCERPC_PKT_AUTH3;
        pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
-       pkt.call_id = next_call_id(c);
+       pkt.call_id = next_call_id(p->conn);
        pkt.auth_length = 0;
        pkt.u.auth3._pad = 0;
        pkt.u.auth3.auth_info = data_blob(NULL, 0);
 
+       if (p->binding->flags & DCERPC_CONCURRENT_MULTIPLEX) {
+               pkt.pfc_flags |= DCERPC_PFC_FLAG_CONC_MPX;
+       }
+
+       if (p->binding->flags & DCERPC_HEADER_SIGNING) {
+               pkt.pfc_flags |= DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN;
+       }
+
        /* construct the NDR form of the packet */
-       status = ncacn_push_auth(&blob, mem_ctx, c->iconv_convenience, &pkt, c->security_state.auth_info);
+       status = ncacn_push_auth(&blob, mem_ctx,
+                                p->conn->iconv_convenience,
+                                &pkt,
+                                p->conn->security_state.auth_info);
        if (!NT_STATUS_IS_OK(status)) {
                return status;
        }
 
        /* send it on its way */
-       status = c->transport.send_request(c, &blob, false);
+       status = p->conn->transport.send_request(p->conn, &blob, false);
        if (!NT_STATUS_IS_OK(status)) {
                return status;
        }
@@ -1023,6 +1042,7 @@ static void dcerpc_ship_next_request(struct dcerpc_connection *c)
        DATA_BLOB blob;
        uint32_t remaining, chunk_size;
        bool first_packet = true;
+       size_t sig_size = 0;
 
        req = c->request_queue;
        if (req == NULL) {
@@ -1046,7 +1066,15 @@ static void dcerpc_ship_next_request(struct dcerpc_connection *c)
 
        /* we can write a full max_recv_frag size, minus the dcerpc
           request header size */
-       chunk_size = p->conn->srv_max_recv_frag - (DCERPC_MAX_SIGN_SIZE+DCERPC_REQUEST_LENGTH);
+       chunk_size = p->conn->srv_max_recv_frag;
+       chunk_size -= DCERPC_REQUEST_LENGTH;
+       if (c->security_state.generic_state) {
+               chunk_size -= DCERPC_AUTH_TRAILER_LENGTH;
+               sig_size = gensec_sig_size(c->security_state.generic_state,
+                                          p->conn->srv_max_recv_frag);
+               chunk_size -= sig_size;
+               chunk_size -= (chunk_size % 16);
+       }
 
        pkt.ptype = DCERPC_PKT_REQUEST;
        pkt.call_id = req->call_id;
@@ -1082,7 +1110,7 @@ static void dcerpc_ship_next_request(struct dcerpc_connection *c)
                        (stub_data->length - remaining);
                pkt.u.request.stub_and_verifier.length = chunk;
 
-               req->status = ncacn_push_request_sign(p->conn, &blob, req, &pkt);
+               req->status = ncacn_push_request_sign(p->conn, &blob, req, sig_size, &pkt);
                if (!NT_STATUS_IS_OK(req->status)) {
                        req->state = RPC_REQUEST_DONE;
                        DLIST_REMOVE(p->conn->pending, req);
@@ -1104,7 +1132,7 @@ static void dcerpc_ship_next_request(struct dcerpc_connection *c)
   return the event context for a dcerpc pipe
   used by callers who wish to operate asynchronously
 */
-struct event_context *dcerpc_event_context(struct dcerpc_pipe *p)
+_PUBLIC_ struct event_context *dcerpc_event_context(struct dcerpc_pipe *p)
 {
        return p->conn->event_ctx;
 }
@@ -1503,7 +1531,7 @@ _PUBLIC_ NTSTATUS dcerpc_ndr_request_recv(struct rpc_request *req)
   this can be used when you have ndr push/pull functions in the
   standard format
 */
-NTSTATUS dcerpc_ndr_request(struct dcerpc_pipe *p,
+_PUBLIC_ NTSTATUS dcerpc_ndr_request(struct dcerpc_pipe *p,
                            const struct GUID *object,
                            const struct ndr_interface_table *table,
                            uint32_t opnum, 
@@ -1524,7 +1552,7 @@ NTSTATUS dcerpc_ndr_request(struct dcerpc_pipe *p,
 /*
   a useful function for retrieving the server name we connected to
 */
-const char *dcerpc_server_name(struct dcerpc_pipe *p)
+_PUBLIC_ const char *dcerpc_server_name(struct dcerpc_pipe *p)
 {
        if (!p->conn->transport.target_hostname) {
                if (!p->conn->transport.peer_name) {
@@ -1633,6 +1661,10 @@ struct composite_context *dcerpc_alter_context_send(struct dcerpc_pipe *p,
                pkt.pfc_flags |= DCERPC_PFC_FLAG_CONC_MPX;
        }
 
+       if (p->binding->flags & DCERPC_HEADER_SIGNING) {
+               pkt.pfc_flags |= DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN;
+       }
+
        pkt.u.alter.max_xmit_frag = 5840;
        pkt.u.alter.max_recv_frag = 5840;
        pkt.u.alter.assoc_group_id = p->binding->assoc_group_id;
@@ -1688,7 +1720,7 @@ NTSTATUS dcerpc_alter_context_recv(struct composite_context *ctx)
 /* 
    send a dcerpc alter_context request
 */
-NTSTATUS dcerpc_alter_context(struct dcerpc_pipe *p, 
+_PUBLIC_ NTSTATUS dcerpc_alter_context(struct dcerpc_pipe *p, 
                              TALLOC_CTX *mem_ctx,
                              const struct ndr_syntax_id *syntax,
                              const struct ndr_syntax_id *transfer_syntax)