summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVolker Lendecke <vl@samba.org>2009-01-18 12:12:15 +0100
committerVolker Lendecke <vl@samba.org>2009-01-18 15:40:45 +0100
commit28c35b4c0456ce62bf1a492ad9c1b223e2f31816 (patch)
treecfd2ff3a02130fa73783615133159e3ace46945c
parentecb5184ce57259c709d728ed5f7be1bdf666bbcc (diff)
downloadsamba-28c35b4c0456ce62bf1a492ad9c1b223e2f31816.tar.gz
Make rpc_pipe_bind async
-rw-r--r--source3/include/proto.h5
-rw-r--r--source3/rpc_client/cli_pipe.c447
2 files changed, 452 insertions, 0 deletions
diff --git a/source3/include/proto.h b/source3/include/proto.h
index 72b95d44c3a..71ad2590850 100644
--- a/source3/include/proto.h
+++ b/source3/include/proto.h
@@ -5208,6 +5208,11 @@ NTSTATUS rpc_api_pipe_req(TALLOC_CTX *mem_ctx, struct rpc_pipe_client *cli,
uint8 op_num,
prs_struct *in_data,
prs_struct *out_data);
+struct async_req *rpc_pipe_bind_send(TALLOC_CTX *mem_ctx,
+ struct event_context *ev,
+ struct rpc_pipe_client *cli,
+ struct cli_pipe_auth_data *auth);
+NTSTATUS rpc_pipe_bind_recv(struct async_req *req);
NTSTATUS rpc_pipe_bind(struct rpc_pipe_client *cli,
struct cli_pipe_auth_data *auth);
unsigned int rpccli_set_timeout(struct rpc_pipe_client *cli,
diff --git a/source3/rpc_client/cli_pipe.c b/source3/rpc_client/cli_pipe.c
index c32124f678d..63d3689620f 100644
--- a/source3/rpc_client/cli_pipe.c
+++ b/source3/rpc_client/cli_pipe.c
@@ -1585,6 +1585,7 @@ static NTSTATUS rpc_api_pipe_recv(struct async_req *req, TALLOC_CTX *mem_ctx,
return NT_STATUS_OK;
}
+#if 0
static NTSTATUS rpc_api_pipe(TALLOC_CTX *mem_ctx, struct rpc_pipe_client *cli,
prs_struct *data, /* Outgoing pdu fragment,
* already formatted for
@@ -1617,6 +1618,7 @@ static NTSTATUS rpc_api_pipe(TALLOC_CTX *mem_ctx, struct rpc_pipe_client *cli,
TALLOC_FREE(frame);
return status;
}
+#endif
/*******************************************************************
Creates krb5 auth bind.
@@ -2613,6 +2615,7 @@ static NTSTATUS create_rpc_bind_auth3(struct rpc_pipe_client *cli,
Create and send the third packet in an RPC auth.
****************************************************************************/
+#if 0
static NTSTATUS rpc_finish_auth3_bind(struct rpc_pipe_client *cli,
RPC_HDR *phdr,
prs_struct *rbuf,
@@ -2709,6 +2712,7 @@ static NTSTATUS rpc_finish_auth3_bind(struct rpc_pipe_client *cli,
data_blob_free(&server_response);
return NT_STATUS_OK;
}
+#endif
/*******************************************************************
Creates a DCE/RPC bind alter context authentication request which
@@ -2756,6 +2760,7 @@ static NTSTATUS create_rpc_alter_context(uint32 rpc_call_id,
and gets a response.
********************************************************************/
+#if 0
static NTSTATUS rpc_finish_spnego_ntlmssp_bind(struct rpc_pipe_client *cli,
RPC_HDR *phdr,
prs_struct *rbuf,
@@ -2879,11 +2884,13 @@ static NTSTATUS rpc_finish_spnego_ntlmssp_bind(struct rpc_pipe_client *cli,
return NT_STATUS_OK;
}
+#endif
/****************************************************************************
Do an rpc bind.
****************************************************************************/
+#if 0
NTSTATUS rpc_pipe_bind(struct rpc_pipe_client *cli,
struct cli_pipe_auth_data *auth)
{
@@ -3013,6 +3020,446 @@ NTSTATUS rpc_pipe_bind(struct rpc_pipe_client *cli,
prs_mem_free(&rbuf);
return NT_STATUS_OK;
}
+#endif
+
+struct rpc_pipe_bind_state {
+ struct event_context *ev;
+ struct rpc_pipe_client *cli;
+ prs_struct rpc_out;
+ uint32_t rpc_call_id;
+};
+
+static int rpc_pipe_bind_state_destructor(struct rpc_pipe_bind_state *state)
+{
+ prs_mem_free(&state->rpc_out);
+ return 0;
+}
+
+static void rpc_pipe_bind_step_one_done(struct async_req *subreq);
+static NTSTATUS rpc_finish_auth3_bind_send(struct async_req *req,
+ struct rpc_pipe_bind_state *state,
+ struct rpc_hdr_info *phdr,
+ prs_struct *reply_pdu);
+static void rpc_bind_auth3_write_done(struct async_req *subreq);
+static NTSTATUS rpc_finish_spnego_ntlmssp_bind_send(struct async_req *req,
+ struct rpc_pipe_bind_state *state,
+ struct rpc_hdr_info *phdr,
+ prs_struct *reply_pdu);
+static void rpc_bind_ntlmssp_api_done(struct async_req *subreq);
+
+struct async_req *rpc_pipe_bind_send(TALLOC_CTX *mem_ctx,
+ struct event_context *ev,
+ struct rpc_pipe_client *cli,
+ struct cli_pipe_auth_data *auth)
+{
+ struct async_req *result, *subreq;
+ struct rpc_pipe_bind_state *state;
+ NTSTATUS status;
+
+ result = async_req_new(mem_ctx);
+ if (result == NULL) {
+ return NULL;
+ }
+ state = talloc(result, struct rpc_pipe_bind_state);
+ if (state == NULL) {
+ goto fail;
+ }
+ result->private_data = state;
+
+ DEBUG(5,("Bind RPC Pipe: %s auth_type %u, auth_level %u\n",
+ rpccli_pipe_txt(debug_ctx(), cli),
+ (unsigned int)auth->auth_type,
+ (unsigned int)auth->auth_level ));
+
+ state->ev = ev;
+ state->cli = cli;
+ state->rpc_call_id = get_rpc_call_id();
+
+ prs_init_empty(&state->rpc_out, state, MARSHALL);
+ talloc_set_destructor(state, rpc_pipe_bind_state_destructor);
+
+ cli->auth = talloc_move(cli, &auth);
+
+ /* Marshall the outgoing data. */
+ status = create_rpc_bind_req(cli, &state->rpc_out,
+ state->rpc_call_id,
+ &cli->abstract_syntax,
+ &cli->transfer_syntax,
+ cli->auth->auth_type,
+ cli->auth->auth_level);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ goto post_status;
+ }
+
+ subreq = rpc_api_pipe_send(state, ev, cli, &state->rpc_out,
+ RPC_BINDACK);
+ if (subreq == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto post_status;
+ }
+ subreq->async.fn = rpc_pipe_bind_step_one_done;
+ subreq->async.priv = result;
+ return result;
+
+ post_status:
+ if (async_post_status(result, ev, status)) {
+ return result;
+ }
+ fail:
+ TALLOC_FREE(result);
+ return NULL;
+}
+
+static void rpc_pipe_bind_step_one_done(struct async_req *subreq)
+{
+ struct async_req *req = talloc_get_type_abort(
+ subreq->async.priv, struct async_req);
+ struct rpc_pipe_bind_state *state = talloc_get_type_abort(
+ req->private_data, struct rpc_pipe_bind_state);
+ prs_struct reply_pdu;
+ struct rpc_hdr_info hdr;
+ struct rpc_hdr_ba_info hdr_ba;
+ NTSTATUS status;
+
+ status = rpc_api_pipe_recv(subreq, talloc_tos(), &reply_pdu);
+ TALLOC_FREE(subreq);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(3, ("rpc_pipe_bind: %s bind request returned %s\n",
+ rpccli_pipe_txt(debug_ctx(), state->cli),
+ nt_errstr(status)));
+ async_req_error(req, status);
+ return;
+ }
+
+ /* Unmarshall the RPC header */
+ if (!smb_io_rpc_hdr("hdr", &hdr, &reply_pdu, 0)) {
+ DEBUG(0, ("rpc_pipe_bind: failed to unmarshall RPC_HDR.\n"));
+ async_req_error(req, NT_STATUS_BUFFER_TOO_SMALL);
+ return;
+ }
+
+ if (!smb_io_rpc_hdr_ba("", &hdr_ba, &reply_pdu, 0)) {
+ DEBUG(0, ("rpc_pipe_bind: Failed to unmarshall "
+ "RPC_HDR_BA.\n"));
+ async_req_error(req, NT_STATUS_BUFFER_TOO_SMALL);
+ return;
+ }
+
+ if (!check_bind_response(&hdr_ba, &state->cli->transfer_syntax)) {
+ DEBUG(2, ("rpc_pipe_bind: check_bind_response failed.\n"));
+ async_req_error(req, NT_STATUS_BUFFER_TOO_SMALL);
+ return;
+ }
+
+ state->cli->max_xmit_frag = hdr_ba.bba.max_tsize;
+ state->cli->max_recv_frag = hdr_ba.bba.max_rsize;
+
+ /*
+ * For authenticated binds we may need to do 3 or 4 leg binds.
+ */
+
+ switch(state->cli->auth->auth_type) {
+
+ case PIPE_AUTH_TYPE_NONE:
+ case PIPE_AUTH_TYPE_SCHANNEL:
+ /* Bind complete. */
+ async_req_done(req);
+ break;
+
+ case PIPE_AUTH_TYPE_NTLMSSP:
+ /* Need to send AUTH3 packet - no reply. */
+ status = rpc_finish_auth3_bind_send(req, state, &hdr,
+ &reply_pdu);
+ if (!NT_STATUS_IS_OK(status)) {
+ async_req_error(req, status);
+ }
+ break;
+
+ case PIPE_AUTH_TYPE_SPNEGO_NTLMSSP:
+ /* Need to send alter context request and reply. */
+ status = rpc_finish_spnego_ntlmssp_bind_send(req, state, &hdr,
+ &reply_pdu);
+ if (!NT_STATUS_IS_OK(status)) {
+ async_req_error(req, status);
+ }
+ break;
+
+ case PIPE_AUTH_TYPE_KRB5:
+ /* */
+
+ default:
+ DEBUG(0,("cli_finish_bind_auth: unknown auth type %u\n",
+ (unsigned int)state->cli->auth->auth_type));
+ async_req_error(req, NT_STATUS_INTERNAL_ERROR);
+ }
+}
+
+static NTSTATUS rpc_finish_auth3_bind_send(struct async_req *req,
+ struct rpc_pipe_bind_state *state,
+ struct rpc_hdr_info *phdr,
+ prs_struct *reply_pdu)
+{
+ DATA_BLOB server_response = data_blob_null;
+ DATA_BLOB client_reply = data_blob_null;
+ struct rpc_hdr_auth_info hdr_auth;
+ struct async_req *subreq;
+ NTSTATUS status;
+
+ if ((phdr->auth_len == 0)
+ || (phdr->frag_len < phdr->auth_len + RPC_HDR_AUTH_LEN)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (!prs_set_offset(
+ reply_pdu,
+ phdr->frag_len - phdr->auth_len - RPC_HDR_AUTH_LEN)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (!smb_io_rpc_hdr_auth("hdr_auth", &hdr_auth, reply_pdu, 0)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* TODO - check auth_type/auth_level match. */
+
+ server_response = data_blob_talloc(talloc_tos(), NULL, phdr->auth_len);
+ prs_copy_data_out((char *)server_response.data, reply_pdu,
+ phdr->auth_len);
+
+ status = ntlmssp_update(state->cli->auth->a_u.ntlmssp_state,
+ server_response, &client_reply);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("rpc_finish_auth3_bind: NTLMSSP update using server "
+ "blob failed: %s.\n", nt_errstr(status)));
+ return status;
+ }
+
+ prs_init_empty(&state->rpc_out, talloc_tos(), MARSHALL);
+
+ status = create_rpc_bind_auth3(state->cli, state->rpc_call_id,
+ state->cli->auth->auth_type,
+ state->cli->auth->auth_level,
+ &client_reply, &state->rpc_out);
+ data_blob_free(&client_reply);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ subreq = rpc_write_send(state, state->ev, state->cli,
+ prs_data_p(&state->rpc_out),
+ prs_offset(&state->rpc_out));
+ if (subreq == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ subreq->async.fn = rpc_bind_auth3_write_done;
+ subreq->async.priv = req;
+ return NT_STATUS_OK;
+}
+
+static void rpc_bind_auth3_write_done(struct async_req *subreq)
+{
+ struct async_req *req = talloc_get_type_abort(
+ subreq->async.priv, struct async_req);
+ NTSTATUS status;
+
+ status = rpc_write_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (!NT_STATUS_IS_OK(status)) {
+ async_req_error(req, status);
+ return;
+ }
+ async_req_done(req);
+}
+
+static NTSTATUS rpc_finish_spnego_ntlmssp_bind_send(struct async_req *req,
+ struct rpc_pipe_bind_state *state,
+ struct rpc_hdr_info *phdr,
+ prs_struct *reply_pdu)
+{
+ DATA_BLOB server_spnego_response = data_blob_null;
+ DATA_BLOB server_ntlm_response = data_blob_null;
+ DATA_BLOB client_reply = data_blob_null;
+ DATA_BLOB tmp_blob = data_blob_null;
+ RPC_HDR_AUTH hdr_auth;
+ struct async_req *subreq;
+ NTSTATUS status;
+
+ if ((phdr->auth_len == 0)
+ || (phdr->frag_len < phdr->auth_len + RPC_HDR_AUTH_LEN)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* Process the returned NTLMSSP blob first. */
+ if (!prs_set_offset(
+ reply_pdu,
+ phdr->frag_len - phdr->auth_len - RPC_HDR_AUTH_LEN)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (!smb_io_rpc_hdr_auth("hdr_auth", &hdr_auth, reply_pdu, 0)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ server_spnego_response = data_blob(NULL, phdr->auth_len);
+ prs_copy_data_out((char *)server_spnego_response.data,
+ reply_pdu, phdr->auth_len);
+
+ /*
+ * The server might give us back two challenges - tmp_blob is for the
+ * second.
+ */
+ if (!spnego_parse_challenge(server_spnego_response,
+ &server_ntlm_response, &tmp_blob)) {
+ data_blob_free(&server_spnego_response);
+ data_blob_free(&server_ntlm_response);
+ data_blob_free(&tmp_blob);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* We're finished with the server spnego response and the tmp_blob. */
+ data_blob_free(&server_spnego_response);
+ data_blob_free(&tmp_blob);
+
+ status = ntlmssp_update(state->cli->auth->a_u.ntlmssp_state,
+ server_ntlm_response, &client_reply);
+
+ /* Finished with the server_ntlm response */
+ data_blob_free(&server_ntlm_response);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("rpc_finish_spnego_ntlmssp_bind: NTLMSSP update "
+ "using server blob failed.\n"));
+ data_blob_free(&client_reply);
+ return status;
+ }
+
+ /* SPNEGO wrap the client reply. */
+ tmp_blob = spnego_gen_auth(client_reply);
+ data_blob_free(&client_reply);
+ client_reply = tmp_blob;
+ tmp_blob = data_blob_null;
+
+ /* Now prepare the alter context pdu. */
+ prs_init_empty(&state->rpc_out, state, MARSHALL);
+
+ status = create_rpc_alter_context(state->rpc_call_id,
+ &state->cli->abstract_syntax,
+ &state->cli->transfer_syntax,
+ state->cli->auth->auth_level,
+ &client_reply,
+ &state->rpc_out);
+ data_blob_free(&client_reply);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ subreq = rpc_api_pipe_send(state, state->ev, state->cli,
+ &state->rpc_out, RPC_ALTCONTRESP);
+ if (subreq == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ subreq->async.fn = rpc_bind_ntlmssp_api_done;
+ subreq->async.priv = req;
+ return NT_STATUS_OK;
+}
+
+static void rpc_bind_ntlmssp_api_done(struct async_req *subreq)
+{
+ struct async_req *req = talloc_get_type_abort(
+ subreq->async.priv, struct async_req);
+ struct rpc_pipe_bind_state *state = talloc_get_type_abort(
+ req->private_data, struct rpc_pipe_bind_state);
+ DATA_BLOB server_spnego_response = data_blob_null;
+ DATA_BLOB tmp_blob = data_blob_null;
+ prs_struct reply_pdu;
+ struct rpc_hdr_info hdr;
+ struct rpc_hdr_auth_info hdr_auth;
+ NTSTATUS status;
+
+ status = rpc_api_pipe_recv(subreq, talloc_tos(), &reply_pdu);
+ TALLOC_FREE(subreq);
+ if (!NT_STATUS_IS_OK(status)) {
+ async_req_error(req, status);
+ return;
+ }
+
+ /* Get the auth blob from the reply. */
+ if (!smb_io_rpc_hdr("rpc_hdr ", &hdr, &reply_pdu, 0)) {
+ DEBUG(0, ("rpc_finish_spnego_ntlmssp_bind: Failed to "
+ "unmarshall RPC_HDR.\n"));
+ async_req_error(req, NT_STATUS_BUFFER_TOO_SMALL);
+ return;
+ }
+
+ if (!prs_set_offset(
+ &reply_pdu,
+ hdr.frag_len - hdr.auth_len - RPC_HDR_AUTH_LEN)) {
+ async_req_error(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+
+ if (!smb_io_rpc_hdr_auth("hdr_auth", &hdr_auth, &reply_pdu, 0)) {
+ async_req_error(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+
+ server_spnego_response = data_blob(NULL, hdr.auth_len);
+ prs_copy_data_out((char *)server_spnego_response.data, &reply_pdu,
+ hdr.auth_len);
+
+ /* Check we got a valid auth response. */
+ if (!spnego_parse_auth_response(server_spnego_response, NT_STATUS_OK,
+ OID_NTLMSSP, &tmp_blob)) {
+ data_blob_free(&server_spnego_response);
+ data_blob_free(&tmp_blob);
+ async_req_error(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+
+ data_blob_free(&server_spnego_response);
+ data_blob_free(&tmp_blob);
+
+ DEBUG(5,("rpc_finish_spnego_ntlmssp_bind: alter context request to "
+ "%s.\n", rpccli_pipe_txt(debug_ctx(), state->cli)));
+ async_req_done(req);
+}
+
+NTSTATUS rpc_pipe_bind_recv(struct async_req *req)
+{
+ return async_req_simple_recv(req);
+}
+
+NTSTATUS rpc_pipe_bind(struct rpc_pipe_client *cli,
+ struct cli_pipe_auth_data *auth)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct event_context *ev;
+ struct async_req *req;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+ ev = event_context_init(frame);
+ if (ev == NULL) {
+ goto fail;
+ }
+
+ req = rpc_pipe_bind_send(frame, ev, cli, auth);
+ if (req == NULL) {
+ goto fail;
+ }
+
+ while (req->state < ASYNC_REQ_DONE) {
+ event_loop_once(ev);
+ }
+
+ status = rpc_pipe_bind_recv(req);
+ fail:
+ TALLOC_FREE(frame);
+ return status;
+}
unsigned int rpccli_set_timeout(struct rpc_pipe_client *cli,
unsigned int timeout)