STEP01: librpc/rpc/dcerpc_connection.c trailer ...
authorStefan Metzmacher <metze@samba.org>
Mon, 13 Jan 2014 14:43:48 +0000 (15:43 +0100)
committerStefan Metzmacher <metze@samba.org>
Tue, 4 Jun 2019 10:45:39 +0000 (12:45 +0200)
librpc/rpc/dcerpc_connection.c

index fc4fc5c085384528c9ba50be55524bd5f5a31c20..abe9955540e71b9829b5ca010c2cea5636034a9b 100644 (file)
@@ -81,6 +81,9 @@ struct dcerpc_security {
        enum dcerpc_AuthType auth_type;
        enum dcerpc_AuthLevel auth_level;
        struct gensec_security *gensec;
+       bool client_hdr_signing;
+       bool hdr_signing;
+       bool verified_bitmask1;
 };
 
 struct dcerpc_presentation {
@@ -91,6 +94,7 @@ struct dcerpc_presentation {
                struct dcerpc_ctx_list req;
                struct dcerpc_ack_ctx ack;
        } negotiate;
+       bool verified_pcontext;
 };
 
 struct dcerpc_call {
@@ -866,6 +870,7 @@ struct dcerpc_do_bind_state {
        struct dcerpc_connection *conn;
        struct dcerpc_call *call;
        struct dcerpc_security *sec;
+       bool proposed_hdr_signing;
        DATA_BLOB sec_in;
        NTSTATUS sec_status;
        DATA_BLOB sec_out;
@@ -931,7 +936,7 @@ struct tevent_req *dcerpc_do_bind_send(TALLOC_CTX *mem_ctx,
        tevent_req_set_cleanup_fn(req, dcerpc_do_bind_cleanup);
        tevent_req_defer_callback(req, ev);
 
-       if (state->sec != NULL && state->sec->gensec != NULL) {
+       if (state->sec && state->sec->auth_type != DCERPC_AUTH_TYPE_NONE) {
                subreq = gensec_update_send(state, ev,
                                            state->sec->gensec,
                                            state->sec_in);
@@ -1004,6 +1009,12 @@ static void dcerpc_do_bind_sec_next(struct tevent_req *subreq)
                return;
        }
 
+       if (state->sec->auth_level >= DCERPC_AUTH_LEVEL_INTEGRITY) {
+               state->sec->client_hdr_signing =
+                       gensec_have_feature(state->sec->gensec,
+                                           GENSEC_FEATURE_SIGN_PKT_HEADER);
+       }
+
        subreq = tevent_queue_wait_send(state, state->ev,
                                        state->conn->calls.out_queue);
        if (tevent_req_nomem(subreq, req)) {
@@ -1028,14 +1039,18 @@ static void dcerpc_do_bind_out_frag_next(struct tevent_req *subreq)
        size_t auth_len = 0;
        NTSTATUS status;
        DATA_BLOB auth_info = data_blob_null;
+       uint8_t pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
        union dcerpc_payload u;
        uint32_t i;
+       bool require_ack = false;
        bool use_trans = true;
        bool ok;
 
        ok = tevent_queue_wait_recv(subreq);
        if (!ok) {
-
+               //status = NT_STATUS_INTERNAL_ERROR;
+               tevent_req_oom(req);
+               return;
        }
        TALLOC_FREE(subreq);
 
@@ -1052,17 +1067,33 @@ static void dcerpc_do_bind_out_frag_next(struct tevent_req *subreq)
        frag->req = req;
        state->out_frag = frag;
 
+       if (state->sec && state->sec->auth_type != DCERPC_AUTH_TYPE_NONE) {
+               if (state->sec->client_hdr_signing &&
+                   !state->proposed_hdr_signing)
+               {
+                       state->proposed_hdr_signing = true;
+                       pfc_flags |= DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN;
+                       require_ack = true;
+               }
+       }
+
+       //TODO : DCERPC_PFC_FLAG_CONC_MPX
+
+       //TODO: remaining_pres
+
        if (!state->conn->features.bind_done) {
                frag->ptype = DCERPC_PKT_BIND;
+       } else if (require_ack) {
+               frag->ptype = DCERPC_PKT_ALTER;
        } else if (state->remaining_pres > 0) {
                frag->ptype = DCERPC_PKT_ALTER;
        } else if (!NT_STATUS_IS_OK(state->sec_status)) {
                frag->ptype = DCERPC_PKT_ALTER;
-       } else if (state->sec_out.length > 0) {
+       } else {
                frag->ptype = DCERPC_PKT_AUTH3;
        }
 
-       if (state->sec) {
+       if (state->sec && state->sec->auth_type != DCERPC_AUTH_TYPE_NONE) {
                status = dcerpc_auth_blob(frag,
                                          state->sec->auth_type,
                                          state->sec->auth_level,
@@ -1133,8 +1164,7 @@ static void dcerpc_do_bind_out_frag_next(struct tevent_req *subreq)
 
        status = dcerpc_ncacn_packet_blob(frag,
                                          frag->ptype,
-                                         DCERPC_PFC_FLAG_FIRST |
-                                         DCERPC_PFC_FLAG_LAST,
+                                         pfc_flags,
                                          auth_len,
                                          state->call->call_id,
                                          &u,
@@ -1212,6 +1242,10 @@ static void dcerpc_do_bind_out_frag_next(struct tevent_req *subreq)
                                        frag);
        }
 
+       if (frag->ptype == DCERPC_PKT_AUTH3) {
+               return;
+       }
+
        status = dcerpc_connection_loop_restart(frag->conn, frag->ev);
        if (tevent_req_nterror(req, status)) {
                return;
@@ -1396,10 +1430,6 @@ static NTSTATUS dcerpc_do_bind_handle_in_frag(void *private_data,
        switch (pkt->ptype) {
        case DCERPC_PKT_BIND_ACK:
        case DCERPC_PKT_ALTER_RESP:
-               if (pkt->auth_length != 0) {
-               //      return NT_STATUS_NOT_IMPLEMENTED;
-               }
-
                if (!state->conn->features.bind_done) {
                        if (pkt->u.bind_ack.max_recv_frag < 1234) {
                                return NT_STATUS_RPC_PROTOCOL_ERROR;
@@ -1432,6 +1462,12 @@ static NTSTATUS dcerpc_do_bind_handle_in_frag(void *private_data,
                        return NT_STATUS_RPC_PROTOCOL_ERROR;
                }
 
+               if (state->proposed_hdr_signing) {
+                       if (pkt->pfc_flags & DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN) {
+                               state->sec->hdr_signing = true;
+                       }
+               }
+
                for (i = 0; i < pkt->u.bind_ack.num_results; i++) {
                        struct dcerpc_ack_ctx *ack = &pkt->u.bind_ack.ctx_list[i];
 
@@ -1628,7 +1664,11 @@ struct dcerpc_do_request_state {
                const DATA_BLOB *blob;
                size_t ofs;
                bool bigendian;
+               DATA_BLOB trailer;
+               size_t trailer_ofs;
        } request;
+       bool verify_bitmask1;
+       bool verify_pcontext;
        struct dcerpc_do_request_out_frag *out_frag;
        struct {
                DATA_BLOB blob;
@@ -1651,6 +1691,7 @@ struct dcerpc_do_request_out_frag {
 static void dcerpc_do_request_cleanup(struct tevent_req *req,
                                      enum tevent_req_state req_state);
 
+static void dcerpc_do_request_verification_trailer(struct tevent_req *req);
 static void dcerpc_do_request_out_frag_next(struct tevent_req *req,
                                            void *private_data);
 
@@ -1684,6 +1725,11 @@ struct tevent_req *dcerpc_do_request_send(TALLOC_CTX *mem_ctx,
        state->request.blob = request;
        state->request.bigendian = bigendian;
 
+       dcerpc_do_request_verification_trailer(req);
+       if (!tevent_req_is_in_progress(req)) {
+               return tevent_req_post(req, ev);
+       }
+
        state->call->incoming.private_data = req;
        state->call->incoming.handler = dcerpc_do_request_handle_in_frag;
        DLIST_ADD_END(state->conn->calls.list, state->call, NULL);
@@ -1723,6 +1769,139 @@ static void dcerpc_do_request_cleanup(struct tevent_req *req,
        }
 }
 
+static void dcerpc_do_request_verification_trailer(struct tevent_req *req)
+{
+       struct dcerpc_do_request_state *state =
+               tevent_req_data(req,
+               struct dcerpc_do_request_state);
+       struct dcerpc_sec_verification_trailer *t;
+       struct dcerpc_sec_vt *c = NULL;
+       struct ndr_push *ndr = NULL;
+       enum ndr_err_code ndr_err;
+       size_t align = 0;
+       size_t pad = 0;
+
+       if (state->call->sec->auth_level < DCERPC_AUTH_LEVEL_INTEGRITY) {
+               return;
+       }
+
+       t = talloc_zero(state, struct dcerpc_sec_verification_trailer);
+       if (tevent_req_nomem(t, req)) {
+               return;
+       }
+
+       if (!state->call->sec->verified_bitmask1) {
+               t->commands = talloc_realloc(t, t->commands,
+                                            struct dcerpc_sec_vt,
+                                            t->count.count + 1);
+               if (tevent_req_nomem(t->commands, req)) {
+                       return;
+               }
+               c = &t->commands[t->count.count++];
+               ZERO_STRUCTP(c);
+
+               c->command = DCERPC_SEC_VT_COMMAND_BITMASK1;
+               if (state->call->sec->client_hdr_signing) {
+                       c->u.bitmask1 = DCERPC_SEC_VT_CLIENT_SUPPORTS_HEADER_SIGNING;
+               }
+               state->verify_bitmask1 = true;
+       }
+
+       if (!state->call->pres->verified_pcontext) {
+               t->commands = talloc_realloc(t, t->commands,
+                                            struct dcerpc_sec_vt,
+                                            t->count.count + 1);
+               if (tevent_req_nomem(t->commands, req)) {
+                       return;
+               }
+               c = &t->commands[t->count.count++];
+               ZERO_STRUCTP(c);
+
+               c->command = DCERPC_SEC_VT_COMMAND_PCONTEXT;
+               c->u.pcontext.abstract_syntax = state->call->pres->table->syntax_id;
+               c->u.pcontext.transfer_syntax = state->call->pres->transfer;
+
+               state->verify_pcontext = true;
+       }
+
+       if (!state->call->sec->hdr_signing) {
+               t->commands = talloc_realloc(t, t->commands,
+                                            struct dcerpc_sec_vt,
+                                            t->count.count + 1);
+               if (tevent_req_nomem(t->commands, req)) {
+                       return;
+               }
+               c = &t->commands[t->count.count++];
+               ZERO_STRUCTP(c);
+
+               c->command = DCERPC_SEC_VT_COMMAND_HEADER2;
+               c->u.header2.ptype = DCERPC_PKT_REQUEST;
+               if (state->request.bigendian) {
+                       c->u.header2.drep[0] = 0;
+               } else {
+                       c->u.header2.drep[0] = DCERPC_DREP_LE;
+               }
+               c->u.header2.drep[1] = 0;
+               c->u.header2.drep[2] = 0;
+               c->u.header2.drep[3] = 0;
+               c->u.header2.call_id = state->call->call_id;
+               c->u.header2.context_id = 0;
+               c->u.header2.opnum = state->opnum;
+       }
+
+       if (t->count.count == 0) {
+               TALLOC_FREE(t);
+               return;
+       }
+
+       c = &t->commands[t->count.count - 1];
+       c->command |= DCERPC_SEC_VT_COMMAND_END;
+
+       if (DEBUGLEVEL >= 10) {
+               NDR_PRINT_DEBUG(dcerpc_sec_verification_trailer, t);
+       }
+
+       ndr = ndr_push_init_ctx(state);
+       if (tevent_req_nomem(ndr, req)) {
+               return;
+       }
+
+       //TODO if (state->request.bigendian)
+
+       ndr_err = ndr_push_dcerpc_sec_verification_trailer(ndr,
+                                               NDR_SCALARS | NDR_BUFFERS,
+                                               t);
+       if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+               NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
+               tevent_req_nterror(req, status);
+               return;
+       }
+       state->request.trailer = ndr_push_blob(ndr);
+
+       align = state->request.blob->length & 0x3;
+       if (align > 0) {
+               pad = 4 - align;
+       }
+       if (pad > 0) {
+               bool ok;
+               uint8_t *p;
+               const uint8_t zeros[4] = { 0, };
+
+               ok = data_blob_append(ndr, &state->request.trailer, zeros, pad);
+               if (!ok) {
+                       tevent_req_oom(req);
+                       return;
+               }
+
+               /* move the padding to the start */
+               p = state->request.trailer.data;
+               memmove(p + pad, p, state->request.trailer.length - pad);
+               memset(p, 0, pad);
+       }
+
+       return;
+}
+
 static void dcerpc_do_request_out_frag_trans_wait1(struct tevent_req *subreq);
 static void dcerpc_do_request_out_frag_done(struct tevent_req *subreq);
 static void dcerpc_do_request_out_frag_trans_wait2(struct tevent_req *subreq);
@@ -1734,16 +1913,19 @@ static void dcerpc_do_request_out_frag_next(struct tevent_req *req,
                tevent_req_data(req,
                struct dcerpc_do_request_state);
        struct dcerpc_do_request_out_frag *frag;
-       size_t data_sent_thistime;
        size_t hdr_len = DCERPC_REQUEST_LENGTH;
        size_t auth_len;
        size_t frag_len;
        uint8_t flags = 0;
        size_t pad_len;
        size_t data_left;
+       size_t data_thistime;
+       size_t trailer_left;
+       size_t trailer_thistime = 0;
+       size_t total_left;
+       size_t total_thistime;
        NTSTATUS status;
        union dcerpc_payload u;
-       DATA_BLOB payload;
        bool ok;
        struct tevent_req *subreq;
        bool use_trans = true;
@@ -1767,12 +1949,18 @@ static void dcerpc_do_request_out_frag_next(struct tevent_req *req,
        state->out_frag = frag;
 
        data_left = state->request.blob->length - state->request.ofs;
+       trailer_left = state->request.trailer.length - state->request.trailer_ofs;
+       total_left = data_left + trailer_left;
+       if (total_left < data_left || total_left < trailer_left) {
+               tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_MIX);
+               return;
+       }
 
        status = dcerpc_guess_pdu_sizes(state->call->sec,
-                                   hdr_len, data_left,
+                                   hdr_len, total_left,
                                    state->conn->features.max_xmit_frag,
                                    16,//TODO
-                                   &data_sent_thistime,
+                                   &total_thistime,
                                    &frag_len, &auth_len, &pad_len);
        if (!NT_STATUS_IS_OK(status)) {
                tevent_req_nterror(req, status);
@@ -1783,19 +1971,18 @@ static void dcerpc_do_request_out_frag_next(struct tevent_req *req,
                flags |= DCERPC_PFC_FLAG_FIRST;
        }
 
-       payload.data = state->request.blob->data + state->request.ofs;
-       payload.length = data_sent_thistime;
-
-       state->request.ofs += data_sent_thistime;
-
-       if (state->request.blob->length == state->request.ofs) {
+       if (total_thistime == total_left) {
                flags |= DCERPC_PFC_FLAG_LAST;
        }
 
+       data_thistime = MIN(total_thistime, data_left);
+       if (data_thistime < total_thistime) {
+               trailer_thistime = total_thistime - data_thistime;
+       }
+
        ZERO_STRUCT(u.request);
 
-       u.request.alloc_hint    = state->request.blob->length -
-                                 state->request.ofs;
+       u.request.alloc_hint    = total_left;
        u.request.context_id    = state->call->pres->context_id;
        u.request.opnum         = state->opnum;
        if (state->object) {
@@ -1819,12 +2006,38 @@ static void dcerpc_do_request_out_frag_next(struct tevent_req *req,
         * at this stage */
        dcerpc_set_frag_length(&frag->blob, frag_len);
 
-       /* Copy in the data. */
-       ok = data_blob_append(frag, &frag->blob,
-                             payload.data, payload.length);
-       if (!ok) {
-               tevent_req_nomem(NULL, req);
-               return;
+       if (data_thistime > 0) {
+               const uint8_t *data_ptr;
+
+               data_ptr = state->request.blob->data;
+               data_ptr += state->request.ofs;
+
+               /* Copy in the data. */
+               ok = data_blob_append(frag, &frag->blob,
+                                     data_ptr, data_thistime);
+               if (!ok) {
+                       tevent_req_oom(req);
+                       return;
+               }
+
+               state->request.ofs += data_thistime;
+       }
+
+       if (trailer_thistime > 0) {
+               const uint8_t *trailer_ptr;
+
+               trailer_ptr = state->request.trailer.data;
+               trailer_ptr += state->request.trailer_ofs;
+
+               /* Copy in the data. */
+               ok = data_blob_append(frag, &frag->blob,
+                                     trailer_ptr, trailer_thistime);
+               if (!ok) {
+                       tevent_req_oom(req);
+                       return;
+               }
+
+               state->request.trailer_ofs += trailer_thistime;
        }
 
        switch (state->call->sec->auth_level) {
@@ -2118,6 +2331,14 @@ static NTSTATUS dcerpc_do_request_handle_in_frag(void *private_data,
                        return error;
                }
 
+               if (state->verify_bitmask1) {
+                       state->call->sec->verified_bitmask1 = true;
+               }
+
+               if (state->verify_pcontext) {
+                       state->call->pres->verified_pcontext = true;
+               }
+
                if (frag.length < DCERPC_RESPONSE_LENGTH + pad_len) {
                        return NT_STATUS_RPC_PROTOCOL_ERROR;
                }