X-Git-Url: http://git.samba.org/?a=blobdiff_plain;f=source3%2Frpc_client%2Fcli_pipe.c;h=db283060148fedb3d0a90fc5aee4a2fa545050e7;hb=fb42b02c9f75804bc471c1f88fbda28865d9f01e;hp=a8f3dc1a3d97574e574ed4e449628d7a25796c0e;hpb=f0532fe0cd69aeb161088ca990d376f119102e61;p=obnox%2Fsamba%2Fsamba-obnox.git diff --git a/source3/rpc_client/cli_pipe.c b/source3/rpc_client/cli_pipe.c index a8f3dc1a3d9..db283060148 100644 --- a/source3/rpc_client/cli_pipe.c +++ b/source3/rpc_client/cli_pipe.c @@ -24,6 +24,7 @@ #include "librpc/gen_ndr/ndr_epmapper_c.h" #include "../librpc/gen_ndr/ndr_dssetup.h" #include "../libcli/auth/schannel.h" +#include "../libcli/auth/netlogon_creds_cli.h" #include "auth_generic.h" #include "librpc/gen_ndr/ndr_dcerpc.h" #include "librpc/gen_ndr/ndr_netlogon_c.h" @@ -1006,11 +1007,10 @@ static NTSTATUS create_generic_auth_rpc_bind_req(struct rpc_pipe_client *cli, DATA_BLOB null_blob = data_blob_null; NTSTATUS status; - gensec_security = talloc_get_type_abort(cli->auth->auth_ctx, - struct gensec_security); + gensec_security = cli->auth->auth_ctx; DEBUG(5, ("create_generic_auth_rpc_bind_req: generate first token\n")); - status = gensec_update(gensec_security, mem_ctx, NULL, null_blob, auth_token); + status = gensec_update(gensec_security, mem_ctx, null_blob, auth_token); if (!NT_STATUS_IS_OK(status) && !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) @@ -1018,11 +1018,18 @@ static NTSTATUS create_generic_auth_rpc_bind_req(struct rpc_pipe_client *cli, return status; } - if (client_hdr_signing != NULL) { - *client_hdr_signing = gensec_have_feature(gensec_security, - GENSEC_FEATURE_SIGN_PKT_HEADER); + if (client_hdr_signing == NULL) { + return status; + } + + if (cli->auth->auth_level < DCERPC_AUTH_LEVEL_INTEGRITY) { + *client_hdr_signing = false; + return status; } + *client_hdr_signing = gensec_have_feature(gensec_security, + GENSEC_FEATURE_SIGN_PKT_HEADER); + return status; } @@ -1096,10 +1103,10 @@ static NTSTATUS create_rpc_bind_req(TALLOC_CTX *mem_ctx, NTSTATUS ret = NT_STATUS_OK; switch (auth->auth_type) { - case DCERPC_AUTH_TYPE_SCHANNEL: - case DCERPC_AUTH_TYPE_NTLMSSP: - case DCERPC_AUTH_TYPE_KRB5: - case DCERPC_AUTH_TYPE_SPNEGO: + case DCERPC_AUTH_TYPE_NONE: + break; + + default: ret = create_generic_auth_rpc_bind_req(cli, mem_ctx, &auth_token, &auth->client_hdr_signing); @@ -1109,19 +1116,6 @@ static NTSTATUS create_rpc_bind_req(TALLOC_CTX *mem_ctx, 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; - - default: - /* "Can't" happen. */ - return NT_STATUS_INVALID_INFO_CLASS; } if (auth_token.length != 0) { @@ -1163,12 +1157,17 @@ struct rpc_api_pipe_req_state { uint32_t call_id; const DATA_BLOB *req_data; uint32_t req_data_sent; + DATA_BLOB req_trailer; + uint32_t req_trailer_sent; + bool verify_bitmask1; + bool verify_pcontext; DATA_BLOB rpc_out; DATA_BLOB reply_pdu; }; static void rpc_api_pipe_req_write_done(struct tevent_req *subreq); static void rpc_api_pipe_req_done(struct tevent_req *subreq); +static NTSTATUS prepare_verification_trailer(struct rpc_api_pipe_req_state *state); static NTSTATUS prepare_next_frag(struct rpc_api_pipe_req_state *state, bool *is_last_frag); @@ -1204,6 +1203,11 @@ static struct tevent_req *rpc_api_pipe_req_send(TALLOC_CTX *mem_ctx, goto post_status; } + status = prepare_verification_trailer(state); + if (!NT_STATUS_IS_OK(status)) { + goto post_status; + } + status = prepare_next_frag(state, &is_last_frag); if (!NT_STATUS_IS_OK(status)) { goto post_status; @@ -1238,25 +1242,164 @@ static struct tevent_req *rpc_api_pipe_req_send(TALLOC_CTX *mem_ctx, return NULL; } +static NTSTATUS prepare_verification_trailer(struct rpc_api_pipe_req_state *state) +{ + struct pipe_auth_data *a = state->cli->auth; + struct dcerpc_sec_verification_trailer *t; + struct dcerpc_sec_vt *c = NULL; + struct ndr_push *ndr = NULL; + enum ndr_err_code ndr_err; + size_t align = 0; + size_t pad = 0; + + if (a == NULL) { + return NT_STATUS_OK; + } + + if (a->auth_level < DCERPC_AUTH_LEVEL_INTEGRITY) { + return NT_STATUS_OK; + } + + t = talloc_zero(state, struct dcerpc_sec_verification_trailer); + if (t == NULL) { + return NT_STATUS_NO_MEMORY; + } + + if (!a->verified_bitmask1) { + t->commands = talloc_realloc(t, t->commands, + struct dcerpc_sec_vt, + t->count.count + 1); + if (t->commands == NULL) { + return NT_STATUS_NO_MEMORY; + } + c = &t->commands[t->count.count++]; + ZERO_STRUCTP(c); + + c->command = DCERPC_SEC_VT_COMMAND_BITMASK1; + if (a->client_hdr_signing) { + c->u.bitmask1 = DCERPC_SEC_VT_CLIENT_SUPPORTS_HEADER_SIGNING; + } + state->verify_bitmask1 = true; + } + + if (!state->cli->verified_pcontext) { + t->commands = talloc_realloc(t, t->commands, + struct dcerpc_sec_vt, + t->count.count + 1); + if (t->commands == NULL) { + return NT_STATUS_NO_MEMORY; + } + c = &t->commands[t->count.count++]; + ZERO_STRUCTP(c); + + c->command = DCERPC_SEC_VT_COMMAND_PCONTEXT; + c->u.pcontext.abstract_syntax = state->cli->abstract_syntax; + c->u.pcontext.transfer_syntax = state->cli->transfer_syntax; + + state->verify_pcontext = true; + } + + if (!a->hdr_signing) { + t->commands = talloc_realloc(t, t->commands, + struct dcerpc_sec_vt, + t->count.count + 1); + if (t->commands == NULL) { + return NT_STATUS_NO_MEMORY; + } + c = &t->commands[t->count.count++]; + ZERO_STRUCTP(c); + + c->command = DCERPC_SEC_VT_COMMAND_HEADER2; + c->u.header2.ptype = DCERPC_PKT_REQUEST; + c->u.header2.drep[0] = DCERPC_DREP_LE; + c->u.header2.drep[1] = 0; + c->u.header2.drep[2] = 0; + c->u.header2.drep[3] = 0; + c->u.header2.call_id = state->call_id; + c->u.header2.context_id = 0; + c->u.header2.opnum = state->op_num; + } + + if (t->count.count == 0) { + TALLOC_FREE(t); + return NT_STATUS_OK; + } + + c = &t->commands[t->count.count - 1]; + c->command |= DCERPC_SEC_VT_COMMAND_END; + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_DEBUG(dcerpc_sec_verification_trailer, t); + } + + ndr = ndr_push_init_ctx(state); + if (ndr == NULL) { + return NT_STATUS_NO_MEMORY; + } + + ndr_err = ndr_push_dcerpc_sec_verification_trailer(ndr, + NDR_SCALARS | NDR_BUFFERS, + t); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + return ndr_map_error2ntstatus(ndr_err); + } + state->req_trailer = ndr_push_blob(ndr); + + align = state->req_data->length & 0x3; + if (align > 0) { + pad = 4 - align; + } + if (pad > 0) { + bool ok; + uint8_t *p; + const uint8_t zeros[4] = { 0, }; + + ok = data_blob_append(ndr, &state->req_trailer, zeros, pad); + if (!ok) { + return NT_STATUS_NO_MEMORY; + } + + /* move the padding to the start */ + p = state->req_trailer.data; + memmove(p + pad, p, state->req_trailer.length - pad); + memset(p, 0, pad); + } + + return NT_STATUS_OK; +} + static NTSTATUS prepare_next_frag(struct rpc_api_pipe_req_state *state, bool *is_last_frag) { - size_t data_sent_thistime; size_t auth_len; size_t frag_len; uint8_t flags = 0; size_t pad_len; size_t data_left; + size_t data_thistime; + size_t trailer_left; + size_t trailer_thistime = 0; + size_t total_left; + size_t total_thistime; NTSTATUS status; + bool ok; union dcerpc_payload u; data_left = state->req_data->length - state->req_data_sent; + trailer_left = state->req_trailer.length - state->req_trailer_sent; + total_left = data_left + trailer_left; + if ((total_left < data_left) || (total_left < trailer_left)) { + /* + * overflow + */ + return NT_STATUS_INVALID_PARAMETER_MIX; + } status = dcerpc_guess_sizes(state->cli->auth, - DCERPC_REQUEST_LENGTH, data_left, + DCERPC_REQUEST_LENGTH, total_left, state->cli->max_xmit_frag, CLIENT_NDR_PADDING_SIZE, - &data_sent_thistime, + &total_thistime, &frag_len, &auth_len, &pad_len); if (!NT_STATUS_IS_OK(status)) { return status; @@ -1266,15 +1409,20 @@ static NTSTATUS prepare_next_frag(struct rpc_api_pipe_req_state *state, flags = DCERPC_PFC_FLAG_FIRST; } - if (data_sent_thistime == data_left) { + if (total_thistime == total_left) { flags |= DCERPC_PFC_FLAG_LAST; } + data_thistime = MIN(total_thistime, data_left); + if (data_thistime < total_thistime) { + trailer_thistime = total_thistime - data_thistime; + } + data_blob_free(&state->rpc_out); ZERO_STRUCT(u.request); - u.request.alloc_hint = data_left; + u.request.alloc_hint = total_left; u.request.context_id = 0; u.request.opnum = state->op_num; @@ -1294,11 +1442,26 @@ static NTSTATUS prepare_next_frag(struct rpc_api_pipe_req_state *state, * at this stage */ dcerpc_set_frag_length(&state->rpc_out, frag_len); - /* Copy in the data. */ - if (!data_blob_append(NULL, &state->rpc_out, + if (data_thistime > 0) { + /* Copy in the data. */ + ok = data_blob_append(NULL, &state->rpc_out, state->req_data->data + state->req_data_sent, - data_sent_thistime)) { - return NT_STATUS_NO_MEMORY; + data_thistime); + if (!ok) { + return NT_STATUS_NO_MEMORY; + } + state->req_data_sent += data_thistime; + } + + if (trailer_thistime > 0) { + /* Copy in the verification trailer. */ + ok = data_blob_append(NULL, &state->rpc_out, + state->req_trailer.data + state->req_trailer_sent, + trailer_thistime); + if (!ok) { + return NT_STATUS_NO_MEMORY; + } + state->req_trailer_sent += trailer_thistime; } switch (state->cli->auth->auth_level) { @@ -1318,7 +1481,6 @@ static NTSTATUS prepare_next_frag(struct rpc_api_pipe_req_state *state, return NT_STATUS_INVALID_PARAMETER; } - state->req_data_sent += data_sent_thistime; *is_last_frag = ((flags & DCERPC_PFC_FLAG_LAST) != 0); return status; @@ -1382,6 +1544,20 @@ static void rpc_api_pipe_req_done(struct tevent_req *subreq) tevent_req_nterror(req, status); return; } + + if (state->cli->auth == NULL) { + tevent_req_done(req); + return; + } + + if (state->verify_bitmask1) { + state->cli->auth->verified_bitmask1 = true; + } + + if (state->verify_pcontext) { + state->cli->verified_pcontext = true; + } + tevent_req_done(req); } @@ -1437,7 +1613,7 @@ static bool check_bind_response(const struct dcerpc_bind_ack *r, if (r->num_results != 0x1 || ctx.result != 0) { DEBUG(2,("bind_rpc_pipe: bind denied results: %d reason: %x\n", - r->num_results, ctx.reason)); + r->num_results, ctx.reason.value)); } DEBUG(5,("check_bind_response: accepted!\n")); @@ -1646,15 +1822,11 @@ static void rpc_pipe_bind_step_one_done(struct tevent_req *subreq) switch(pauth->auth_type) { case DCERPC_AUTH_TYPE_NONE: - case DCERPC_AUTH_TYPE_NCALRPC_AS_SYSTEM: /* Bind complete. */ tevent_req_done(req); return; - case DCERPC_AUTH_TYPE_SCHANNEL: - case DCERPC_AUTH_TYPE_NTLMSSP: - case DCERPC_AUTH_TYPE_SPNEGO: - case DCERPC_AUTH_TYPE_KRB5: + default: /* Paranoid lenght checks */ if (pkt->frag_length < DCERPC_AUTH_TRAILER_LENGTH + pkt->auth_length) { @@ -1673,9 +1845,6 @@ static void rpc_pipe_bind_step_one_done(struct tevent_req *subreq) return; } break; - - default: - goto err_out; } /* @@ -1685,17 +1854,12 @@ static void rpc_pipe_bind_step_one_done(struct tevent_req *subreq) switch(pauth->auth_type) { case DCERPC_AUTH_TYPE_NONE: - case DCERPC_AUTH_TYPE_NCALRPC_AS_SYSTEM: /* Bind complete. */ tevent_req_done(req); return; - case DCERPC_AUTH_TYPE_SCHANNEL: - case DCERPC_AUTH_TYPE_NTLMSSP: - case DCERPC_AUTH_TYPE_KRB5: - case DCERPC_AUTH_TYPE_SPNEGO: - gensec_security = talloc_get_type_abort(pauth->auth_ctx, - struct gensec_security); + default: + gensec_security = pauth->auth_ctx; if (pkt->pfc_flags & DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN) { if (pauth->client_hdr_signing) { @@ -1705,7 +1869,7 @@ static void rpc_pipe_bind_step_one_done(struct tevent_req *subreq) } } - status = gensec_update(gensec_security, state, NULL, + status = gensec_update(gensec_security, state, auth.credentials, &auth_token); if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { @@ -1721,20 +1885,12 @@ static void rpc_pipe_bind_step_one_done(struct tevent_req *subreq) &auth_token); } break; - - default: - goto err_out; } if (!NT_STATUS_IS_OK(status)) { tevent_req_nterror(req, status); } return; - -err_out: - 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_bind_next_send(struct tevent_req *req, @@ -2127,50 +2283,53 @@ struct dcerpc_binding_handle *rpccli_bh_create(struct rpc_pipe_client *c, return h; } -NTSTATUS rpccli_ncalrpc_bind_data(TALLOC_CTX *mem_ctx, - struct pipe_auth_data **presult) +NTSTATUS rpccli_anon_bind_data(TALLOC_CTX *mem_ctx, + struct pipe_auth_data **presult) { struct pipe_auth_data *result; + struct auth_generic_state *auth_generic_ctx; + NTSTATUS status; result = talloc_zero(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->auth_type = DCERPC_AUTH_TYPE_NONE; + result->auth_level = DCERPC_AUTH_LEVEL_NONE; - 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; + status = auth_generic_client_prepare(result, + &auth_generic_ctx); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("Failed to create auth_generic context: %s\n", + nt_errstr(status))); } - *presult = result; - return NT_STATUS_OK; -} - -NTSTATUS rpccli_anon_bind_data(TALLOC_CTX *mem_ctx, - struct pipe_auth_data **presult) -{ - struct pipe_auth_data *result; - - result = talloc_zero(mem_ctx, struct pipe_auth_data); - if (result == NULL) { - return NT_STATUS_NO_MEMORY; + status = auth_generic_set_username(auth_generic_ctx, ""); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("Failed to set username: %s\n", + nt_errstr(status))); } - result->auth_type = DCERPC_AUTH_TYPE_NONE; - result->auth_level = DCERPC_AUTH_LEVEL_NONE; + status = auth_generic_set_domain(auth_generic_ctx, ""); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("Failed to set domain: %s\n", + nt_errstr(status))); + return status; + } - 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; + status = gensec_set_credentials(auth_generic_ctx->gensec_security, + auth_generic_ctx->credentials); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("Failed to set GENSEC credentials: %s\n", + nt_errstr(status))); + return status; } + talloc_unlink(auth_generic_ctx, auth_generic_ctx->credentials); + auth_generic_ctx->credentials = NULL; + result->auth_ctx = talloc_move(result, &auth_generic_ctx->gensec_security); + talloc_free(auth_generic_ctx); *presult = result; return NT_STATUS_OK; } @@ -2199,13 +2358,6 @@ static NTSTATUS rpccli_generic_bind_data(TALLOC_CTX *mem_ctx, result->auth_type = auth_type; result->auth_level = auth_level; - result->user_name = talloc_strdup(result, username); - result->domain = talloc_strdup(result, domain); - if ((result->user_name == NULL) || (result->domain == NULL)) { - status = NT_STATUS_NO_MEMORY; - goto fail; - } - status = auth_generic_client_prepare(result, &auth_generic_ctx); if (!NT_STATUS_IS_OK(status)) { @@ -2255,6 +2407,79 @@ static NTSTATUS rpccli_generic_bind_data(TALLOC_CTX *mem_ctx, return status; } +/* This routine steals the creds pointer that is passed in */ +static NTSTATUS rpccli_generic_bind_data_from_creds(TALLOC_CTX *mem_ctx, + enum dcerpc_AuthType auth_type, + enum dcerpc_AuthLevel auth_level, + const char *server, + const char *target_service, + struct cli_credentials *creds, + struct pipe_auth_data **presult) +{ + struct auth_generic_state *auth_generic_ctx; + struct pipe_auth_data *result; + NTSTATUS status; + + result = talloc_zero(mem_ctx, struct pipe_auth_data); + if (result == NULL) { + return NT_STATUS_NO_MEMORY; + } + + result->auth_type = auth_type; + result->auth_level = auth_level; + + status = auth_generic_client_prepare(result, + &auth_generic_ctx); + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + + status = auth_generic_set_creds(auth_generic_ctx, creds); + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + + status = gensec_set_target_service(auth_generic_ctx->gensec_security, target_service); + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + + status = gensec_set_target_hostname(auth_generic_ctx->gensec_security, server); + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + + status = auth_generic_client_start_by_authtype(auth_generic_ctx, auth_type, auth_level); + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + + result->auth_ctx = talloc_move(result, &auth_generic_ctx->gensec_security); + talloc_free(auth_generic_ctx); + *presult = result; + return NT_STATUS_OK; + + fail: + TALLOC_FREE(result); + return status; +} + +NTSTATUS rpccli_ncalrpc_bind_data(TALLOC_CTX *mem_ctx, + struct pipe_auth_data **presult) +{ + return rpccli_generic_bind_data(mem_ctx, + DCERPC_AUTH_TYPE_NCALRPC_AS_SYSTEM, + DCERPC_AUTH_LEVEL_CONNECT, + NULL, /* server */ + "host", /* target_service */ + NAME_NT_AUTHORITY, /* domain */ + "SYSTEM", + "", /* password */ + CRED_DONT_USE_KERBEROS, + NULL, /* netlogon_creds_CredentialState */ + presult); +} + /** * Create an rpc pipe client struct, connecting to a tcp port. */ @@ -2341,6 +2566,8 @@ static NTSTATUS rpc_pipe_get_tcp_port(const char *host, struct pipe_auth_data *auth = NULL; struct dcerpc_binding *map_binding = NULL; struct dcerpc_binding *res_binding = NULL; + enum dcerpc_transport_t transport; + const char *endpoint = NULL; struct epm_twr_t *map_tower = NULL; struct epm_twr_t *res_towers = NULL; struct policy_handle *entry_handle = NULL; @@ -2384,16 +2611,17 @@ static NTSTATUS rpc_pipe_get_tcp_port(const char *host, /* create tower for asking the epmapper */ - map_binding = talloc_zero(tmp_ctx, struct dcerpc_binding); - if (map_binding == NULL) { - status = NT_STATUS_NO_MEMORY; + status = dcerpc_parse_binding(tmp_ctx, "ncacn_ip_tcp:[135]", + &map_binding); + if (!NT_STATUS_IS_OK(status)) { goto done; } - map_binding->transport = NCACN_IP_TCP; - map_binding->object = table->syntax_id; - map_binding->host = host; /* needed? */ - map_binding->endpoint = "0"; /* correct? needed? */ + status = dcerpc_binding_set_abstract_syntax(map_binding, + &table->syntax_id); + if (!NT_STATUS_IS_OK(status)) { + goto done; + } map_tower = talloc_zero(tmp_ctx, struct epm_twr_t); if (map_tower == NULL) { @@ -2458,13 +2686,21 @@ static NTSTATUS rpc_pipe_get_tcp_port(const char *host, goto done; } + transport = dcerpc_binding_get_transport(res_binding); + endpoint = dcerpc_binding_get_string_option(res_binding, "endpoint"); + /* are further checks here necessary? */ - if (res_binding->transport != NCACN_IP_TCP) { - status = NT_STATUS_UNSUCCESSFUL; + if (transport != NCACN_IP_TCP) { + status = NT_STATUS_INVALID_NETWORK_RESPONSE; goto done; } - *pport = (uint16_t)atoi(res_binding->endpoint); + if (endpoint == NULL) { + status = NT_STATUS_INVALID_NETWORK_RESPONSE; + goto done; + } + + *pport = (uint16_t)atoi(endpoint); done: TALLOC_FREE(tmp_ctx); @@ -2706,12 +2942,6 @@ NTSTATUS cli_rpc_pipe_open_noauth_transport(struct cli_state *cli, * from the enclosing SMB creds */ - TALLOC_FREE(auth->user_name); - TALLOC_FREE(auth->domain); - - auth->user_name = talloc_strdup(auth, cli->user_name); - auth->domain = talloc_strdup(auth, cli->domain); - if (transport == NCACN_NP) { struct smbXcli_session *session; @@ -2728,11 +2958,6 @@ NTSTATUS cli_rpc_pipe_open_noauth_transport(struct cli_state *cli, } } - if ((auth->user_name == NULL) || (auth->domain == NULL)) { - TALLOC_FREE(result); - return NT_STATUS_NO_MEMORY; - } - status = rpc_pipe_bind(result, auth); if (!NT_STATUS_IS_OK(status)) { int lvl = 0; @@ -2772,11 +2997,71 @@ NTSTATUS cli_rpc_pipe_open_noauth(struct cli_state *cli, /**************************************************************************** Open a named pipe to an SMB server and bind using the mech specified + + This routine references the creds pointer that is passed in + ****************************************************************************/ + +NTSTATUS cli_rpc_pipe_open_with_creds(struct cli_state *cli, + const struct ndr_interface_table *table, + enum dcerpc_transport_t transport, + enum dcerpc_AuthType auth_type, + enum dcerpc_AuthLevel auth_level, + const char *server, + struct cli_credentials *creds, + struct rpc_pipe_client **presult) +{ + struct rpc_pipe_client *result; + struct pipe_auth_data *auth = NULL; + const char *target_service = table->authservices->names[0]; + + NTSTATUS status; + + status = cli_rpc_pipe_open(cli, transport, table, &result); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + status = rpccli_generic_bind_data_from_creds(result, + auth_type, auth_level, + server, target_service, + creds, + &auth); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("rpccli_generic_bind_data returned %s\n", + nt_errstr(status))); + goto err; + } + + status = rpc_pipe_bind(result, auth); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("cli_rpc_pipe_open_generic_auth: cli_rpc_pipe_bind failed with error %s\n", + nt_errstr(status) )); + goto err; + } + + DEBUG(10,("cli_rpc_pipe_open_generic_auth: opened pipe %s to " + "machine %s and bound as user %s.\n", table->name, + result->desthost, cli_credentials_get_unparsed_name(creds, talloc_tos()))); + + *presult = result; + return NT_STATUS_OK; + + err: + + TALLOC_FREE(result); + return status; +} + +/**************************************************************************** + Open a named pipe to an SMB server and bind using the mech specified + + This routine steals the creds pointer that is passed in ****************************************************************************/ NTSTATUS cli_rpc_pipe_open_generic_auth(struct cli_state *cli, const struct ndr_interface_table *table, enum dcerpc_transport_t transport, + enum credentials_use_kerberos use_kerberos, enum dcerpc_AuthType auth_type, enum dcerpc_AuthLevel auth_level, const char *server, @@ -2840,36 +3125,43 @@ NTSTATUS cli_rpc_pipe_open_generic_auth(struct cli_state *cli, NTSTATUS cli_rpc_pipe_open_schannel_with_key(struct cli_state *cli, const struct ndr_interface_table *table, enum dcerpc_transport_t transport, - enum dcerpc_AuthLevel auth_level, const char *domain, - struct netlogon_creds_CredentialState **pdc, + struct netlogon_creds_cli_context *netlogon_creds, struct rpc_pipe_client **_rpccli) { struct rpc_pipe_client *rpccli; struct pipe_auth_data *rpcauth; + struct netlogon_creds_CredentialState *creds = NULL; + enum dcerpc_AuthLevel auth_level; NTSTATUS status; - NTSTATUS result; - struct netlogon_creds_CredentialState save_creds; - struct netr_Authenticator auth; - struct netr_Authenticator return_auth; - union netr_Capabilities capabilities; const char *target_service = table->authservices->names[0]; + int rpc_pipe_bind_dbglvl = 0; status = cli_rpc_pipe_open(cli, transport, table, &rpccli); if (!NT_STATUS_IS_OK(status)) { return status; } + status = netlogon_creds_cli_lock(netlogon_creds, rpccli, &creds); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("netlogon_creds_cli_get returned %s\n", + nt_errstr(status))); + TALLOC_FREE(rpccli); + return status; + } + + auth_level = netlogon_creds_cli_auth_level(netlogon_creds); + status = rpccli_generic_bind_data(rpccli, DCERPC_AUTH_TYPE_SCHANNEL, auth_level, NULL, target_service, domain, - (*pdc)->computer_name, + creds->computer_name, NULL, CRED_AUTO_USE_KERBEROS, - *pdc, + creds, &rpcauth); if (!NT_STATUS_IS_OK(status)) { DEBUG(0, ("rpccli_generic_bind_data returned %s\n", @@ -2878,121 +3170,35 @@ NTSTATUS cli_rpc_pipe_open_schannel_with_key(struct cli_state *cli, return status; } - /* - * The credentials on a new netlogon pipe are the ones we are passed - * in - copy them over - * - * This may get overwritten... in rpc_pipe_bind()... - */ - rpccli->dc = netlogon_creds_copy(rpccli, *pdc); - if (rpccli->dc == NULL) { - TALLOC_FREE(rpccli); - return NT_STATUS_NO_MEMORY; - } - status = rpc_pipe_bind(rpccli, rpcauth); + if (NT_STATUS_EQUAL(status, NT_STATUS_NETWORK_ACCESS_DENIED)) { + rpc_pipe_bind_dbglvl = 1; + netlogon_creds_cli_delete(netlogon_creds, &creds); + } if (!NT_STATUS_IS_OK(status)) { - DEBUG(0, ("cli_rpc_pipe_open_schannel_with_key: " - "cli_rpc_pipe_bind failed with error %s\n", - nt_errstr(status) )); + DEBUG(rpc_pipe_bind_dbglvl, + ("cli_rpc_pipe_open_schannel_with_key: " + "rpc_pipe_bind failed with error %s\n", + nt_errstr(status))); TALLOC_FREE(rpccli); return status; } - if (!ndr_syntax_id_equal(&table->syntax_id, &ndr_table_netlogon.syntax_id)) { - goto done; - } - - save_creds = *rpccli->dc; - ZERO_STRUCT(return_auth); - ZERO_STRUCT(capabilities); - - netlogon_creds_client_authenticator(&save_creds, &auth); + TALLOC_FREE(creds); - status = dcerpc_netr_LogonGetCapabilities(rpccli->binding_handle, - talloc_tos(), - rpccli->srv_name_slash, - save_creds.computer_name, - &auth, &return_auth, - 1, &capabilities, - &result); - if (NT_STATUS_EQUAL(status, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE)) { - if (save_creds.negotiate_flags & NETLOGON_NEG_SUPPORTS_AES) { - DEBUG(5, ("AES was negotiated and the error was %s - " - "downgrade detected\n", - nt_errstr(status))); - TALLOC_FREE(rpccli); - return NT_STATUS_INVALID_NETWORK_RESPONSE; - } - - /* This is probably an old Samba Version */ - DEBUG(5, ("We are checking against an NT or old Samba - %s\n", - nt_errstr(status))); + if (!ndr_syntax_id_equal(&table->syntax_id, &ndr_table_netlogon.syntax_id)) { goto done; } + status = netlogon_creds_cli_check(netlogon_creds, + rpccli->binding_handle); if (!NT_STATUS_IS_OK(status)) { - DEBUG(0, ("dcerpc_netr_LogonGetCapabilities failed with %s\n", + DEBUG(0, ("netlogon_creds_cli_check failed with %s\n", nt_errstr(status))); TALLOC_FREE(rpccli); return status; } - if (NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED)) { - if (save_creds.negotiate_flags & NETLOGON_NEG_SUPPORTS_AES) { - /* This means AES isn't supported. */ - DEBUG(5, ("AES was negotiated and the result was %s - " - "downgrade detected\n", - nt_errstr(result))); - TALLOC_FREE(rpccli); - return NT_STATUS_INVALID_NETWORK_RESPONSE; - } - - /* This is probably an old Windows version */ - DEBUG(5, ("We are checking against an win2k3 or Samba - %s\n", - nt_errstr(result))); - goto done; - } - - /* - * We need to check the credential state here, cause win2k3 and earlier - * returns NT_STATUS_NOT_IMPLEMENTED - */ - if (!netlogon_creds_client_check(&save_creds, &return_auth.cred)) { - /* - * Server replied with bad credential. Fail. - */ - DEBUG(0,("cli_rpc_pipe_open_schannel_with_key: server %s " - "replied with bad credential\n", - rpccli->desthost)); - TALLOC_FREE(rpccli); - return NT_STATUS_INVALID_NETWORK_RESPONSE; - } - *rpccli->dc = save_creds; - - if (!NT_STATUS_IS_OK(result)) { - DEBUG(0, ("dcerpc_netr_LogonGetCapabilities failed with %s\n", - nt_errstr(result))); - TALLOC_FREE(rpccli); - return result; - } - - if (!(save_creds.negotiate_flags & NETLOGON_NEG_SUPPORTS_AES)) { - /* This means AES isn't supported. */ - DEBUG(5, ("AES is not negotiated, but netr_LogonGetCapabilities " - "was OK - downgrade detected\n")); - TALLOC_FREE(rpccli); - return NT_STATUS_INVALID_NETWORK_RESPONSE; - } - - if (save_creds.negotiate_flags != capabilities.server_capabilities) { - DEBUG(0, ("The client capabilities don't match the server " - "capabilities: local[0x%08X] remote[0x%08X]\n", - save_creds.negotiate_flags, - capabilities.server_capabilities)); - TALLOC_FREE(rpccli); - return NT_STATUS_INVALID_NETWORK_RESPONSE; - } done: DEBUG(10,("cli_rpc_pipe_open_schannel_with_key: opened pipe %s to machine %s " @@ -3004,67 +3210,90 @@ done: return NT_STATUS_OK; } -NTSTATUS cli_rpc_pipe_open_spnego(struct cli_state *cli, - const struct ndr_interface_table *table, - enum dcerpc_transport_t transport, - const char *oid, - enum dcerpc_AuthLevel auth_level, - const char *server, - const char *domain, - const char *username, - const char *password, - struct rpc_pipe_client **presult) +NTSTATUS cli_rpc_pipe_open_schannel_with_creds(struct cli_state *cli, + const struct ndr_interface_table *table, + enum dcerpc_transport_t transport, + struct cli_credentials *cli_creds, + struct netlogon_creds_cli_context *netlogon_creds, + struct rpc_pipe_client **_rpccli) { - struct rpc_pipe_client *result; - struct pipe_auth_data *auth = NULL; + struct rpc_pipe_client *rpccli; + struct pipe_auth_data *rpcauth; const char *target_service = table->authservices->names[0]; - + struct netlogon_creds_CredentialState *ncreds = NULL; + enum dcerpc_AuthLevel auth_level; NTSTATUS status; - enum credentials_use_kerberos use_kerberos; + int rpc_pipe_bind_dbglvl = 0; - if (strcmp(oid, GENSEC_OID_KERBEROS5) == 0) { - use_kerberos = CRED_MUST_USE_KERBEROS; - } else if (strcmp(oid, GENSEC_OID_NTLMSSP) == 0) { - use_kerberos = CRED_DONT_USE_KERBEROS; - } else { - return NT_STATUS_INVALID_PARAMETER; + status = cli_rpc_pipe_open(cli, transport, table, &rpccli); + if (!NT_STATUS_IS_OK(status)) { + return status; } - status = cli_rpc_pipe_open(cli, transport, table, &result); + status = netlogon_creds_cli_lock(netlogon_creds, rpccli, &ncreds); if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("netlogon_creds_cli_get returned %s\n", + nt_errstr(status))); + TALLOC_FREE(rpccli); return status; } - status = rpccli_generic_bind_data(result, - DCERPC_AUTH_TYPE_SPNEGO, auth_level, - server, target_service, - domain, username, password, - use_kerberos, NULL, - &auth); + auth_level = netlogon_creds_cli_auth_level(netlogon_creds); + + cli_credentials_set_netlogon_creds(cli_creds, ncreds); + + status = rpccli_generic_bind_data_from_creds(rpccli, + DCERPC_AUTH_TYPE_SCHANNEL, + auth_level, + rpccli->desthost, + target_service, + cli_creds, + &rpcauth); if (!NT_STATUS_IS_OK(status)) { - DEBUG(0, ("rpccli_generic_bind_data returned %s\n", + DEBUG(0, ("rpccli_generic_bind_data_from_creds returned %s\n", nt_errstr(status))); - goto err; + TALLOC_FREE(rpccli); + return status; } - status = rpc_pipe_bind(result, auth); + status = rpc_pipe_bind(rpccli, rpcauth); + cli_credentials_set_netlogon_creds(cli_creds, NULL); + if (NT_STATUS_EQUAL(status, NT_STATUS_NETWORK_ACCESS_DENIED)) { + rpc_pipe_bind_dbglvl = 1; + netlogon_creds_cli_delete(netlogon_creds, &ncreds); + } if (!NT_STATUS_IS_OK(status)) { - DEBUG(0, ("cli_rpc_pipe_open_spnego: cli_rpc_pipe_bind failed with error %s\n", - nt_errstr(status) )); - goto err; + DEBUG(rpc_pipe_bind_dbglvl, + ("%s: rpc_pipe_bind failed with error %s\n", + __func__, nt_errstr(status))); + TALLOC_FREE(rpccli); + return status; } - DEBUG(10,("cli_rpc_pipe_open_spnego: opened pipe %s to " - "machine %s.\n", table->name, - result->desthost)); + TALLOC_FREE(ncreds); - *presult = result; - return NT_STATUS_OK; + if (!ndr_syntax_id_equal(&table->syntax_id, &ndr_table_netlogon.syntax_id)) { + goto done; + } - err: + status = netlogon_creds_cli_check(netlogon_creds, + rpccli->binding_handle); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("netlogon_creds_cli_check failed with %s\n", + nt_errstr(status))); + TALLOC_FREE(rpccli); + return status; + } - TALLOC_FREE(result); - return status; + +done: + DEBUG(10,("%s: opened pipe %s to machine %s " + "for domain %s and bound using schannel.\n", + __func__, table->name, + rpccli->desthost, cli_credentials_get_domain(cli_creds))); + + *_rpccli = rpccli; + return NT_STATUS_OK; } NTSTATUS cli_get_session_key(TALLOC_CTX *mem_ctx, @@ -3088,24 +3317,18 @@ NTSTATUS cli_get_session_key(TALLOC_CTX *mem_ctx, } switch (cli->auth->auth_type) { - case DCERPC_AUTH_TYPE_SPNEGO: - case DCERPC_AUTH_TYPE_NTLMSSP: - case DCERPC_AUTH_TYPE_KRB5: - gensec_security = talloc_get_type_abort(a->auth_ctx, - struct gensec_security); - status = gensec_session_key(gensec_security, mem_ctx, &sk); - if (!NT_STATUS_IS_OK(status)) { - return status; - } - make_dup = false; - break; - case DCERPC_AUTH_TYPE_NCALRPC_AS_SYSTEM: case DCERPC_AUTH_TYPE_NONE: sk = data_blob_const(a->transport_session_key.data, a->transport_session_key.length); make_dup = true; break; default: + gensec_security = a->auth_ctx; + status = gensec_session_key(gensec_security, mem_ctx, &sk); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + make_dup = false; break; }