s3-dcerpc: Add sign/seal with gssapi
[kai/samba.git] / source3 / rpc_client / cli_pipe.c
index 12100c344955ea1682b70cefe47ec09be89674e4..85888755069da4b85363ea4dc72827ef036de612 100644 (file)
@@ -30,6 +30,7 @@
 #include "rpc_client/cli_netlogon.h"
 #include "librpc/gen_ndr/ndr_dcerpc.h"
 #include "librpc/rpc/dcerpc.h"
+#include "librpc/rpc/dcerpc_gssapi.h"
 
 #undef DBGC_CLASS
 #define DBGC_CLASS DBGC_RPC_CLI
@@ -947,61 +948,40 @@ static NTSTATUS rpc_api_pipe_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
  Creates krb5 auth bind.
  ********************************************************************/
 
-static NTSTATUS create_krb5_auth_bind_req(struct rpc_pipe_client *cli,
-                                         enum dcerpc_AuthLevel auth_level,
-                                         DATA_BLOB *auth_info)
+static NTSTATUS create_gssapi_auth_bind_req(TALLOC_CTX *mem_ctx,
+                                           struct pipe_auth_data *auth,
+                                           DATA_BLOB *auth_info)
 {
-#ifdef HAVE_KRB5
-       int ret;
+       DATA_BLOB in_token = data_blob_null;
+       DATA_BLOB auth_token = data_blob_null;
        NTSTATUS status;
-       struct kerberos_auth_struct *a = cli->auth->a_u.kerberos_auth;
-       DATA_BLOB tkt = data_blob_null;
-       DATA_BLOB tkt_wrapped = data_blob_null;
-
-       DEBUG(5, ("create_krb5_auth_bind_req: creating a service ticket for principal %s\n",
-               a->service_principal ));
-
-       /* Create the ticket for the service principal and return it in a gss-api wrapped blob. */
-
-       ret = cli_krb5_get_ticket(a, a->service_principal, 0,
-                                 &tkt, &a->session_key,
-                                 AP_OPTS_MUTUAL_REQUIRED, NULL,
-                                 NULL, NULL);
 
-       if (ret) {
-               DEBUG(1,("create_krb5_auth_bind_req: cli_krb5_get_ticket for principal %s "
-                       "failed with %s\n",
-                       a->service_principal,
-                       error_message(ret) ));
-
-               data_blob_free(&tkt);
-               return NT_STATUS_INVALID_PARAMETER;
+       /* Negotiate the initial auth token */
+       status = gse_get_client_auth_token(mem_ctx,
+                                          auth->a_u.gssapi_state,
+                                          &in_token,
+                                          &auth_token);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
        }
 
-       /* wrap that up in a nice GSS-API wrapping */
-       tkt_wrapped = spnego_gen_krb5_wrap(talloc_tos(), tkt, TOK_ID_KRB_AP_REQ);
-
-       data_blob_free(&tkt);
-
-       status = dcerpc_push_dcerpc_auth(cli,
-                                        DCERPC_AUTH_TYPE_KRB5,
-                                        auth_level,
+       status = dcerpc_push_dcerpc_auth(mem_ctx,
+                                        auth->auth_type,
+                                        auth->auth_level,
                                         0, /* auth_pad_length */
                                         1, /* auth_context_id */
-                                        &tkt_wrapped,
+                                        &auth_token,
                                         auth_info);
        if (!NT_STATUS_IS_OK(status)) {
-               data_blob_free(&tkt_wrapped);
+               data_blob_free(&auth_token);
                return status;
        }
 
-       DEBUG(5, ("create_krb5_auth_bind_req: Created krb5 GSS blob :\n"));
-       dump_data(5, tkt_wrapped.data, tkt_wrapped.length);
+       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;
-#else
-       return NT_STATUS_INVALID_PARAMETER;
-#endif
 }
 
 /*******************************************************************
@@ -1240,9 +1220,7 @@ static NTSTATUS create_rpc_bind_req(TALLOC_CTX *mem_ctx,
                break;
 
        case DCERPC_AUTH_TYPE_KRB5:
-               ret = create_krb5_auth_bind_req(cli,
-                                               auth->auth_level,
-                                               &auth_info);
+               ret = create_gssapi_auth_bind_req(mem_ctx, auth, &auth_info);
                if (!NT_STATUS_IS_OK(ret)) {
                        return ret;
                }
@@ -1271,32 +1249,34 @@ static NTSTATUS create_rpc_bind_req(TALLOC_CTX *mem_ctx,
  work out any sign/seal padding length.
  ********************************************************************/
 
-static uint32 calculate_data_len_tosend(struct rpc_pipe_client *cli,
-                                       uint32 data_left,
-                                       uint16 *p_frag_len,
-                                       uint16 *p_auth_len,
-                                       uint32 *p_ss_padding)
+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 data_space, data_len;
-
-#if 0
-       if ((data_left > 0) && (sys_random() % 2)) {
-               data_left = MAX(data_left/2, 1);
-       }
-#endif
+       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;
-               return 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:
@@ -1304,9 +1284,11 @@ static uint32 calculate_data_len_tosend(struct rpc_pipe_client *cli,
                        case PIPE_AUTH_TYPE_SPNEGO_NTLMSSP:
                                *p_auth_len = NTLMSSP_SIG_SIZE;
                                break;
-                       default:
-                               smb_panic("bad auth type");
+                       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;
@@ -1314,15 +1296,18 @@ static uint32 calculate_data_len_tosend(struct rpc_pipe_client *cli,
                case DCERPC_AUTH_TYPE_SCHANNEL:
                        *p_auth_len = NL_AUTH_SIGNATURE_SIZE;
                        break;
-               default:
-                       smb_panic("bad auth type");
+               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 = cli->max_xmit_frag
-                               - DCERPC_REQUEST_LENGTH
-                               - DCERPC_AUTH_TRAILER_LENGTH
-                               - *p_auth_len;
+               data_space = max_len - *p_auth_len;
 
                data_len = MIN(data_space, data_left);
                *p_ss_padding = 0;
@@ -1333,13 +1318,14 @@ static uint32 calculate_data_len_tosend(struct rpc_pipe_client *cli,
                                + data_len + *p_ss_padding
                                + DCERPC_AUTH_TRAILER_LENGTH
                                + *p_auth_len;
-               return data_len;
+               *data_to_send = data_len;
+               return NT_STATUS_OK;
 
        default:
-               smb_panic("bad auth level");
-               /* Notreached. */
-               return 0;
+               break;
        }
+
+       return NT_STATUS_INVALID_PARAMETER;
 }
 
 /*******************************************************************
@@ -1437,15 +1423,20 @@ static NTSTATUS prepare_next_frag(struct rpc_api_pipe_req_state *state,
        uint16_t auth_len;
        uint16_t frag_len;
        uint8_t flags = 0;
-       uint32_t ss_padding;
+       uint32_t pad_len;
        uint32_t data_left;
        NTSTATUS status;
        union dcerpc_payload u;
 
        data_left = state->req_data->length - state->req_data_sent;
 
-       data_sent_thistime = calculate_data_len_tosend(
-               state->cli, data_left, &frag_len, &auth_len, &ss_padding);
+       status = calculate_data_len_tosend(state->cli, data_left,
+                                          &data_sent_thistime,
+                                          &frag_len, &auth_len,
+                                          &pad_len);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
 
        if (state->req_data_sent == 0) {
                flags = DCERPC_PFC_FLAG_FIRST;
@@ -1486,10 +1477,21 @@ static NTSTATUS prepare_next_frag(struct rpc_api_pipe_req_state *state,
                return NT_STATUS_NO_MEMORY;
        }
 
-       status = dcerpc_add_auth_footer(state->cli->auth, ss_padding,
-                                       &state->rpc_out);
-       if (!NT_STATUS_IS_OK(status)) {
-               return status;
+       switch (state->cli->auth->auth_level) {
+       case DCERPC_AUTH_LEVEL_NONE:
+       case DCERPC_AUTH_LEVEL_CONNECT:
+       case DCERPC_AUTH_LEVEL_PACKET:
+               break;
+       case DCERPC_AUTH_LEVEL_INTEGRITY:
+       case DCERPC_AUTH_LEVEL_PRIVACY:
+               status = dcerpc_add_auth_footer(state->cli->auth, pad_len,
+                                               &state->rpc_out);
+               if (!NT_STATUS_IS_OK(status)) {
+                       return status;
+               }
+               break;
+       default:
+               return NT_STATUS_INVALID_PARAMETER;
        }
 
        state->req_data_sent += data_sent_thistime;
@@ -1718,10 +1720,11 @@ static NTSTATUS create_rpc_bind_auth3(TALLOC_CTX *mem_ctx,
  ********************************************************************/
 
 static NTSTATUS create_rpc_alter_context(TALLOC_CTX *mem_ctx,
+                                       enum dcerpc_AuthType auth_type,
+                                       enum dcerpc_AuthLevel auth_level,
                                        uint32 rpc_call_id,
                                        const struct ndr_syntax_id *abstract,
                                        const struct ndr_syntax_id *transfer,
-                                       enum dcerpc_AuthLevel auth_level,
                                        const DATA_BLOB *pauth_blob, /* spnego auth blob already created. */
                                        DATA_BLOB *rpc_out)
 {
@@ -1729,7 +1732,7 @@ static NTSTATUS create_rpc_alter_context(TALLOC_CTX *mem_ctx,
        NTSTATUS status;
 
        status = dcerpc_push_dcerpc_auth(mem_ctx,
-                                        DCERPC_AUTH_TYPE_SPNEGO,
+                                        auth_type,
                                         auth_level,
                                         0, /* auth_pad_length */
                                         1, /* auth_context_id */
@@ -1764,13 +1767,18 @@ struct rpc_pipe_bind_state {
 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,
-                                          struct ncacn_packet *r);
+                                          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,
-                                                   struct ncacn_packet *r,
-                                                   DATA_BLOB *reply_pdu);
+                                       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);
+static NTSTATUS rpc_bind_finish_send(struct tevent_req *req,
+                                    struct rpc_pipe_bind_state *state,
+                                    DATA_BLOB *credentials);
 
 struct tevent_req *rpc_pipe_bind_send(TALLOC_CTX *mem_ctx,
                                      struct event_context *ev,
@@ -1833,8 +1841,11 @@ static void rpc_pipe_bind_step_one_done(struct tevent_req *subreq)
                subreq, struct tevent_req);
        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 dcerpc_auth auth;
+       DATA_BLOB auth_token = data_blob_null;
        NTSTATUS status;
 
        status = rpc_api_pipe_recv(subreq, talloc_tos(), &pkt, &reply_pdu);
@@ -1856,6 +1867,40 @@ 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) {
+
+       case DCERPC_AUTH_TYPE_NONE:
+       case DCERPC_AUTH_TYPE_SCHANNEL:
+               /* Bind complete. */
+               tevent_req_done(req);
+               return;
+
+       case DCERPC_AUTH_TYPE_NTLMSSP:
+       case DCERPC_AUTH_TYPE_SPNEGO:
+       case DCERPC_AUTH_TYPE_KRB5:
+               /* Paranoid lenght checks */
+               if (pkt->frag_length < DCERPC_AUTH_TRAILER_LENGTH
+                                               + pkt->auth_length) {
+                       tevent_req_nterror(req,
+                                       NT_STATUS_INFO_LENGTH_MISMATCH);
+                       return;
+               }
+               /* get auth credentials */
+               status = dcerpc_pull_dcerpc_auth(talloc_tos(),
+                                                &pkt->u.bind_ack.auth_info,
+                                                &auth, false);
+               if (!NT_STATUS_IS_OK(status)) {
+                       DEBUG(0, ("Failed to pull dcerpc auth: %s.\n",
+                                 nt_errstr(status)));
+                       tevent_req_nterror(req, status);
+                       return;
+               }
+               break;
+
+       default:
+               goto err_out;
+       }
+
        /*
         * For authenticated binds we may need to do 3 or 4 leg binds.
         */
@@ -1870,33 +1915,46 @@ static void rpc_pipe_bind_step_one_done(struct tevent_req *subreq)
 
        case DCERPC_AUTH_TYPE_NTLMSSP:
                /* Need to send AUTH3 packet - no reply. */
-               status = rpc_finish_auth3_bind_send(req, state, pkt);
-               if (!NT_STATUS_IS_OK(status)) {
-                       tevent_req_nterror(req, status);
-               }
-               return;
+               status = rpc_finish_auth3_bind_send(req, state,
+                                                   &auth.credentials);
+               break;
 
        case DCERPC_AUTH_TYPE_SPNEGO:
                if (state->cli->auth->spnego_type !=
                                        PIPE_AUTH_TYPE_SPNEGO_NTLMSSP) {
-                       break;
+                       goto err_out;
                }
                /* Need to send alter context request and reply. */
-               status = rpc_finish_spnego_ntlmssp_bind_send(req, state, pkt,
-                                                            &reply_pdu);
+               status = rpc_finish_spnego_ntlmssp_bind_send(req, state,
+                                                       &auth.credentials);
+               break;
+
+       case DCERPC_AUTH_TYPE_KRB5:
+               status = gse_get_client_auth_token(state,
+                                                  pauth->a_u.gssapi_state,
+                                                  &auth.credentials,
+                                                  &auth_token);
                if (!NT_STATUS_IS_OK(status)) {
-                       tevent_req_nterror(req, status);
+                       break;
                }
-               return;
 
-       case DCERPC_AUTH_TYPE_KRB5:
-               /* */
+               if (gse_require_more_processing(pauth->a_u.gssapi_state)) {
+                       status = rpc_bind_next_send(req, state, &auth_token);
+               } else {
+                       status = rpc_bind_finish_send(req, state, &auth_token);
+               }
                break;
 
        default:
-               break;
+               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(%u)\n",
                 (unsigned int)state->cli->auth->auth_type,
                 (unsigned int)state->cli->auth->spnego_type));
