r24560: rename some DCERPC_ prefixes into NDR_
[ira/wip.git] / source / librpc / rpc / dcerpc_schannel.c
index b4dbfbb5a5d805607ff91cf341db887a25165ace..c6b3a5fc0f380cf0302ee4b1c1eac3c4f7672d71 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"
+#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;
+};
+
+
+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
+  Stage 2 of schannel_key: Receive endpoint mapping and request secondary
+  rpc connection
 */
-static NTSTATUS schan_unseal_packet(struct dcerpc_security *dcerpc_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 schannel_state *schannel_state = dcerpc_security->private;
-       return schannel_unseal_packet(schannel_state, mem_ctx, 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",
+                        NDR_NETLOGON_UUID, nt_errstr(c->status)));
+               composite_error(c, c->status);
+               return;
+       }
 
-static NTSTATUS schan_check_packet(struct dcerpc_security *dcerpc_security, 
-                                  TALLOC_CTX *mem_ctx, 
-                                  const uint8_t *data, size_t length, 
-                                  const DATA_BLOB *sig)
-{
-       struct schannel_state *schannel_state = dcerpc_security->private;
-       return schannel_check_packet(schannel_state, data, length, sig);
-}
+       /* 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;
 
-static NTSTATUS schan_seal_packet(struct dcerpc_security *dcerpc_security, 
-                                 TALLOC_CTX *mem_ctx, 
-                                 uint8_t *data, size_t length, 
-                                 DATA_BLOB *sig)
-{
-       struct schannel_state *schannel_state = dcerpc_security->private;
-       return schannel_seal_packet(schannel_state, mem_ctx, data, length, sig);
+       composite_continue(c, sec_conn_req, continue_secondary_connection, c);
 }
 
-static NTSTATUS schan_sign_packet(struct dcerpc_security *dcerpc_security, 
-                                TALLOC_CTX *mem_ctx, 
-                                const uint8_t *data, size_t length, 
-                                DATA_BLOB *sig)
-{
-       struct schannel_state *schannel_state = dcerpc_security->private;
-       return schannel_sign_packet(schannel_state, mem_ctx, data, length, sig);
-}
 
-static NTSTATUS schan_session_key(struct dcerpc_security *dcerpc_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 void schan_security_end(struct dcerpc_security *dcerpc_security)
-{
-       struct schannel_state *schannel_state = dcerpc_security->private;
-       schannel_end(&schannel_state);
+       c = talloc_get_type(ctx->async.private_data, struct composite_context);
+       s = talloc_get_type(c->private_data, struct schannel_key_state);
+
+       /* receive secondary rpc connection */
+       c->status = dcerpc_secondary_connection_recv(ctx, &s->pipe2);
+       if (!composite_is_ok(c)) return;
+
+       talloc_steal(s, s->pipe2);
+
+       /* 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;
+
+       composite_continue(c, auth_none_req, continue_bind_auth_none, c);
 }
 
 
 /*
-  get a schannel key using a netlogon challenge on a secondary pipe
+  Stage 4 of schannel_key: Receive non-authenticated bind and get
+  a netlogon challenge
 */
-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[8])
+static void continue_bind_auth_none(struct composite_context *ctx)
 {
-       NTSTATUS status;
-       struct dcerpc_pipe *p2;
-       struct netr_ServerReqChallenge r;
-       struct netr_ServerAuthenticate2 a;
-       struct netr_Credential credentials1, credentials2, credentials3;
-       uint8_t mach_pwd[16];
-       struct creds_CredentialState creds;
-       const char *workgroup, *workstation;
-       uint32_t negotiate_flags = 0;
+       struct composite_context *c;
+       struct schannel_key_state *s;
+       struct rpc_request *srv_challenge_req;
+
+       c = talloc_get_type(ctx->async.private_data, struct composite_context);
+       s = talloc_get_type(c->private_data, struct schannel_key_state);
 
-       workstation = username;
-       workgroup = domain;
+       /* receive result of non-authenticated bind request */
+       c->status = dcerpc_bind_auth_none_recv(ctx);
+       if (!composite_is_ok(c)) return;
+       
+       /* 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;
+       
+       generate_random_buffer(s->credentials1.data, sizeof(s->credentials1.data));
 
        /*
-         step 1 - establish a netlogon connection, with no authentication
+         request a netlogon challenge - a rpc request over opened secondary pipe
        */
-       status = dcerpc_secondary_smb(p, &p2, 
-                                     DCERPC_NETLOGON_NAME, 
-                                     DCERPC_NETLOGON_UUID, 
-                                     DCERPC_NETLOGON_VERSION);
+       srv_challenge_req = dcerpc_netr_ServerReqChallenge_send(s->pipe2, c, &s->r);
+       if (composite_nomem(srv_challenge_req, c)) return;
+
+       composite_continue_rpc(c, srv_challenge_req, continue_srv_challenge, c);
+}
 
 
+/*
+  Stage 5 of schannel_key: Receive a challenge and perform authentication
+  on the netlogon pipe
+*/
+static void continue_srv_challenge(struct rpc_request *req)
+{
+       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;
+
        /*
-         step 2 - request a netlogon challenge
+         authenticate on the netlogon pipe - a rpc request over secondary pipe
        */
-       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;
+       srv_auth2_req = dcerpc_netr_ServerAuthenticate2_send(s->pipe2, c, &s->a);
+       if (composite_nomem(srv_auth2_req, c)) return;
 
-       generate_random_buffer(credentials1.data, sizeof(credentials1.data), False);
+       composite_continue_rpc(c, srv_auth2_req, continue_srv_auth2, c);
+}
 
