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 {
struct dcerpc_ctx_list req;
struct dcerpc_ack_ctx ack;
} negotiate;
+ bool verified_pcontext;
};
struct dcerpc_call {
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;
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);
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)) {
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);
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,
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,
frag);
}
+ if (frag->ptype == DCERPC_PKT_AUTH3) {
+ return;
+ }
+
status = dcerpc_connection_loop_restart(frag->conn, frag->ev);
if (tevent_req_nterror(req, status)) {
return;
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;
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];
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;
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);
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);
}
}
+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);
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;
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);
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) {
* 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) {
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;
}