@@ -1905,32 +1963,17 @@ 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,
-                                          struct ncacn_packet *r)
+                                          DATA_BLOB *credentials)
 {
+       struct pipe_auth_data *auth = state->cli->auth;
        DATA_BLOB client_reply = data_blob_null;
-       struct dcerpc_auth auth;
        struct tevent_req *subreq;
        NTSTATUS status;
 
-       if ((r->auth_length == 0)
-           || (r->frag_length < DCERPC_AUTH_TRAILER_LENGTH
-                                       + r->auth_length)) {
-               return NT_STATUS_INVALID_PARAMETER;
-       }
-
-       status = dcerpc_pull_dcerpc_auth(talloc_tos(),
-                                        &r->u.bind_ack.auth_info,
-                                        &auth, false);
-       if (!NT_STATUS_IS_OK(status)) {
-               DEBUG(0, ("Failed to pull dcerpc auth: %s.\n",
-                         nt_errstr(status)));
-               return status;
-       }
-
        /* TODO - check auth_type/auth_level match. */
 
-       status = auth_ntlmssp_update(state->cli->auth->a_u.auth_ntlmssp_state,
-                               auth.credentials, &client_reply);
+       status = auth_ntlmssp_update(auth->a_u.auth_ntlmssp_state,
+                                    *credentials, &client_reply);
 
        if (!NT_STATUS_IS_OK(status)) {
                DEBUG(0, ("rpc_finish_auth3_bind: NTLMSSP update using server "
@@ -1940,11 +1983,12 @@ static NTSTATUS rpc_finish_auth3_bind_send(struct tevent_req *req,
 
        data_blob_free(&state->rpc_out);
 
-       status = create_rpc_bind_auth3(state,
-                                      state->cli, state->rpc_call_id,
-                                      state->cli->auth->auth_type,
-                                      state->cli->auth->auth_level,
-                                      &client_reply, &state->rpc_out);
+       status = create_rpc_bind_auth3(state, state->cli,
+                                       state->rpc_call_id,
+                                       auth->auth_type,
+                                       auth->auth_level,
+                                       &client_reply,
+                                       &state->rpc_out);
        data_blob_free(&client_reply);
 
        if (!NT_STATUS_IS_OK(status)) {
@@ -1976,44 +2020,23 @@ 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,
-                                                   struct ncacn_packet *r,
-                                                   DATA_BLOB *reply_pdu)
+                                       struct rpc_pipe_bind_state *state,
+                                       DATA_BLOB *credentials)
 {
+       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 dcerpc_auth auth_info;
-       DATA_BLOB auth_blob;
        struct tevent_req *subreq;
        NTSTATUS status;
 
-       if ((r->auth_length == 0)
-           || (r->frag_length < DCERPC_AUTH_TRAILER_LENGTH
-                                       + r->auth_length)) {
-               return NT_STATUS_INVALID_PARAMETER;
-       }
-
-       /* Process the returned NTLMSSP blob first. */
-       auth_blob = data_blob_const(reply_pdu->data
-                                       + r->frag_length
-                                       - DCERPC_AUTH_TRAILER_LENGTH
-                                       - r->auth_length,
-                                   DCERPC_AUTH_TRAILER_LENGTH
-                                       + r->auth_length);
-
-       status = dcerpc_pull_dcerpc_auth(state, &auth_blob, &auth_info, false);
-       if (!NT_STATUS_IS_OK(status)) {
-               DEBUG(0, ("Failed to unmarshall dcerpc_auth.\n"));
-               return status;
-       }
-
        /*
         * The server might give us back two challenges - tmp_blob is for the
         * second.
         */
-       if (!spnego_parse_challenge(state, auth_info.credentials,
-                                   &server_ntlm_response, &tmp_blob)) {
+       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;
@@ -2022,8 +2045,8 @@ static NTSTATUS rpc_finish_spnego_ntlmssp_bind_send(struct tevent_req *req,
        /* We're finished with the server spnego response and the tmp_blob. */
        data_blob_free(&tmp_blob);
 
-       status = auth_ntlmssp_update(state->cli->auth->a_u.auth_ntlmssp_state,
-                               server_ntlm_response, &client_reply);
+       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);
@@ -2045,10 +2068,11 @@ static NTSTATUS rpc_finish_spnego_ntlmssp_bind_send(struct tevent_req *req,
        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,
-                                         state->cli->auth->auth_level,
                                          &client_reply,
                                          &state->rpc_out);
        data_blob_free(&client_reply);
@@ -2108,6 +2132,68 @@ static void rpc_bind_ntlmssp_api_done(struct tevent_req *subreq)
        tevent_req_done(req);
 }
 
+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;
+       struct tevent_req *subreq;
+       NTSTATUS status;
+
+       /* 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)) {
+               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;
+
+       /* 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,
+                                       auth_token,
+                                       &state->rpc_out);
+       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);
+       if (subreq == NULL) {
+               return NT_STATUS_NO_MEMORY;
+       }
+       tevent_req_set_callback(subreq, rpc_bind_auth3_write_done, req);
+       return NT_STATUS_OK;
+}
+
 NTSTATUS rpc_pipe_bind_recv(struct tevent_req *req)
 {
        return tevent_req_simple_recv_ntstatus(req);
@@ -2351,74 +2437,6 @@ NTSTATUS rpccli_schannel_bind_data(TALLOC_CTX *mem_ctx, const char *domain,
        return NT_STATUS_NO_MEMORY;
 }
 
-#ifdef HAVE_KRB5
-static int cli_auth_kerberos_data_destructor(struct kerberos_auth_struct *auth)
-{
-       data_blob_free(&auth->session_key);
-       return 0;
-}
-#endif
-
-static NTSTATUS rpccli_kerberos_bind_data(TALLOC_CTX *mem_ctx,
-                                  enum dcerpc_AuthLevel auth_level,
-                                  const char *service_princ,
-                                  const char *username,
-                                  const char *password,
-                                  struct pipe_auth_data **presult)
-{
-#ifdef HAVE_KRB5
-       struct pipe_auth_data *result;
-
-       if ((username != NULL) && (password != NULL)) {
-               int ret = kerberos_kinit_password(username, password, 0, NULL);
-               if (ret != 0) {
-                       return NT_STATUS_ACCESS_DENIED;
-               }
-       }
-
-       result = talloc(mem_ctx, struct pipe_auth_data);
-       if (result == NULL) {
-               return NT_STATUS_NO_MEMORY;
-       }
-
-       result->auth_type = DCERPC_AUTH_TYPE_KRB5;
-       result->spnego_type = PIPE_AUTH_TYPE_SPNEGO_NONE;
-       result->auth_level = auth_level;
-
-       /*
-        * Username / domain need fixing!
-        */
-       result->user_name = talloc_strdup(result, "");
-       result->domain = talloc_strdup(result, "");
-       if ((result->user_name == NULL) || (result->domain == NULL)) {
-               goto fail;
-       }
-
-       result->a_u.kerberos_auth = TALLOC_ZERO_P(
-               result, struct kerberos_auth_struct);
-       if (result->a_u.kerberos_auth == NULL) {
-               goto fail;
-       }
-       talloc_set_destructor(result->a_u.kerberos_auth,
-                             cli_auth_kerberos_data_destructor);
-
-       result->a_u.kerberos_auth->service_principal = talloc_strdup(
-               result, service_princ);
-       if (result->a_u.kerberos_auth->service_principal == NULL) {
-               goto fail;
-       }
-
-       *presult = result;
-       return NT_STATUS_OK;
-
- fail:
-       TALLOC_FREE(result);
-       return NT_STATUS_NO_MEMORY;
-#else
-       return NT_STATUS_NOT_SUPPORTED;
-#endif
-}
-
 /**
  * Create an rpc pipe client struct, connecting to a tcp port.
  */
@@ -3319,26 +3337,29 @@ NTSTATUS cli_rpc_pipe_open_schannel(struct cli_state *cli,
 
 NTSTATUS cli_rpc_pipe_open_krb5(struct cli_state *cli,
                                const struct ndr_syntax_id *interface,
+                               enum dcerpc_transport_t transport,
                                enum dcerpc_AuthLevel auth_level,
-                               const char *service_princ,
+                               const char *server,
                                const char *username,
                                const char *password,
                                struct rpc_pipe_client **presult)
 {
-#ifdef HAVE_KRB5
+#ifdef HAVE_GSSAPI_H
        struct rpc_pipe_client *result;
        struct pipe_auth_data *auth;
        NTSTATUS status;
 
-       status = cli_rpc_pipe_open(cli, NCACN_NP, interface, &result);
+       status = cli_rpc_pipe_open(cli, transport, interface, &result);
        if (!NT_STATUS_IS_OK(status)) {
                return status;
        }
 
-       status = rpccli_kerberos_bind_data(result, auth_level, service_princ,
-                                          username, password, &auth);
+       status = gse_init_client(result, DCERPC_AUTH_TYPE_KRB5, auth_level,
+                                NULL, server, "cifs", username, password,
+                                GSS_C_DCE_STYLE, &auth);
+
        if (!NT_STATUS_IS_OK(status)) {
-               DEBUG(0, ("rpccli_kerberos_bind_data returned %s\n",
+               DEBUG(0, ("gse_init_client returned %s\n",
                          nt_errstr(status)));
                TALLOC_FREE(result);
                return status;
@@ -3387,9 +3408,7 @@ NTSTATUS cli_get_session_key(TALLOC_CTX *mem_ctx,
                                                a->a_u.auth_ntlmssp_state);
                        break;
                case PIPE_AUTH_TYPE_SPNEGO_KRB5:
-                       sk = data_blob_const(
-                               a->a_u.kerberos_auth->session_key.data,
-                               a->a_u.kerberos_auth->session_key.length);
+                       sk = gse_get_session_key(a->a_u.gssapi_state);
                        break;
                default:
                        return NT_STATUS_NO_USER_SESSION_KEY;
@@ -3399,8 +3418,7 @@ NTSTATUS cli_get_session_key(TALLOC_CTX *mem_ctx,
                sk = auth_ntlmssp_get_session_key(a->a_u.auth_ntlmssp_state);
                break;
        case DCERPC_AUTH_TYPE_KRB5:
-               sk = data_blob_const(a->a_u.kerberos_auth->session_key.data,
-                                    a->a_u.kerberos_auth->session_key.length);
+               sk = gse_get_session_key(a->a_u.gssapi_state);
                break;
        case DCERPC_AUTH_TYPE_NONE:
                sk = data_blob_const(a->user_session_key.data,