s3-rpc_client: Add capabilities check for AES encrypted connections.
authorAndreas Schneider <asn@samba.org>
Mon, 2 Jan 2012 17:54:47 +0000 (18:54 +0100)
committerAndreas Schneider <asn@samba.org>
Tue, 17 Jul 2012 11:53:37 +0000 (13:53 +0200)
Signed-off-by: Günther Deschner <gd@samba.org>
source3/rpc_client/cli_pipe.c

index 6b052f95ed032617ef59b5d512307d415b80b498..38f0d7aa1ac2918c6cf447a399142f0a961590d6 100644 (file)
@@ -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)