r24557: rename 'dcerpc_table_' -> 'ndr_table_'
[jelmer/samba4-debian.git] / source / librpc / rpc / dcerpc_schannel.c
index b3d7048501adbed275ed4e2d126b7d734d003219..6b0a5a2455d133a98c67979834ba61225fab0f1f 100644 (file)
@@ -4,10 +4,12 @@
    dcerpc schannel operations
 
    Copyright (C) Andrew Tridgell 2004
-   
+   Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005
+   Copyright (C) Rafal Szczesniak 2006
+
    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 2 of the License, or
+   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,
    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, write to the Free Software
-   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
 #include "includes.h"
-
-enum schannel_position {
-       DCERPC_SCHANNEL_STATE_START = 0,
-       DCERPC_SCHANNEL_STATE_UPDATE_1
+#include "auth/auth.h"
+#include "libcli/composite/composite.h"
+#include "libcli/auth/libcli_auth.h"
+#include "librpc/gen_ndr/ndr_netlogon.h"
+#include "librpc/gen_ndr/ndr_netlogon_c.h"
+#include "auth/credentials/credentials.h"
+
+struct schannel_key_state {
+       struct dcerpc_pipe *pipe;
+       struct dcerpc_pipe *pipe2;
+       struct dcerpc_binding *binding;
+       struct cli_credentials *credentials;
+       struct creds_CredentialState *creds;
+       uint32_t negotiate_flags;
+       struct netr_Credential credentials1;
+       struct netr_Credential credentials2;
+       struct netr_Credential credentials3;
+       struct netr_ServerReqChallenge r;
+       struct netr_ServerAuthenticate2 a;
+       const struct samr_Password *mach_pwd;
 };
 
-struct dcerpc_schannel_state {
-       TALLOC_CTX *mem_ctx;
-       enum schannel_position state;
-       struct schannel_state *schannel_state;
-       struct creds_CredentialState creds;
-       char *account_name;
-};
 
-static NTSTATUS dcerpc_schannel_key(struct dcerpc_pipe *p,
-                                   const char *domain,
-                                   const char *username,
-                                   const char *password,
-                                   int chan_type,
-                                   uint8_t new_session_key[16]);
+static void continue_secondary_connection(struct composite_context *ctx);
+static void continue_bind_auth_none(struct composite_context *ctx);
+static void continue_srv_challenge(struct rpc_request *req);
+static void continue_srv_auth2(struct rpc_request *req);
 
-/*
-  wrappers for the schannel_*() functions
 
-  These will become static again, when we get dynamic registration, and
-  decrpc_schannel_security_ops come back here.
+/*
+  Stage 2 of schannel_key: Receive endpoint mapping and request secondary
+  rpc connection
 */
-static NTSTATUS dcerpc_schannel_unseal_packet(struct gensec_security *gensec_security, 
-                                             TALLOC_CTX *mem_ctx, 
-                                             uint8_t *data, size_t length, DATA_BLOB *sig)
+static void continue_epm_map_binding(struct composite_context *ctx)
 {
-       struct dcerpc_schannel_state *dce_schan_state = gensec_security->private_data;
-       
-       return schannel_unseal_packet(dce_schan_state->schannel_state, mem_ctx, data, length, sig);
-}
-
-static NTSTATUS dcerpc_schannel_check_packet(struct gensec_security *gensec_security, 
-                                            TALLOC_CTX *mem_ctx, 
-                                            const uint8_t *data, size_t length, 
-                                            const DATA_BLOB *sig)
-{
-       struct dcerpc_schannel_state *dce_schan_state = gensec_security->private_data;
-
-       return schannel_check_packet(dce_schan_state->schannel_state, data, length, sig);
-}
+       struct composite_context *c;
+       struct schannel_key_state *s;
+       struct composite_context *sec_conn_req;
+
+       c = talloc_get_type(ctx->async.private_data, struct composite_context);
+       s = talloc_get_type(c->private_data, struct schannel_key_state);
+
+       /* receive endpoint mapping */
+       c->status = dcerpc_epm_map_binding_recv(ctx);
+       if (!NT_STATUS_IS_OK(c->status)) {
+               DEBUG(0,("Failed to map DCERPC/TCP NCACN_NP pipe for '%s' - %s\n",
+                        DCERPC_NETLOGON_UUID, nt_errstr(c->status)));
+               composite_error(c, c->status);
+               return;
+       }
 
-static NTSTATUS dcerpc_schannel_seal_packet(struct gensec_security *gensec_security, 
-                                           TALLOC_CTX *mem_ctx, 
-                                           uint8_t *data, size_t length, 
-                                           DATA_BLOB *sig)
-{
-       struct dcerpc_schannel_state *dce_schan_state = gensec_security->private_data;
+       /* send a request for secondary rpc connection */
+       sec_conn_req = dcerpc_secondary_connection_send(s->pipe,
+                                                       s->binding);
+       if (composite_nomem(sec_conn_req, c)) return;
 
-       return schannel_seal_packet(dce_schan_state->schannel_state, mem_ctx, data, length, sig);
+       composite_continue(c, sec_conn_req, continue_secondary_connection, c);
 }
 
-static NTSTATUS dcerpc_schannel_sign_packet(struct gensec_security *gensec_security, 
-                                           TALLOC_CTX *mem_ctx, 
-                                           const uint8_t *data, size_t length, 
-                                           DATA_BLOB *sig)
-{
-       struct dcerpc_schannel_state *dce_schan_state = gensec_security->private_data;
 
-       return schannel_sign_packet(dce_schan_state->schannel_state, mem_ctx, data, length, sig);
-}
-
-static NTSTATUS dcerpc_schannel_session_key(struct gensec_security *gensec_security, 
-                                           DATA_BLOB *session_key)
+/*
+  Stage 3 of schannel_key: Receive secondary rpc connection and perform
+  non-authenticated bind request
+*/
+static void continue_secondary_connection(struct composite_context *ctx)
 {
-       return NT_STATUS_NOT_IMPLEMENTED;
-}
+       struct composite_context *c;
+       struct schannel_key_state *s;
+       struct composite_context *auth_none_req;
 
-static NTSTATUS dcerpc_schannel_update(struct gensec_security *gensec_security, TALLOC_CTX *out_mem_ctx, 
-                                      const DATA_BLOB in, DATA_BLOB *out) 
-{
-       struct dcerpc_schannel_state *dce_schan_state = gensec_security->private_data;
-       NTSTATUS status;
-       struct schannel_bind bind_schannel;
-       struct schannel_bind_ack bind_schannel_ack;
-       const char *account_name;
-       *out = data_blob(NULL, 0);
-
-       switch (gensec_security->gensec_role) {
-       case GENSEC_CLIENT:
-               if (dce_schan_state->state != DCERPC_SCHANNEL_STATE_START) {
-                       /* we could parse the bind ack, but we don't know what it is yet */
-                       return NT_STATUS_OK;
-               }
-               
-               bind_schannel.unknown1 = 0;
-#if 0
-               /* to support this we'd need to have access to the full domain name */
-               bind_schannel.bind_type = 23;
-               bind_schannel.u.info23.domain = gensec_security->user.domain;
-               bind_schannel.u.info23.account_name = gensec_security->user.name;
-               bind_schannel.u.info23.dnsdomain = str_format_nbt_domain(out_mem_ctx, fulldomainname);
-               bind_schannel.u.info23.workstation = str_format_nbt_domain(out_mem_ctx, gensec_security->user.name);
-#else
-               bind_schannel.bind_type = 3;
-               bind_schannel.u.info3.domain = gensec_security->user.domain;
-               bind_schannel.u.info3.account_name = gensec_security->user.name;
-#endif
-               
-               status = ndr_push_struct_blob(out, out_mem_ctx, &bind_schannel,
-                                             (ndr_push_flags_fn_t)ndr_push_schannel_bind);
-               if (!NT_STATUS_IS_OK(status)) {
-                       DEBUG(3, ("Could not create schannel bind: %s\n",
-                                 nt_errstr(status)));
-                       return status;
-               }
-               
-               dce_schan_state->state = DCERPC_SCHANNEL_STATE_UPDATE_1;
-
-               return NT_STATUS_MORE_PROCESSING_REQUIRED;
-       case GENSEC_SERVER:
-               
-               if (dce_schan_state->state != DCERPC_SCHANNEL_STATE_START) {
-                       /* no third leg on this protocol */
-                       return NT_STATUS_INVALID_PARAMETER;
-               }
-               
-               /* parse the schannel startup blob */
-               status = ndr_pull_struct_blob(&in, out_mem_ctx, &bind_schannel, 
-                                             (ndr_pull_flags_fn_t)ndr_pull_schannel_bind);
-               if (!NT_STATUS_IS_OK(status)) {
-                       return status;
-               }
-               
-               if (bind_schannel.bind_type == 23) {
-                       account_name = bind_schannel.u.info23.account_name;
-               } else {
-                       account_name = bind_schannel.u.info3.account_name;
-               }
-               
-               /* pull the session key for this client */
-               status = schannel_fetch_session_key(out_mem_ctx, account_name, &dce_schan_state->creds);
-               if (!NT_STATUS_IS_OK(status)) {
-                       DEBUG(3, ("Could not find session key for attempted schannel connection on %s: %s\n",
-                                 account_name, nt_errstr(status)));
-                       return status;
-               }
-
-               dce_schan_state->account_name = talloc_strdup(dce_schan_state->mem_ctx, account_name);
-               
-               /* start up the schannel server code */
-               status = schannel_start(&dce_schan_state->schannel_state, 
-                                       dce_schan_state->creds.session_key, False);
-               if (!NT_STATUS_IS_OK(status)) {
-                       DEBUG(3, ("Could not initialise schannel state for account %s: %s\n",
-                                 account_name, nt_errstr(status)));
-                       return status;
-               }
-               
-               bind_schannel_ack.unknown1 = 1;
-               bind_schannel_ack.unknown2 = 0;
-               bind_schannel_ack.unknown3 = 0x6c0000;
-               
-               status = ndr_push_struct_blob(out, out_mem_ctx, &bind_schannel_ack, 
-                                             (ndr_push_flags_fn_t)ndr_push_schannel_bind_ack);
-               if (!NT_STATUS_IS_OK(status)) {
-                       DEBUG(3, ("Could not return schannel bind ack for account %s: %s\n",
-                                 account_name, nt_errstr(status)));
-                       return status;
-               }
-
-               dce_schan_state->state = DCERPC_SCHANNEL_STATE_UPDATE_1;
-
-               return NT_STATUS_OK;
-       }
-       return NT_STATUS_INVALID_PARAMETER;
-}
+       c = talloc_get_type(ctx->async.private_data, struct composite_context);
+       s = talloc_get_type(c->private_data, struct schannel_key_state);
 
-/** 
- * Return the credentials of a logged on user, including session keys
- * etc.
- *
- * Only valid after a successful authentication
- *
- * May only be called once per authentication.
- *
- */
+       /* receive secondary rpc connection */
+       c->status = dcerpc_secondary_connection_recv(ctx, &s->pipe2);
+       if (!composite_is_ok(c)) return;
 
-NTSTATUS dcerpc_schannel_session_info(struct gensec_security *gensec_security,
-                                     struct auth_session_info **session_info)
-{ 
-       struct dcerpc_schannel_state *dce_schan_state = gensec_security->private_data;
-       TALLOC_CTX *mem_ctx;
-       mem_ctx = talloc_init("dcerpc_schannel_start");
-       if (!mem_ctx) {
-               return NT_STATUS_NO_MEMORY;
-       }
+       talloc_steal(s, s->pipe2);
 
-       (*session_info) = talloc_p(mem_ctx, struct auth_session_info);
-       if (*session_info == NULL) {
-               talloc_destroy(mem_ctx);
-               return NT_STATUS_NO_MEMORY;
-       }
+       /* initiate a non-authenticated bind */
+       auth_none_req = dcerpc_bind_auth_none_send(c, s->pipe2, &ndr_table_netlogon);
+       if (composite_nomem(auth_none_req, c)) return;
 
-       ZERO_STRUCTP(*session_info);
-       (*session_info)->mem_ctx = mem_ctx;
-       (*session_info)->refcount = 1;
-       
-       (*session_info)->workstation = talloc_strdup(mem_ctx, dce_schan_state->account_name);
-       if ((*session_info)->workstation == NULL) {
-               talloc_destroy(mem_ctx);
-               return NT_STATUS_NO_MEMORY;
-       }
-       return NT_STATUS_OK;
+       composite_continue(c, auth_none_req, continue_bind_auth_none, c);
 }
-               
-
-/**
- * Return the struct creds_CredentialState.
- *
- * Make sure not to call this unless gensec is using schannel...
- */
 
-NTSTATUS dcerpc_schannel_creds(struct gensec_security *gensec_security,
-                              TALLOC_CTX *mem_ctx,
-                              struct creds_CredentialState **creds)
-{ 
-       struct dcerpc_schannel_state *dce_schan_state = gensec_security->private_data;
-
-       *creds = talloc_p(mem_ctx, struct creds_CredentialState);
-       if (!*creds) {
-               return NT_STATUS_NO_MEMORY;
-       }
-
-       **creds = dce_schan_state->creds;
-       return NT_STATUS_OK;
-}
-               
 
-static NTSTATUS dcerpc_schannel_start(struct gensec_security *gensec_security)
+/*
+  Stage 4 of schannel_key: Receive non-authenticated bind and get
+  a netlogon challenge
+*/
+static void continue_bind_auth_none(struct composite_context *ctx)
 {
-       struct dcerpc_schannel_state *dce_schan_state;
-       TALLOC_CTX *mem_ctx;
-       mem_ctx = talloc_init("dcerpc_schannel_start");
-       if (!mem_ctx) {
-               return NT_STATUS_NO_MEMORY;
-       }
+       struct composite_context *c;
+       struct schannel_key_state *s;
+       struct rpc_request *srv_challenge_req;
 
-       dce_schan_state = talloc_p(mem_ctx, struct dcerpc_schannel_state);
-       if (!dce_schan_state) {
-               talloc_destroy(mem_ctx);
-               return NT_STATUS_NO_MEMORY;
-       }
+       c = talloc_get_type(ctx->async.private_data, struct composite_context);
+       s = talloc_get_type(c->private_data, struct schannel_key_state);
 
-       dce_schan_state->mem_ctx = mem_ctx;
-       dce_schan_state->state = DCERPC_SCHANNEL_STATE_START;
+       /* receive result of non-authenticated bind request */
+       c->status = dcerpc_bind_auth_none_recv(ctx);
+       if (!composite_is_ok(c)) return;
        
-
-       gensec_security->private_data = dce_schan_state;
+       /* prepare a challenge request */
+       s->r.in.server_name   = talloc_asprintf(c, "\\\\%s", dcerpc_server_name(s->pipe));
+       if (composite_nomem(s->r.in.server_name, c)) return;
+       s->r.in.computer_name = cli_credentials_get_workstation(s->credentials);
+       s->r.in.credentials   = &s->credentials1;
+       s->r.out.credentials  = &s->credentials2;
        
-       return NT_STATUS_OK;
-}
+       generate_random_buffer(s->credentials1.data, sizeof(s->credentials1.data));
 
-static NTSTATUS dcerpc_schannel_server_start(struct gensec_security *gensec_security) 
-{
-       NTSTATUS status;
-
-       status = dcerpc_schannel_start(gensec_security);
+       /*
+         request a netlogon challenge - a rpc request over opened secondary pipe
+       */
+       srv_challenge_req = dcerpc_netr_ServerReqChallenge_send(s->pipe2, c, &s->r);
+       if (composite_nomem(srv_challenge_req, c)) return;
 
-       if (!NT_STATUS_IS_OK(status)) {
-               return status;
-       }
-       return NT_STATUS_OK;
+       composite_continue_rpc(c, srv_challenge_req, continue_srv_challenge, c);
 }
 
-static NTSTATUS dcerpc_schannel_client_start(struct gensec_security *gensec_security) 
+
+/*
+  Stage 5 of schannel_key: Receive a challenge and perform authentication
+  on the netlogon pipe
+*/
+static void continue_srv_challenge(struct rpc_request *req)
 {
-       NTSTATUS status;
-       struct dcerpc_schannel_state *dce_schan_state;
+       struct composite_context *c;
+       struct schannel_key_state *s;
+       struct rpc_request *srv_auth2_req;
+
+       c = talloc_get_type(req->async.private_data, struct composite_context);
+       s = talloc_get_type(c->private_data, struct schannel_key_state);
+
+       /* receive rpc request result - netlogon challenge */
+       c->status = dcerpc_ndr_request_recv(req);
+       if (!composite_is_ok(c)) return;
+
+       /* prepare credentials for auth2 request */
+       s->mach_pwd = cli_credentials_get_nt_hash(s->credentials, c);
+
+       creds_client_init(s->creds, &s->credentials1, &s->credentials2,
+                         s->mach_pwd, &s->credentials3, s->negotiate_flags);
+
+       /* auth2 request arguments */
+       s->a.in.server_name      = s->r.in.server_name;
+       s->a.in.account_name     = cli_credentials_get_username(s->credentials);
+       s->a.in.secure_channel_type =
+               cli_credentials_get_secure_channel_type(s->credentials);
+       s->a.in.computer_name    = cli_credentials_get_workstation(s->credentials);
+       s->a.in.negotiate_flags  = &s->negotiate_flags;
+       s->a.in.credentials      = &s->credentials3;
+       s->a.out.negotiate_flags = &s->negotiate_flags;
+       s->a.out.credentials     = &s->credentials3;
 
-       status = dcerpc_schannel_start(gensec_security);
-       if (!NT_STATUS_IS_OK(status)) {
-               return status;
-       }
-       dce_schan_state = gensec_security->private_data;
-
-       status = schannel_start(&dce_schan_state->schannel_state, 
-                               gensec_security->user.schan_session_key, 
-                               True);
-       if (!NT_STATUS_IS_OK(status)) {
-               DEBUG(1, ("Failed to start schannel client\n"));
-               return status;
-       }
+       /*
+         authenticate on the netlogon pipe - a rpc request over secondary pipe
+       */
+       srv_auth2_req = dcerpc_netr_ServerAuthenticate2_send(s->pipe2, c, &s->a);
+       if (composite_nomem(srv_auth2_req, c)) return;
 
-       dump_data_pw("session key:\n", dce_schan_state->schannel_state->session_key, 16);
-       return NT_STATUS_OK;
+       composite_continue_rpc(c, srv_auth2_req, continue_srv_auth2, c);
 }
 
+
 /*
-  end crypto state
+  Stage 6 of schannel_key: Receive authentication request result and verify
+  received credentials
 */
-static void dcerpc_schannel_end(struct gensec_security *gensec_security)
+static void continue_srv_auth2(struct rpc_request *req)
 {
-       struct dcerpc_schannel_state *dce_schan_state = gensec_security->private_data;
+       struct composite_context *c;
+       struct schannel_key_state *s;
+
+       c = talloc_get_type(req->async.private_data, struct composite_context);
+       s = talloc_get_type(c->private_data, struct schannel_key_state);
+
+       /* receive rpc request result - auth2 credentials */ 
+       c->status = dcerpc_ndr_request_recv(req);
+       if (!composite_is_ok(c)) return;
 
-       schannel_end(&dce_schan_state->schannel_state);
+       /* verify credentials */
+       if (!creds_client_check(s->creds, s->a.out.credentials)) {
+               composite_error(c, NT_STATUS_UNSUCCESSFUL);
+               return;
+       }
 
-       talloc_destroy(dce_schan_state->mem_ctx);
+       /* setup current netlogon credentials */
+       cli_credentials_set_netlogon_creds(s->credentials, s->creds);
 
-       gensec_security->private_data = NULL;
+       composite_done(c);
 }
 
 
 /*
-  get a schannel key using a netlogon challenge on a secondary pipe
+  Initiate establishing a schannel key using netlogon challenge
+  on a secondary pipe
 */
-static NTSTATUS dcerpc_schannel_key(struct dcerpc_pipe *p,
-                                   const char *domain,
-                                   const char *username,
-                                   const char *password,
-                                   int chan_type,
-                                   uint8_t new_session_key[16])
+struct composite_context *dcerpc_schannel_key_send(TALLOC_CTX *mem_ctx,
+                                                  struct dcerpc_pipe *p,
+                                                  struct cli_credentials *credentials)
 {
-       NTSTATUS status;
-       struct dcerpc_pipe *p2;
-       struct netr_ServerReqChallenge r;
-       struct netr_ServerAuthenticate2 a;
-       struct netr_Credential credentials1, credentials2, credentials3;
-       struct samr_Password mach_pwd;
-       struct creds_CredentialState creds;
-       const char *workgroup, *workstation;
-       uint32_t negotiate_flags;
+       struct composite_context *c;
+       struct schannel_key_state *s;
+       struct composite_context *epm_map_req;
+       
+       /* composite context allocation and setup */
+       c = composite_create(mem_ctx, p->conn->event_ctx);
+       if (c == NULL) return NULL;
+
+       s = talloc_zero(c, struct schannel_key_state);
+       if (composite_nomem(s, c)) return c;
+       c->private_data = s;
+
+       /* store parameters in the state structure */
+       s->pipe        = p;
+       s->credentials = credentials;
+
+       /* allocate credentials */
+       s->creds = talloc(c, struct creds_CredentialState);
+       if (composite_nomem(s->creds, c)) return c;
 
-       if (p->flags & DCERPC_SCHANNEL_128) {
-               negotiate_flags = NETLOGON_NEG_AUTH2_ADS_FLAGS;
+       /* type of authentication depends on schannel type */
+       if (s->pipe->conn->flags & DCERPC_SCHANNEL_128) {
+               s->negotiate_flags = NETLOGON_NEG_AUTH2_ADS_FLAGS;
        } else {
-               negotiate_flags = NETLOGON_NEG_AUTH2_FLAGS;
+               s->negotiate_flags = NETLOGON_NEG_AUTH2_FLAGS;
        }
 
-       workstation = username;
-       workgroup = domain;
+       /* allocate binding structure */
+       s->binding = talloc(c, struct dcerpc_binding);
+       if (composite_nomem(s->binding, c)) return c;
 
-       /*
-         step 1 - establish a netlogon connection, with no authentication
-       */
-       status = dcerpc_secondary_connection(p, &p2, 
-                                            DCERPC_NETLOGON_NAME, 
-                                            DCERPC_NETLOGON_UUID, 
-                                            DCERPC_NETLOGON_VERSION);
+       *s->binding = *s->pipe->binding;
 
+       /* request the netlogon endpoint mapping */
+       epm_map_req = dcerpc_epm_map_binding_send(c, s->binding,
+                                                 &ndr_table_netlogon,
+                                                 s->pipe->conn->event_ctx);
+       if (composite_nomem(epm_map_req, c)) return c;
 
-       /*
-         step 2 - request a netlogon challenge
-       */
-       r.in.server_name = talloc_asprintf(p->mem_ctx, "\\\\%s", dcerpc_server_name(p));
-       r.in.computer_name = workstation;
-       r.in.credentials = &credentials1;
-       r.out.credentials = &credentials2;
+       composite_continue(c, epm_map_req, continue_epm_map_binding, c);
+       return c;
+}
 
-       generate_random_buffer(credentials1.data, sizeof(credentials1.data));
 
-       status = dcerpc_netr_ServerReqChallenge(p2, p->mem_ctx, &r);
-       if (!NT_STATUS_IS_OK(status)) {
-               return status;
-       }
+/*
+  Receive result of schannel key request
+ */
+NTSTATUS dcerpc_schannel_key_recv(struct composite_context *c)
+{
+       NTSTATUS status = composite_wait(c);
+       
+       talloc_free(c);
+       return status;
+}
 
-       /*
-         step 3 - authenticate on the netlogon pipe
-       */
-       E_md4hash(password, mach_pwd.hash);
-       creds_client_init(&creds, &credentials1, &credentials2, &mach_pwd, &credentials3,
-                         negotiate_flags);
-
-       a.in.server_name = r.in.server_name;
-       a.in.account_name = talloc_asprintf(p->mem_ctx, "%s$", workstation);
-       a.in.secure_channel_type = chan_type;
-       a.in.computer_name = workstation;
-       a.in.negotiate_flags = &negotiate_flags;
-       a.out.negotiate_flags = &negotiate_flags;
-       a.in.credentials = &credentials3;
-       a.out.credentials = &credentials3;
-
-       status = dcerpc_netr_ServerAuthenticate2(p2, p->mem_ctx, &a);
-       if (!NT_STATUS_IS_OK(status)) {
-               return status;
-       }
 
-       if (!creds_client_check(&creds, a.out.credentials)) {
-               return NT_STATUS_UNSUCCESSFUL;
-       }
+struct auth_schannel_state {
+       struct dcerpc_pipe *pipe;
+       struct cli_credentials *credentials;
+       const struct ndr_interface_table *table;
+       uint8_t auth_level;
+};
 
-       /*
-         the schannel session key is now in creds.session_key
 
-         we no longer need the netlogon pipe open
-       */
-       dcerpc_pipe_close(p2);
+static void continue_bind_auth(struct composite_context *ctx);
 
-       memcpy(new_session_key, creds.session_key, 16);
 
-       return NT_STATUS_OK;
+/*
+  Stage 2 of auth_schannel: Receive schannel key and intitiate an
+  authenticated bind using received credentials
+ */
+static void continue_schannel_key(struct composite_context *ctx)
+{
+       struct composite_context *auth_req;
+       struct composite_context *c = talloc_get_type(ctx->async.private_data,
+                                                     struct composite_context);
+       struct auth_schannel_state *s = talloc_get_type(c->private_data,
+                                                       struct auth_schannel_state);
+
+       /* receive schannel key */
+       c->status = dcerpc_schannel_key_recv(ctx);
+       if (!composite_is_ok(c)) {
+               DEBUG(1, ("Failed to setup credentials for account %s: %s\n",
+                         cli_credentials_get_username(s->credentials), nt_errstr(c->status)));
+               return;
+       }
+
+       /* send bind auth request with received creds */
+       auth_req = dcerpc_bind_auth_send(c, s->pipe, s->table, s->credentials, 
+                                        DCERPC_AUTH_TYPE_SCHANNEL, s->auth_level,
+                                        NULL);
+       if (composite_nomem(auth_req, c)) return;
+       
+       composite_continue(c, auth_req, continue_bind_auth, c);
 }
 
+
 /*
-  do a schannel style bind on a dcerpc pipe. The username is usually
-  of the form HOSTNAME$ and the password is the domain trust password
+  Stage 3 of auth_schannel: Receivce result of authenticated bind
+  and say if we're done ok.
 */
-NTSTATUS dcerpc_bind_auth_schannel(struct dcerpc_pipe *p,
-                                  const char *uuid, uint_t version,
-                                  const char *domain,
-                                  const char *username,
-                                  const char *password)
+static void continue_bind_auth(struct composite_context *ctx)
 {
-       NTSTATUS status;
-       int chan_type = 0;
+       struct composite_context *c = talloc_get_type(ctx->async.private_data,
+                                                     struct composite_context);
 
-       status = gensec_client_start(&p->security_state.generic_state);
-       if (!NT_STATUS_IS_OK(status)) {
-               return status;
-       }
+       c->status = dcerpc_bind_auth_recv(ctx);
+       if (!composite_is_ok(c)) return;
 
-       if (p->flags & DCERPC_SCHANNEL_BDC) {
-               chan_type = SEC_CHAN_BDC;
-       } else if (p->flags & DCERPC_SCHANNEL_WORKSTATION) {
-               chan_type = SEC_CHAN_WKSTA;
-       } else if (p->flags & DCERPC_SCHANNEL_DOMAIN) {
-               chan_type = SEC_CHAN_DOMAIN;
-       }
+       composite_done(c);
+}
 
-       status = dcerpc_schannel_key(p, domain, 
-                                    username,
-                                    password, 
-                                    chan_type, 
-                                    p->security_state.generic_state->user.schan_session_key);
-       if (!NT_STATUS_IS_OK(status)) {
-               DEBUG(1, ("Failed to fetch schannel session key: %s\n", nt_errstr(status)));
-               gensec_end(&p->security_state.generic_state);
-               return status;
-       }
-       
-       status = gensec_set_username(p->security_state.generic_state, username);
-       if (!NT_STATUS_IS_OK(status)) {
-               DEBUG(1, ("Failed to set schannel username to %s: %s\n", username, nt_errstr(status)));
-               gensec_end(&p->security_state.generic_state);
-               return status;
-       }
-       
-       status = gensec_set_domain(p->security_state.generic_state, domain);
-       if (!NT_STATUS_IS_OK(status)) {
-               DEBUG(1, ("Failed to set schannel domain to %s: %s\n", domain, nt_errstr(status)));
-               gensec_end(&p->security_state.generic_state);
-               return status;
-       }
-       
-       status = gensec_start_mech_by_authtype(p->security_state.generic_state, DCERPC_AUTH_TYPE_SCHANNEL);
 
-       if (!NT_STATUS_IS_OK(status)) {
-               DEBUG(1, ("Failed to start SCHANNEL GENSEC backend: %s\n", nt_errstr(status)));
-               gensec_end(&p->security_state.generic_state);
-               return status;
-       }
+/*
+  Initiate schannel authentication request
+*/
+struct composite_context *dcerpc_bind_auth_schannel_send(TALLOC_CTX *tmp_ctx, 
+                                                        struct dcerpc_pipe *p,
+                                                        const struct ndr_interface_table *table,
+                                                        struct cli_credentials *credentials,
+                                                        uint8_t auth_level)
+{
+       struct composite_context *c;
+       struct auth_schannel_state *s;
+       struct composite_context *schan_key_req;
 
-       status = dcerpc_bind_auth3(p, DCERPC_AUTH_TYPE_SCHANNEL,
-                                 uuid, version);
+       /* composite context allocation and setup */
+       c = composite_create(tmp_ctx, p->conn->event_ctx);
+       if (c == NULL) return NULL;
+       
+       s = talloc_zero(c, struct auth_schannel_state);
+       if (composite_nomem(s, c)) return c;
+       c->private_data = s;
+
+       /* store parameters in the state structure */
+       s->pipe        = p;
+       s->credentials = credentials;
+       s->table       = table;
+       s->auth_level  = auth_level;
+
+       /* start getting schannel key first */
+       schan_key_req = dcerpc_schannel_key_send(c, p, credentials);
+       if (composite_nomem(schan_key_req, c)) return c;
+
+       composite_continue(c, schan_key_req, continue_schannel_key, c);
+       return c;
+}
 
-       if (!NT_STATUS_IS_OK(status)) {
-               DEBUG(1, ("Failed to bind to pipe with SCHANNEL: %s\n", nt_errstr(status)));
-               gensec_end(&p->security_state.generic_state);
-               return status;
-       }
 
-       return NT_STATUS_OK;
+/*
+  Receive result of schannel authentication request
+*/
+NTSTATUS dcerpc_bind_auth_schannel_recv(struct composite_context *c)
+{
+       NTSTATUS status = composite_wait(c);
+       
+       talloc_free(c);
+       return status;
 }
 
 
-static const struct gensec_security_ops gensec_dcerpc_schannel_security_ops = {
-       .name           = "dcerpc_schannel",
-       .auth_type      = DCERPC_AUTH_TYPE_SCHANNEL,
-       .client_start   = dcerpc_schannel_client_start,
-       .server_start   = dcerpc_schannel_server_start,
-       .update         = dcerpc_schannel_update,
-       .seal_packet    = dcerpc_schannel_seal_packet,
-       .sign_packet    = dcerpc_schannel_sign_packet,
-       .check_packet   = dcerpc_schannel_check_packet,
-       .unseal_packet  = dcerpc_schannel_unseal_packet,
-       .session_key    = dcerpc_schannel_session_key,
-       .session_info   = dcerpc_schannel_session_info,
-       .end            = dcerpc_schannel_end
-};
-
-NTSTATUS gensec_dcerpc_schannel_init(void)
+/*
+  Perform schannel authenticated bind - sync version
+ */
+NTSTATUS dcerpc_bind_auth_schannel(TALLOC_CTX *tmp_ctx, 
+                                  struct dcerpc_pipe *p,
+                                  const struct ndr_interface_table *table,
+                                  struct cli_credentials *credentials,
+                                  uint8_t auth_level)
 {
-       NTSTATUS ret;
-       ret = register_backend("gensec", &gensec_dcerpc_schannel_security_ops);
-       if (!NT_STATUS_IS_OK(ret)) {
-               DEBUG(0,("Failed to register '%s' gensec backend!\n",
-                       gensec_dcerpc_schannel_security_ops.name));
-               return ret;
-       }
+       struct composite_context *c;
 
-       return ret;
+       c = dcerpc_bind_auth_schannel_send(tmp_ctx, p, table, credentials,
+                                          auth_level);
+       return dcerpc_bind_auth_schannel_recv(c);
 }