-       status = dcerpc_netr_ServerReqChallenge(p2, p->mem_ctx, &r);
-       if (!NT_STATUS_IS_OK(status)) {
-               return status;
-       }
 
-       /*
-         step 3 - authenticate on the netlogon pipe
-       */
-       E_md4hash(password, mach_pwd);
-       creds_client_init(&creds, &credentials1, &credentials2, mach_pwd, &credentials3,
-                         negotiate_flags);
-
-       a.in.server_name = r.in.server_name;
-       a.in.username = 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;
-       }
+/*
+  Stage 6 of schannel_key: Receive authentication request result and verify
+  received credentials
+*/
+static void continue_srv_auth2(struct rpc_request *req)
+{
+       struct composite_context *c;
+       struct schannel_key_state *s;
 
-       if (!creds_client_check(&creds, a.out.credentials)) {
-               return NT_STATUS_UNSUCCESSFUL;
-       }
+       c = talloc_get_type(req->async.private_data, struct composite_context);
+       s = talloc_get_type(c->private_data, struct schannel_key_state);
 
-       /*
-         the schannel session key is now in creds.session_key
+       /* receive rpc request result - auth2 credentials */ 
+       c->status = dcerpc_ndr_request_recv(req);
+       if (!composite_is_ok(c)) return;
 
-         we no longer need the netlogon pipe open
-       */
-       dcerpc_pipe_close(p2);
+       /* verify credentials */
+       if (!creds_client_check(s->creds, s->a.out.credentials)) {
+               composite_error(c, NT_STATUS_UNSUCCESSFUL);
+               return;
+       }
 
-       memcpy(new_session_key, creds.session_key, 8);
+       /* setup current netlogon credentials */
+       cli_credentials_set_netlogon_creds(s->credentials, s->creds);
 
-       return NT_STATUS_OK;
+       composite_done(c);
 }
 
 
 /*
-  do a schannel style bind on a dcerpc pipe with the given schannel
-  key. The username is usually of the form HOSTNAME$ and the password
-  is the domain trust password
+  Initiate establishing a schannel key using netlogon challenge
+  on a secondary pipe
 */
