diff options
Diffstat (limited to 'source4')
-rw-r--r-- | source4/rpc_server/dcerpc_server.h | 3 | ||||
-rw-r--r-- | source4/rpc_server/service_rpc.c | 399 |
2 files changed, 335 insertions, 67 deletions
diff --git a/source4/rpc_server/dcerpc_server.h b/source4/rpc_server/dcerpc_server.h index 7e12a3840b..23806630d2 100644 --- a/source4/rpc_server/dcerpc_server.h +++ b/source4/rpc_server/dcerpc_server.h @@ -223,6 +223,9 @@ struct dcesrv_connection { struct socket_address *(*get_my_addr)(struct dcesrv_connection *, TALLOC_CTX *mem_ctx); struct socket_address *(*get_peer_addr)(struct dcesrv_connection *, TALLOC_CTX *mem_ctx); } transport; + + struct tstream_context *stream; + struct tevent_queue *send_queue; }; diff --git a/source4/rpc_server/service_rpc.c b/source4/rpc_server/service_rpc.c index 5596944bd8..01bc00762d 100644 --- a/source4/rpc_server/service_rpc.c +++ b/source4/rpc_server/service_rpc.c @@ -39,28 +39,17 @@ #include "system/network.h" #include "lib/socket/netif.h" #include "param/param.h" +#include "../lib/tsocket/tsocket.h" +#include "librpc/rpc/dcerpc_proto.h" +#include "../lib/util/tevent_ntstatus.h" +#include "libcli/raw/smb.h" +#include "../libcli/named_pipe_auth/npa_tstream.h" struct dcesrv_socket_context { const struct dcesrv_endpoint *endpoint; struct dcesrv_context *dcesrv_ctx; }; -/* - write_fn callback for dcesrv_output() -*/ -static NTSTATUS dcerpc_write_fn(void *private_data, DATA_BLOB *out, size_t *nwritten) -{ - NTSTATUS status; - struct socket_context *sock = talloc_get_type(private_data, struct socket_context); - size_t sendlen; - - status = socket_send(sock, out, &sendlen); - NT_STATUS_IS_ERR_RETURN(status); - - *nwritten = sendlen; - return status; -} - static void dcesrv_terminate_connection(struct dcesrv_connection *dce_conn, const char *reason) { struct stream_connection *srv_conn; @@ -70,14 +59,83 @@ static void dcesrv_terminate_connection(struct dcesrv_connection *dce_conn, cons stream_terminate_connection(srv_conn, reason); } -static void dcesrv_sock_report_output_data(struct dcesrv_connection *dcesrv_conn) +static void dcesrv_sock_reply_done(struct tevent_req *subreq); + +struct dcesrv_sock_reply_state { + struct dcesrv_connection *dce_conn; + struct dcesrv_call_state *call; + struct iovec iov; +}; + +static void dcesrv_sock_report_output_data(struct dcesrv_connection *dce_conn) { - struct stream_connection *srv_conn; - srv_conn = talloc_get_type(dcesrv_conn->transport.private_data, - struct stream_connection); + struct dcesrv_call_state *call; + + call = dce_conn->call_list; + if (!call || !call->replies) { + return; + } + + while (call->replies) { + struct data_blob_list_item *rep = call->replies; + struct dcesrv_sock_reply_state *substate; + struct tevent_req *subreq; + + substate = talloc(call, struct dcesrv_sock_reply_state); + if (!substate) { + dcesrv_terminate_connection(dce_conn, "no memory"); + return; + } + + substate->dce_conn = dce_conn; + substate->call = NULL; + + DLIST_REMOVE(call->replies, rep); + + if (call->replies == NULL) { + substate->call = call; + } - if (srv_conn && srv_conn->event.fde) { - EVENT_FD_WRITEABLE(srv_conn->event.fde); + substate->iov.iov_base = rep->blob.data; + substate->iov.iov_len = rep->blob.length; + + subreq = tstream_writev_queue_send(substate, + dce_conn->event_ctx, + dce_conn->stream, + dce_conn->send_queue, + &substate->iov, 1); + if (!subreq) { + dcesrv_terminate_connection(dce_conn, "no memory"); + return; + } + tevent_req_set_callback(subreq, dcesrv_sock_reply_done, + substate); + } + + DLIST_REMOVE(call->conn->call_list, call); + call->list = DCESRV_LIST_NONE; +} + +static void dcesrv_sock_reply_done(struct tevent_req *subreq) +{ + struct dcesrv_sock_reply_state *substate = tevent_req_callback_data(subreq, + struct dcesrv_sock_reply_state); + int ret; + int sys_errno; + NTSTATUS status; + struct dcesrv_call_state *call = substate->call; + + ret = tstream_writev_queue_recv(subreq, &sys_errno); + TALLOC_FREE(subreq); + if (ret == -1) { + status = map_nt_error_from_unix(sys_errno); + dcesrv_terminate_connection(substate->dce_conn, nt_errstr(status)); + return; + } + + talloc_free(substate); + if (call) { + talloc_free(call); } } @@ -99,17 +157,199 @@ static struct socket_address *dcesrv_sock_get_peer_addr(struct dcesrv_connection return socket_get_peer_addr(srv_conn->socket, mem_ctx); } +struct dcerpc_read_ncacn_packet_state { + struct { + struct smb_iconv_convenience *smb_iconv_c; + } caller; + DATA_BLOB buffer; + struct ncacn_packet *pkt; +}; + +static int dcerpc_read_ncacn_packet_next_vector(struct tstream_context *stream, + void *private_data, + TALLOC_CTX *mem_ctx, + struct iovec **_vector, + size_t *_count); +static void dcerpc_read_ncacn_packet_done(struct tevent_req *subreq); + +static struct tevent_req *dcerpc_read_ncacn_packet_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct tstream_context *stream, + struct smb_iconv_convenience *ic) +{ + struct tevent_req *req; + struct dcerpc_read_ncacn_packet_state *state; + struct tevent_req *subreq; + + req = tevent_req_create(mem_ctx, &state, + struct dcerpc_read_ncacn_packet_state); + if (req == NULL) { + return NULL; + } + + state->caller.smb_iconv_c = ic; + state->buffer = data_blob_const(NULL, 0); + state->pkt = talloc(state, struct ncacn_packet); + if (tevent_req_nomem(state->pkt, req)) { + goto post; + } + + subreq = tstream_readv_pdu_send(state, ev, + stream, + dcerpc_read_ncacn_packet_next_vector, + state); + if (tevent_req_nomem(subreq, req)) { + goto post; + } + tevent_req_set_callback(subreq, dcerpc_read_ncacn_packet_done, req); + + return req; + post: + tevent_req_post(req, ev); + return req; +} + +static int dcerpc_read_ncacn_packet_next_vector(struct tstream_context *stream, + void *private_data, + TALLOC_CTX *mem_ctx, + struct iovec **_vector, + size_t *_count) +{ + struct dcerpc_read_ncacn_packet_state *state = + talloc_get_type_abort(private_data, + struct dcerpc_read_ncacn_packet_state); + struct iovec *vector; + off_t ofs = 0; + + if (state->buffer.length == 0) { + /* first get enough to read the fragment length */ + ofs = 0; + state->buffer.length = DCERPC_FRAG_LEN_OFFSET + 2; + state->buffer.data = talloc_array(state, uint8_t, + state->buffer.length); + if (!state->buffer.data) { + return -1; + } + } else if (state->buffer.length == (DCERPC_FRAG_LEN_OFFSET + 2)) { + /* now read the fragment length and allocate the full buffer */ + size_t frag_len = dcerpc_get_frag_length(&state->buffer); + + ofs = state->buffer.length; + + state->buffer.data = talloc_realloc(state, + state->buffer.data, + uint8_t, frag_len); + if (!state->buffer.data) { + return -1; + } + state->buffer.length = frag_len; + } else { + /* if we reach this we have a full fragment */ + *_vector = NULL; + *_count = 0; + return 0; + } + + /* now create the vector that we want to be filled */ + vector = talloc_array(mem_ctx, struct iovec, 1); + if (!vector) { + return -1; + } + + vector[0].iov_base = state->buffer.data + ofs; + vector[0].iov_len = state->buffer.length - ofs; + + *_vector = vector; + *_count = 1; + return 0; +} + +static void dcerpc_read_ncacn_packet_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct dcerpc_read_ncacn_packet_state *state = tevent_req_data(req, + struct dcerpc_read_ncacn_packet_state); + int ret; + int sys_errno; + struct ndr_pull *ndr; + enum ndr_err_code ndr_err; + NTSTATUS status; + + ret = tstream_readv_pdu_recv(subreq, &sys_errno); + TALLOC_FREE(subreq); + if (ret == -1) { + status = map_nt_error_from_unix(sys_errno); + tevent_req_nterror(req, status); + return; + } + + ndr = ndr_pull_init_blob(&state->buffer, + state->pkt, + state->caller.smb_iconv_c); + if (tevent_req_nomem(ndr, req)) { + return; + } + + if (!(CVAL(ndr->data, DCERPC_DREP_OFFSET) & DCERPC_DREP_LE)) { + ndr->flags |= LIBNDR_FLAG_BIGENDIAN; + } + + if (CVAL(ndr->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, state->pkt); + TALLOC_FREE(ndr); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + status = ndr_map_error2ntstatus(ndr_err); + tevent_req_nterror(req, status); + return; + } + + tevent_req_done(req); +} + +static NTSTATUS dcerpc_read_ncacn_packet_recv(struct tevent_req *req, + TALLOC_CTX *mem_ctx, + struct ncacn_packet **pkt, + DATA_BLOB *buffer) +{ + struct dcerpc_read_ncacn_packet_state *state = tevent_req_data(req, + struct dcerpc_read_ncacn_packet_state); + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + tevent_req_received(req); + return status; + } + + *pkt = talloc_move(mem_ctx, &state->pkt); + if (buffer) { + buffer->data = talloc_move(mem_ctx, &state->buffer.data); + buffer->length = state->buffer.length; + } + + tevent_req_received(req); + return NT_STATUS_OK; +} + +static void dcesrv_read_fragment_done(struct tevent_req *subreq); + static void dcesrv_sock_accept(struct stream_connection *srv_conn) { NTSTATUS status; struct dcesrv_socket_context *dcesrv_sock = talloc_get_type(srv_conn->private_data, struct dcesrv_socket_context); struct dcesrv_connection *dcesrv_conn = NULL; + int ret; + struct tevent_req *subreq; + struct loadparm_context *lp_ctx = dcesrv_sock->dcesrv_ctx->lp_ctx; if (!srv_conn->session_info) { status = auth_anonymous_session_info(srv_conn, srv_conn->event.ctx, - srv_conn->lp_ctx, + lp_ctx, &srv_conn->session_info); if (!NT_STATUS_IS_OK(status)) { DEBUG(0,("dcesrv_sock_accept: auth_anonymous_session_info failed: %s\n", @@ -140,78 +380,103 @@ static void dcesrv_sock_accept(struct stream_connection *srv_conn) dcesrv_conn->transport.get_my_addr = dcesrv_sock_get_my_addr; dcesrv_conn->transport.get_peer_addr = dcesrv_sock_get_peer_addr; + TALLOC_FREE(srv_conn->event.fde); + + dcesrv_conn->send_queue = tevent_queue_create(dcesrv_conn, "dcesrv send queue"); + if (!dcesrv_conn->send_queue) { + status = NT_STATUS_NO_MEMORY; + DEBUG(0,("dcesrv_sock_accept: tevent_queue_create(%s)\n", + nt_errstr(status))); + stream_terminate_connection(srv_conn, nt_errstr(status)); + return; + } + if (dcesrv_sock->endpoint->ep_description->transport == NCACN_NP) { dcesrv_conn->auth_state.session_key = dcesrv_inherited_session_key; + ret = tstream_npa_existing_socket(dcesrv_conn, + socket_get_fd(srv_conn->socket), + FILE_TYPE_MESSAGE_MODE_PIPE, + &dcesrv_conn->stream); + } else { + ret = tstream_bsd_existing_socket(dcesrv_conn, + socket_get_fd(srv_conn->socket), + &dcesrv_conn->stream); + } + if (ret == -1) { + status = map_nt_error_from_unix(errno); + DEBUG(0,("dcesrv_sock_accept: failed to setup tstream: %s\n", + nt_errstr(status))); + stream_terminate_connection(srv_conn, nt_errstr(status)); + return; } srv_conn->private_data = dcesrv_conn; irpc_add_name(srv_conn->msg_ctx, "rpc_server"); - return; + subreq = dcerpc_read_ncacn_packet_send(dcesrv_conn, + dcesrv_conn->event_ctx, + dcesrv_conn->stream, + lp_iconv_convenience(lp_ctx)); + if (!subreq) { + status = NT_STATUS_NO_MEMORY; + DEBUG(0,("dcesrv_sock_accept: dcerpc_read_fragment_buffer_send(%s)\n", + nt_errstr(status))); + stream_terminate_connection(srv_conn, nt_errstr(status)); + return; + } + tevent_req_set_callback(subreq, dcesrv_read_fragment_done, dcesrv_conn); + + return; } -static void dcesrv_sock_recv(struct stream_connection *conn, uint16_t flags) +static void dcesrv_read_fragment_done(struct tevent_req *subreq) { + struct dcesrv_connection *dce_conn = tevent_req_callback_data(subreq, + struct dcesrv_connection); + struct ncacn_packet *pkt; + DATA_BLOB buffer; NTSTATUS status; - struct dcesrv_connection *dce_conn = talloc_get_type(conn->private_data, struct dcesrv_connection); - DATA_BLOB tmp_blob; - size_t nread; - - if (dce_conn->processing) { - EVENT_FD_NOT_READABLE(conn->event.fde); - return; - } + struct loadparm_context *lp_ctx = dce_conn->dce_ctx->lp_ctx; - tmp_blob = data_blob_talloc(conn->socket, NULL, 0x1000); - if (tmp_blob.data == NULL) { - dcesrv_terminate_connection(dce_conn, "out of memory"); + status = dcerpc_read_ncacn_packet_recv(subreq, dce_conn, + &pkt, &buffer); + TALLOC_FREE(subreq); + if (!NT_STATUS_IS_OK(status)) { + dcesrv_terminate_connection(dce_conn, nt_errstr(status)); return; } - status = socket_recv(conn->socket, tmp_blob.data, tmp_blob.length, &nread); - if (NT_STATUS_IS_ERR(status)) { + status = dcesrv_process_ncacn_packet(dce_conn, pkt, buffer); + if (!NT_STATUS_IS_OK(status)) { dcesrv_terminate_connection(dce_conn, nt_errstr(status)); return; } - if (nread == 0) { - talloc_free(tmp_blob.data); - return; - } - - tmp_blob.length = nread; - - dce_conn->processing = true; - status = dcesrv_input(dce_conn, &tmp_blob); - dce_conn->processing = false; - talloc_free(tmp_blob.data); - EVENT_FD_READABLE(conn->event.fde); - - if (!NT_STATUS_IS_OK(status)) { + subreq = dcerpc_read_ncacn_packet_send(dce_conn, + dce_conn->event_ctx, + dce_conn->stream, + lp_iconv_convenience(lp_ctx)); + if (!subreq) { + status = NT_STATUS_NO_MEMORY; dcesrv_terminate_connection(dce_conn, nt_errstr(status)); return; } + tevent_req_set_callback(subreq, dcesrv_read_fragment_done, dce_conn); +} - if (dce_conn->call_list && dce_conn->call_list->replies) { - EVENT_FD_WRITEABLE(conn->event.fde); - } +static void dcesrv_sock_recv(struct stream_connection *conn, uint16_t flags) +{ + struct dcesrv_connection *dce_conn = talloc_get_type(conn->private_data, + struct dcesrv_connection); + dcesrv_terminate_connection(dce_conn, "dcesrv_sock_recv triggered"); } static void dcesrv_sock_send(struct stream_connection *conn, uint16_t flags) { - struct dcesrv_connection *dce_conn = talloc_get_type(conn->private_data, struct dcesrv_connection); - NTSTATUS status; - - status = dcesrv_output(dce_conn, conn->socket, dcerpc_write_fn); - if (NT_STATUS_IS_ERR(status)) { - dcesrv_terminate_connection(dce_conn, "eof on socket"); - return; - } - - if (!dce_conn->call_list || !dce_conn->call_list->replies) { - EVENT_FD_NOT_WRITEABLE(conn->event.fde); - } + struct dcesrv_connection *dce_conn = talloc_get_type(conn->private_data, + struct dcesrv_connection); + dcesrv_terminate_connection(dce_conn, "dcesrv_sock_send triggered"); } |