diff options
-rw-r--r-- | source3/rpc_client/cli_pipe.c | 159 |
1 files changed, 158 insertions, 1 deletions
diff --git a/source3/rpc_client/cli_pipe.c b/source3/rpc_client/cli_pipe.c index 6b052f95ed..38f0d7aa1a 100644 --- a/source3/rpc_client/cli_pipe.c +++ b/source3/rpc_client/cli_pipe.c @@ -29,6 +29,7 @@ #include "../auth/ntlmssp/ntlmssp.h" #include "auth_generic.h" #include "librpc/gen_ndr/ndr_dcerpc.h" +#include "librpc/gen_ndr/ndr_netlogon_c.h" #include "librpc/rpc/dcerpc.h" #include "rpc_dce.h" #include "cli_pipe.h" @@ -1544,9 +1545,15 @@ struct rpc_pipe_bind_state { DATA_BLOB rpc_out; bool auth3; uint32_t rpc_call_id; + struct netr_Authenticator auth; + struct netr_Authenticator return_auth; + struct netlogon_creds_CredentialState *creds; + union netr_Capabilities capabilities; + struct netr_LogonGetCapabilities r; }; static void rpc_pipe_bind_step_one_done(struct tevent_req *subreq); +static void rpc_pipe_bind_step_two_trigger(struct tevent_req *req); static NTSTATUS rpc_bind_next_send(struct tevent_req *req, struct rpc_pipe_bind_state *state, DATA_BLOB *credentials); @@ -1649,11 +1656,14 @@ static void rpc_pipe_bind_step_one_done(struct tevent_req *subreq) 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_SCHANNEL: + rpc_pipe_bind_step_two_trigger(req); + return; + case DCERPC_AUTH_TYPE_NTLMSSP: case DCERPC_AUTH_TYPE_SPNEGO: case DCERPC_AUTH_TYPE_KRB5: @@ -1730,6 +1740,153 @@ err_out: tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR); } +static void rpc_pipe_bind_step_two_done(struct tevent_req *subreq); + +static void rpc_pipe_bind_step_two_trigger(struct tevent_req *req) +{ + struct rpc_pipe_bind_state *state = + tevent_req_data(req, + struct rpc_pipe_bind_state); + struct dcerpc_binding_handle *b = state->cli->binding_handle; + struct schannel_state *schannel_auth = + talloc_get_type_abort(state->cli->auth->auth_ctx, + struct schannel_state); + struct tevent_req *subreq; + + if (schannel_auth == NULL || + !ndr_syntax_id_equal(&state->cli->abstract_syntax, + &ndr_table_netlogon.syntax_id)) { + tevent_req_done(req); + return; + } + + ZERO_STRUCT(state->return_auth); + + state->creds = netlogon_creds_copy(state, schannel_auth->creds); + if (state->creds == NULL) { + tevent_req_nterror(req, NT_STATUS_NO_MEMORY); + return; + } + + netlogon_creds_client_authenticator(state->creds, &state->auth); + + state->r.in.server_name = state->cli->srv_name_slash; + state->r.in.computer_name = state->creds->computer_name; + state->r.in.credential = &state->auth; + state->r.in.query_level = 1; + state->r.in.return_authenticator = &state->return_auth; + + state->r.out.capabilities = &state->capabilities; + state->r.out.return_authenticator = &state->return_auth; + + subreq = dcerpc_netr_LogonGetCapabilities_r_send(talloc_tos(), + state->ev, + b, + &state->r); + if (subreq == NULL) { + tevent_req_nterror(req, NT_STATUS_NO_MEMORY); + return; + } + + tevent_req_set_callback(subreq, rpc_pipe_bind_step_two_done, req); + return; +} + +static void rpc_pipe_bind_step_two_done(struct tevent_req *subreq) +{ + 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); + struct schannel_state *schannel_auth = + talloc_get_type_abort(state->cli->auth->auth_ctx, + struct schannel_state); + NTSTATUS status; + + status = dcerpc_netr_LogonGetCapabilities_r_recv(subreq, talloc_tos()); + TALLOC_FREE(subreq); + if (NT_STATUS_EQUAL(status, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE)) { + if (state->cli->dc->negotiate_flags & + NETLOGON_NEG_SUPPORTS_AES) { + DEBUG(5, ("AES is not supported and the error was %s\n", + nt_errstr(status))); + tevent_req_nterror(req, + NT_STATUS_INVALID_NETWORK_RESPONSE); + return; + } + + /* This is probably NT */ + DEBUG(5, ("We are checking against an NT - %s\n", + nt_errstr(status))); + tevent_req_done(req); + return; + } else if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("dcerpc_netr_LogonGetCapabilities_r_recv failed with %s\n", + nt_errstr(status))); + tevent_req_nterror(req, status); + return; + } + + if (NT_STATUS_EQUAL(state->r.out.result, NT_STATUS_NOT_IMPLEMENTED)) { + if (state->creds->negotiate_flags & NETLOGON_NEG_SUPPORTS_AES) { + /* This means AES isn't supported. */ + DEBUG(5, ("AES is not supported and the error was %s\n", + nt_errstr(state->r.out.result))); + tevent_req_nterror(req, + NT_STATUS_INVALID_NETWORK_RESPONSE); + return; + } + + /* This is probably an old Samba version */ + DEBUG(5, ("We are checking against an old Samba version - %s\n", + nt_errstr(state->r.out.result))); + tevent_req_done(req); + return; + } + + /* We need to check the credential state here, cause win2k3 and earlier + * returns NT_STATUS_NOT_IMPLEMENTED */ + if (!netlogon_creds_client_check(state->creds, + &state->r.out.return_authenticator->cred)) { + /* + * Server replied with bad credential. Fail. + */ + DEBUG(0,("rpc_pipe_bind_step_two_done: server %s " + "replied with bad credential\n", + state->cli->desthost)); + tevent_req_nterror(req, NT_STATUS_UNSUCCESSFUL); + return; + } + + TALLOC_FREE(schannel_auth->creds); + schannel_auth->creds = talloc_steal(state->cli, state->creds); + + if (!NT_STATUS_IS_OK(state->r.out.result)) { + DEBUG(0, ("dcerpc_netr_LogonGetCapabilities_r_recv failed with %s\n", + nt_errstr(state->r.out.result))); + tevent_req_nterror(req, state->r.out.result); + return; + } + + if (state->creds->negotiate_flags != + state->r.out.capabilities->server_capabilities) { + DEBUG(0, ("The client capabilities don't match the server " + "capabilities: local[0x%08X] remote[0x%08X]\n", + state->creds->negotiate_flags, + state->capabilities.server_capabilities)); + tevent_req_nterror(req, + NT_STATUS_INVALID_NETWORK_RESPONSE); + return; + } + + /* TODO: Add downgrade dectection. */ + + tevent_req_done(req); + return; +} + static NTSTATUS rpc_bind_next_send(struct tevent_req *req, struct rpc_pipe_bind_state *state, DATA_BLOB *auth_token) |