-NTSTATUS dcerpc_bind_auth_schannel_key(struct dcerpc_pipe *p,
-                                      const char *uuid, uint_t version,
-                                      const char *domain,
-                                      const char *username,
-                                      const uint8_t session_key[8])
+struct composite_context *dcerpc_schannel_key_send(TALLOC_CTX *mem_ctx,
+                                                  struct dcerpc_pipe *p,
+                                                  struct cli_credentials *credentials)
 {
-       NTSTATUS status;
-       uint8_t full_session_key[16];
-       struct schannel_state *schannel_state;
-       const char *workgroup, *workstation;
+       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;
 
-       memcpy(full_session_key, session_key, 8);
-       memset(full_session_key+8, 0, 8);
+       s = talloc_zero(c, struct schannel_key_state);
+       if (composite_nomem(s, c)) return c;
+       c->private_data = s;
 
-       workstation = username;
-       workgroup = domain;
+       /* store parameters in the state structure */
+       s->pipe        = p;
+       s->credentials = credentials;
 
-       /*
-         perform a bind with security type schannel
-       */
-       p->auth_info = talloc(p->mem_ctx, sizeof(*p->auth_info));
-       if (!p->auth_info) {
-               status = NT_STATUS_NO_MEMORY;
-               goto done;
-       }
+       /* allocate credentials */
+       s->creds = talloc(c, struct creds_CredentialState);
+       if (composite_nomem(s->creds, c)) return c;
 
-       p->auth_info->auth_type = DCERPC_AUTH_TYPE_SCHANNEL;
-       
-       if (p->flags & DCERPC_SEAL) {
-               p->auth_info->auth_level = DCERPC_AUTH_LEVEL_PRIVACY;
+       /* type of authentication depends on schannel type */
+       if (s->pipe->conn->flags & DCERPC_SCHANNEL_128) {
+               s->negotiate_flags = NETLOGON_NEG_AUTH2_ADS_FLAGS;
        } else {
-               /* note that DCERPC_AUTH_LEVEL_NONE does not make any 
-                  sense, and would be rejected by the server */
-               p->auth_info->auth_level = DCERPC_AUTH_LEVEL_INTEGRITY;
-       }
-       p->auth_info->auth_pad_length = 0;
-       p->auth_info->auth_reserved = 0;
-       p->auth_info->auth_context_id = random();
-       p->security_state = NULL;
-
-       p->auth_info->credentials = data_blob_talloc(p->mem_ctx, 
-                                                    NULL,
-                                                    8 +
-                                                    strlen(workgroup)+1 +
-                                                    strlen(workstation)+1);
-       if (!p->auth_info->credentials.data) {
-               return NT_STATUS_NO_MEMORY;
+               s->negotiate_flags = NETLOGON_NEG_AUTH2_FLAGS;
        }
 
-       /* oh, this is ugly! */
-       SIVAL(p->auth_info->credentials.data, 0, 0);
-       SIVAL(p->auth_info->credentials.data, 4, 3);
-       memcpy(p->auth_info->credentials.data+8, workgroup, strlen(workgroup)+1);
-       memcpy(p->auth_info->credentials.data+8+strlen(workgroup)+1, 
-              workstation, strlen(workstation)+1);
-
-       /* send the authenticated bind request */
-       status = dcerpc_bind_byuuid(p, p->mem_ctx, uuid, version);
-       if (!NT_STATUS_IS_OK(status)) {
-               goto done;
-       }
+       /* allocate binding structure */
+       s->binding = talloc(c, struct dcerpc_binding);
+       if (composite_nomem(s->binding, c)) return c;
 
-       p->security_state = talloc_p(p->mem_ctx, struct dcerpc_security);
-       if (!p->security_state) {
-               status = NT_STATUS_NO_MEMORY;
-               goto done;
-       }
+       *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;
+
+       composite_continue(c, epm_map_req, continue_epm_map_binding, c);
+       return c;
+}
 
-       schannel_state = talloc_p(p->mem_ctx, struct schannel_state);
-       if (!schannel_state) {
-               status = NT_STATUS_NO_MEMORY;
-               goto done;
-       }
 
-       status = schannel_start(&schannel_state, full_session_key, True);
-       if (!NT_STATUS_IS_OK(status)) {
-               goto done;
+/*
+  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;
+}
+
+
+struct auth_schannel_state {
+       struct dcerpc_pipe *pipe;
+       struct cli_credentials *credentials;
+       const struct ndr_interface_table *table;
+       uint8_t auth_level;
+};
+
+
+static void continue_bind_auth(struct composite_context *ctx);
+
+
+/*
+  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;
        }
 
-       dump_data_pw("session key:\n", schannel_state->session_key, 16);
+       /* 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);
+}
 
-       p->security_state->private = schannel_state;
-       p->security_state->unseal_packet = schan_unseal_packet;
-       p->security_state->check_packet = schan_check_packet;
-       p->security_state->seal_packet = schan_seal_packet;
-       p->security_state->sign_packet = schan_sign_packet;
-       p->security_state->session_key = schan_session_key;
-       p->security_state->security_end = schan_security_end;
 
-done:
-       return status;
+/*
+  Stage 3 of auth_schannel: Receivce result of authenticated bind
+  and say if we're done ok.
+*/
+static void continue_bind_auth(struct composite_context *ctx)
+{
+       struct composite_context *c = talloc_get_type(ctx->async.private_data,
+                                                     struct composite_context);
+
+       c->status = dcerpc_bind_auth_recv(ctx);
+       if (!composite_is_ok(c)) return;
+
+       composite_done(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
+  Initiate schannel authentication request
 */
-NTSTATUS dcerpc_bind_auth_schannel(struct dcerpc_pipe *p,
-                                  const char *uuid, uint_t version,
-                                  const char *domain,
-                                  const char *username,
-                                  const char *password)
+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)
 {
-       NTSTATUS status;
-       uint8_t session_key[8];
-
-       status = dcerpc_schannel_key(p, domain, username, password, 
-                                    lp_server_role() == ROLE_DOMAIN_BDC? SEC_CHAN_BDC:SEC_CHAN_WKSTA,
-                                    session_key);
-       if (!NT_STATUS_IS_OK(status)) {
-               return status;
-       }
+       struct composite_context *c;
+       struct auth_schannel_state *s;
+       struct composite_context *schan_key_req;
+
+       /* 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;
+}
 
-       status = dcerpc_bind_auth_schannel_key(p, uuid, version, domain, username, session_key);
 
+/*
+  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;
 }
 
+
+/*
+  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)
+{
+       struct composite_context *c;
+
+       c = dcerpc_bind_auth_schannel_send(tmp_ctx, p, table, credentials,
+                                          auth_level);
+       return dcerpc_bind_auth_schannel_recv(c);
+}