return result;
}
-/********************************************************************
- Rpc pipe call id.
- ********************************************************************/
-
-static uint32_t get_rpc_call_id(void)
-{
- static uint32_t call_id = 0;
- return ++call_id;
-}
-
-/*******************************************************************
- Use SMBreadX to get rest of one fragment's worth of rpc data.
- Reads the whole size or give an error message
- ********************************************************************/
-
-struct rpc_read_state {
- struct tevent_context *ev;
- struct rpc_cli_transport *transport;
- uint8_t *data;
- size_t size;
- size_t num_read;
-};
-
-static void rpc_read_done(struct tevent_req *subreq);
-
-static struct tevent_req *rpc_read_send(TALLOC_CTX *mem_ctx,
- struct tevent_context *ev,
- struct rpc_cli_transport *transport,
- uint8_t *data, size_t size)
-{
- struct tevent_req *req, *subreq;
- struct rpc_read_state *state;
-
- req = tevent_req_create(mem_ctx, &state, struct rpc_read_state);
- if (req == NULL) {
- return NULL;
- }
- state->ev = ev;
- state->transport = transport;
- state->data = data;
- state->size = size;
- state->num_read = 0;
-
- DEBUG(5, ("rpc_read_send: data_to_read: %u\n", (unsigned int)size));
-
- subreq = transport->read_send(state, ev, (uint8_t *)data, size,
- transport->priv);
- if (subreq == NULL) {
- goto fail;
- }
- tevent_req_set_callback(subreq, rpc_read_done, req);
- return req;
-
- fail:
- TALLOC_FREE(req);
- return NULL;
-}
-
-static void rpc_read_done(struct tevent_req *subreq)
-{
- struct tevent_req *req = tevent_req_callback_data(
- subreq, struct tevent_req);
- struct rpc_read_state *state = tevent_req_data(
- req, struct rpc_read_state);
- NTSTATUS status;
- ssize_t received;
-
- status = state->transport->read_recv(subreq, &received);
- TALLOC_FREE(subreq);
- if (!NT_STATUS_IS_OK(status)) {
- tevent_req_nterror(req, status);
- return;
- }
-
- state->num_read += received;
- if (state->num_read == state->size) {
- tevent_req_done(req);
- return;
- }
-
- subreq = state->transport->read_send(state, state->ev,
- state->data + state->num_read,
- state->size - state->num_read,
- state->transport->priv);
- if (tevent_req_nomem(subreq, req)) {
- return;
- }
- tevent_req_set_callback(subreq, rpc_read_done, req);
-}
-
-static NTSTATUS rpc_read_recv(struct tevent_req *req)
-{
- return tevent_req_simple_recv_ntstatus(req);
-}
-
-struct rpc_write_state {
- struct tevent_context *ev;
- struct rpc_cli_transport *transport;
- const uint8_t *data;
- size_t size;
- size_t num_written;
-};
-
-static void rpc_write_done(struct tevent_req *subreq);
-
-static struct tevent_req *rpc_write_send(TALLOC_CTX *mem_ctx,
- struct tevent_context *ev,
- struct rpc_cli_transport *transport,
- const uint8_t *data, size_t size)
-{
- struct tevent_req *req, *subreq;
- struct rpc_write_state *state;
-
- req = tevent_req_create(mem_ctx, &state, struct rpc_write_state);
- if (req == NULL) {
- return NULL;
- }
- state->ev = ev;
- state->transport = transport;
- state->data = data;
- state->size = size;
- state->num_written = 0;
-
- DEBUG(5, ("rpc_write_send: data_to_write: %u\n", (unsigned int)size));
-
- subreq = transport->write_send(state, ev, data, size, transport->priv);
- if (subreq == NULL) {
- goto fail;
- }
- tevent_req_set_callback(subreq, rpc_write_done, req);
- return req;
- fail:
- TALLOC_FREE(req);
- return NULL;
-}
-
-static void rpc_write_done(struct tevent_req *subreq)
-{
- struct tevent_req *req = tevent_req_callback_data(
- subreq, struct tevent_req);
- struct rpc_write_state *state = tevent_req_data(
- req, struct rpc_write_state);
- NTSTATUS status;
- ssize_t written;
-
- status = state->transport->write_recv(subreq, &written);
- TALLOC_FREE(subreq);
- if (!NT_STATUS_IS_OK(status)) {
- tevent_req_nterror(req, status);
- return;
- }
-
- state->num_written += written;
-
- if (state->num_written == state->size) {
- tevent_req_done(req);
- return;
- }
-
- subreq = state->transport->write_send(state, state->ev,
- state->data + state->num_written,
- state->size - state->num_written,
- state->transport->priv);
- if (tevent_req_nomem(subreq, req)) {
- return;
- }
- tevent_req_set_callback(subreq, rpc_write_done, req);
-}
-
-static NTSTATUS rpc_write_recv(struct tevent_req *req)
-{
- return tevent_req_simple_recv_ntstatus(req);
-}
-
-
-/****************************************************************************
- Try and get a PDU's worth of data from current_pdu. If not, then read more
- from the wire.
- ****************************************************************************/
-
-struct get_complete_frag_state {
- struct tevent_context *ev;
- struct rpc_pipe_client *cli;
- uint16_t frag_len;
- DATA_BLOB *pdu;
-};
-
-static void get_complete_frag_got_header(struct tevent_req *subreq);
-static void get_complete_frag_got_rest(struct tevent_req *subreq);
-
-static struct tevent_req *get_complete_frag_send(TALLOC_CTX *mem_ctx,
- struct tevent_context *ev,
- struct rpc_pipe_client *cli,
- DATA_BLOB *pdu)
-{
- struct tevent_req *req, *subreq;
- struct get_complete_frag_state *state;
- size_t received;
- NTSTATUS status;
-
- req = tevent_req_create(mem_ctx, &state,
- struct get_complete_frag_state);
- if (req == NULL) {
- return NULL;
- }
- state->ev = ev;
- state->cli = cli;
- state->frag_len = RPC_HEADER_LEN;
- state->pdu = pdu;
-
- received = pdu->length;
- if (received < RPC_HEADER_LEN) {
- if (!data_blob_realloc(mem_ctx, pdu, RPC_HEADER_LEN)) {
- status = NT_STATUS_NO_MEMORY;
- goto post_status;
- }
- subreq = rpc_read_send(state, state->ev,
- state->cli->transport,
- pdu->data + received,
- RPC_HEADER_LEN - received);
- if (subreq == NULL) {
- status = NT_STATUS_NO_MEMORY;
- goto post_status;
- }
- tevent_req_set_callback(subreq, get_complete_frag_got_header,
- req);
- return req;
- }
-
- state->frag_len = dcerpc_get_frag_length(pdu);
- if (state->frag_len < RPC_HEADER_LEN) {
- tevent_req_nterror(req, NT_STATUS_RPC_PROTOCOL_ERROR);
- return tevent_req_post(req, ev);
- }
-
- /*
- * Ensure we have frag_len bytes of data.
- */
- if (received < state->frag_len) {
- if (!data_blob_realloc(NULL, pdu, state->frag_len)) {
- status = NT_STATUS_NO_MEMORY;
- goto post_status;
- }
- subreq = rpc_read_send(state, state->ev,
- state->cli->transport,
- pdu->data + received,
- state->frag_len - received);
- if (subreq == NULL) {
- status = NT_STATUS_NO_MEMORY;
- goto post_status;
- }
- tevent_req_set_callback(subreq, get_complete_frag_got_rest,
- req);
- return req;
- }
-
- status = NT_STATUS_OK;
- post_status:
- if (NT_STATUS_IS_OK(status)) {
- tevent_req_done(req);
- } else {
- tevent_req_nterror(req, status);
- }
- return tevent_req_post(req, ev);
-}
-
-static void get_complete_frag_got_header(struct tevent_req *subreq)
-{
- struct tevent_req *req = tevent_req_callback_data(
- subreq, struct tevent_req);
- struct get_complete_frag_state *state = tevent_req_data(
- req, struct get_complete_frag_state);
- NTSTATUS status;
-
- status = rpc_read_recv(subreq);
- TALLOC_FREE(subreq);
- if (!NT_STATUS_IS_OK(status)) {
- tevent_req_nterror(req, status);
- return;
- }
-
- state->frag_len = dcerpc_get_frag_length(state->pdu);
- if (state->frag_len < RPC_HEADER_LEN) {
- tevent_req_nterror(req, NT_STATUS_RPC_PROTOCOL_ERROR);
- return;
- }
-
- if (!data_blob_realloc(NULL, state->pdu, state->frag_len)) {
- tevent_req_nterror(req, NT_STATUS_NO_MEMORY);
- return;
- }
-
- /*
- * We're here in this piece of code because we've read exactly
- * RPC_HEADER_LEN bytes into state->pdu.
- */
-
- subreq = rpc_read_send(state, state->ev, state->cli->transport,
- state->pdu->data + RPC_HEADER_LEN,
- state->frag_len - RPC_HEADER_LEN);
- if (tevent_req_nomem(subreq, req)) {
- return;
- }
- tevent_req_set_callback(subreq, get_complete_frag_got_rest, req);
-}
-
-static void get_complete_frag_got_rest(struct tevent_req *subreq)
-{
- struct tevent_req *req = tevent_req_callback_data(
- subreq, struct tevent_req);
- NTSTATUS status;
-
- status = rpc_read_recv(subreq);
- TALLOC_FREE(subreq);
- if (!NT_STATUS_IS_OK(status)) {
- tevent_req_nterror(req, status);
- return;
- }
- tevent_req_done(req);
-}
-
-static NTSTATUS get_complete_frag_recv(struct tevent_req *req)
-{
- return tevent_req_simple_recv_ntstatus(req);
-}
-
-/****************************************************************************
- Do basic authentication checks on an incoming pdu.
- ****************************************************************************/
-
-static NTSTATUS cli_pipe_validate_current_pdu(TALLOC_CTX *mem_ctx,
- struct rpc_pipe_client *cli,
- struct ncacn_packet *pkt,
- DATA_BLOB *pdu,
- uint8_t expected_pkt_type,
- uint32_t call_id,
- DATA_BLOB *rdata,
- DATA_BLOB *reply_pdu)
-{
- const struct dcerpc_response *r = NULL;
- DATA_BLOB tmp_stub = data_blob_null;
- NTSTATUS ret = NT_STATUS_OK;
-
- /*
- * Point the return values at the real data including the RPC
- * header. Just in case the caller wants it.
- */
- *rdata = *pdu;
-
- if ((pkt->ptype == DCERPC_PKT_BIND_ACK) &&
- !(pkt->pfc_flags & DCERPC_PFC_FLAG_LAST)) {
- /*
- * TODO: do we still need this hack which was introduced
- * in commit a42afcdcc7ab9aa9ed193ae36d3dbb10843447f0.
- *
- * I don't even know what AS/U might be...
- */
- DEBUG(5, (__location__ ": bug in server (AS/U?), setting "
- "fragment first/last ON.\n"));
- pkt->pfc_flags |= DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
- }
-
- /* Ensure we have the correct type. */
- switch (pkt->ptype) {
- case DCERPC_PKT_BIND_NAK:
- DEBUG(1, (__location__ ": Bind NACK received from %s!\n",
- rpccli_pipe_txt(talloc_tos(), cli)));
-
- ret = dcerpc_verify_ncacn_packet_header(pkt,
- DCERPC_PKT_BIND_NAK,
- 0, /* max_auth_info */
- DCERPC_PFC_FLAG_FIRST |
- DCERPC_PFC_FLAG_LAST,
- 0); /* optional flags */
- if (!NT_STATUS_IS_OK(ret)) {
- DEBUG(1, (__location__ ": Connection to %s got an unexpected "
- "RPC packet type - %u, expected %u: %s\n",
- rpccli_pipe_txt(talloc_tos(), cli),
- pkt->ptype, expected_pkt_type,
- nt_errstr(ret)));
- NDR_PRINT_DEBUG(ncacn_packet, pkt);
- return ret;
- }
-
- /* Use this for now... */
- return NT_STATUS_NETWORK_ACCESS_DENIED;
-
- case DCERPC_PKT_BIND_ACK:
- ret = dcerpc_verify_ncacn_packet_header(pkt,
- expected_pkt_type,
- pkt->u.bind_ack.auth_info.length,
- DCERPC_PFC_FLAG_FIRST |
- DCERPC_PFC_FLAG_LAST,
- DCERPC_PFC_FLAG_CONC_MPX |
- DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN);
- if (!NT_STATUS_IS_OK(ret)) {
- DEBUG(1, (__location__ ": Connection to %s got an unexpected "
- "RPC packet type - %u, expected %u: %s\n",
- rpccli_pipe_txt(talloc_tos(), cli),
- pkt->ptype, expected_pkt_type,
- nt_errstr(ret)));
- NDR_PRINT_DEBUG(ncacn_packet, pkt);
- return ret;
- }
-
- break;
-
- case DCERPC_PKT_ALTER_RESP:
- ret = dcerpc_verify_ncacn_packet_header(pkt,
- expected_pkt_type,
- pkt->u.alter_resp.auth_info.length,
- DCERPC_PFC_FLAG_FIRST |
- DCERPC_PFC_FLAG_LAST,
- DCERPC_PFC_FLAG_CONC_MPX |
- DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN);
- if (!NT_STATUS_IS_OK(ret)) {
- DEBUG(1, (__location__ ": Connection to %s got an unexpected "
- "RPC packet type - %u, expected %u: %s\n",
- rpccli_pipe_txt(talloc_tos(), cli),
- pkt->ptype, expected_pkt_type,
- nt_errstr(ret)));
- NDR_PRINT_DEBUG(ncacn_packet, pkt);
- return ret;
- }
-
- break;
-
- case DCERPC_PKT_RESPONSE:
-
- r = &pkt->u.response;
-
- ret = dcerpc_verify_ncacn_packet_header(pkt,
- expected_pkt_type,
- r->stub_and_verifier.length,
- 0, /* required_flags */
- DCERPC_PFC_FLAG_FIRST |
- DCERPC_PFC_FLAG_LAST);
- if (!NT_STATUS_IS_OK(ret)) {
- DEBUG(1, (__location__ ": Connection to %s got an unexpected "
- "RPC packet type - %u, expected %u: %s\n",
- rpccli_pipe_txt(talloc_tos(), cli),
- pkt->ptype, expected_pkt_type,
- nt_errstr(ret)));
- NDR_PRINT_DEBUG(ncacn_packet, pkt);
- return ret;
- }
-
- tmp_stub.data = r->stub_and_verifier.data;
- tmp_stub.length = r->stub_and_verifier.length;
-
- /* Here's where we deal with incoming sign/seal. */
- ret = dcerpc_check_auth(cli->auth, pkt,
- &tmp_stub,
- DCERPC_RESPONSE_LENGTH,
- pdu);
- if (!NT_STATUS_IS_OK(ret)) {
- DEBUG(1, (__location__ ": Connection to %s got an unexpected "
- "RPC packet type - %u, expected %u: %s\n",
- rpccli_pipe_txt(talloc_tos(), cli),
- pkt->ptype, expected_pkt_type,
- nt_errstr(ret)));
- NDR_PRINT_DEBUG(ncacn_packet, pkt);
- return ret;
- }
-
- /* Point the return values at the NDR data. */
- *rdata = tmp_stub;
-
- DEBUG(10, ("Got pdu len %lu, data_len %lu\n",
- (long unsigned int)pdu->length,
- (long unsigned int)rdata->length));
-
- /*
- * If this is the first reply, and the allocation hint is
- * reasonable, try and set up the reply_pdu DATA_BLOB to the
- * correct size.
- */
-
- if ((reply_pdu->length == 0) &&
- r->alloc_hint && (r->alloc_hint < 15*1024*1024)) {
- if (!data_blob_realloc(mem_ctx, reply_pdu,
- r->alloc_hint)) {
- DEBUG(0, ("reply alloc hint %d too "
- "large to allocate\n",
- (int)r->alloc_hint));
- return NT_STATUS_NO_MEMORY;
- }
- }
-
- break;
-
- case DCERPC_PKT_FAULT:
-
- ret = dcerpc_verify_ncacn_packet_header(pkt,
- DCERPC_PKT_FAULT,
- 0, /* max_auth_info */
- DCERPC_PFC_FLAG_FIRST |
- DCERPC_PFC_FLAG_LAST,
- DCERPC_PFC_FLAG_DID_NOT_EXECUTE);
- if (!NT_STATUS_IS_OK(ret)) {
- DEBUG(1, (__location__ ": Connection to %s got an unexpected "
- "RPC packet type - %u, expected %u: %s\n",
- rpccli_pipe_txt(talloc_tos(), cli),
- pkt->ptype, expected_pkt_type,
- nt_errstr(ret)));
- NDR_PRINT_DEBUG(ncacn_packet, pkt);
- return ret;
- }
-
- DEBUG(1, (__location__ ": RPC fault code %s received "
- "from %s!\n",
- dcerpc_errstr(talloc_tos(),
- pkt->u.fault.status),
- rpccli_pipe_txt(talloc_tos(), cli)));
-
- return dcerpc_fault_to_nt_status(pkt->u.fault.status);
-
- default:
- DEBUG(0, (__location__ "Unknown packet type %u received "
- "from %s!\n",
- (unsigned int)pkt->ptype,
- rpccli_pipe_txt(talloc_tos(), cli)));
- return NT_STATUS_RPC_PROTOCOL_ERROR;
- }
-
-
- if (pkt->call_id != call_id) {
- DEBUG(3, (__location__ ": Connection to %s got an unexpected "
- "RPC call_id - %u, not %u\n",
- rpccli_pipe_txt(talloc_tos(), cli),
- pkt->call_id, call_id));
- return NT_STATUS_RPC_PROTOCOL_ERROR;
- }
-
- return NT_STATUS_OK;
-}
-
-/****************************************************************************
- Call a remote api on an arbitrary pipe. takes param, data and setup buffers.
-****************************************************************************/
-
-struct cli_api_pipe_state {
- struct tevent_context *ev;
- struct rpc_cli_transport *transport;
- uint8_t *rdata;
- uint32_t rdata_len;
-};
-
-static void cli_api_pipe_trans_done(struct tevent_req *subreq);
-static void cli_api_pipe_write_done(struct tevent_req *subreq);
-static void cli_api_pipe_read_done(struct tevent_req *subreq);
-
-static struct tevent_req *cli_api_pipe_send(TALLOC_CTX *mem_ctx,
- struct tevent_context *ev,
- struct rpc_cli_transport *transport,
- uint8_t *data, size_t data_len,
- uint32_t max_rdata_len)
-{
- struct tevent_req *req, *subreq;
- struct cli_api_pipe_state *state;
- NTSTATUS status;
-
- req = tevent_req_create(mem_ctx, &state, struct cli_api_pipe_state);
- if (req == NULL) {
- return NULL;
- }
- state->ev = ev;
- state->transport = transport;
-
- if (max_rdata_len < RPC_HEADER_LEN) {
- /*
- * For a RPC reply we always need at least RPC_HEADER_LEN
- * bytes. We check this here because we will receive
- * RPC_HEADER_LEN bytes in cli_trans_sock_send_done.
- */
- status = NT_STATUS_INVALID_PARAMETER;
- goto post_status;
- }
-
- if (transport->trans_send != NULL) {
- subreq = transport->trans_send(state, ev, data, data_len,
- max_rdata_len, transport->priv);
- if (subreq == NULL) {
- goto fail;
- }
- tevent_req_set_callback(subreq, cli_api_pipe_trans_done, req);
- return req;
- }
-
- /*
- * If the transport does not provide a "trans" routine, i.e. for
- * example the ncacn_ip_tcp transport, do the write/read step here.
- */
-
- subreq = rpc_write_send(state, ev, transport, data, data_len);
- if (subreq == NULL) {
- goto fail;
- }
- tevent_req_set_callback(subreq, cli_api_pipe_write_done, req);
- return req;
-
- post_status:
- tevent_req_nterror(req, status);
- return tevent_req_post(req, ev);
- fail:
- TALLOC_FREE(req);
- return NULL;
-}
-
-static void cli_api_pipe_trans_done(struct tevent_req *subreq)
-{
- struct tevent_req *req = tevent_req_callback_data(
- subreq, struct tevent_req);
- struct cli_api_pipe_state *state = tevent_req_data(
- req, struct cli_api_pipe_state);
- NTSTATUS status;
-
- status = state->transport->trans_recv(subreq, state, &state->rdata,
- &state->rdata_len);
- TALLOC_FREE(subreq);
- if (!NT_STATUS_IS_OK(status)) {
- tevent_req_nterror(req, status);
- return;
- }
- tevent_req_done(req);
-}
-
-static void cli_api_pipe_write_done(struct tevent_req *subreq)
-{
- struct tevent_req *req = tevent_req_callback_data(
- subreq, struct tevent_req);
- struct cli_api_pipe_state *state = tevent_req_data(
- req, struct cli_api_pipe_state);
- NTSTATUS status;
-
- status = rpc_write_recv(subreq);
- TALLOC_FREE(subreq);
- if (!NT_STATUS_IS_OK(status)) {
- tevent_req_nterror(req, status);
- return;
- }
-
- state->rdata = talloc_array(state, uint8_t, RPC_HEADER_LEN);
- if (tevent_req_nomem(state->rdata, req)) {
- return;
- }
-
- /*
- * We don't need to use rpc_read_send here, the upper layer will cope
- * with a short read, transport->trans_send could also return less
- * than state->max_rdata_len.
- */
- subreq = state->transport->read_send(state, state->ev, state->rdata,
- RPC_HEADER_LEN,
- state->transport->priv);
- if (tevent_req_nomem(subreq, req)) {
- return;
- }
- tevent_req_set_callback(subreq, cli_api_pipe_read_done, req);
-}
-
-static void cli_api_pipe_read_done(struct tevent_req *subreq)
-{
- struct tevent_req *req = tevent_req_callback_data(
- subreq, struct tevent_req);
- struct cli_api_pipe_state *state = tevent_req_data(
- req, struct cli_api_pipe_state);
- NTSTATUS status;
- ssize_t received;
-
- status = state->transport->read_recv(subreq, &received);
- TALLOC_FREE(subreq);
- if (!NT_STATUS_IS_OK(status)) {
- tevent_req_nterror(req, status);
- return;
- }
- state->rdata_len = received;
- tevent_req_done(req);
-}
-
-static NTSTATUS cli_api_pipe_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
- uint8_t **prdata, uint32_t *prdata_len)
-{
- struct cli_api_pipe_state *state = tevent_req_data(
- req, struct cli_api_pipe_state);
- NTSTATUS status;
-
- if (tevent_req_is_nterror(req, &status)) {
- return status;
- }
-
- *prdata = talloc_move(mem_ctx, &state->rdata);
- *prdata_len = state->rdata_len;
- return NT_STATUS_OK;
-}
-
-/****************************************************************************
- Send data on an rpc pipe via trans. The data must be the last
- pdu fragment of an NDR data stream.
-
- Receive response data from an rpc pipe, which may be large...
-
- Read the first fragment: unfortunately have to use SMBtrans for the first
- bit, then SMBreadX for subsequent bits.
-
- If first fragment received also wasn't the last fragment, continue
- getting fragments until we _do_ receive the last fragment.
-
- Request/Response PDU's look like the following...
-
- |<------------------PDU len----------------------------------------------->|
- |<-HDR_LEN-->|<--REQ LEN------>|.............|<-AUTH_HDRLEN->|<-AUTH_LEN-->|
-
- +------------+-----------------+-------------+---------------+-------------+
- | RPC HEADER | REQ/RESP HEADER | DATA ...... | AUTH_HDR | AUTH DATA |
- +------------+-----------------+-------------+---------------+-------------+
-
- Where the presence of the AUTH_HDR and AUTH DATA are dependent on the
- signing & sealing being negotiated.
-
- ****************************************************************************/
-
-struct rpc_api_pipe_state {
- struct tevent_context *ev;
- struct rpc_pipe_client *cli;
- uint8_t expected_pkt_type;
- uint32_t call_id;
-
- DATA_BLOB incoming_frag;
- struct ncacn_packet *pkt;
-
- /* Incoming reply */
- DATA_BLOB reply_pdu;
- size_t reply_pdu_offset;
- uint8_t endianess;
-};
-
-static void rpc_api_pipe_trans_done(struct tevent_req *subreq);
-static void rpc_api_pipe_got_pdu(struct tevent_req *subreq);
-static void rpc_api_pipe_auth3_done(struct tevent_req *subreq);
-
-static struct tevent_req *rpc_api_pipe_send(TALLOC_CTX *mem_ctx,
- struct tevent_context *ev,
- struct rpc_pipe_client *cli,
- DATA_BLOB *data, /* Outgoing PDU */
- uint8_t expected_pkt_type,
- uint32_t call_id)
-{
- struct tevent_req *req, *subreq;
- struct rpc_api_pipe_state *state;
- uint16_t max_recv_frag;
- NTSTATUS status;
-
- req = tevent_req_create(mem_ctx, &state, struct rpc_api_pipe_state);
- if (req == NULL) {
- return NULL;
- }
- state->ev = ev;
- state->cli = cli;
- state->expected_pkt_type = expected_pkt_type;
- state->call_id = call_id;
- state->endianess = DCERPC_DREP_LE;
-
- /*
- * Ensure we're not sending too much.
- */
- if (data->length > cli->max_xmit_frag) {
- status = NT_STATUS_INVALID_PARAMETER;
- goto post_status;
- }
-
- DEBUG(5,("rpc_api_pipe: %s\n", rpccli_pipe_txt(talloc_tos(), cli)));
-
- if (state->expected_pkt_type == DCERPC_PKT_AUTH3) {
- subreq = rpc_write_send(state, ev, cli->transport,
- data->data, data->length);
- if (subreq == NULL) {
- goto fail;
- }
- tevent_req_set_callback(subreq, rpc_api_pipe_auth3_done, req);
- return req;
- }
-
- /* get the header first, then fetch the rest once we have
- * the frag_length available */
- max_recv_frag = RPC_HEADER_LEN;
-
- subreq = cli_api_pipe_send(state, ev, cli->transport,
- data->data, data->length, max_recv_frag);
- if (subreq == NULL) {
- goto fail;
- }
- tevent_req_set_callback(subreq, rpc_api_pipe_trans_done, req);
- return req;
-
- post_status:
- tevent_req_nterror(req, status);
- return tevent_req_post(req, ev);
- fail:
- TALLOC_FREE(req);
- return NULL;
-}
-
-static void rpc_api_pipe_auth3_done(struct tevent_req *subreq)
-{
- struct tevent_req *req =
- tevent_req_callback_data(subreq,
- struct tevent_req);
- NTSTATUS status;
-
- status = rpc_write_recv(subreq);
- TALLOC_FREE(subreq);
- if (!NT_STATUS_IS_OK(status)) {
- tevent_req_nterror(req, status);
- return;
- }
-
- tevent_req_done(req);
-}
-
-static void rpc_api_pipe_trans_done(struct tevent_req *subreq)
-{
- struct tevent_req *req = tevent_req_callback_data(
- subreq, struct tevent_req);
- struct rpc_api_pipe_state *state = tevent_req_data(
- req, struct rpc_api_pipe_state);
- NTSTATUS status;
- uint8_t *rdata = NULL;
- uint32_t rdata_len = 0;
-
- status = cli_api_pipe_recv(subreq, state, &rdata, &rdata_len);
- TALLOC_FREE(subreq);
- if (!NT_STATUS_IS_OK(status)) {
- DEBUG(5, ("cli_api_pipe failed: %s\n", nt_errstr(status)));
- tevent_req_nterror(req, status);
- return;
- }
-
- if (rdata == NULL) {
- DEBUG(3,("rpc_api_pipe: %s failed to return data.\n",
- rpccli_pipe_txt(talloc_tos(), state->cli)));
- tevent_req_done(req);
- return;
- }
-
- /*
- * Move data on state->incoming_frag.
- */
- state->incoming_frag.data = talloc_move(state, &rdata);
- state->incoming_frag.length = rdata_len;
- if (!state->incoming_frag.data) {
- tevent_req_nterror(req, NT_STATUS_NO_MEMORY);
- return;
- }
-
- /* Ensure we have enough data for a pdu. */
- subreq = get_complete_frag_send(state, state->ev, state->cli,
- &state->incoming_frag);
- if (tevent_req_nomem(subreq, req)) {
- return;
- }
- tevent_req_set_callback(subreq, rpc_api_pipe_got_pdu, req);
-}
-
-static void rpc_api_pipe_got_pdu(struct tevent_req *subreq)
-{
- struct tevent_req *req = tevent_req_callback_data(
- subreq, struct tevent_req);
- struct rpc_api_pipe_state *state = tevent_req_data(
- req, struct rpc_api_pipe_state);
- NTSTATUS status;
- DATA_BLOB rdata = data_blob_null;
-
- status = get_complete_frag_recv(subreq);
- TALLOC_FREE(subreq);
- if (!NT_STATUS_IS_OK(status)) {
- DEBUG(5, ("get_complete_frag failed: %s\n",
- nt_errstr(status)));
- tevent_req_nterror(req, status);
- return;
- }
-
- state->pkt = talloc(state, struct ncacn_packet);
- if (!state->pkt) {
- /*
- * TODO: do a real async disconnect ...
- *
- * For now do it sync...
- */
- TALLOC_FREE(state->cli->transport);
- tevent_req_nterror(req, NT_STATUS_NO_MEMORY);
- return;
- }
-
- status = dcerpc_pull_ncacn_packet(state->pkt,
- &state->incoming_frag,
- state->pkt);
- if (!NT_STATUS_IS_OK(status)) {
- /*
- * TODO: do a real async disconnect ...
- *
- * For now do it sync...
- */
- TALLOC_FREE(state->cli->transport);
- tevent_req_nterror(req, status);
- return;
- }
-
- if (DEBUGLEVEL >= 10) {
- NDR_PRINT_DEBUG(ncacn_packet, state->pkt);
- }
-
- status = cli_pipe_validate_current_pdu(state,
- state->cli, state->pkt,
- &state->incoming_frag,
- state->expected_pkt_type,
- state->call_id,
- &rdata,
- &state->reply_pdu);
-
- DEBUG(10,("rpc_api_pipe: got frag len of %u at offset %u: %s\n",
- (unsigned)state->incoming_frag.length,
- (unsigned)state->reply_pdu_offset,
- nt_errstr(status)));
-
- if (state->pkt->ptype != DCERPC_PKT_FAULT && !NT_STATUS_IS_OK(status)) {
- /*
- * TODO: do a real async disconnect ...
- *
- * For now do it sync...
- */
- TALLOC_FREE(state->cli->transport);
- } else if (NT_STATUS_EQUAL(status, NT_STATUS_RPC_PROTOCOL_ERROR)) {
- /*
- * TODO: do a real async disconnect ...
- *
- * For now do it sync...
- */
- TALLOC_FREE(state->cli->transport);
- } else if (NT_STATUS_EQUAL(status, NT_STATUS_RPC_SEC_PKG_ERROR)) {
- /*
- * TODO: do a real async disconnect ...
- *
- * For now do it sync...
- */
- TALLOC_FREE(state->cli->transport);
- }
- if (!NT_STATUS_IS_OK(status)) {
- tevent_req_nterror(req, status);
- return;
- }
-
- if ((state->pkt->pfc_flags & DCERPC_PFC_FLAG_FIRST)
- && (state->pkt->drep[0] != DCERPC_DREP_LE)) {
- /*
- * Set the data type correctly for big-endian data on the
- * first packet.
- */
- DEBUG(10,("rpc_api_pipe: On %s PDU data format is "
- "big-endian.\n",
- rpccli_pipe_txt(talloc_tos(), state->cli)));
- state->endianess = 0x00; /* BIG ENDIAN */
- }
- /*
- * Check endianness on subsequent packets.
- */
- if (state->endianess != state->pkt->drep[0]) {
- DEBUG(0,("rpc_api_pipe: Error : Endianness changed from %s to "
- "%s\n",
- state->endianess?"little":"big",
- state->pkt->drep[0]?"little":"big"));
- /*
- * TODO: do a real async disconnect ...
- *
- * For now do it sync...
- */
- TALLOC_FREE(state->cli->transport);
- tevent_req_nterror(req, NT_STATUS_RPC_PROTOCOL_ERROR);
- return;
- }
-
- if (state->reply_pdu_offset + rdata.length > MAX_RPC_DATA_SIZE) {
- /*
- * TODO: do a real async disconnect ...
- *
- * For now do it sync...
- */
- TALLOC_FREE(state->cli->transport);
- tevent_req_nterror(req, NT_STATUS_RPC_PROTOCOL_ERROR);
- return;
- }
-
- /* Now copy the data portion out of the pdu into rbuf. */
- if (state->reply_pdu.length < state->reply_pdu_offset + rdata.length) {
- if (!data_blob_realloc(NULL, &state->reply_pdu,
- state->reply_pdu_offset + rdata.length)) {
- /*
- * TODO: do a real async disconnect ...
- *
- * For now do it sync...
- */
- TALLOC_FREE(state->cli->transport);
- tevent_req_nterror(req, NT_STATUS_NO_MEMORY);
- return;
- }
- }
-
- memcpy(state->reply_pdu.data + state->reply_pdu_offset,
- rdata.data, rdata.length);
- state->reply_pdu_offset += rdata.length;
-
- /* reset state->incoming_frag, there is no need to free it,
- * it will be reallocated to the right size the next time
- * it is used */
- state->incoming_frag.length = 0;
-
- if (state->pkt->pfc_flags & DCERPC_PFC_FLAG_LAST) {
- /* make sure the pdu length is right now that we
- * have all the data available (alloc hint may
- * have allocated more than was actually used) */
- state->reply_pdu.length = state->reply_pdu_offset;
- DEBUG(10,("rpc_api_pipe: %s returned %u bytes.\n",
- rpccli_pipe_txt(talloc_tos(), state->cli),
- (unsigned)state->reply_pdu.length));
- tevent_req_done(req);
- return;
- }
-
- subreq = get_complete_frag_send(state, state->ev, state->cli,
- &state->incoming_frag);
- if (subreq == NULL) {
- /*
- * TODO: do a real async disconnect ...
- *
- * For now do it sync...
- */
- TALLOC_FREE(state->cli->transport);
- }
- if (tevent_req_nomem(subreq, req)) {
- return;
- }
- tevent_req_set_callback(subreq, rpc_api_pipe_got_pdu, req);
-}
-
-static NTSTATUS rpc_api_pipe_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
- struct ncacn_packet **pkt,
- DATA_BLOB *reply_pdu)
-{
- struct rpc_api_pipe_state *state = tevent_req_data(
- req, struct rpc_api_pipe_state);
- NTSTATUS status;
-
- if (tevent_req_is_nterror(req, &status)) {
- return status;
- }
-
- /* return data to caller and assign it ownership of memory */
- if (reply_pdu) {
- reply_pdu->data = talloc_move(mem_ctx, &state->reply_pdu.data);
- reply_pdu->length = state->reply_pdu.length;
- state->reply_pdu.length = 0;
- } else {
- data_blob_free(&state->reply_pdu);
- }
-
- if (pkt) {
- *pkt = talloc_steal(mem_ctx, state->pkt);
- }
-
- return NT_STATUS_OK;
-}
-
-/*******************************************************************
- Creates NTLMSSP auth bind.
- ********************************************************************/
-
-static NTSTATUS create_generic_auth_rpc_bind_req(struct rpc_pipe_client *cli,
- TALLOC_CTX *mem_ctx,
- DATA_BLOB *auth_token,
- bool *client_hdr_signing)
-{
- struct gensec_security *gensec_security;
- DATA_BLOB null_blob = data_blob_null;
- NTSTATUS status;
-
- gensec_security = cli->auth->auth_ctx;
-
- DEBUG(5, ("create_generic_auth_rpc_bind_req: generate first token\n"));
- status = gensec_update(gensec_security, mem_ctx, null_blob, auth_token);
-
- if (!NT_STATUS_IS_OK(status) &&
- !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED))
- {
- return status;
- }
-
- if (client_hdr_signing == NULL) {
- return status;
- }
-
- if (cli->auth->auth_level < DCERPC_AUTH_LEVEL_PACKET) {
- *client_hdr_signing = false;
- return status;
- }
-
- *client_hdr_signing = gensec_have_feature(gensec_security,
- GENSEC_FEATURE_SIGN_PKT_HEADER);
-
- return status;
-}
-
-/*******************************************************************
- Creates the internals of a DCE/RPC bind request or alter context PDU.
- ********************************************************************/
-
-static NTSTATUS create_bind_or_alt_ctx_internal(TALLOC_CTX *mem_ctx,
- enum dcerpc_pkt_type ptype,
- uint32_t rpc_call_id,
- const struct ndr_syntax_id *abstract,
- const struct ndr_syntax_id *transfer,
- const DATA_BLOB *auth_info,
- bool client_hdr_signing,
- DATA_BLOB *blob)
-{
- uint16_t auth_len = auth_info->length;
- NTSTATUS status;
- union dcerpc_payload u;
- struct dcerpc_ctx_list ctx_list;
- uint8_t pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
-
- if (auth_len) {
- auth_len -= DCERPC_AUTH_TRAILER_LENGTH;
- }
-
- if (client_hdr_signing) {
- pfc_flags |= DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN;
- }
-
- ctx_list.context_id = 0;
- ctx_list.num_transfer_syntaxes = 1;
- ctx_list.abstract_syntax = *abstract;
- ctx_list.transfer_syntaxes = (struct ndr_syntax_id *)discard_const(transfer);
-
- u.bind.max_xmit_frag = RPC_MAX_PDU_FRAG_LEN;
- u.bind.max_recv_frag = RPC_MAX_PDU_FRAG_LEN;
- u.bind.assoc_group_id = 0x0;
- u.bind.num_contexts = 1;
- u.bind.ctx_list = &ctx_list;
- u.bind.auth_info = *auth_info;
-
- status = dcerpc_push_ncacn_packet(mem_ctx,
- ptype, pfc_flags,
- auth_len,
- rpc_call_id,
- &u,
- blob);
- if (!NT_STATUS_IS_OK(status)) {
- DEBUG(0, ("Failed to marshall bind/alter ncacn_packet.\n"));
- return status;
- }
-
- return NT_STATUS_OK;
-}
-
-/*******************************************************************
- Creates a DCE/RPC bind request.
- ********************************************************************/
-
-static NTSTATUS create_rpc_bind_req(TALLOC_CTX *mem_ctx,
- struct rpc_pipe_client *cli,
- struct pipe_auth_data *auth,
- uint32_t rpc_call_id,
- const struct ndr_syntax_id *abstract,
- const struct ndr_syntax_id *transfer,
- DATA_BLOB *rpc_out)
-{
- DATA_BLOB auth_token = data_blob_null;
- DATA_BLOB auth_info = data_blob_null;
- NTSTATUS ret = NT_STATUS_OK;
-
- switch (auth->auth_type) {
- case DCERPC_AUTH_TYPE_NONE:
- break;
-
- default:
- ret = create_generic_auth_rpc_bind_req(cli, mem_ctx,
- &auth_token,
- &auth->client_hdr_signing);
-
- if (!NT_STATUS_IS_OK(ret) &&
- !NT_STATUS_EQUAL(ret, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
- return ret;
- }
- break;
- }
-
- if (auth_token.length != 0) {
- ret = dcerpc_push_dcerpc_auth(cli,
- auth->auth_type,
- auth->auth_level,
- 0, /* auth_pad_length */
- auth->auth_context_id,
- &auth_token,
- &auth_info);
- if (!NT_STATUS_IS_OK(ret)) {
- return ret;
- }
- data_blob_free(&auth_token);
- }
-
- ret = create_bind_or_alt_ctx_internal(mem_ctx,
- DCERPC_PKT_BIND,
- rpc_call_id,
- abstract,
- transfer,
- &auth_info,
- auth->client_hdr_signing,
- rpc_out);
- return ret;
-}
-
-/*******************************************************************
- External interface.
- Does an rpc request on a pipe. Incoming data is NDR encoded in in_data.
- Reply is NDR encoded in out_data. Splits the data stream into RPC PDU's
- and deals with signing/sealing details.
- ********************************************************************/
-
-struct rpc_api_pipe_req_state {
- struct tevent_context *ev;
- struct rpc_pipe_client *cli;
- uint8_t op_num;
- uint32_t call_id;
- const DATA_BLOB *req_data;
- const struct GUID *object_uuid;
- uint32_t req_data_sent;
- DATA_BLOB req_trailer;
- uint32_t req_trailer_sent;
- bool verify_bitmask1;
- bool verify_pcontext;
- DATA_BLOB rpc_out;
- DATA_BLOB reply_pdu;
-};
-
-static void rpc_api_pipe_req_write_done(struct tevent_req *subreq);
-static void rpc_api_pipe_req_done(struct tevent_req *subreq);
-static NTSTATUS prepare_verification_trailer(struct rpc_api_pipe_req_state *state);
-static NTSTATUS prepare_next_frag(struct rpc_api_pipe_req_state *state,
- bool *is_last_frag);
-
-static struct tevent_req *rpc_api_pipe_req_send(TALLOC_CTX *mem_ctx,
- struct tevent_context *ev,
- struct rpc_pipe_client *cli,
- uint8_t op_num,
- const struct GUID *object_uuid,
- const DATA_BLOB *req_data)
-{
- struct tevent_req *req, *subreq;
- struct rpc_api_pipe_req_state *state;
- NTSTATUS status;
- bool is_last_frag;
-
- req = tevent_req_create(mem_ctx, &state,
- struct rpc_api_pipe_req_state);
- if (req == NULL) {
- return NULL;
- }
- state->ev = ev;
- state->cli = cli;
- state->op_num = op_num;
- state->object_uuid = object_uuid;
- state->req_data = req_data;
- state->req_data_sent = 0;
- state->call_id = get_rpc_call_id();
- state->reply_pdu = data_blob_null;
- state->rpc_out = data_blob_null;
-
- if (cli->max_xmit_frag < DCERPC_REQUEST_LENGTH
- + RPC_MAX_SIGN_SIZE) {
- /* Server is screwed up ! */
- status = NT_STATUS_INVALID_PARAMETER;
- goto post_status;
- }
-
- status = prepare_verification_trailer(state);
- if (!NT_STATUS_IS_OK(status)) {
- goto post_status;
- }
-
- status = prepare_next_frag(state, &is_last_frag);
- if (!NT_STATUS_IS_OK(status)) {
- goto post_status;
- }
-
- if (is_last_frag) {
- subreq = rpc_api_pipe_send(state, ev, state->cli,
- &state->rpc_out,
- DCERPC_PKT_RESPONSE,
- state->call_id);
- if (subreq == NULL) {
- goto fail;
- }
- tevent_req_set_callback(subreq, rpc_api_pipe_req_done, req);
- } else {
- subreq = rpc_write_send(state, ev, cli->transport,
- state->rpc_out.data,
- state->rpc_out.length);
- if (subreq == NULL) {
- goto fail;
- }
- tevent_req_set_callback(subreq, rpc_api_pipe_req_write_done,
- req);
- }
- return req;
-
- post_status:
- tevent_req_nterror(req, status);
- return tevent_req_post(req, ev);
- fail:
- TALLOC_FREE(req);
- return NULL;
-}
-
-static NTSTATUS prepare_verification_trailer(struct rpc_api_pipe_req_state *state)
-{
- struct pipe_auth_data *a = state->cli->auth;
- 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 (a == NULL) {
- return NT_STATUS_OK;
- }
-
- if (a->auth_level < DCERPC_AUTH_LEVEL_PACKET) {
- return NT_STATUS_OK;
- }
-
- t = talloc_zero(state, struct dcerpc_sec_verification_trailer);
- if (t == NULL) {
- return NT_STATUS_NO_MEMORY;
- }
-
- if (!a->verified_bitmask1) {
- t->commands = talloc_realloc(t, t->commands,
- struct dcerpc_sec_vt,
- t->count.count + 1);
- if (t->commands == NULL) {
- return NT_STATUS_NO_MEMORY;
- }
- c = &t->commands[t->count.count++];
- ZERO_STRUCTP(c);
-
- c->command = DCERPC_SEC_VT_COMMAND_BITMASK1;
- if (a->client_hdr_signing) {
- c->u.bitmask1 = DCERPC_SEC_VT_CLIENT_SUPPORTS_HEADER_SIGNING;
- }
- state->verify_bitmask1 = true;
- }
-
- if (!state->cli->verified_pcontext) {
- t->commands = talloc_realloc(t, t->commands,
- struct dcerpc_sec_vt,
- t->count.count + 1);
- if (t->commands == NULL) {
- return NT_STATUS_NO_MEMORY;
- }
- c = &t->commands[t->count.count++];
- ZERO_STRUCTP(c);
-
- c->command = DCERPC_SEC_VT_COMMAND_PCONTEXT;
- c->u.pcontext.abstract_syntax = state->cli->abstract_syntax;
- c->u.pcontext.transfer_syntax = state->cli->transfer_syntax;
-
- state->verify_pcontext = true;
- }
-
- if (!a->hdr_signing) {
- t->commands = talloc_realloc(t, t->commands,
- struct dcerpc_sec_vt,
- t->count.count + 1);
- if (t->commands == NULL) {
- return NT_STATUS_NO_MEMORY;
- }
- c = &t->commands[t->count.count++];
- ZERO_STRUCTP(c);
-
- c->command = DCERPC_SEC_VT_COMMAND_HEADER2;
- c->u.header2.ptype = DCERPC_PKT_REQUEST;
- 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_id;
- c->u.header2.context_id = 0;
- c->u.header2.opnum = state->op_num;
- }
-
- if (t->count.count == 0) {
- TALLOC_FREE(t);
- return NT_STATUS_OK;
- }
-
- 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 (ndr == NULL) {
- return NT_STATUS_NO_MEMORY;
- }
-
- ndr_err = ndr_push_dcerpc_sec_verification_trailer(ndr,
- NDR_SCALARS | NDR_BUFFERS,
- t);
- if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
- return ndr_map_error2ntstatus(ndr_err);
- }
- state->req_trailer = ndr_push_blob(ndr);
-
- align = state->req_data->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->req_trailer, zeros, pad);
- if (!ok) {
- return NT_STATUS_NO_MEMORY;
- }
-
- /* move the padding to the start */
- p = state->req_trailer.data;
- memmove(p + pad, p, state->req_trailer.length - pad);
- memset(p, 0, pad);
- }
-
- return NT_STATUS_OK;
-}
-
-static NTSTATUS prepare_next_frag(struct rpc_api_pipe_req_state *state,
- bool *is_last_frag)
-{
- 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;
- bool ok;
- union dcerpc_payload u;
-
- data_left = state->req_data->length - state->req_data_sent;
- trailer_left = state->req_trailer.length - state->req_trailer_sent;
- total_left = data_left + trailer_left;
- if ((total_left < data_left) || (total_left < trailer_left)) {
- /*
- * overflow
- */
- return NT_STATUS_INVALID_PARAMETER_MIX;
- }
-
- status = dcerpc_guess_sizes(state->cli->auth,
- DCERPC_REQUEST_LENGTH, total_left,
- state->cli->max_xmit_frag,
- &total_thistime,
- &frag_len, &auth_len, &pad_len);
- if (!NT_STATUS_IS_OK(status)) {
- return status;
- }
-
- if (state->req_data_sent == 0) {
- flags = DCERPC_PFC_FLAG_FIRST;
- }
-
- 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;
- }
-
- data_blob_free(&state->rpc_out);
-
- ZERO_STRUCT(u.request);
-
- u.request.alloc_hint = total_left;
- u.request.context_id = 0;
- u.request.opnum = state->op_num;
-
- if (state->object_uuid) {
- flags |= DCERPC_PFC_FLAG_OBJECT_UUID;
- u.request.object.object = *state->object_uuid;
- frag_len += ndr_size_GUID(state->object_uuid, 0);
- }
-
- status = dcerpc_push_ncacn_packet(state,
- DCERPC_PKT_REQUEST,
- flags,
- auth_len,
- state->call_id,
- &u,
- &state->rpc_out);
- if (!NT_STATUS_IS_OK(status)) {
- return status;
- }
-
- /* explicitly set frag_len here as dcerpc_push_ncacn_packet() can't
- * compute it right for requests because the auth trailer is missing
- * at this stage */
- dcerpc_set_frag_length(&state->rpc_out, frag_len);
-
- if (data_thistime > 0) {
- /* Copy in the data. */
- ok = data_blob_append(NULL, &state->rpc_out,
- state->req_data->data + state->req_data_sent,
- data_thistime);
- if (!ok) {
- return NT_STATUS_NO_MEMORY;
- }
- state->req_data_sent += data_thistime;
- }
-
- if (trailer_thistime > 0) {
- /* Copy in the verification trailer. */
- ok = data_blob_append(NULL, &state->rpc_out,
- state->req_trailer.data + state->req_trailer_sent,
- trailer_thistime);
- if (!ok) {
- return NT_STATUS_NO_MEMORY;
- }
- state->req_trailer_sent += trailer_thistime;
- }
-
- switch (state->cli->auth->auth_level) {
- case DCERPC_AUTH_LEVEL_NONE:
- case DCERPC_AUTH_LEVEL_CONNECT:
- break;
- case DCERPC_AUTH_LEVEL_PACKET:
- case DCERPC_AUTH_LEVEL_INTEGRITY:
- case DCERPC_AUTH_LEVEL_PRIVACY:
- status = dcerpc_add_auth_footer(state->cli->auth, pad_len,
- &state->rpc_out);
- if (!NT_STATUS_IS_OK(status)) {
- return status;
- }
- break;
- default:
- return NT_STATUS_INVALID_PARAMETER;
- }
-
- *is_last_frag = ((flags & DCERPC_PFC_FLAG_LAST) != 0);
-
- return status;
-}
-
-static void rpc_api_pipe_req_write_done(struct tevent_req *subreq)
-{
- struct tevent_req *req = tevent_req_callback_data(
- subreq, struct tevent_req);
- struct rpc_api_pipe_req_state *state = tevent_req_data(
- req, struct rpc_api_pipe_req_state);
- NTSTATUS status;
- bool is_last_frag;
-
- status = rpc_write_recv(subreq);
- TALLOC_FREE(subreq);
- if (!NT_STATUS_IS_OK(status)) {
- tevent_req_nterror(req, status);
- return;
- }
-
- status = prepare_next_frag(state, &is_last_frag);
- if (!NT_STATUS_IS_OK(status)) {
- tevent_req_nterror(req, status);
- return;
- }
-
- if (is_last_frag) {
- subreq = rpc_api_pipe_send(state, state->ev, state->cli,
- &state->rpc_out,
- DCERPC_PKT_RESPONSE,
- state->call_id);
- if (tevent_req_nomem(subreq, req)) {
- return;
- }
- tevent_req_set_callback(subreq, rpc_api_pipe_req_done, req);
- } else {
- subreq = rpc_write_send(state, state->ev,
- state->cli->transport,
- state->rpc_out.data,
- state->rpc_out.length);
- if (tevent_req_nomem(subreq, req)) {
- return;
- }
- tevent_req_set_callback(subreq, rpc_api_pipe_req_write_done,
- req);
- }
-}
-
-static void rpc_api_pipe_req_done(struct tevent_req *subreq)
-{
- struct tevent_req *req = tevent_req_callback_data(
- subreq, struct tevent_req);
- struct rpc_api_pipe_req_state *state = tevent_req_data(
- req, struct rpc_api_pipe_req_state);
- NTSTATUS status;
-
- status = rpc_api_pipe_recv(subreq, state, NULL, &state->reply_pdu);
- TALLOC_FREE(subreq);
- if (!NT_STATUS_IS_OK(status)) {
- tevent_req_nterror(req, status);
- return;
- }
-
- if (state->cli->auth == NULL) {
- tevent_req_done(req);
- return;
- }
-
- if (state->verify_bitmask1) {
- state->cli->auth->verified_bitmask1 = true;
- }
-
- if (state->verify_pcontext) {
- state->cli->verified_pcontext = true;
- }
-
- tevent_req_done(req);
-}
-
-static NTSTATUS rpc_api_pipe_req_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
- DATA_BLOB *reply_pdu)
-{
- struct rpc_api_pipe_req_state *state = tevent_req_data(
- req, struct rpc_api_pipe_req_state);
- NTSTATUS status;
-
- if (tevent_req_is_nterror(req, &status)) {
- /*
- * We always have to initialize to reply pdu, even if there is
- * none. The rpccli_* caller routines expect this.
- */
- *reply_pdu = data_blob_null;
- return status;
- }
-
- /* return data to caller and assign it ownership of memory */
- reply_pdu->data = talloc_move(mem_ctx, &state->reply_pdu.data);
- reply_pdu->length = state->reply_pdu.length;
- state->reply_pdu.length = 0;
-
- return NT_STATUS_OK;
-}
-
-/****************************************************************************
- Check the rpc bind acknowledge response.
-****************************************************************************/
-
-static bool check_bind_response(const struct dcerpc_bind_ack *r,
- const struct ndr_syntax_id *transfer)
-{
- struct dcerpc_ack_ctx ctx;
-
- if (r->secondary_address_size == 0) {
- DEBUG(4,("Ignoring length check -- ASU bug (server didn't fill in the pipe name correctly)"));
- }
-
- if (r->num_results < 1 || !r->ctx_list) {
- return false;
- }
-
- ctx = r->ctx_list[0];
-
- /* check the transfer syntax */
- if ((ctx.syntax.if_version != transfer->if_version) ||
- (memcmp(&ctx.syntax.uuid, &transfer->uuid, sizeof(transfer->uuid)) !=0)) {
- DEBUG(2,("bind_rpc_pipe: transfer syntax differs\n"));
- return False;
- }
-
- if (r->num_results != 0x1 || ctx.result != 0) {
- DEBUG(2,("bind_rpc_pipe: bind denied results: %d reason: %x\n",
- r->num_results, ctx.reason.value));
- }
-
- DEBUG(5,("check_bind_response: accepted!\n"));
- return True;
-}
-
-/*******************************************************************
- Creates a DCE/RPC bind authentication response.
- This is the packet that is sent back to the server once we
- have received a BIND-ACK, to finish the third leg of
- the authentication handshake.
- ********************************************************************/
-
-static NTSTATUS create_rpc_bind_auth3(TALLOC_CTX *mem_ctx,
- struct rpc_pipe_client *cli,
- struct pipe_auth_data *auth,
- uint32_t rpc_call_id,
- DATA_BLOB *pauth_blob,
- DATA_BLOB *rpc_out)
-{
- NTSTATUS status;
- union dcerpc_payload u;
-
- u.auth3._pad = 0;
-
- status = dcerpc_push_dcerpc_auth(mem_ctx,
- auth->auth_type,
- auth->auth_level,
- 0, /* auth_pad_length */
- auth->auth_context_id,
- pauth_blob,
- &u.auth3.auth_info);
- if (!NT_STATUS_IS_OK(status)) {
- return status;
- }
-
- status = dcerpc_push_ncacn_packet(mem_ctx,
- DCERPC_PKT_AUTH3,
- DCERPC_PFC_FLAG_FIRST |
- DCERPC_PFC_FLAG_LAST,
- pauth_blob->length,
- rpc_call_id,
- &u,
- rpc_out);
- data_blob_free(&u.auth3.auth_info);
- if (!NT_STATUS_IS_OK(status)) {
- DEBUG(0,("create_bind_or_alt_ctx_internal: failed to marshall RPC_HDR_RB.\n"));
- return status;
- }
-
- return NT_STATUS_OK;
-}
-
-/*******************************************************************
- Creates a DCE/RPC bind alter context authentication request which
- may contain a spnego auth blobl
- ********************************************************************/
-
-static NTSTATUS create_rpc_alter_context(TALLOC_CTX *mem_ctx,
- struct pipe_auth_data *auth,
- uint32_t rpc_call_id,
- const struct ndr_syntax_id *abstract,
- const struct ndr_syntax_id *transfer,
- const DATA_BLOB *pauth_blob, /* spnego auth blob already created. */
- DATA_BLOB *rpc_out)
-{
- DATA_BLOB auth_info;
- NTSTATUS status;
-
- status = dcerpc_push_dcerpc_auth(mem_ctx,
- auth->auth_type,
- auth->auth_level,
- 0, /* auth_pad_length */
- auth->auth_context_id,
- pauth_blob,
- &auth_info);
- if (!NT_STATUS_IS_OK(status)) {
- return status;
- }
-
- status = create_bind_or_alt_ctx_internal(mem_ctx,
- DCERPC_PKT_ALTER,
- rpc_call_id,
- abstract,
- transfer,
- &auth_info,
- false, /* client_hdr_signing */
- rpc_out);
- data_blob_free(&auth_info);
- return status;
-}
-
/****************************************************************************
Do an rpc bind.
****************************************************************************/