X-Git-Url: http://git.samba.org/samba.git/?p=nivanova%2Fsamba-autobuild%2F.git;a=blobdiff_plain;f=source3%2Frpc_client%2Fcli_pipe.c;h=682f35ece07634a0200ad552f308e0dc96410ead;hp=6dc2cd69afd750757501f807dcfb7e436718ea86;hb=ad0a07c531fadd1639c5298951cfaf5cfe0cb10e;hpb=0b24e8e869207dcb567b61272794daef48ee492a diff --git a/source3/rpc_client/cli_pipe.c b/source3/rpc_client/cli_pipe.c index 6dc2cd69afd..682f35ece07 100644 --- a/source3/rpc_client/cli_pipe.c +++ b/source3/rpc_client/cli_pipe.c @@ -1,37 +1,39 @@ -/* +/* * Unix SMB/CIFS implementation. - * RPC Pipe client / server routines + * RPC Pipe client routines * Largely rewritten by Jeremy Allison 2005. - * + * Heavily modified by Simo Sorce 2010. + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include "includes.h" -#include "librpc/gen_ndr/cli_epmapper.h" +#include "../lib/util/tevent_ntstatus.h" +#include "librpc/gen_ndr/ndr_epmapper_c.h" #include "../librpc/gen_ndr/ndr_schannel.h" #include "../librpc/gen_ndr/ndr_dssetup.h" -#include "../librpc/gen_ndr/ndr_netlogon.h" #include "../libcli/auth/schannel.h" #include "../libcli/auth/spnego.h" -#include "smb_krb5.h" #include "../libcli/auth/ntlmssp.h" #include "ntlmssp_wrap.h" -#include "rpc_client/cli_netlogon.h" #include "librpc/gen_ndr/ndr_dcerpc.h" #include "librpc/rpc/dcerpc.h" -#include "librpc/rpc/dcerpc_gssapi.h" -#include "librpc/rpc/dcerpc_spnego.h" +#include "librpc/crypto/gse.h" +#include "librpc/crypto/spnego.h" +#include "rpc_dce.h" +#include "cli_pipe.h" +#include "client.h" #undef DBGC_CLASS #define DBGC_CLASS DBGC_RPC_CLI @@ -379,21 +381,10 @@ static NTSTATUS cli_pipe_validate_current_pdu(TALLOC_CTX *mem_ctx, DATA_BLOB *rdata, DATA_BLOB *reply_pdu) { + struct dcerpc_response *r; NTSTATUS ret = NT_STATUS_OK; size_t pad_len = 0; - ret = dcerpc_pull_ncacn_packet(cli, pdu, pkt, false); - if (!NT_STATUS_IS_OK(ret)) { - return ret; - } - - if (pdu->length != pkt->frag_length) { - DEBUG(5, ("Incorrect pdu length %u, expected %u\n", - (unsigned int)pdu->length, - (unsigned int)pkt->frag_length)); - return NT_STATUS_INVALID_PARAMETER; - } - /* * Point the return values at the real data including the RPC * header. Just in case the caller wants it. @@ -405,38 +396,39 @@ static NTSTATUS cli_pipe_validate_current_pdu(TALLOC_CTX *mem_ctx, case DCERPC_PKT_ALTER_RESP: case DCERPC_PKT_BIND_ACK: - /* Alter context and bind ack share the same packet definitions. */ + /* Client code never receives this kind of packets */ break; case DCERPC_PKT_RESPONSE: + r = &pkt->u.response; + /* Here's where we deal with incoming sign/seal. */ ret = dcerpc_check_auth(cli->auth, pkt, - &pkt->u.response.stub_and_verifier, + &r->stub_and_verifier, DCERPC_RESPONSE_LENGTH, pdu, &pad_len); if (!NT_STATUS_IS_OK(ret)) { return ret; } - if (pdu->length < DCERPC_RESPONSE_LENGTH + pad_len) { + if (pkt->frag_length < DCERPC_RESPONSE_LENGTH + pad_len) { return NT_STATUS_BUFFER_TOO_SMALL; } /* Point the return values at the NDR data. */ - rdata->data = pdu->data + DCERPC_RESPONSE_LENGTH; + rdata->data = r->stub_and_verifier.data; if (pkt->auth_length) { /* We've already done integer wrap tests in * dcerpc_check_auth(). */ - rdata->length = pdu->length - - DCERPC_RESPONSE_LENGTH + rdata->length = r->stub_and_verifier.length - pad_len - DCERPC_AUTH_TRAILER_LENGTH - pkt->auth_length; } else { - rdata->length = pdu->length - DCERPC_RESPONSE_LENGTH; + rdata->length = r->stub_and_verifier.length; } DEBUG(10, ("Got pdu len %lu, data_len %lu, ss_len %u\n", @@ -451,13 +443,12 @@ static NTSTATUS cli_pipe_validate_current_pdu(TALLOC_CTX *mem_ctx, */ if ((reply_pdu->length == 0) && - pkt->u.response.alloc_hint && - (pkt->u.response.alloc_hint < 15*1024*1024)) { + r->alloc_hint && (r->alloc_hint < 15*1024*1024)) { if (!data_blob_realloc(mem_ctx, reply_pdu, - pkt->u.response.alloc_hint)) { + r->alloc_hint)) { DEBUG(0, ("reply alloc hint %d too " "large to allocate\n", - (int)pkt->u.response.alloc_hint)); + (int)r->alloc_hint)); return NT_STATUS_NO_MEMORY; } } @@ -465,39 +456,34 @@ static NTSTATUS cli_pipe_validate_current_pdu(TALLOC_CTX *mem_ctx, break; case DCERPC_PKT_BIND_NAK: - DEBUG(1, ("cli_pipe_validate_current_pdu: Bind NACK " - "received from %s!\n", + DEBUG(1, (__location__ ": Bind NACK received from %s!\n", rpccli_pipe_txt(talloc_tos(), cli))); /* Use this for now... */ return NT_STATUS_NETWORK_ACCESS_DENIED; case DCERPC_PKT_FAULT: - DEBUG(1, ("cli_pipe_validate_current_pdu: RPC fault " - "code %s received from %s!\n", + 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))); + rpccli_pipe_txt(talloc_tos(), cli))); - if (NT_STATUS_IS_OK(NT_STATUS(pkt->u.fault.status))) { - return NT_STATUS_UNSUCCESSFUL; - } else { - return NT_STATUS(pkt->u.fault.status); - } + return dcerpc_fault_to_nt_status(pkt->u.fault.status); default: - DEBUG(0, ("Unknown packet type %u received from %s!\n", - (unsigned int)pkt->ptype, - rpccli_pipe_txt(talloc_tos(), cli))); + DEBUG(0, (__location__ "Unknown packet type %u received " + "from %s!\n", + (unsigned int)pkt->ptype, + rpccli_pipe_txt(talloc_tos(), cli))); return NT_STATUS_INVALID_INFO_CLASS; } if (pkt->ptype != expected_pkt_type) { - DEBUG(3, ("cli_pipe_validate_current_pdu: Connection to %s " - "got an unexpected RPC packet type - %u, not %u\n", - rpccli_pipe_txt(talloc_tos(), cli), - pkt->ptype, - expected_pkt_type)); + DEBUG(3, (__location__ ": Connection to %s got an unexpected " + "RPC packet type - %u, not %u\n", + rpccli_pipe_txt(talloc_tos(), cli), + pkt->ptype, expected_pkt_type)); return NT_STATUS_INVALID_INFO_CLASS; } @@ -507,10 +493,9 @@ static NTSTATUS cli_pipe_validate_current_pdu(TALLOC_CTX *mem_ctx, if ((pkt->ptype == DCERPC_PKT_BIND_ACK) && !(pkt->pfc_flags & DCERPC_PFC_FLAG_LAST)) { - DEBUG(5,("cli_pipe_validate_current_pdu: bug in server (AS/U?), " - "setting fragment first/last ON.\n")); - pkt->pfc_flags |= DCERPC_PFC_FLAG_FIRST | - DCERPC_PFC_FLAG_LAST; + 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; } return NT_STATUS_OK; @@ -621,7 +606,7 @@ static void cli_api_pipe_write_done(struct tevent_req *subreq) return; } - state->rdata = TALLOC_ARRAY(state, uint8_t, RPC_HEADER_LEN); + state->rdata = talloc_array(state, uint8_t, RPC_HEADER_LEN); if (tevent_req_nomem(state->rdata, req)) { return; } @@ -717,6 +702,7 @@ struct rpc_api_pipe_state { 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 event_context *ev, @@ -751,6 +737,16 @@ static struct tevent_req *rpc_api_pipe_send(TALLOC_CTX *mem_ctx, 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; @@ -771,6 +767,23 @@ static struct tevent_req *rpc_api_pipe_send(TALLOC_CTX *mem_ctx, 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( @@ -839,6 +852,23 @@ static void rpc_api_pipe_got_pdu(struct tevent_req *subreq) return; } + status = dcerpc_pull_ncacn_packet(state->pkt, + &state->incoming_frag, + state->pkt, + !state->endianess); + if (!NT_STATUS_IS_OK(status)) { + tevent_req_nterror(req, status); + return; + } + + if (state->incoming_frag.length != state->pkt->frag_length) { + DEBUG(5, ("Incorrect pdu length %u, expected %u\n", + (unsigned int)state->incoming_frag.length, + (unsigned int)state->pkt->frag_length)); + tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER); + return; + } + status = cli_pipe_validate_current_pdu(state, state->cli, state->pkt, &state->incoming_frag, @@ -951,36 +981,25 @@ static NTSTATUS rpc_api_pipe_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, static NTSTATUS create_spnego_auth_bind_req(TALLOC_CTX *mem_ctx, struct pipe_auth_data *auth, - DATA_BLOB *auth_info) + DATA_BLOB *auth_token) { + struct spnego_context *spnego_ctx; DATA_BLOB in_token = data_blob_null; - DATA_BLOB auth_token = data_blob_null; NTSTATUS status; - /* Negotiate the initial auth token */ - status = spnego_get_client_auth_token(mem_ctx, - auth->a_u.spnego_state, - &in_token, &auth_token); - if (!NT_STATUS_IS_OK(status)) { - return status; - } + spnego_ctx = talloc_get_type_abort(auth->auth_ctx, + struct spnego_context); - status = dcerpc_push_dcerpc_auth(mem_ctx, - auth->auth_type, - auth->auth_level, - 0, /* auth_pad_length */ - 1, /* auth_context_id */ - &auth_token, - auth_info); + /* Negotiate the initial auth token */ + status = spnego_get_client_auth_token(mem_ctx, spnego_ctx, + &in_token, auth_token); if (!NT_STATUS_IS_OK(status)) { - data_blob_free(&auth_token); return status; } DEBUG(5, ("Created GSS Authentication Token:\n")); - dump_data(5, auth_token.data, auth_token.length); + dump_data(5, auth_token->data, auth_token->length); - data_blob_free(&auth_token); return NT_STATUS_OK; } @@ -990,85 +1009,25 @@ static NTSTATUS create_spnego_auth_bind_req(TALLOC_CTX *mem_ctx, static NTSTATUS create_gssapi_auth_bind_req(TALLOC_CTX *mem_ctx, struct pipe_auth_data *auth, - DATA_BLOB *auth_info) + DATA_BLOB *auth_token) { + struct gse_context *gse_ctx; DATA_BLOB in_token = data_blob_null; - DATA_BLOB auth_token = data_blob_null; NTSTATUS status; + gse_ctx = talloc_get_type_abort(auth->auth_ctx, + struct gse_context); + /* Negotiate the initial auth token */ - status = gse_get_client_auth_token(mem_ctx, - auth->a_u.gssapi_state, + status = gse_get_client_auth_token(mem_ctx, gse_ctx, &in_token, - &auth_token); - if (!NT_STATUS_IS_OK(status)) { - return status; - } - - status = dcerpc_push_dcerpc_auth(mem_ctx, - auth->auth_type, - auth->auth_level, - 0, /* auth_pad_length */ - 1, /* auth_context_id */ - &auth_token, - auth_info); + auth_token); if (!NT_STATUS_IS_OK(status)) { - data_blob_free(&auth_token); return status; } DEBUG(5, ("Created GSS Authentication Token:\n")); - dump_data(5, auth_token.data, auth_token.length); - - data_blob_free(&auth_token); - return NT_STATUS_OK; -} - -/******************************************************************* - Creates SPNEGO NTLMSSP auth bind. - ********************************************************************/ - -static NTSTATUS create_spnego_ntlmssp_auth_rpc_bind_req(struct rpc_pipe_client *cli, - enum dcerpc_AuthLevel auth_level, - DATA_BLOB *auth_info) -{ - NTSTATUS status; - DATA_BLOB null_blob = data_blob_null; - DATA_BLOB request = data_blob_null; - DATA_BLOB spnego_msg = data_blob_null; - const char *OIDs_ntlm[] = {OID_NTLMSSP, NULL}; - - DEBUG(5, ("create_spnego_ntlmssp_auth_rpc_bind_req: Processing NTLMSSP Negotiate\n")); - status = auth_ntlmssp_update(cli->auth->a_u.auth_ntlmssp_state, - null_blob, - &request); - - if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { - data_blob_free(&request); - return status; - } - - /* Wrap this in SPNEGO. */ - spnego_msg = spnego_gen_negTokenInit(talloc_tos(), OIDs_ntlm, &request, NULL); - - data_blob_free(&request); - - status = dcerpc_push_dcerpc_auth(cli, - DCERPC_AUTH_TYPE_SPNEGO, - auth_level, - 0, /* auth_pad_length */ - 1, /* auth_context_id */ - &spnego_msg, - auth_info); - - if (!NT_STATUS_IS_OK(status)) { - data_blob_free(&spnego_msg); - return status; - } - - DEBUG(5, ("create_spnego_ntlmssp_auth_rpc_bind_req: NTLMSSP Negotiate:\n")); - dump_data(5, spnego_msg.data, spnego_msg.length); - data_blob_free(&spnego_msg); + dump_data(5, auth_token->data, auth_token->length); return NT_STATUS_OK; } @@ -1078,37 +1037,25 @@ static NTSTATUS create_spnego_ntlmssp_auth_rpc_bind_req(struct rpc_pipe_client * ********************************************************************/ static NTSTATUS create_ntlmssp_auth_rpc_bind_req(struct rpc_pipe_client *cli, - enum dcerpc_AuthLevel auth_level, - DATA_BLOB *auth_info) + DATA_BLOB *auth_token) { - NTSTATUS status; + struct auth_ntlmssp_state *ntlmssp_ctx; DATA_BLOB null_blob = data_blob_null; - DATA_BLOB request = data_blob_null; + NTSTATUS status; + + ntlmssp_ctx = talloc_get_type_abort(cli->auth->auth_ctx, + struct auth_ntlmssp_state); DEBUG(5, ("create_ntlmssp_auth_rpc_bind_req: Processing NTLMSSP Negotiate\n")); - status = auth_ntlmssp_update(cli->auth->a_u.auth_ntlmssp_state, - null_blob, - &request); + status = auth_ntlmssp_update(ntlmssp_ctx, null_blob, auth_token); if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { - data_blob_free(&request); - return status; - } - - status = dcerpc_push_dcerpc_auth(cli, - DCERPC_AUTH_TYPE_NTLMSSP, - auth_level, - 0, /* auth_pad_length */ - 1, /* auth_context_id */ - &request, - auth_info); - if (!NT_STATUS_IS_OK(status)) { - data_blob_free(&request); + data_blob_free(auth_token); return status; } DEBUG(5, ("create_ntlmssp_auth_rpc_bind_req: NTLMSSP Negotiate:\n")); - dump_data(5, request.data, request.length); + dump_data(5, auth_token->data, auth_token->length); return NT_STATUS_OK; } @@ -1118,12 +1065,10 @@ static NTSTATUS create_ntlmssp_auth_rpc_bind_req(struct rpc_pipe_client *cli, ********************************************************************/ static NTSTATUS create_schannel_auth_rpc_bind_req(struct rpc_pipe_client *cli, - enum dcerpc_AuthLevel auth_level, - DATA_BLOB *auth_info) + DATA_BLOB *auth_token) { NTSTATUS status; struct NL_AUTH_MESSAGE r; - DATA_BLOB schannel_blob; /* Use lp_workgroup() if domain not specified */ @@ -1144,18 +1089,7 @@ static NTSTATUS create_schannel_auth_rpc_bind_req(struct rpc_pipe_client *cli, r.oem_netbios_domain.a = cli->auth->domain; r.oem_netbios_computer.a = global_myname(); - status = dcerpc_push_schannel_bind(cli, &r, &schannel_blob); - if (!NT_STATUS_IS_OK(status)) { - return status; - } - - status = dcerpc_push_dcerpc_auth(cli, - DCERPC_AUTH_TYPE_SCHANNEL, - auth_level, - 0, /* auth_pad_length */ - 1, /* auth_context_id */ - &schannel_blob, - auth_info); + status = dcerpc_push_schannel_bind(cli, &r, auth_token); if (!NT_STATUS_IS_OK(status)) { return status; } @@ -1224,55 +1158,45 @@ static NTSTATUS create_rpc_bind_req(TALLOC_CTX *mem_ctx, 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_SCHANNEL: - ret = create_schannel_auth_rpc_bind_req(cli, - auth->auth_level, - &auth_info); + ret = create_schannel_auth_rpc_bind_req(cli, &auth_token); if (!NT_STATUS_IS_OK(ret)) { return ret; } break; case DCERPC_AUTH_TYPE_NTLMSSP: - ret = create_ntlmssp_auth_rpc_bind_req(cli, - auth->auth_level, - &auth_info); + ret = create_ntlmssp_auth_rpc_bind_req(cli, &auth_token); if (!NT_STATUS_IS_OK(ret)) { return ret; } break; case DCERPC_AUTH_TYPE_SPNEGO: - switch (auth->spnego_type) { - case PIPE_AUTH_TYPE_SPNEGO_NTLMSSP: - ret = create_spnego_ntlmssp_auth_rpc_bind_req(cli, - auth->auth_level, - &auth_info); - break; - - case PIPE_AUTH_TYPE_SPNEGO_KRB5: - ret = create_spnego_auth_bind_req(cli, auth, - &auth_info); - break; - default: - return NT_STATUS_INTERNAL_ERROR; - } + ret = create_spnego_auth_bind_req(cli, auth, &auth_token); if (!NT_STATUS_IS_OK(ret)) { return ret; } break; case DCERPC_AUTH_TYPE_KRB5: - ret = create_gssapi_auth_bind_req(mem_ctx, auth, &auth_info); + ret = create_gssapi_auth_bind_req(mem_ctx, auth, &auth_token); if (!NT_STATUS_IS_OK(ret)) { return ret; } break; + case DCERPC_AUTH_TYPE_NCALRPC_AS_SYSTEM: + auth_token = data_blob_talloc(mem_ctx, + "NCALRPC_AUTH_TOKEN", + 18); + break; + case DCERPC_AUTH_TYPE_NONE: break; @@ -1281,6 +1205,20 @@ static NTSTATUS create_rpc_bind_req(TALLOC_CTX *mem_ctx, return NT_STATUS_INVALID_INFO_CLASS; } + if (auth_token.length != 0) { + ret = dcerpc_push_dcerpc_auth(cli, + auth->auth_type, + auth->auth_level, + 0, /* auth_pad_length */ + 1, /* 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, @@ -1291,90 +1229,6 @@ static NTSTATUS create_rpc_bind_req(TALLOC_CTX *mem_ctx, return ret; } -/******************************************************************* - Calculate how much data we're going to send in this packet, also - work out any sign/seal padding length. - ********************************************************************/ - -static NTSTATUS calculate_data_len_tosend(struct rpc_pipe_client *cli, - uint32_t data_left, - uint32_t *data_to_send, - uint16_t *p_frag_len, - uint16_t *p_auth_len, - uint32_t *p_ss_padding) -{ - uint32_t data_space, data_len; - size_t max_len; - - switch (cli->auth->auth_level) { - case DCERPC_AUTH_LEVEL_NONE: - case DCERPC_AUTH_LEVEL_CONNECT: - case DCERPC_AUTH_LEVEL_PACKET: - data_space = cli->max_xmit_frag - DCERPC_REQUEST_LENGTH; - data_len = MIN(data_space, data_left); - *p_ss_padding = 0; - *p_auth_len = 0; - *p_frag_len = DCERPC_REQUEST_LENGTH + data_len; - *data_to_send = data_len; - return NT_STATUS_OK; - - case DCERPC_AUTH_LEVEL_INTEGRITY: - case DCERPC_AUTH_LEVEL_PRIVACY: - max_len = cli->max_xmit_frag - - DCERPC_REQUEST_LENGTH - - DCERPC_AUTH_TRAILER_LENGTH; - - /* Treat the same for all authenticated rpc requests. */ - switch(cli->auth->auth_type) { - case DCERPC_AUTH_TYPE_SPNEGO: - switch (cli->auth->spnego_type) { - case PIPE_AUTH_TYPE_SPNEGO_NTLMSSP: - *p_auth_len = NTLMSSP_SIG_SIZE; - break; - case PIPE_AUTH_TYPE_SPNEGO_KRB5: - *p_auth_len = 0; /* TODO */ - break; - default: - return NT_STATUS_INVALID_PARAMETER; - } - case DCERPC_AUTH_TYPE_NTLMSSP: - *p_auth_len = NTLMSSP_SIG_SIZE; - break; - case DCERPC_AUTH_TYPE_SCHANNEL: - *p_auth_len = NL_AUTH_SIGNATURE_SIZE; - break; - case DCERPC_AUTH_TYPE_KRB5: - *p_auth_len = gse_get_signature_length( - cli->auth->a_u.gssapi_state, - (cli->auth->auth_level == - DCERPC_AUTH_LEVEL_PRIVACY), - max_len); - break; - default: - return NT_STATUS_INVALID_PARAMETER; - } - - data_space = max_len - *p_auth_len; - - data_len = MIN(data_space, data_left); - *p_ss_padding = 0; - if (data_len % CLIENT_NDR_PADDING_SIZE) { - *p_ss_padding = CLIENT_NDR_PADDING_SIZE - (data_len % CLIENT_NDR_PADDING_SIZE); - } - *p_frag_len = DCERPC_REQUEST_LENGTH - + data_len + *p_ss_padding - + DCERPC_AUTH_TRAILER_LENGTH - + *p_auth_len; - *data_to_send = data_len; - return NT_STATUS_OK; - - default: - break; - } - - return NT_STATUS_INVALID_PARAMETER; -} - /******************************************************************* External interface. Does an rpc request on a pipe. Incoming data is NDR encoded in in_data. @@ -1466,21 +1320,23 @@ struct tevent_req *rpc_api_pipe_req_send(TALLOC_CTX *mem_ctx, static NTSTATUS prepare_next_frag(struct rpc_api_pipe_req_state *state, bool *is_last_frag) { - uint32_t data_sent_thistime; - uint16_t auth_len; - uint16_t frag_len; + size_t data_sent_thistime; + size_t auth_len; + size_t frag_len; uint8_t flags = 0; - uint32_t pad_len; - uint32_t data_left; + size_t pad_len; + size_t data_left; NTSTATUS status; union dcerpc_payload u; data_left = state->req_data->length - state->req_data_sent; - status = calculate_data_len_tosend(state->cli, data_left, - &data_sent_thistime, - &frag_len, &auth_len, - &pad_len); + status = dcerpc_guess_sizes(state->cli->auth, + DCERPC_REQUEST_LENGTH, data_left, + state->cli->max_xmit_frag, + CLIENT_NDR_PADDING_SIZE, + &data_sent_thistime, + &frag_len, &auth_len, &pad_len); if (!NT_STATUS_IS_OK(status)) { return status; } @@ -1631,53 +1487,6 @@ NTSTATUS rpc_api_pipe_req_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, return NT_STATUS_OK; } -#if 0 -/**************************************************************************** - Set the handle state. -****************************************************************************/ - -static bool rpc_pipe_set_hnd_state(struct rpc_pipe_client *cli, - const char *pipe_name, uint16 device_state) -{ - bool state_set = False; - char param[2]; - uint16 setup[2]; /* only need 2 uint16 setup parameters */ - char *rparam = NULL; - char *rdata = NULL; - uint32 rparam_len, rdata_len; - - if (pipe_name == NULL) - return False; - - DEBUG(5,("Set Handle state Pipe[%x]: %s - device state:%x\n", - cli->fnum, pipe_name, device_state)); - - /* create parameters: device state */ - SSVAL(param, 0, device_state); - - /* create setup parameters. */ - setup[0] = 0x0001; - setup[1] = cli->fnum; /* pipe file handle. got this from an SMBOpenX. */ - - /* send the data on \PIPE\ */ - if (cli_api_pipe(cli->cli, "\\PIPE\\", - setup, 2, 0, /* setup, length, max */ - param, 2, 0, /* param, length, max */ - NULL, 0, 1024, /* data, length, max */ - &rparam, &rparam_len, /* return param, length */ - &rdata, &rdata_len)) /* return data, length */ - { - DEBUG(5, ("Set Handle state: return OK\n")); - state_set = True; - } - - SAFE_FREE(rparam); - SAFE_FREE(rdata); - - return state_set; -} -#endif - /**************************************************************************** Check the rpc bind acknowledge response. ****************************************************************************/ @@ -1808,18 +1617,11 @@ struct rpc_pipe_bind_state { struct event_context *ev; struct rpc_pipe_client *cli; DATA_BLOB rpc_out; + bool auth3; uint32_t rpc_call_id; }; static void rpc_pipe_bind_step_one_done(struct tevent_req *subreq); -static NTSTATUS rpc_finish_auth3_bind_send(struct tevent_req *req, - struct rpc_pipe_bind_state *state, - DATA_BLOB *credentials); -static void rpc_bind_auth3_write_done(struct tevent_req *subreq); -static NTSTATUS rpc_finish_spnego_ntlmssp_bind_send(struct tevent_req *req, - struct rpc_pipe_bind_state *state, - DATA_BLOB *credentials); -static void rpc_bind_ntlmssp_api_done(struct tevent_req *subreq); static NTSTATUS rpc_bind_next_send(struct tevent_req *req, struct rpc_pipe_bind_state *state, DATA_BLOB *credentials); @@ -1841,16 +1643,14 @@ struct tevent_req *rpc_pipe_bind_send(TALLOC_CTX *mem_ctx, return NULL; } - DEBUG(5,("Bind RPC Pipe: %s auth_type %u(%u), auth_level %u\n", + DEBUG(5,("Bind RPC Pipe: %s auth_type %u, auth_level %u\n", rpccli_pipe_txt(talloc_tos(), cli), (unsigned int)auth->auth_type, - (unsigned int)auth->spnego_type, (unsigned int)auth->auth_level )); state->ev = ev; state->cli = cli; state->rpc_call_id = get_rpc_call_id(); - state->rpc_out = data_blob_null; cli->auth = talloc_move(cli, &auth); @@ -1889,13 +1689,15 @@ static void rpc_pipe_bind_step_one_done(struct tevent_req *subreq) struct rpc_pipe_bind_state *state = tevent_req_data( req, struct rpc_pipe_bind_state); struct pipe_auth_data *pauth = state->cli->auth; - DATA_BLOB reply_pdu; - struct ncacn_packet *pkt; + struct auth_ntlmssp_state *ntlmssp_ctx; + struct spnego_context *spnego_ctx; + struct gse_context *gse_ctx; + struct ncacn_packet *pkt = NULL; struct dcerpc_auth auth; DATA_BLOB auth_token = data_blob_null; NTSTATUS status; - status = rpc_api_pipe_recv(subreq, talloc_tos(), &pkt, &reply_pdu); + status = rpc_api_pipe_recv(subreq, talloc_tos(), &pkt, NULL); TALLOC_FREE(subreq); if (!NT_STATUS_IS_OK(status)) { DEBUG(3, ("rpc_pipe_bind: %s bind request returned %s\n", @@ -1905,6 +1707,11 @@ static void rpc_pipe_bind_step_one_done(struct tevent_req *subreq) return; } + if (state->auth3) { + tevent_req_done(req); + return; + } + if (!check_bind_response(&pkt->u.bind_ack, &state->cli->transfer_syntax)) { DEBUG(2, ("rpc_pipe_bind: check_bind_response failed.\n")); tevent_req_nterror(req, NT_STATUS_BUFFER_TOO_SMALL); @@ -1914,9 +1721,10 @@ static void rpc_pipe_bind_step_one_done(struct tevent_req *subreq) state->cli->max_xmit_frag = pkt->u.bind_ack.max_xmit_frag; state->cli->max_recv_frag = pkt->u.bind_ack.max_recv_frag; - switch(state->cli->auth->auth_type) { + switch(pauth->auth_type) { case DCERPC_AUTH_TYPE_NONE: + case DCERPC_AUTH_TYPE_NCALRPC_AS_SYSTEM: case DCERPC_AUTH_TYPE_SCHANNEL: /* Bind complete. */ tevent_req_done(req); @@ -1952,64 +1760,66 @@ static void rpc_pipe_bind_step_one_done(struct tevent_req *subreq) * For authenticated binds we may need to do 3 or 4 leg binds. */ - switch(state->cli->auth->auth_type) { + switch(pauth->auth_type) { case DCERPC_AUTH_TYPE_NONE: + case DCERPC_AUTH_TYPE_NCALRPC_AS_SYSTEM: case DCERPC_AUTH_TYPE_SCHANNEL: /* Bind complete. */ tevent_req_done(req); return; case DCERPC_AUTH_TYPE_NTLMSSP: - /* Need to send AUTH3 packet - no reply. */ - status = rpc_finish_auth3_bind_send(req, state, - &auth.credentials); + ntlmssp_ctx = talloc_get_type_abort(pauth->auth_ctx, + struct auth_ntlmssp_state); + status = auth_ntlmssp_update(ntlmssp_ctx, + auth.credentials, &auth_token); + if (NT_STATUS_EQUAL(status, + NT_STATUS_MORE_PROCESSING_REQUIRED)) { + status = rpc_bind_next_send(req, state, + &auth_token); + } else if (NT_STATUS_IS_OK(status)) { + status = rpc_bind_finish_send(req, state, + &auth_token); + } break; case DCERPC_AUTH_TYPE_SPNEGO: - switch (pauth->spnego_type) { - case PIPE_AUTH_TYPE_SPNEGO_NTLMSSP: - /* Need to send alter context request and reply. */ - status = rpc_finish_spnego_ntlmssp_bind_send(req, - state, &auth.credentials); - break; - - case PIPE_AUTH_TYPE_SPNEGO_KRB5: - status = spnego_get_client_auth_token(state, - pauth->a_u.spnego_state, + spnego_ctx = talloc_get_type_abort(pauth->auth_ctx, + struct spnego_context); + status = spnego_get_client_auth_token(state, + spnego_ctx, &auth.credentials, &auth_token); - if (!NT_STATUS_IS_OK(status)) { - break; - } - if (auth_token.length == 0) { - /* Bind complete. */ - tevent_req_done(req); - return; - } - if (spnego_require_more_processing(pauth->a_u.spnego_state)) { - status = rpc_bind_next_send(req, state, - &auth_token); - } else { - status = rpc_bind_finish_send(req, state, - &auth_token); - } + if (!NT_STATUS_IS_OK(status)) { break; - default: - status = NT_STATUS_INTERNAL_ERROR; + } + if (auth_token.length == 0) { + /* Bind complete. */ + tevent_req_done(req); + return; + } + if (spnego_require_more_processing(spnego_ctx)) { + status = rpc_bind_next_send(req, state, + &auth_token); + } else { + status = rpc_bind_finish_send(req, state, + &auth_token); } break; case DCERPC_AUTH_TYPE_KRB5: + gse_ctx = talloc_get_type_abort(pauth->auth_ctx, + struct gse_context); status = gse_get_client_auth_token(state, - pauth->a_u.gssapi_state, + gse_ctx, &auth.credentials, &auth_token); if (!NT_STATUS_IS_OK(status)) { break; } - if (gse_require_more_processing(pauth->a_u.gssapi_state)) { + if (gse_require_more_processing(gse_ctx)) { status = rpc_bind_next_send(req, state, &auth_token); } else { status = rpc_bind_finish_send(req, state, &auth_token); @@ -2026,325 +1836,407 @@ static void rpc_pipe_bind_step_one_done(struct tevent_req *subreq) return; err_out: - DEBUG(0,("cli_finish_bind_auth: unknown auth type %u(%u)\n", - (unsigned int)state->cli->auth->auth_type, - (unsigned int)state->cli->auth->spnego_type)); + DEBUG(0,("cli_finish_bind_auth: unknown auth type %u\n", + (unsigned int)state->cli->auth->auth_type)); tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR); } -static NTSTATUS rpc_finish_auth3_bind_send(struct tevent_req *req, - struct rpc_pipe_bind_state *state, - DATA_BLOB *credentials) +static NTSTATUS rpc_bind_next_send(struct tevent_req *req, + struct rpc_pipe_bind_state *state, + DATA_BLOB *auth_token) { struct pipe_auth_data *auth = state->cli->auth; - DATA_BLOB client_reply = data_blob_null; struct tevent_req *subreq; NTSTATUS status; - /* TODO - check auth_type/auth_level match. */ - - status = auth_ntlmssp_update(auth->a_u.auth_ntlmssp_state, - *credentials, &client_reply); + /* Now prepare the alter context pdu. */ + data_blob_free(&state->rpc_out); + status = create_rpc_alter_context(state, + auth->auth_type, + auth->auth_level, + state->rpc_call_id, + &state->cli->abstract_syntax, + &state->cli->transfer_syntax, + auth_token, + &state->rpc_out); 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; } + subreq = rpc_api_pipe_send(state, state->ev, state->cli, + &state->rpc_out, DCERPC_PKT_ALTER_RESP); + if (subreq == NULL) { + return NT_STATUS_NO_MEMORY; + } + tevent_req_set_callback(subreq, rpc_pipe_bind_step_one_done, req); + return NT_STATUS_OK; +} + +static NTSTATUS rpc_bind_finish_send(struct tevent_req *req, + struct rpc_pipe_bind_state *state, + DATA_BLOB *auth_token) +{ + struct pipe_auth_data *auth = state->cli->auth; + struct tevent_req *subreq; + NTSTATUS status; + + state->auth3 = true; + + /* Now prepare the auth3 context pdu. */ data_blob_free(&state->rpc_out); status = create_rpc_bind_auth3(state, state->cli, state->rpc_call_id, auth->auth_type, auth->auth_level, - &client_reply, + auth_token, &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->transport, - state->rpc_out.data, state->rpc_out.length); + subreq = rpc_api_pipe_send(state, state->ev, state->cli, + &state->rpc_out, DCERPC_PKT_AUTH3); if (subreq == NULL) { return NT_STATUS_NO_MEMORY; } - tevent_req_set_callback(subreq, rpc_bind_auth3_write_done, req); + tevent_req_set_callback(subreq, rpc_pipe_bind_step_one_done, req); return NT_STATUS_OK; } -static void rpc_bind_auth3_write_done(struct tevent_req *subreq) +NTSTATUS rpc_pipe_bind_recv(struct tevent_req *req) { - 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); + return tevent_req_simple_recv_ntstatus(req); } -static NTSTATUS rpc_finish_spnego_ntlmssp_bind_send(struct tevent_req *req, - struct rpc_pipe_bind_state *state, - DATA_BLOB *credentials) +NTSTATUS rpc_pipe_bind(struct rpc_pipe_client *cli, + struct pipe_auth_data *auth) { - struct pipe_auth_data *auth = state->cli->auth; - DATA_BLOB server_ntlm_response = data_blob_null; - DATA_BLOB client_reply = data_blob_null; - DATA_BLOB tmp_blob = data_blob_null; - struct tevent_req *subreq; - NTSTATUS status; + TALLOC_CTX *frame = talloc_stackframe(); + struct event_context *ev; + struct tevent_req *req; + NTSTATUS status = NT_STATUS_OK; - /* - * The server might give us back two challenges - tmp_blob is for the - * second. - */ - if (!spnego_parse_challenge(state, *credentials, - &server_ntlm_response, - &tmp_blob)) { - data_blob_free(&server_ntlm_response); - data_blob_free(&tmp_blob); - return NT_STATUS_INVALID_PARAMETER; + ev = event_context_init(frame); + if (ev == NULL) { + status = NT_STATUS_NO_MEMORY; + goto fail; } - /* We're finished with the server spnego response and the tmp_blob. */ - data_blob_free(&tmp_blob); - - status = auth_ntlmssp_update(auth->a_u.auth_ntlmssp_state, - server_ntlm_response, &client_reply); - - /* Finished with the server_ntlm response */ - data_blob_free(&server_ntlm_response); + req = rpc_pipe_bind_send(frame, ev, cli, auth); + if (req == NULL) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } - 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; + if (!tevent_req_poll(req, ev)) { + status = map_nt_error_from_unix(errno); + goto fail; } - /* SPNEGO wrap the client reply. */ - tmp_blob = spnego_gen_auth(state, client_reply); - data_blob_free(&client_reply); - client_reply = tmp_blob; - tmp_blob = data_blob_null; + status = rpc_pipe_bind_recv(req); + fail: + TALLOC_FREE(frame); + return status; +} - /* Now prepare the alter context pdu. */ - data_blob_free(&state->rpc_out); +#define RPCCLI_DEFAULT_TIMEOUT 10000 /* 10 seconds. */ - status = create_rpc_alter_context(state, - auth->auth_type, - auth->auth_level, - state->rpc_call_id, - &state->cli->abstract_syntax, - &state->cli->transfer_syntax, - &client_reply, - &state->rpc_out); - data_blob_free(&client_reply); +unsigned int rpccli_set_timeout(struct rpc_pipe_client *rpc_cli, + unsigned int timeout) +{ + unsigned int old; - if (!NT_STATUS_IS_OK(status)) { - return status; + if (rpc_cli->transport == NULL) { + return RPCCLI_DEFAULT_TIMEOUT; } - subreq = rpc_api_pipe_send(state, state->ev, state->cli, - &state->rpc_out, DCERPC_PKT_ALTER_RESP); - if (subreq == NULL) { - return NT_STATUS_NO_MEMORY; + if (rpc_cli->transport->set_timeout == NULL) { + return RPCCLI_DEFAULT_TIMEOUT; } - tevent_req_set_callback(subreq, rpc_bind_ntlmssp_api_done, req); - return NT_STATUS_OK; + + old = rpc_cli->transport->set_timeout(rpc_cli->transport->priv, timeout); + if (old == 0) { + return RPCCLI_DEFAULT_TIMEOUT; + } + + return old; } -static void rpc_bind_ntlmssp_api_done(struct tevent_req *subreq) +bool rpccli_is_connected(struct rpc_pipe_client *rpc_cli) { - struct tevent_req *req = tevent_req_callback_data( - subreq, struct tevent_req); - struct rpc_pipe_bind_state *state = tevent_req_data( - req, struct rpc_pipe_bind_state); - DATA_BLOB tmp_blob = data_blob_null; - struct ncacn_packet *pkt; - struct dcerpc_auth auth; - NTSTATUS status; - - status = rpc_api_pipe_recv(subreq, talloc_tos(), &pkt, NULL); - TALLOC_FREE(subreq); - if (!NT_STATUS_IS_OK(status)) { - tevent_req_nterror(req, status); - return; + if (rpc_cli == NULL) { + return false; } - status = dcerpc_pull_dcerpc_auth(pkt, - &pkt->u.alter_resp.auth_info, - &auth, false); - if (!NT_STATUS_IS_OK(status)) { - tevent_req_nterror(req, status); - return; + if (rpc_cli->transport == NULL) { + return false; } - /* Check we got a valid auth response. */ - if (!spnego_parse_auth_response(talloc_tos(), auth.credentials, - NT_STATUS_OK, - OID_NTLMSSP, &tmp_blob)) { - data_blob_free(&tmp_blob); - tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER); - return; - } + return rpc_cli->transport->is_connected(rpc_cli->transport->priv); +} - data_blob_free(&tmp_blob); +struct rpccli_bh_state { + struct rpc_pipe_client *rpc_cli; +}; - DEBUG(5,("rpc_finish_spnego_ntlmssp_bind: alter context request to " - "%s.\n", rpccli_pipe_txt(talloc_tos(), state->cli))); - tevent_req_done(req); +static bool rpccli_bh_is_connected(struct dcerpc_binding_handle *h) +{ + struct rpccli_bh_state *hs = dcerpc_binding_handle_data(h, + struct rpccli_bh_state); + + return rpccli_is_connected(hs->rpc_cli); } -static NTSTATUS rpc_bind_next_send(struct tevent_req *req, - struct rpc_pipe_bind_state *state, - DATA_BLOB *auth_token) +static uint32_t rpccli_bh_set_timeout(struct dcerpc_binding_handle *h, + uint32_t timeout) { - struct pipe_auth_data *auth = state->cli->auth; + struct rpccli_bh_state *hs = dcerpc_binding_handle_data(h, + struct rpccli_bh_state); + + return rpccli_set_timeout(hs->rpc_cli, timeout); +} + +struct rpccli_bh_raw_call_state { + DATA_BLOB in_data; + DATA_BLOB out_data; + uint32_t out_flags; +}; + +static void rpccli_bh_raw_call_done(struct tevent_req *subreq); + +static struct tevent_req *rpccli_bh_raw_call_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct dcerpc_binding_handle *h, + const struct GUID *object, + uint32_t opnum, + uint32_t in_flags, + const uint8_t *in_data, + size_t in_length) +{ + struct rpccli_bh_state *hs = dcerpc_binding_handle_data(h, + struct rpccli_bh_state); + struct tevent_req *req; + struct rpccli_bh_raw_call_state *state; + bool ok; struct tevent_req *subreq; - NTSTATUS status; - /* Now prepare the alter context pdu. */ - data_blob_free(&state->rpc_out); + req = tevent_req_create(mem_ctx, &state, + struct rpccli_bh_raw_call_state); + if (req == NULL) { + return NULL; + } + state->in_data.data = discard_const_p(uint8_t, in_data); + state->in_data.length = in_length; - status = create_rpc_alter_context(state, - auth->auth_type, - auth->auth_level, - state->rpc_call_id, - &state->cli->abstract_syntax, - &state->cli->transfer_syntax, - auth_token, - &state->rpc_out); - if (!NT_STATUS_IS_OK(status)) { - return status; + ok = rpccli_bh_is_connected(h); + if (!ok) { + tevent_req_nterror(req, NT_STATUS_INVALID_CONNECTION); + return tevent_req_post(req, ev); } - subreq = rpc_api_pipe_send(state, state->ev, state->cli, - &state->rpc_out, DCERPC_PKT_ALTER_RESP); - if (subreq == NULL) { - return NT_STATUS_NO_MEMORY; + subreq = rpc_api_pipe_req_send(state, ev, hs->rpc_cli, + opnum, &state->in_data); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); } - tevent_req_set_callback(subreq, rpc_pipe_bind_step_one_done, req); - return NT_STATUS_OK; + tevent_req_set_callback(subreq, rpccli_bh_raw_call_done, req); + + return req; } -static NTSTATUS rpc_bind_finish_send(struct tevent_req *req, - struct rpc_pipe_bind_state *state, - DATA_BLOB *auth_token) +static void rpccli_bh_raw_call_done(struct tevent_req *subreq) { - struct pipe_auth_data *auth = state->cli->auth; - struct tevent_req *subreq; + struct tevent_req *req = + tevent_req_callback_data(subreq, + struct tevent_req); + struct rpccli_bh_raw_call_state *state = + tevent_req_data(req, + struct rpccli_bh_raw_call_state); NTSTATUS status; - /* Now prepare the auth3 context pdu. */ - data_blob_free(&state->rpc_out); + state->out_flags = 0; - status = create_rpc_bind_auth3(state, state->cli, - state->rpc_call_id, - auth->auth_type, - auth->auth_level, - auth_token, - &state->rpc_out); + /* TODO: support bigendian responses */ + + status = rpc_api_pipe_req_recv(subreq, state, &state->out_data); + TALLOC_FREE(subreq); if (!NT_STATUS_IS_OK(status)) { - return status; + tevent_req_nterror(req, status); + return; } - subreq = rpc_write_send(state, state->ev, state->cli->transport, - state->rpc_out.data, state->rpc_out.length); - if (subreq == NULL) { - return NT_STATUS_NO_MEMORY; - } - tevent_req_set_callback(subreq, rpc_bind_auth3_write_done, req); - return NT_STATUS_OK; + tevent_req_done(req); } -NTSTATUS rpc_pipe_bind_recv(struct tevent_req *req) +static NTSTATUS rpccli_bh_raw_call_recv(struct tevent_req *req, + TALLOC_CTX *mem_ctx, + uint8_t **out_data, + size_t *out_length, + uint32_t *out_flags) { - return tevent_req_simple_recv_ntstatus(req); + struct rpccli_bh_raw_call_state *state = + tevent_req_data(req, + struct rpccli_bh_raw_call_state); + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + tevent_req_received(req); + return status; + } + + *out_data = talloc_move(mem_ctx, &state->out_data.data); + *out_length = state->out_data.length; + *out_flags = state->out_flags; + tevent_req_received(req); + return NT_STATUS_OK; } -NTSTATUS rpc_pipe_bind(struct rpc_pipe_client *cli, - struct pipe_auth_data *auth) +struct rpccli_bh_disconnect_state { + uint8_t _dummy; +}; + +static struct tevent_req *rpccli_bh_disconnect_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct dcerpc_binding_handle *h) { - TALLOC_CTX *frame = talloc_stackframe(); - struct event_context *ev; + struct rpccli_bh_state *hs = dcerpc_binding_handle_data(h, + struct rpccli_bh_state); struct tevent_req *req; - NTSTATUS status = NT_STATUS_OK; + struct rpccli_bh_disconnect_state *state; + bool ok; - ev = event_context_init(frame); - if (ev == NULL) { - status = NT_STATUS_NO_MEMORY; - goto fail; + req = tevent_req_create(mem_ctx, &state, + struct rpccli_bh_disconnect_state); + if (req == NULL) { + return NULL; } - req = rpc_pipe_bind_send(frame, ev, cli, auth); - if (req == NULL) { - status = NT_STATUS_NO_MEMORY; - goto fail; + ok = rpccli_bh_is_connected(h); + if (!ok) { + tevent_req_nterror(req, NT_STATUS_INVALID_CONNECTION); + return tevent_req_post(req, ev); } - if (!tevent_req_poll(req, ev)) { - status = map_nt_error_from_unix(errno); - goto fail; + /* + * TODO: do a real async disconnect ... + * + * For now the caller needs to free rpc_cli + */ + hs->rpc_cli = NULL; + + tevent_req_done(req); + return tevent_req_post(req, ev); +} + +static NTSTATUS rpccli_bh_disconnect_recv(struct tevent_req *req) +{ + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + tevent_req_received(req); + return status; } - status = rpc_pipe_bind_recv(req); - fail: - TALLOC_FREE(frame); - return status; + tevent_req_received(req); + return NT_STATUS_OK; } -#define RPCCLI_DEFAULT_TIMEOUT 10000 /* 10 seconds. */ +static bool rpccli_bh_ref_alloc(struct dcerpc_binding_handle *h) +{ + return true; +} -unsigned int rpccli_set_timeout(struct rpc_pipe_client *rpc_cli, - unsigned int timeout) +static void rpccli_bh_do_ndr_print(struct dcerpc_binding_handle *h, + int ndr_flags, + const void *_struct_ptr, + const struct ndr_interface_call *call) { - unsigned int old; + void *struct_ptr = discard_const(_struct_ptr); - if (rpc_cli->transport == NULL) { - return RPCCLI_DEFAULT_TIMEOUT; + if (DEBUGLEVEL < 10) { + return; } - if (rpc_cli->transport->set_timeout == NULL) { - return RPCCLI_DEFAULT_TIMEOUT; + if (ndr_flags & NDR_IN) { + ndr_print_function_debug(call->ndr_print, + call->name, + ndr_flags, + struct_ptr); } - - old = rpc_cli->transport->set_timeout(rpc_cli->transport->priv, timeout); - if (old == 0) { - return RPCCLI_DEFAULT_TIMEOUT; + if (ndr_flags & NDR_OUT) { + ndr_print_function_debug(call->ndr_print, + call->name, + ndr_flags, + struct_ptr); } - - return old; } -bool rpccli_is_connected(struct rpc_pipe_client *rpc_cli) +static const struct dcerpc_binding_handle_ops rpccli_bh_ops = { + .name = "rpccli", + .is_connected = rpccli_bh_is_connected, + .set_timeout = rpccli_bh_set_timeout, + .raw_call_send = rpccli_bh_raw_call_send, + .raw_call_recv = rpccli_bh_raw_call_recv, + .disconnect_send = rpccli_bh_disconnect_send, + .disconnect_recv = rpccli_bh_disconnect_recv, + + .ref_alloc = rpccli_bh_ref_alloc, + .do_ndr_print = rpccli_bh_do_ndr_print, +}; + +/* initialise a rpc_pipe_client binding handle */ +struct dcerpc_binding_handle *rpccli_bh_create(struct rpc_pipe_client *c) { - if (rpc_cli == NULL) { - return false; - } + struct dcerpc_binding_handle *h; + struct rpccli_bh_state *hs; - if (rpc_cli->transport == NULL) { - return false; + h = dcerpc_binding_handle_create(c, + &rpccli_bh_ops, + NULL, + NULL, /* TODO */ + &hs, + struct rpccli_bh_state, + __location__); + if (h == NULL) { + return NULL; } + hs->rpc_cli = c; - return rpc_cli->transport->is_connected(rpc_cli->transport->priv); + return h; } bool rpccli_get_pwd_hash(struct rpc_pipe_client *rpc_cli, uint8_t nt_hash[16]) { + struct auth_ntlmssp_state *a = NULL; struct cli_state *cli; - if ((rpc_cli->auth->auth_type == DCERPC_AUTH_TYPE_NTLMSSP) - || ((rpc_cli->auth->auth_type == DCERPC_AUTH_TYPE_SPNEGO - && rpc_cli->auth->spnego_type == PIPE_AUTH_TYPE_SPNEGO_NTLMSSP))) { - memcpy(nt_hash, auth_ntlmssp_get_nt_hash(rpc_cli->auth->a_u.auth_ntlmssp_state), 16); + if (rpc_cli->auth->auth_type == DCERPC_AUTH_TYPE_NTLMSSP) { + a = talloc_get_type_abort(rpc_cli->auth->auth_ctx, + struct auth_ntlmssp_state); + } else if (rpc_cli->auth->auth_type == DCERPC_AUTH_TYPE_SPNEGO) { + struct spnego_context *spnego_ctx; + enum spnego_mech auth_type; + void *auth_ctx; + NTSTATUS status; + + spnego_ctx = talloc_get_type_abort(rpc_cli->auth->auth_ctx, + struct spnego_context); + status = spnego_get_negotiated_mech(spnego_ctx, + &auth_type, &auth_ctx); + if (!NT_STATUS_IS_OK(status)) { + return false; + } + + if (auth_type == SPNEGO_NTLMSSP) { + a = talloc_get_type_abort(auth_ctx, + struct auth_ntlmssp_state); + } + } + + if (a) { + memcpy(nt_hash, auth_ntlmssp_get_nt_hash(a), 16); return true; } @@ -2356,6 +2248,30 @@ bool rpccli_get_pwd_hash(struct rpc_pipe_client *rpc_cli, uint8_t nt_hash[16]) return true; } +NTSTATUS rpccli_ncalrpc_bind_data(TALLOC_CTX *mem_ctx, + struct pipe_auth_data **presult) +{ + struct pipe_auth_data *result; + + result = talloc(mem_ctx, struct pipe_auth_data); + if (result == NULL) { + return NT_STATUS_NO_MEMORY; + } + + result->auth_type = DCERPC_AUTH_TYPE_NCALRPC_AS_SYSTEM; + result->auth_level = DCERPC_AUTH_LEVEL_CONNECT; + + result->user_name = talloc_strdup(result, ""); + result->domain = talloc_strdup(result, ""); + if ((result->user_name == NULL) || (result->domain == NULL)) { + TALLOC_FREE(result); + return NT_STATUS_NO_MEMORY; + } + + *presult = result; + return NT_STATUS_OK; +} + NTSTATUS rpccli_anon_bind_data(TALLOC_CTX *mem_ctx, struct pipe_auth_data **presult) { @@ -2367,7 +2283,6 @@ NTSTATUS rpccli_anon_bind_data(TALLOC_CTX *mem_ctx, } result->auth_type = DCERPC_AUTH_TYPE_NONE; - result->spnego_type = PIPE_AUTH_TYPE_SPNEGO_NONE; result->auth_level = DCERPC_AUTH_LEVEL_NONE; result->user_name = talloc_strdup(result, ""); @@ -2383,19 +2298,19 @@ NTSTATUS rpccli_anon_bind_data(TALLOC_CTX *mem_ctx, static int cli_auth_ntlmssp_data_destructor(struct pipe_auth_data *auth) { - TALLOC_FREE(auth->a_u.auth_ntlmssp_state); + TALLOC_FREE(auth->auth_ctx); return 0; } static NTSTATUS rpccli_ntlmssp_bind_data(TALLOC_CTX *mem_ctx, enum dcerpc_AuthType auth_type, - enum pipe_auth_type_spnego spnego_type, enum dcerpc_AuthLevel auth_level, const char *domain, const char *username, const char *password, struct pipe_auth_data **presult) { + struct auth_ntlmssp_state *ntlmssp_ctx; struct pipe_auth_data *result; NTSTATUS status; @@ -2405,7 +2320,6 @@ static NTSTATUS rpccli_ntlmssp_bind_data(TALLOC_CTX *mem_ctx, } result->auth_type = auth_type; - result->spnego_type = spnego_type; result->auth_level = auth_level; result->user_name = talloc_strdup(result, username); @@ -2419,27 +2333,24 @@ static NTSTATUS rpccli_ntlmssp_bind_data(TALLOC_CTX *mem_ctx, global_myname(), lp_workgroup(), lp_client_ntlmv2_auth(), - &result->a_u.auth_ntlmssp_state); + &ntlmssp_ctx); if (!NT_STATUS_IS_OK(status)) { goto fail; } talloc_set_destructor(result, cli_auth_ntlmssp_data_destructor); - status = auth_ntlmssp_set_username(result->a_u.auth_ntlmssp_state, - username); + status = auth_ntlmssp_set_username(ntlmssp_ctx, username); if (!NT_STATUS_IS_OK(status)) { goto fail; } - status = auth_ntlmssp_set_domain(result->a_u.auth_ntlmssp_state, - domain); + status = auth_ntlmssp_set_domain(ntlmssp_ctx, domain); if (!NT_STATUS_IS_OK(status)) { goto fail; } - status = auth_ntlmssp_set_password(result->a_u.auth_ntlmssp_state, - password); + status = auth_ntlmssp_set_password(ntlmssp_ctx, password); if (!NT_STATUS_IS_OK(status)) { goto fail; } @@ -2447,19 +2358,17 @@ static NTSTATUS rpccli_ntlmssp_bind_data(TALLOC_CTX *mem_ctx, /* * Turn off sign+seal to allow selected auth level to turn it back on. */ - auth_ntlmssp_and_flags(result->a_u.auth_ntlmssp_state, - ~(NTLMSSP_NEGOTIATE_SIGN | - NTLMSSP_NEGOTIATE_SEAL)); + auth_ntlmssp_and_flags(ntlmssp_ctx, ~(NTLMSSP_NEGOTIATE_SIGN | + NTLMSSP_NEGOTIATE_SEAL)); if (auth_level == DCERPC_AUTH_LEVEL_INTEGRITY) { - auth_ntlmssp_or_flags(result->a_u.auth_ntlmssp_state, - NTLMSSP_NEGOTIATE_SIGN); + auth_ntlmssp_or_flags(ntlmssp_ctx, NTLMSSP_NEGOTIATE_SIGN); } else if (auth_level == DCERPC_AUTH_LEVEL_PRIVACY) { - auth_ntlmssp_or_flags(result->a_u.auth_ntlmssp_state, - NTLMSSP_NEGOTIATE_SEAL | - NTLMSSP_NEGOTIATE_SIGN); + auth_ntlmssp_or_flags(ntlmssp_ctx, NTLMSSP_NEGOTIATE_SEAL | + NTLMSSP_NEGOTIATE_SIGN); } + result->auth_ctx = ntlmssp_ctx; *presult = result; return NT_STATUS_OK; @@ -2473,6 +2382,7 @@ NTSTATUS rpccli_schannel_bind_data(TALLOC_CTX *mem_ctx, const char *domain, struct netlogon_creds_CredentialState *creds, struct pipe_auth_data **presult) { + struct schannel_state *schannel_auth; struct pipe_auth_data *result; result = talloc(mem_ctx, struct pipe_auth_data); @@ -2481,7 +2391,6 @@ NTSTATUS rpccli_schannel_bind_data(TALLOC_CTX *mem_ctx, const char *domain, } result->auth_type = DCERPC_AUTH_TYPE_SCHANNEL; - result->spnego_type = PIPE_AUTH_TYPE_SPNEGO_NONE; result->auth_level = auth_level; result->user_name = talloc_strdup(result, ""); @@ -2490,16 +2399,17 @@ NTSTATUS rpccli_schannel_bind_data(TALLOC_CTX *mem_ctx, const char *domain, goto fail; } - result->a_u.schannel_auth = talloc(result, struct schannel_state); - if (result->a_u.schannel_auth == NULL) { + schannel_auth = talloc(result, struct schannel_state); + if (schannel_auth == NULL) { goto fail; } - result->a_u.schannel_auth->state = SCHANNEL_STATE_START; - result->a_u.schannel_auth->seq_num = 0; - result->a_u.schannel_auth->initiator = true; - result->a_u.schannel_auth->creds = creds; + schannel_auth->state = SCHANNEL_STATE_START; + schannel_auth->seq_num = 0; + schannel_auth->initiator = true; + schannel_auth->creds = netlogon_creds_copy(result, creds); + result->auth_ctx = schannel_auth; *presult = result; return NT_STATUS_OK; @@ -2521,16 +2431,13 @@ static NTSTATUS rpc_pipe_open_tcp_port(TALLOC_CTX *mem_ctx, const char *host, NTSTATUS status; int fd; - result = TALLOC_ZERO_P(mem_ctx, struct rpc_pipe_client); + result = talloc_zero(mem_ctx, struct rpc_pipe_client); if (result == NULL) { return NT_STATUS_NO_MEMORY; } result->abstract_syntax = *abstract_syntax; result->transfer_syntax = ndr_transfer_syntax; - result->dispatch = cli_do_rpc_ndr; - result->dispatch_send = cli_do_rpc_ndr_send; - result->dispatch_recv = cli_do_rpc_ndr_recv; result->desthost = talloc_strdup(result, host); result->srv_name_slash = talloc_asprintf_strupper_m( @@ -2548,7 +2455,7 @@ static NTSTATUS rpc_pipe_open_tcp_port(TALLOC_CTX *mem_ctx, const char *host, goto fail; } - status = open_socket_out(&addr, port, 60, &fd); + status = open_socket_out(&addr, port, 60*1000, &fd); if (!NT_STATUS_IS_OK(status)) { goto fail; } @@ -2562,6 +2469,12 @@ static NTSTATUS rpc_pipe_open_tcp_port(TALLOC_CTX *mem_ctx, const char *host, result->transport->transport = NCACN_IP_TCP; + result->binding_handle = rpccli_bh_create(result); + if (result->binding_handle == NULL) { + TALLOC_FREE(result); + return NT_STATUS_NO_MEMORY; + } + *presult = result; return NT_STATUS_OK; @@ -2581,6 +2494,7 @@ static NTSTATUS rpc_pipe_get_tcp_port(const char *host, { NTSTATUS status; struct rpc_pipe_client *epm_pipe = NULL; + struct dcerpc_binding_handle *epm_handle = NULL; struct pipe_auth_data *auth = NULL; struct dcerpc_binding *map_binding = NULL; struct dcerpc_binding *res_binding = NULL; @@ -2591,12 +2505,19 @@ static NTSTATUS rpc_pipe_get_tcp_port(const char *host, uint32_t max_towers = 1; struct epm_twr_p_t towers; TALLOC_CTX *tmp_ctx = talloc_stackframe(); + uint32_t result = 0; if (pport == NULL) { status = NT_STATUS_INVALID_PARAMETER; goto done; } + if (ndr_syntax_id_equal(abstract_syntax, + &ndr_table_epmapper.syntax_id)) { + *pport = 135; + return NT_STATUS_OK; + } + /* open the connection to the endpoint mapper */ status = rpc_pipe_open_tcp_port(tmp_ctx, host, 135, &ndr_table_epmapper.syntax_id, @@ -2605,6 +2526,7 @@ static NTSTATUS rpc_pipe_get_tcp_port(const char *host, if (!NT_STATUS_IS_OK(status)) { goto done; } + epm_handle = epm_pipe->binding_handle; status = rpccli_anon_bind_data(tmp_ctx, &auth); if (!NT_STATUS_IS_OK(status)) { @@ -2618,7 +2540,7 @@ static NTSTATUS rpc_pipe_get_tcp_port(const char *host, /* create tower for asking the epmapper */ - map_binding = TALLOC_ZERO_P(tmp_ctx, struct dcerpc_binding); + map_binding = talloc_zero(tmp_ctx, struct dcerpc_binding); if (map_binding == NULL) { status = NT_STATUS_NO_MEMORY; goto done; @@ -2629,7 +2551,7 @@ static NTSTATUS rpc_pipe_get_tcp_port(const char *host, map_binding->host = host; /* needed? */ map_binding->endpoint = "0"; /* correct? needed? */ - map_tower = TALLOC_ZERO_P(tmp_ctx, struct epm_twr_t); + map_tower = talloc_zero(tmp_ctx, struct epm_twr_t); if (map_tower == NULL) { status = NT_STATUS_NO_MEMORY; goto done; @@ -2643,14 +2565,14 @@ static NTSTATUS rpc_pipe_get_tcp_port(const char *host, /* allocate further parameters for the epm_Map call */ - res_towers = TALLOC_ARRAY(tmp_ctx, struct epm_twr_t, max_towers); + res_towers = talloc_array(tmp_ctx, struct epm_twr_t, max_towers); if (res_towers == NULL) { status = NT_STATUS_NO_MEMORY; goto done; } towers.twr = res_towers; - entry_handle = TALLOC_ZERO_P(tmp_ctx, struct policy_handle); + entry_handle = talloc_zero(tmp_ctx, struct policy_handle); if (entry_handle == NULL) { status = NT_STATUS_NO_MEMORY; goto done; @@ -2658,20 +2580,26 @@ static NTSTATUS rpc_pipe_get_tcp_port(const char *host, /* ask the endpoint mapper for the port */ - status = rpccli_epm_Map(epm_pipe, + status = dcerpc_epm_Map(epm_handle, tmp_ctx, - CONST_DISCARD(struct GUID *, + discard_const_p(struct GUID, &(abstract_syntax->uuid)), map_tower, entry_handle, max_towers, &num_towers, - &towers); + &towers, + &result); if (!NT_STATUS_IS_OK(status)) { goto done; } + if (result != EPMAPPER_STATUS_OK) { + status = NT_STATUS_UNSUCCESSFUL; + goto done; + } + if (num_towers != 1) { status = NT_STATUS_UNSUCCESSFUL; goto done; @@ -2739,9 +2667,6 @@ NTSTATUS rpc_pipe_open_ncalrpc(TALLOC_CTX *mem_ctx, const char *socket_path, result->abstract_syntax = *abstract_syntax; result->transfer_syntax = ndr_transfer_syntax; - result->dispatch = cli_do_rpc_ndr; - result->dispatch_send = cli_do_rpc_ndr_send; - result->dispatch_recv = cli_do_rpc_ndr_recv; result->desthost = get_myname(result); result->srv_name_slash = talloc_asprintf_strupper_m( @@ -2762,7 +2687,7 @@ NTSTATUS rpc_pipe_open_ncalrpc(TALLOC_CTX *mem_ctx, const char *socket_path, ZERO_STRUCT(addr); addr.sun_family = AF_UNIX; - strncpy(addr.sun_path, socket_path, sizeof(addr.sun_path)); + strlcpy(addr.sun_path, socket_path, sizeof(addr.sun_path)); if (sys_connect(fd, (struct sockaddr *)(void *)&addr) == -1) { DEBUG(0, ("connect(%s) failed: %s\n", socket_path, @@ -2779,6 +2704,12 @@ NTSTATUS rpc_pipe_open_ncalrpc(TALLOC_CTX *mem_ctx, const char *socket_path, result->transport->transport = NCALRPC; + result->binding_handle = rpccli_bh_create(result); + if (result->binding_handle == NULL) { + TALLOC_FREE(result); + return NT_STATUS_NO_MEMORY; + } + *presult = result; return NT_STATUS_OK; @@ -2825,16 +2756,13 @@ static NTSTATUS rpc_pipe_open_np(struct cli_state *cli, return NT_STATUS_INVALID_HANDLE; } - result = TALLOC_ZERO_P(NULL, struct rpc_pipe_client); + result = talloc_zero(NULL, struct rpc_pipe_client); if (result == NULL) { return NT_STATUS_NO_MEMORY; } result->abstract_syntax = *abstract_syntax; result->transfer_syntax = ndr_transfer_syntax; - result->dispatch = cli_do_rpc_ndr; - result->dispatch_send = cli_do_rpc_ndr_send; - result->dispatch_recv = cli_do_rpc_ndr_recv; result->desthost = talloc_strdup(result, cli->desthost); result->srv_name_slash = talloc_asprintf_strupper_m( result, "\\\\%s", result->desthost); @@ -2867,65 +2795,12 @@ static NTSTATUS rpc_pipe_open_np(struct cli_state *cli, DLIST_ADD(np_ref->cli->pipe_list, np_ref->pipe); talloc_set_destructor(np_ref, rpc_pipe_client_np_ref_destructor); - *presult = result; - return NT_STATUS_OK; -} - -NTSTATUS rpc_pipe_open_local(TALLOC_CTX *mem_ctx, - struct rpc_cli_smbd_conn *conn, - const struct ndr_syntax_id *syntax, - struct rpc_pipe_client **presult) -{ - struct rpc_pipe_client *result; - struct pipe_auth_data *auth; - NTSTATUS status; - - result = talloc(mem_ctx, struct rpc_pipe_client); - if (result == NULL) { - return NT_STATUS_NO_MEMORY; - } - result->abstract_syntax = *syntax; - result->transfer_syntax = ndr_transfer_syntax; - result->dispatch = cli_do_rpc_ndr; - result->dispatch_send = cli_do_rpc_ndr_send; - result->dispatch_recv = cli_do_rpc_ndr_recv; - result->max_xmit_frag = RPC_MAX_PDU_FRAG_LEN; - result->max_recv_frag = RPC_MAX_PDU_FRAG_LEN; - - result->desthost = talloc_strdup(result, global_myname()); - result->srv_name_slash = talloc_asprintf_strupper_m( - result, "\\\\%s", global_myname()); - if ((result->desthost == NULL) || (result->srv_name_slash == NULL)) { + result->binding_handle = rpccli_bh_create(result); + if (result->binding_handle == NULL) { TALLOC_FREE(result); return NT_STATUS_NO_MEMORY; } - status = rpc_transport_smbd_init(result, conn, syntax, - &result->transport); - if (!NT_STATUS_IS_OK(status)) { - DEBUG(1, ("rpc_transport_smbd_init failed: %s\n", - nt_errstr(status))); - TALLOC_FREE(result); - return status; - } - - status = rpccli_anon_bind_data(result, &auth); - if (!NT_STATUS_IS_OK(status)) { - DEBUG(1, ("rpccli_anon_bind_data failed: %s\n", - nt_errstr(status))); - TALLOC_FREE(result); - return status; - } - - status = rpc_pipe_bind(result, auth); - if (!NT_STATUS_IS_OK(status)) { - DEBUG(1, ("rpc_pipe_bind failed: %s\n", nt_errstr(status))); - TALLOC_FREE(result); - return status; - } - - result->transport->transport = NCACN_INTERNAL; - *presult = result; return NT_STATUS_OK; } @@ -3037,20 +2912,18 @@ NTSTATUS cli_rpc_pipe_open_noauth(struct cli_state *cli, Open a named pipe to an SMB server and bind using NTLMSSP or SPNEGO NTLMSSP ****************************************************************************/ -static NTSTATUS cli_rpc_pipe_open_ntlmssp_internal(struct cli_state *cli, - const struct ndr_syntax_id *interface, - enum dcerpc_transport_t transport, - bool use_spnego, - enum dcerpc_AuthLevel auth_level, - const char *domain, - const char *username, - const char *password, - struct rpc_pipe_client **presult) +NTSTATUS cli_rpc_pipe_open_ntlmssp(struct cli_state *cli, + const struct ndr_syntax_id *interface, + enum dcerpc_transport_t transport, + enum dcerpc_AuthLevel auth_level, + const char *domain, + const char *username, + const char *password, + struct rpc_pipe_client **presult) { struct rpc_pipe_client *result; - struct pipe_auth_data *auth; + struct pipe_auth_data *auth = NULL; enum dcerpc_AuthType auth_type = DCERPC_AUTH_TYPE_NTLMSSP; - enum pipe_auth_type_spnego spnego_type = PIPE_AUTH_TYPE_SPNEGO_NONE; NTSTATUS status; status = cli_rpc_pipe_open(cli, transport, interface, &result); @@ -3058,13 +2931,8 @@ static NTSTATUS cli_rpc_pipe_open_ntlmssp_internal(struct cli_state *cli, return status; } - if (use_spnego) { - auth_type = DCERPC_AUTH_TYPE_SPNEGO; - spnego_type = PIPE_AUTH_TYPE_SPNEGO_NTLMSSP; - } - status = rpccli_ntlmssp_bind_data(result, - auth_type, spnego_type, auth_level, + auth_type, auth_level, domain, username, password, &auth); if (!NT_STATUS_IS_OK(status)) { @@ -3094,137 +2962,6 @@ static NTSTATUS cli_rpc_pipe_open_ntlmssp_internal(struct cli_state *cli, return status; } -/**************************************************************************** - External interface. - Open a named pipe to an SMB server and bind using NTLMSSP (bind type 10) - ****************************************************************************/ - -NTSTATUS cli_rpc_pipe_open_ntlmssp(struct cli_state *cli, - const struct ndr_syntax_id *interface, - enum dcerpc_transport_t transport, - enum dcerpc_AuthLevel auth_level, - const char *domain, - const char *username, - const char *password, - struct rpc_pipe_client **presult) -{ - return cli_rpc_pipe_open_ntlmssp_internal(cli, - interface, - transport, - false, - auth_level, - domain, - username, - password, - presult); -} - -/**************************************************************************** - External interface. - Open a named pipe to an SMB server and bind using spnego NTLMSSP (bind type 9) - ****************************************************************************/ - -NTSTATUS cli_rpc_pipe_open_spnego_ntlmssp(struct cli_state *cli, - const struct ndr_syntax_id *interface, - enum dcerpc_transport_t transport, - enum dcerpc_AuthLevel auth_level, - const char *domain, - const char *username, - const char *password, - struct rpc_pipe_client **presult) -{ - return cli_rpc_pipe_open_ntlmssp_internal(cli, - interface, - transport, - true, - auth_level, - domain, - username, - password, - presult); -} - -/**************************************************************************** - Get a the schannel session key out of an already opened netlogon pipe. - ****************************************************************************/ -static NTSTATUS get_schannel_session_key_common(struct rpc_pipe_client *netlogon_pipe, - struct cli_state *cli, - const char *domain, - uint32 *pneg_flags) -{ - enum netr_SchannelType sec_chan_type = 0; - unsigned char machine_pwd[16]; - const char *machine_account; - NTSTATUS status; - - /* Get the machine account credentials from secrets.tdb. */ - if (!get_trust_pw_hash(domain, machine_pwd, &machine_account, - &sec_chan_type)) - { - DEBUG(0, ("get_schannel_session_key: could not fetch " - "trust account password for domain '%s'\n", - domain)); - return NT_STATUS_CANT_ACCESS_DOMAIN_INFO; - } - - status = rpccli_netlogon_setup_creds(netlogon_pipe, - cli->desthost, /* server name */ - domain, /* domain */ - global_myname(), /* client name */ - machine_account, /* machine account name */ - machine_pwd, - sec_chan_type, - pneg_flags); - - if (!NT_STATUS_IS_OK(status)) { - DEBUG(3, ("get_schannel_session_key_common: " - "rpccli_netlogon_setup_creds failed with result %s " - "to server %s, domain %s, machine account %s.\n", - nt_errstr(status), cli->desthost, domain, - machine_account )); - return status; - } - - if (((*pneg_flags) & NETLOGON_NEG_SCHANNEL) == 0) { - DEBUG(3, ("get_schannel_session_key: Server %s did not offer schannel\n", - cli->desthost)); - return NT_STATUS_INVALID_NETWORK_RESPONSE; - } - - return NT_STATUS_OK;; -} - -/**************************************************************************** - Open a netlogon pipe and get the schannel session key. - Now exposed to external callers. - ****************************************************************************/ - - -NTSTATUS get_schannel_session_key(struct cli_state *cli, - const char *domain, - uint32 *pneg_flags, - struct rpc_pipe_client **presult) -{ - struct rpc_pipe_client *netlogon_pipe = NULL; - NTSTATUS status; - - status = cli_rpc_pipe_open_noauth(cli, &ndr_table_netlogon.syntax_id, - &netlogon_pipe); - if (!NT_STATUS_IS_OK(status)) { - return status; - } - - status = get_schannel_session_key_common(netlogon_pipe, cli, domain, - pneg_flags); - if (!NT_STATUS_IS_OK(status)) { - TALLOC_FREE(netlogon_pipe); - return status; - } - - *presult = netlogon_pipe; - return NT_STATUS_OK; -} - /**************************************************************************** External interface. Open a named pipe to an SMB server and bind using schannel (bind type 68) @@ -3270,9 +3007,13 @@ NTSTATUS cli_rpc_pipe_open_schannel_with_key(struct cli_state *cli, /* * The credentials on a new netlogon pipe are the ones we are passed - * in - reference them in + * in - copy them over */ - result->dc = talloc_move(result, pdc); + result->dc = netlogon_creds_copy(result, *pdc); + if (result->dc == NULL) { + TALLOC_FREE(result); + return NT_STATUS_NO_MEMORY; + } DEBUG(10,("cli_rpc_pipe_open_schannel_with_key: opened pipe %s to machine %s " "for domain %s and bound using schannel.\n", @@ -3283,123 +3024,6 @@ NTSTATUS cli_rpc_pipe_open_schannel_with_key(struct cli_state *cli, return NT_STATUS_OK; } -/**************************************************************************** - Open a named pipe to an SMB server and bind using schannel (bind type 68). - Fetch the session key ourselves using a temporary netlogon pipe. This - version uses an ntlmssp auth bound netlogon pipe to get the key. - ****************************************************************************/ - -static NTSTATUS get_schannel_session_key_auth_ntlmssp(struct cli_state *cli, - const char *domain, - const char *username, - const char *password, - uint32 *pneg_flags, - struct rpc_pipe_client **presult) -{ - struct rpc_pipe_client *netlogon_pipe = NULL; - NTSTATUS status; - - status = cli_rpc_pipe_open_spnego_ntlmssp( - cli, &ndr_table_netlogon.syntax_id, NCACN_NP, - DCERPC_AUTH_LEVEL_PRIVACY, - domain, username, password, &netlogon_pipe); - if (!NT_STATUS_IS_OK(status)) { - return status; - } - - status = get_schannel_session_key_common(netlogon_pipe, cli, domain, - pneg_flags); - if (!NT_STATUS_IS_OK(status)) { - TALLOC_FREE(netlogon_pipe); - return status; - } - - *presult = netlogon_pipe; - return NT_STATUS_OK; -} - -/**************************************************************************** - Open a named pipe to an SMB server and bind using schannel (bind type 68). - Fetch the session key ourselves using a temporary netlogon pipe. This version - uses an ntlmssp bind to get the session key. - ****************************************************************************/ - -NTSTATUS cli_rpc_pipe_open_ntlmssp_auth_schannel(struct cli_state *cli, - const struct ndr_syntax_id *interface, - enum dcerpc_transport_t transport, - enum dcerpc_AuthLevel auth_level, - const char *domain, - const char *username, - const char *password, - struct rpc_pipe_client **presult) -{ - uint32_t neg_flags = NETLOGON_NEG_AUTH2_ADS_FLAGS; - struct rpc_pipe_client *netlogon_pipe = NULL; - struct rpc_pipe_client *result = NULL; - NTSTATUS status; - - status = get_schannel_session_key_auth_ntlmssp( - cli, domain, username, password, &neg_flags, &netlogon_pipe); - if (!NT_STATUS_IS_OK(status)) { - DEBUG(0,("cli_rpc_pipe_open_ntlmssp_auth_schannel: failed to get schannel session " - "key from server %s for domain %s.\n", - cli->desthost, domain )); - return status; - } - - status = cli_rpc_pipe_open_schannel_with_key( - cli, interface, transport, auth_level, domain, &netlogon_pipe->dc, - &result); - - /* Now we've bound using the session key we can close the netlog pipe. */ - TALLOC_FREE(netlogon_pipe); - - if (NT_STATUS_IS_OK(status)) { - *presult = result; - } - return status; -} - -/**************************************************************************** - Open a named pipe to an SMB server and bind using schannel (bind type 68). - Fetch the session key ourselves using a temporary netlogon pipe. - ****************************************************************************/ - -NTSTATUS cli_rpc_pipe_open_schannel(struct cli_state *cli, - const struct ndr_syntax_id *interface, - enum dcerpc_transport_t transport, - enum dcerpc_AuthLevel auth_level, - const char *domain, - struct rpc_pipe_client **presult) -{ - uint32_t neg_flags = NETLOGON_NEG_AUTH2_ADS_FLAGS; - struct rpc_pipe_client *netlogon_pipe = NULL; - struct rpc_pipe_client *result = NULL; - NTSTATUS status; - - status = get_schannel_session_key(cli, domain, &neg_flags, - &netlogon_pipe); - if (!NT_STATUS_IS_OK(status)) { - DEBUG(0,("cli_rpc_pipe_open_schannel: failed to get schannel session " - "key from server %s for domain %s.\n", - cli->desthost, domain )); - return status; - } - - status = cli_rpc_pipe_open_schannel_with_key( - cli, interface, transport, auth_level, domain, &netlogon_pipe->dc, - &result); - - /* Now we've bound using the session key we can close the netlog pipe. */ - TALLOC_FREE(netlogon_pipe); - - if (NT_STATUS_IS_OK(status)) { - *presult = result; - } - - return status; -} - /**************************************************************************** Open a named pipe to an SMB server and bind using krb5 (bind type 16). The idea is this can be called with service_princ, username and password all @@ -3417,6 +3041,7 @@ NTSTATUS cli_rpc_pipe_open_krb5(struct cli_state *cli, { struct rpc_pipe_client *result; struct pipe_auth_data *auth; + struct gse_context *gse_ctx; NTSTATUS status; status = cli_rpc_pipe_open(cli, transport, interface, &result); @@ -3448,15 +3073,17 @@ NTSTATUS cli_rpc_pipe_open_krb5(struct cli_state *cli, goto err_out; } - status = gse_init_client(auth, auth->auth_type, auth->auth_level, + status = gse_init_client(auth, + (auth_level == DCERPC_AUTH_LEVEL_INTEGRITY), + (auth_level == DCERPC_AUTH_LEVEL_PRIVACY), NULL, server, "cifs", username, password, - GSS_C_DCE_STYLE, &auth->a_u.gssapi_state); - + GSS_C_DCE_STYLE, &gse_ctx); if (!NT_STATUS_IS_OK(status)) { DEBUG(0, ("gse_init_client returned %s\n", nt_errstr(status))); goto err_out; } + auth->auth_ctx = gse_ctx; status = rpc_pipe_bind(result, auth); if (!NT_STATUS_IS_OK(status)) { @@ -3484,6 +3111,7 @@ NTSTATUS cli_rpc_pipe_open_spnego_krb5(struct cli_state *cli, { struct rpc_pipe_client *result; struct pipe_auth_data *auth; + struct spnego_context *spnego_ctx; NTSTATUS status; status = cli_rpc_pipe_open(cli, transport, interface, &result); @@ -3498,8 +3126,6 @@ NTSTATUS cli_rpc_pipe_open_spnego_krb5(struct cli_state *cli, } auth->auth_type = DCERPC_AUTH_TYPE_SPNEGO; auth->auth_level = auth_level; - /* compat */ - auth->spnego_type = PIPE_AUTH_TYPE_SPNEGO_KRB5; if (!username) { username = ""; @@ -3517,16 +3143,96 @@ NTSTATUS cli_rpc_pipe_open_spnego_krb5(struct cli_state *cli, goto err_out; } - status = spnego_gssapi_init_client(auth, auth->auth_level, + status = spnego_gssapi_init_client(auth, + (auth->auth_level == + DCERPC_AUTH_LEVEL_INTEGRITY), + (auth->auth_level == + DCERPC_AUTH_LEVEL_PRIVACY), + true, NULL, server, "cifs", username, password, - GSS_C_DCE_STYLE, - &auth->a_u.spnego_state); + &spnego_ctx); if (!NT_STATUS_IS_OK(status)) { DEBUG(0, ("spnego_init_client returned %s\n", nt_errstr(status))); goto err_out; } + auth->auth_ctx = spnego_ctx; + + status = rpc_pipe_bind(result, auth); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("cli_rpc_pipe_bind failed with error %s\n", + nt_errstr(status))); + goto err_out; + } + + *presult = result; + return NT_STATUS_OK; + +err_out: + TALLOC_FREE(result); + return status; +} + +NTSTATUS cli_rpc_pipe_open_spnego_ntlmssp(struct cli_state *cli, + const struct ndr_syntax_id *interface, + enum dcerpc_transport_t transport, + enum dcerpc_AuthLevel auth_level, + const char *domain, + const char *username, + const char *password, + struct rpc_pipe_client **presult) +{ + struct rpc_pipe_client *result; + struct pipe_auth_data *auth; + struct spnego_context *spnego_ctx; + NTSTATUS status; + + status = cli_rpc_pipe_open(cli, transport, interface, &result); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + auth = talloc(result, struct pipe_auth_data); + if (auth == NULL) { + status = NT_STATUS_NO_MEMORY; + goto err_out; + } + auth->auth_type = DCERPC_AUTH_TYPE_SPNEGO; + auth->auth_level = auth_level; + + if (!username) { + username = ""; + } + auth->user_name = talloc_strdup(auth, username); + if (!auth->user_name) { + status = NT_STATUS_NO_MEMORY; + goto err_out; + } + + if (!domain) { + domain = ""; + } + auth->domain = talloc_strdup(auth, domain); + if (!auth->domain) { + status = NT_STATUS_NO_MEMORY; + goto err_out; + } + + status = spnego_ntlmssp_init_client(auth, + (auth->auth_level == + DCERPC_AUTH_LEVEL_INTEGRITY), + (auth->auth_level == + DCERPC_AUTH_LEVEL_PRIVACY), + true, + domain, username, password, + &spnego_ctx); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("spnego_init_client returned %s\n", + nt_errstr(status))); + goto err_out; + } + auth->auth_ctx = spnego_ctx; status = rpc_pipe_bind(result, auth); if (!NT_STATUS_IS_OK(status)) { @@ -3547,49 +3253,68 @@ NTSTATUS cli_get_session_key(TALLOC_CTX *mem_ctx, struct rpc_pipe_client *cli, DATA_BLOB *session_key) { - struct pipe_auth_data *a = cli->auth; - DATA_BLOB sk; + struct pipe_auth_data *a; + struct schannel_state *schannel_auth; + struct auth_ntlmssp_state *ntlmssp_ctx; + struct spnego_context *spnego_ctx; + struct gse_context *gse_ctx; + DATA_BLOB sk = data_blob_null; + bool make_dup = false; if (!session_key || !cli) { return NT_STATUS_INVALID_PARAMETER; } - if (!cli->auth) { + a = cli->auth; + + if (a == NULL) { return NT_STATUS_INVALID_PARAMETER; } switch (cli->auth->auth_type) { case DCERPC_AUTH_TYPE_SCHANNEL: - sk = data_blob_const(a->a_u.schannel_auth->creds->session_key, - 16); + schannel_auth = talloc_get_type_abort(a->auth_ctx, + struct schannel_state); + sk = data_blob_const(schannel_auth->creds->session_key, 16); + make_dup = true; break; case DCERPC_AUTH_TYPE_SPNEGO: - switch (cli->auth->spnego_type) { - case PIPE_AUTH_TYPE_SPNEGO_NTLMSSP: - sk = auth_ntlmssp_get_session_key( - a->a_u.auth_ntlmssp_state); - break; - case PIPE_AUTH_TYPE_SPNEGO_KRB5: - sk = gse_get_session_key(a->a_u.gssapi_state); - break; - default: - return NT_STATUS_NO_USER_SESSION_KEY; - } + spnego_ctx = talloc_get_type_abort(a->auth_ctx, + struct spnego_context); + sk = spnego_get_session_key(mem_ctx, spnego_ctx); + make_dup = false; break; case DCERPC_AUTH_TYPE_NTLMSSP: - sk = auth_ntlmssp_get_session_key(a->a_u.auth_ntlmssp_state); + ntlmssp_ctx = talloc_get_type_abort(a->auth_ctx, + struct auth_ntlmssp_state); + sk = auth_ntlmssp_get_session_key(ntlmssp_ctx); + make_dup = true; break; case DCERPC_AUTH_TYPE_KRB5: - sk = gse_get_session_key(a->a_u.gssapi_state); + gse_ctx = talloc_get_type_abort(a->auth_ctx, + struct gse_context); + sk = gse_get_session_key(mem_ctx, gse_ctx); + make_dup = false; break; + case DCERPC_AUTH_TYPE_NCALRPC_AS_SYSTEM: case DCERPC_AUTH_TYPE_NONE: sk = data_blob_const(a->user_session_key.data, a->user_session_key.length); + make_dup = true; break; default: + break; + } + + if (!sk.data) { return NT_STATUS_NO_USER_SESSION_KEY; } - *session_key = data_blob_dup_talloc(mem_ctx, &sk); + if (make_dup) { + *session_key = data_blob_dup_talloc(mem_ctx, &sk); + } else { + *session_key = sk; + } + return NT_STATUS_OK; }