Merge: clarify secure channel connection comment.
[kai/samba.git] / source / rpc_client / cli_pipe.c
index 6eaab39bcc7c0c946b5bd22c6eae56b41f65ac49..a5cb6d425eef64588f87310d9891453faf708255 100644 (file)
@@ -1,6 +1,5 @@
 /* 
- *  Unix SMB/Netbios implementation.
- *  Version 1.9.
+ *  Unix SMB/CIFS implementation.
  *  RPC Pipe client / server routines
  *  Copyright (C) Andrew Tridgell              1992-1998,
  *  Copyright (C) Luke Kenneth Casson Leighton 1996-1998,
 
 #include "includes.h"
 
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_CLI
+
 extern struct pipe_id_info pipe_names[];
-extern fstring global_myworkgroup;
-extern pstring global_myname;
 
 /********************************************************************
  Rpc pipe call id.
@@ -191,9 +191,10 @@ static BOOL rpc_auth_pipe(struct cli_state *cli, prs_struct *rdata, int len, int
 
        BOOL auth_verify = ((cli->ntlmssp_srv_flgs & NTLMSSP_NEGOTIATE_SIGN) != 0);
        BOOL auth_seal = ((cli->ntlmssp_srv_flgs & NTLMSSP_NEGOTIATE_SEAL) != 0);
+       BOOL auth_schannel = (cli->saved_netlogon_pipe_fnum != 0);
 
-       DEBUG(5,("rpc_auth_pipe: len: %d auth_len: %d verify %s seal %s\n",
-                 len, auth_len, BOOLSTR(auth_verify), BOOLSTR(auth_seal)));
+       DEBUG(5,("rpc_auth_pipe: len: %d auth_len: %d verify %s seal %s schannel %s\n",
+                 len, auth_len, BOOLSTR(auth_verify), BOOLSTR(auth_seal), BOOLSTR(auth_schannel)));
 
        /*
         * Unseal any sealed data in the PDU, not including the
@@ -293,12 +294,55 @@ static BOOL rpc_auth_pipe(struct cli_state *cli, prs_struct *rdata, int len, int
                }
                cli->ntlmssp_seq_num++;
        }
+
+       if (auth_schannel) {
+               RPC_AUTH_NETSEC_CHK chk;
+               char data[RPC_AUTH_NETSEC_CHK_LEN];
+               char *dp = prs_data_p(rdata) + len - auth_len;
+               prs_struct auth_verf;
+
+               if (auth_len != RPC_AUTH_NETSEC_CHK_LEN) {
+                       DEBUG(0,("rpc_auth_pipe: wrong schannel auth len %d\n", auth_len));
+                       return False;
+               }
+
+               if (dp - prs_data_p(rdata) > prs_data_size(rdata)) {
+                       DEBUG(0,("rpc_auth_pipe: schannel auth data > data size !\n"));
+                       return False;
+               }
+
+               DEBUG(10,("rpc_auth_pipe: schannel verify netsec\n"));
+               dump_data(100, dp, auth_len);
+
+               memcpy(data, dp, sizeof(data));
+               dump_data(100, data, sizeof(data));
+
+               prs_init(&auth_verf, 0, cli->mem_ctx, UNMARSHALL);
+
+               /* The endinness must be preserved. JRA. */
+               prs_set_endian_data( &auth_verf, rdata->bigendian_data);
+
+               prs_give_memory(&auth_verf, data, RPC_AUTH_NETSEC_CHK_LEN, False);
+
+               if (!smb_io_rpc_auth_netsec_chk("schannel_auth_sign", &chk, &auth_verf, 0)) {
+                       DEBUG(0, ("rpc_auth_pipe: schannel unmarshalling "
+                                 "RPC_AUTH_NETSECK_CHK failed\n"));
+                       return False;
+               }
+
+               cli->auth_info.seq_num++;
+
+               if (!netsec_decode(&cli->auth_info, &chk, reply_data, data_len)) {
+                       DEBUG(0, ("rpc_auth_pipe: Could not decode schannel\n"));
+                       return False;
+               }
+       }
        return True;
 }
 
 
 /****************************************************************************
- Send data on an rpc pipe, which *must* be in one fragment.
+ Send data on an rpc pipe via trans, which *must* be the last fragment.
  receive response data from an rpc pipe, which may be large...
 
  Read the first fragment: unfortunately have to use SMBtrans for the first
@@ -317,11 +361,11 @@ static BOOL rpc_auth_pipe(struct cli_state *cli, prs_struct *rdata, int len, int
  +------------+-----------------+-------------+---------------+-------------+
 
  Where the presence of the AUTH_HDR and AUTH are dependent on the
- signing & sealing being neogitated.
+ signing & sealing being negotiated.
 
  ****************************************************************************/
 
-static BOOL rpc_api_pipe(struct cli_state *cli, uint16 cmd, prs_struct *data, prs_struct *rdata)
+static BOOL rpc_api_pipe(struct cli_state *cli, prs_struct *data, prs_struct *rdata)
 {
        uint32 len;
        char *rparam = NULL;
@@ -335,14 +379,14 @@ static BOOL rpc_api_pipe(struct cli_state *cli, uint16 cmd, prs_struct *data, pr
        char *prdata = NULL;
        uint32 rdata_len = 0;
        uint32 current_offset = 0;
+       uint32 max_data = cli->max_xmit_frag ? cli->max_xmit_frag : 1024;
 
        /* Create setup parameters - must be in native byte order. */
 
-       setup[0] = cmd
+       setup[0] = TRANSACT_DCERPCCMD
        setup[1] = cli->nt_pipe_fnum; /* Pipe file handle. */
 
-       DEBUG(5,("rpc_api_pipe: cmd:%x fnum:%x\n", (int)cmd, 
-                (int)cli->nt_pipe_fnum));
+       DEBUG(5,("rpc_api_pipe: fnum:%x\n", (int)cli->nt_pipe_fnum));
 
        /* Send the RPC request and receive a response.  For short RPC
           calls (about 1024 bytes or so) the RPC request and response
@@ -352,7 +396,7 @@ static BOOL rpc_api_pipe(struct cli_state *cli, uint16 cmd, prs_struct *data, pr
        if (!cli_api_pipe(cli, "\\PIPE\\",
                  setup, 2, 0,                     /* Setup, length, max */
                  NULL, 0, 0,                      /* Params, length, max */
-                 pdata, data_len, data_len,       /* data, length, max */                  
+                 pdata, data_len, max_data,       /* data, length, max */
                  &rparam, &rparam_len,            /* return params, len */
                  &prdata, &rdata_len))            /* return data, len */
        {
@@ -365,8 +409,8 @@ static BOOL rpc_api_pipe(struct cli_state *cli, uint16 cmd, prs_struct *data, pr
        SAFE_FREE(rparam);
 
        if (prdata == NULL) {
-               DEBUG(0,("rpc_api_pipe: cmd %x on pipe %x failed to return data.\n",
-                       (int)cmd, (int)cli->nt_pipe_fnum));
+               DEBUG(0,("rpc_api_pipe: pipe %x failed to return data.\n",
+                       (int)cli->nt_pipe_fnum));
                return False;
        }
 
@@ -542,9 +586,9 @@ static BOOL rpc_api_pipe(struct cli_state *cli, uint16 cmd, prs_struct *data, pr
 
  ********************************************************************/
 
-static BOOL create_rpc_bind_req(prs_struct *rpc_out, BOOL do_auth, uint32 rpc_call_id,
+static BOOL create_rpc_bind_req(prs_struct *rpc_out, BOOL do_auth, BOOL do_netsec, uint32 rpc_call_id,
                                 RPC_IFACE *abstract, RPC_IFACE *transfer,
-                                char *my_name, char *domain, uint32 neg_flags)
+                                const char *my_name, const char *domain, uint32 neg_flags)
 {
        RPC_HDR hdr;
        RPC_HDR_RB hdr_rb;
@@ -596,8 +640,45 @@ static BOOL create_rpc_bind_req(prs_struct *rpc_out, BOOL do_auth, uint32 rpc_ca
                auth_len = prs_offset(&auth_info) - RPC_HDR_AUTH_LEN;
        }
 
+       if (do_netsec) {
+               RPC_HDR_AUTH hdr_auth;
+               RPC_AUTH_NETSEC_NEG netsec_neg;
+
+               /*
+                * Create the auth structs we will marshall.
+                */
+
+               init_rpc_hdr_auth(&hdr_auth, NETSEC_AUTH_TYPE, NETSEC_AUTH_LEVEL,
+                                 0x00, 1);
+               init_rpc_auth_netsec_neg(&netsec_neg, domain, my_name);
+
+               /*
+                * Use the 4k buffer to store the auth info.
+                */
+
+               prs_give_memory( &auth_info, buffer, sizeof(buffer), False);
+
+               /*
+                * Now marshall the data into the temporary parse_struct.
+                */
+
+               if(!smb_io_rpc_hdr_auth("hdr_auth", &hdr_auth, &auth_info, 0)) {
+                       DEBUG(0,("Failed to marshall RPC_HDR_AUTH.\n"));
+                       return False;
+               }
+
+               if(!smb_io_rpc_auth_netsec_neg("netsec_neg",
+                                              &netsec_neg, &auth_info, 0)) {
+                       DEBUG(0,("Failed to marshall RPC_AUTH_NETSEC_NEG.\n"));
+                       return False;
+               }
+
+               /* Auth len in the rpc header doesn't include auth_header. */
+               auth_len = prs_offset(&auth_info) - RPC_HDR_AUTH_LEN;
+       }
+
        /* create the request RPC_HDR */
-       init_rpc_hdr(&hdr, RPC_BIND, 0x0, rpc_call_id, 
+       init_rpc_hdr(&hdr, RPC_BIND, 0x3, rpc_call_id, 
                RPC_HEADER_LEN + RPC_HDR_RB_LEN + prs_offset(&auth_info),
                auth_len);
 
@@ -638,7 +719,7 @@ static BOOL create_rpc_bind_req(prs_struct *rpc_out, BOOL do_auth, uint32 rpc_ca
  ********************************************************************/
 
 static BOOL create_rpc_bind_resp(struct pwd_info *pwd,
-                               char *domain, char *user_name, char *my_name,
+                               const char *domain, const char *user_name, const char *my_name,
                                uint32 ntlmssp_cli_flgs,
                                uint32 rpc_call_id,
                                prs_struct *rpc_out)
@@ -728,17 +809,18 @@ static BOOL create_rpc_bind_resp(struct pwd_info *pwd,
  Creates a DCE/RPC request.
  ********************************************************************/
 
-static BOOL create_rpc_request(prs_struct *rpc_out, uint8 op_num, int data_len, int auth_len)
+static uint32 create_rpc_request(prs_struct *rpc_out, uint8 op_num, int data_len, int auth_len, uint8 flags, uint32 oldid, uint32 data_left)
 {
        uint32 alloc_hint;
        RPC_HDR     hdr;
        RPC_HDR_REQ hdr_req;
+       uint32 callid = oldid ? oldid : get_rpc_call_id();
 
        DEBUG(5,("create_rpc_request: opnum: 0x%x data_len: 0x%x\n", op_num, data_len));
 
        /* create the rpc header RPC_HDR */
-       init_rpc_hdr(&hdr, RPC_REQUEST, RPC_FLG_FIRST | RPC_FLG_LAST,
-                    get_rpc_call_id(), data_len, auth_len);
+       init_rpc_hdr(&hdr, RPC_REQUEST, flags,
+                    callid, data_len, auth_len);
 
        /*
         * The alloc hint should be the amount of data, not including 
@@ -758,136 +840,293 @@ static BOOL create_rpc_request(prs_struct *rpc_out, uint8 op_num, int data_len,
 
        /* stream-time... */
        if(!smb_io_rpc_hdr("hdr    ", &hdr, rpc_out, 0))
-               return False;
+               return 0;
 
        if(!smb_io_rpc_hdr_req("hdr_req", &hdr_req, rpc_out, 0))
-               return False;
+               return 0;
 
        if (prs_offset(rpc_out) != RPC_HEADER_LEN + RPC_HDR_REQ_LEN)
+               return 0;
+
+       return callid;
+}
+
+/*******************************************************************
+ Puts an NTLMSSP auth header into an rpc request.
+ ********************************************************************/
+
+static BOOL create_ntlmssp_auth_hdr(prs_struct *outgoing_packet, BOOL auth_verify)
+{
+       RPC_HDR_AUTH hdr_auth;
+
+       init_rpc_hdr_auth(&hdr_auth, NTLMSSP_AUTH_TYPE,
+                         NTLMSSP_AUTH_LEVEL, 0x08, 
+                         (auth_verify ? 1 : 0));
+       if(!smb_io_rpc_hdr_auth("hdr_auth", &hdr_auth, 
+                               outgoing_packet, 0)) {
+               DEBUG(0,("create_auth_hdr:Failed to marshal RPC_HDR_AUTH.\n"));
                return False;
+       }
+       return True;
+}
+
+/*******************************************************************
+ Puts a NETLOGON schannel auth header into an rpc request.
+ ********************************************************************/
 
+static BOOL create_netsec_auth_hdr(prs_struct *outgoing_packet, int padding)
+{
+       RPC_HDR_AUTH hdr_auth;
+
+       init_rpc_hdr_auth(&hdr_auth, NETSEC_AUTH_TYPE,
+                         NETSEC_AUTH_LEVEL, padding, 1);
+       if(!smb_io_rpc_hdr_auth("hdr_auth", &hdr_auth, 
+                               outgoing_packet, 0)) {
+               DEBUG(0,("create_auth_hdr:Failed to marshal RPC_HDR_AUTH.\n"));
+               return False;
+       }
        return True;
 }
 
+/*******************************************************************
+ Puts auth data into an rpc request.
+ ********************************************************************/
 
-/****************************************************************************
- Send a request on an rpc pipe.
- ****************************************************************************/
+static BOOL create_auth_data(struct cli_state *cli, uint32 crc32, 
+                            prs_struct *outgoing_packet)
+{
+       char *pdata_out = prs_data_p(outgoing_packet);
+       RPC_AUTH_NTLMSSP_CHK chk;
+       uint32 current_offset = prs_offset(outgoing_packet);
+
+       init_rpc_auth_ntlmssp_chk(&chk, NTLMSSP_SIGN_VERSION, 
+                                 crc32, cli->ntlmssp_seq_num++);
+       if(!smb_io_rpc_auth_ntlmssp_chk("auth_sign", &chk, 
+                                       outgoing_packet, 0)) {
+               DEBUG(0,("create_auth_data: Failed to marshal RPC_AUTH_NTLMSSP_CHK.\n"));
+               return False;
+       }
+       NTLMSSPcalc_ap(cli, (unsigned char*)
+                      &pdata_out[current_offset+4], 
+                      RPC_AUTH_NTLMSSP_CHK_LEN - 4);
+       return True;
+}
+
+/**
+ * Send a request on an RPC pipe and get a response.
+ *
+ * @param data NDR contents of the request to be sent.
+ * @param rdata Unparsed NDR response data.
+**/
 
 BOOL rpc_api_pipe_req(struct cli_state *cli, uint8 op_num,
                       prs_struct *data, prs_struct *rdata)
 {
-       prs_struct outgoing_packet;
-       uint32 data_len;
-       uint32 auth_len;
-       BOOL ret;
-       BOOL auth_verify;
-       BOOL auth_seal;
-       uint32 crc32 = 0;
-       char *pdata_out = NULL;
+       uint32 auth_len, max_data, data_left, data_sent;
+       BOOL ret = False;
+       BOOL auth_verify, auth_seal, auth_schannel;
+       uint32 callid = 0;
+       fstring dump_name;
 
        auth_verify = ((cli->ntlmssp_srv_flgs & NTLMSSP_NEGOTIATE_SIGN) != 0);
        auth_seal   = ((cli->ntlmssp_srv_flgs & NTLMSSP_NEGOTIATE_SEAL) != 0);
+       auth_schannel = (cli->saved_netlogon_pipe_fnum != 0);
 
-       /*
-        * The auth_len doesn't include the RPC_HDR_AUTH_LEN.
-        */
+       auth_len = 0;
+
+       if (auth_verify)
+               auth_len = RPC_AUTH_NTLMSSP_CHK_LEN;
 
-       auth_len = (auth_verify ? RPC_AUTH_NTLMSSP_CHK_LEN : 0);
+       if (auth_schannel)
+               auth_len = RPC_AUTH_NETSEC_CHK_LEN;
 
        /*
-        * PDU len is header, plus request header, plus data, plus
-        * auth_header_len (if present), plus auth_len (if present).
-        * NB. The auth stuff should be aligned on an 8 byte boundary
-        * to be totally DCE/RPC spec complient. For now we cheat and
-        * hope that the data structs defined are a multiple of 8 bytes.
+        * calc how much actual data we can send in a PDU fragment
         */
+       max_data = cli->max_xmit_frag - RPC_HEADER_LEN - RPC_HDR_REQ_LEN -
+               (auth_verify ? RPC_HDR_AUTH_LEN : 0) - auth_len - 8;
 
-       if((prs_offset(data) % 8) != 0) {
-               DEBUG(5,("rpc_api_pipe_req: Outgoing data not a multiple of 8 bytes....\n"));
-       }
+       for (data_left = prs_offset(data), data_sent = 0; data_left > 0;) {
+               prs_struct outgoing_packet;
+               uint32 data_len, send_size;
+               uint8 flags = 0;
+               uint32 crc32 = 0;
+               uint32 auth_padding = 0;
+               RPC_AUTH_NETSEC_CHK verf;
 
-       data_len = RPC_HEADER_LEN + RPC_HDR_REQ_LEN + prs_offset(data) +
-                       (auth_verify ? RPC_HDR_AUTH_LEN : 0) + auth_len;
+               /*
+                * how much will we send this time
+                */
+               send_size = MIN(data_left, max_data);
 
-       /*
-        * Malloc a parse struct to hold it (and enough for alignments).
-        */
+               /*
+                * NT expects the data that is sealed to be 8-byte
+                * aligned. The padding must be encrypted as well and
+                * taken into account when generating the
+                * authentication verifier. The amount of padding must
+                * be stored in the auth header.
+                */
 
-       if(!prs_init(&outgoing_packet, data_len + 8, cli->mem_ctx, MARSHALL)) {
-               DEBUG(0,("rpc_api_pipe_req: Failed to malloc %u bytes.\n", (unsigned int)data_len ));
-               return False;
-       }
+               if (auth_schannel)
+                       auth_padding = 8 - (send_size & 7);
 
-       pdata_out = prs_data_p(&outgoing_packet);
-       
-       /*
-        * Write out the RPC header and the request header.
-        */
+               data_len = RPC_HEADER_LEN + RPC_HDR_REQ_LEN + send_size +
+                       ((auth_verify|auth_schannel) ? RPC_HDR_AUTH_LEN : 0) +
+                       auth_len + auth_padding;
 
-       if(!create_rpc_request(&outgoing_packet, op_num, data_len, auth_len)) {
-               DEBUG(0,("rpc_api_pipe_req: Failed to create RPC request.\n"));
-               prs_mem_free(&outgoing_packet);
-               return False;
-       }
+               /*
+                * Malloc parse struct to hold it (and enough for alignments).
+                */
+               if(!prs_init(&outgoing_packet, data_len + 8, 
+                            cli->mem_ctx, MARSHALL)) {
+                       DEBUG(0,("rpc_api_pipe_req: Failed to malloc %u bytes.\n", (unsigned int)data_len ));
+                       return False;
+               }
 
-       /*
-        * Seal the outgoing data if requested.
-        */
+               if (data_left == prs_offset(data))
+                       flags |= RPC_FLG_FIRST;
 
-       if (auth_seal) {
-               crc32 = crc32_calc_buffer(prs_data_p(data), prs_offset(data));
-               NTLMSSPcalc_ap(cli, (unsigned char*)prs_data_p(data), prs_offset(data));
-       }
+               if (data_left <= max_data)
+                       flags |= RPC_FLG_LAST;
+               /*
+                * Write out the RPC header and the request header.
+                */
+               if(!(callid = create_rpc_request(&outgoing_packet, op_num, 
+                                                data_len, auth_len, flags, 
+                                                callid, data_left))) {
+                       DEBUG(0,("rpc_api_pipe_req: Failed to create RPC request.\n"));
+                       prs_mem_free(&outgoing_packet);
+                       return False;
+               }
 
-       /*
-        * Now copy the data into the outgoing packet.
-        */
+               /*
+                * Seal the outgoing data if requested.
+                */
+               if (auth_seal) {
+                       crc32 = crc32_calc_buffer(prs_data_p(data) + data_sent,
+                                                 send_size);
+                       NTLMSSPcalc_ap(cli, (unsigned char*)prs_data_p(data) +
+                                      data_sent, send_size);
+               }
 
-       if(!prs_append_prs_data( &outgoing_packet, data)) {
-               DEBUG(0,("rpc_api_pipe_req: Failed to append data to outgoing packet.\n"));
-               prs_mem_free(&outgoing_packet);
-               return False;
-       }
+               /*
+                * Now copy the data into the outgoing packet.
+                */
 
-       /*
-        * Add a trailing auth_verifier if needed.
-        */
+               if (auth_schannel) {
+                       static const uchar netsec_sig[8] = NETSEC_SIGNATURE;
+                       static const uchar nullbytes[8] = { 0,0,0,0,0,0,0,0 };
+                       uchar sign[8];
+                       prs_struct netsec_blob;
 
-       if (auth_seal || auth_verify) {
-               RPC_HDR_AUTH hdr_auth;
+                       if ((cli->auth_info.seq_num & 1) != 0) {
+                               DEBUG(0,("SCHANNEL ERROR: seq_num must be even in client (seq_num=%d)\n",
+                                       cli->auth_info.seq_num));
+                       }
 
-               init_rpc_hdr_auth(&hdr_auth, NTLMSSP_AUTH_TYPE,
-                       NTLMSSP_AUTH_LEVEL, 0x08, (auth_verify ? 1 : 0));
-               if(!smb_io_rpc_hdr_auth("hdr_auth", &hdr_auth, &outgoing_packet, 0)) {
-                       DEBUG(0,("rpc_api_pipe_req: Failed to marshal RPC_HDR_AUTH.\n"));
-                       prs_mem_free(&outgoing_packet);
-                       return False;
-               }
-       }
+                       DEBUG(10,("SCHANNEL seq_num=%d\n", cli->auth_info.seq_num));
 
-       /*
-        * Finally the auth data itself.
-        */
+                       RSIVAL(sign, 0, cli->auth_info.seq_num);
+                       SIVAL(sign, 4, 0x80);
 
-       if (auth_verify) {
-               RPC_AUTH_NTLMSSP_CHK chk;
-               uint32 current_offset = prs_offset(&outgoing_packet);
+                       if (!prs_init(&netsec_blob, send_size+auth_padding,
+                                     cli->mem_ctx, MARSHALL)) {
+                               DEBUG(0,("Could not malloc %u bytes",
+                                        send_size+auth_padding));
+                               prs_mem_free(&outgoing_packet);
+                               return False;
+                       }
 
-               init_rpc_auth_ntlmssp_chk(&chk, NTLMSSP_SIGN_VERSION, crc32, cli->ntlmssp_seq_num++);
-               if(!smb_io_rpc_auth_ntlmssp_chk("auth_sign", &chk, &outgoing_packet, 0)) {
-                       DEBUG(0,("rpc_api_pipe_req: Failed to marshal RPC_AUTH_NTLMSSP_CHK.\n"));
-                       prs_mem_free(&outgoing_packet);
-                       return False;
+                       if(!prs_append_some_prs_data(&netsec_blob, data, 
+                                                    data_sent, send_size)) {
+                               DEBUG(0,("Failed to append data to netsec blob\n"));
+                               prs_mem_free(&outgoing_packet);
+                               return False;
+                       }
+
+                       netsec_blob.align = 8;
+
+                       if (!prs_align(&netsec_blob)) {
+                               DEBUG(0,("Could not align netsec blob\n"));
+                               prs_mem_free(&outgoing_packet);
+                               return False;
+                       }
+
+                       init_rpc_auth_netsec_chk(&verf, netsec_sig, nullbytes,
+                                                sign, nullbytes);
+
+                       netsec_encode(&cli->auth_info, &verf,
+                                     prs_data_p(&netsec_blob),
+                                     prs_data_size(&netsec_blob));
+
+                       prs_append_prs_data(&outgoing_packet, &netsec_blob);
+                       prs_mem_free(&netsec_blob);
+
+                       cli->auth_info.seq_num++;
+
+               } else {
+                       if(!prs_append_some_prs_data(&outgoing_packet, data, 
+                                                    data_sent, send_size)) {
+                               DEBUG(0,("rpc_api_pipe_req: Failed to append "
+                                        "data to outgoing packet.\n"));
+                               prs_mem_free(&outgoing_packet);
+                               return False;
+                       }
+               }
+
+               /*
+                * Add a trailing auth_verifier if needed.
+                */
+               if (auth_seal || auth_verify) {
+                       if(!create_ntlmssp_auth_hdr(&outgoing_packet, auth_verify)) {
+                               prs_mem_free(&outgoing_packet);
+                               return False;
+                       }
+               }
+
+               /*
+                * Finally the auth data itself.
+                */
+               if (auth_verify) {
+                       if (!create_auth_data(cli, crc32, &outgoing_packet)) {
+                               prs_mem_free(&outgoing_packet);
+                               return False;
+                       }
                }
-               NTLMSSPcalc_ap(cli, (unsigned char*)&pdata_out[current_offset+4], RPC_AUTH_NTLMSSP_CHK_LEN - 4);
-       }
 
-       DEBUG(100,("data_len: %x data_calc_len: %x\n", data_len, prs_offset(&outgoing_packet)));
+               if (auth_schannel) {
 
-       ret = rpc_api_pipe(cli, 0x0026, &outgoing_packet, rdata);
+                       if (!create_netsec_auth_hdr(&outgoing_packet,
+                                                   auth_padding)) {
+                               prs_mem_free(&outgoing_packet);
+                               return False;
+                       }
+
+                       if (!smb_io_rpc_auth_netsec_chk("", &verf,
+                                                       &outgoing_packet, 0)) {
+                               prs_mem_free(&outgoing_packet);
+                               return False;
+                       }
+               }
 
-       prs_mem_free(&outgoing_packet);
+               DEBUG(100,("data_len: %x data_calc_len: %x\n", data_len, 
+                          prs_offset(&outgoing_packet)));
+               
+               if (flags & RPC_FLG_LAST)
+                       ret = rpc_api_pipe(cli, &outgoing_packet, rdata);
+               else {
+                       cli_write(cli, cli->nt_pipe_fnum, 0x0008,
+                                  prs_data_p(&outgoing_packet),
+                                  data_sent, data_len);
+               }
+               prs_mem_free(&outgoing_packet);
+               data_sent += send_size;
+               data_left -= send_size;
+       }
+       /* Also capture received data */
+       slprintf(dump_name, sizeof(dump_name) - 1, "reply_%s",
+                cli_pipe_get_name(cli));
+       prs_dump(dump_name, op_num, rdata);
 
        return ret;
 }
@@ -940,29 +1179,46 @@ static BOOL rpc_pipe_set_hnd_state(struct cli_state *cli, const char *pipe_name,
  check the rpc bind acknowledge response
 ****************************************************************************/
 
-static BOOL valid_pipe_name(const char *pipe_name, RPC_IFACE *abstract, RPC_IFACE *transfer)
+int get_pipe_index( const char *pipe_name )
 {
        int pipe_idx = 0;
 
        while (pipe_names[pipe_idx].client_pipe != NULL) {
-               if (strequal(pipe_name, pipe_names[pipe_idx].client_pipe )) {
-                       DEBUG(5,("Bind Abstract Syntax: "));    
-                       dump_data(5, (char*)&(pipe_names[pipe_idx].abstr_syntax), 
-                                 sizeof(pipe_names[pipe_idx].abstr_syntax));
-                       DEBUG(5,("Bind Transfer Syntax: "));
-                       dump_data(5, (char*)&(pipe_names[pipe_idx].trans_syntax),
-                                 sizeof(pipe_names[pipe_idx].trans_syntax));
-
-                       /* copy the required syntaxes out so we can do the right bind */
-                       *transfer = pipe_names[pipe_idx].trans_syntax;
-                       *abstract = pipe_names[pipe_idx].abstr_syntax;
-
-                       return True;
-               }
+               if (strequal(pipe_name, pipe_names[pipe_idx].client_pipe )) 
+                       return pipe_idx;
                pipe_idx++;
        };
 
-       DEBUG(5,("Bind RPC Pipe[%s] unsupported\n", pipe_name));
+       return -1;
+}
+
+
+/****************************************************************************
+ check the rpc bind acknowledge response
+****************************************************************************/
+
+const char* get_pipe_name_from_index( const int pipe_index )
+{
+
+       if ( (pipe_index < 0) || (pipe_index >= PI_MAX_PIPES) )
+               return NULL;
+
+       return pipe_names[pipe_index].client_pipe;              
+}
+
+/****************************************************************************
+ Check to see if this pipe index points to one of 
+ the pipes only supported by Win2k
+ ****************************************************************************/
+
+BOOL is_win2k_pipe( const int pipe_idx )
+{
+       switch ( pipe_idx )
+       {
+               case PI_LSARPC_DS:
+                       return True;
+       }
+       
        return False;
 }
 
@@ -970,28 +1226,50 @@ static BOOL valid_pipe_name(const char *pipe_name, RPC_IFACE *abstract, RPC_IFAC
  check the rpc bind acknowledge response
 ****************************************************************************/
 
-static BOOL check_bind_response(RPC_HDR_BA *hdr_ba, const char *pipe_name, RPC_IFACE *transfer)
+static BOOL valid_pipe_name(const int pipe_idx, RPC_IFACE *abstract, RPC_IFACE *transfer)
+{
+       if ( pipe_idx >= PI_MAX_PIPES ) {
+               DEBUG(0,("valid_pipe_name: Programmer error!  Invalid pipe index [%d]\n",
+                       pipe_idx));
+               return False;
+       }
+
+       DEBUG(5,("Bind Abstract Syntax: "));    
+       dump_data(5, (char*)&(pipe_names[pipe_idx].abstr_syntax), 
+                 sizeof(pipe_names[pipe_idx].abstr_syntax));
+       DEBUG(5,("Bind Transfer Syntax: "));
+       dump_data(5, (char*)&(pipe_names[pipe_idx].trans_syntax),
+                 sizeof(pipe_names[pipe_idx].trans_syntax));
+
+       /* copy the required syntaxes out so we can do the right bind */
+       
+       *transfer = pipe_names[pipe_idx].trans_syntax;
+       *abstract = pipe_names[pipe_idx].abstr_syntax;
+
+       return True;
+}
+
+/****************************************************************************
+ check the rpc bind acknowledge response
+****************************************************************************/
+
+static BOOL check_bind_response(RPC_HDR_BA *hdr_ba, const int pipe_idx, RPC_IFACE *transfer)
 {
        int i = 0;
 
-       while ((pipe_names[i].client_pipe != NULL) && hdr_ba->addr.len > 0) {
-               if ((strequal(pipe_name, pipe_names[i].client_pipe ))) {
-                       if (strequal(hdr_ba->addr.str, pipe_names[i].server_pipe )) {
-                               DEBUG(5,("bind_rpc_pipe: server pipe_name found: %s\n",
-                                        pipe_names[i].server_pipe ));
-                               break;
-                       } else {
-                               DEBUG(4,("bind_rpc_pipe: pipe_name %s != expected pipe %s.  oh well!\n",
-                                        pipe_names[i].server_pipe ,
-                                        hdr_ba->addr.str));
-                               break;
-                       }
-               } else {
-                       i++;
-               }
+       if ( hdr_ba->addr.len <= 0)
+               return False;
+               
+       if ( !strequal(hdr_ba->addr.str, pipe_names[pipe_idx].server_pipe )) 
+       {
+               DEBUG(4,("bind_rpc_pipe: pipe_name %s != expected pipe %s.  oh well!\n",
+                        pipe_names[i].server_pipe ,hdr_ba->addr.str));
+               return False;
        }
+       
+       DEBUG(5,("bind_rpc_pipe: server pipe_name found: %s\n", pipe_names[i].server_pipe ));
 
-       if (pipe_names[i].server_pipe == NULL) {
+       if (pipe_names[pipe_idx].server_pipe == NULL) {
                DEBUG(2,("bind_rpc_pipe: pipe name %s unsupported\n", hdr_ba->addr.str));
                return False;
        }
@@ -999,7 +1277,7 @@ static BOOL check_bind_response(RPC_HDR_BA *hdr_ba, const char *pipe_name, RPC_I
        /* check the transfer syntax */
        if ((hdr_ba->transfer.version != transfer->version) ||
             (memcmp(&hdr_ba->transfer.uuid, &transfer->uuid, sizeof(transfer->uuid)) !=0)) {
-               DEBUG(0,("bind_rpc_pipe: transfer syntax differs\n"));
+               DEBUG(2,("bind_rpc_pipe: transfer syntax differs\n"));
                return False;
        }
 
@@ -1052,7 +1330,7 @@ static BOOL rpc_send_auth_reply(struct cli_state *cli, prs_struct *rdata, uint32
        prs_give_memory( &rpc_out, buffer, sizeof(buffer), False);
 
        create_rpc_bind_resp(&cli->pwd, cli->domain,
-                            cli->user_name, global_myname, 
+                            cli->user_name, global_myname()
                             cli->ntlmssp_cli_flgs, rpc_call_id,
                             &rpc_out);
                                            
@@ -1104,7 +1382,8 @@ static BOOL rpc_send_auth_reply(struct cli_state *cli, prs_struct *rdata, uint32
  Do an rpc bind.
 ****************************************************************************/
 
-BOOL rpc_pipe_bind(struct cli_state *cli, const char *pipe_name, char *my_name)
+static BOOL rpc_pipe_bind(struct cli_state *cli, int pipe_idx, const char *my_name,
+                         BOOL do_netsec)
 {
        RPC_IFACE abstract;
        RPC_IFACE transfer;
@@ -1114,9 +1393,12 @@ BOOL rpc_pipe_bind(struct cli_state *cli, const char *pipe_name, char *my_name)
        uint32 rpc_call_id;
        char buffer[MAX_PDU_FRAG_LEN];
 
-       DEBUG(5,("Bind RPC Pipe[%x]: %s\n", cli->nt_pipe_fnum, pipe_name));
+       if ( (pipe_idx < 0) || (pipe_idx >= PI_MAX_PIPES) )
+               return False;
 
-       if (!valid_pipe_name(pipe_name, &abstract, &transfer))
+       DEBUG(5,("Bind RPC Pipe[%x]: %s\n", cli->nt_pipe_fnum, pipe_names[pipe_idx].client_pipe));
+
+       if (!valid_pipe_name(pipe_idx, &abstract, &transfer))
                return False;
 
        prs_init(&rpc_out, 0, cli->mem_ctx, MARSHALL);
@@ -1130,15 +1412,15 @@ BOOL rpc_pipe_bind(struct cli_state *cli, const char *pipe_name, char *my_name)
        rpc_call_id = get_rpc_call_id();
 
        /* Marshall the outgoing data. */
-       create_rpc_bind_req(&rpc_out, do_auth, rpc_call_id,
+       create_rpc_bind_req(&rpc_out, do_auth, do_netsec, rpc_call_id,
                            &abstract, &transfer,
-                           global_myname, cli->domain, cli->ntlmssp_cli_flgs);
+                           global_myname(), cli->domain, cli->ntlmssp_cli_flgs);
 
        /* Initialize the incoming data struct. */
        prs_init(&rdata, 0, cli->mem_ctx, UNMARSHALL);
 
        /* send data on \PIPE\.  receive a response */
-       if (rpc_api_pipe(cli, 0x0026, &rpc_out, &rdata)) {
+       if (rpc_api_pipe(cli, &rpc_out, &rdata)) {
                RPC_HDR_BA   hdr_ba;
 
                DEBUG(5, ("rpc_pipe_bind: rpc_api_pipe returned OK.\n"));
@@ -1149,8 +1431,8 @@ BOOL rpc_pipe_bind(struct cli_state *cli, const char *pipe_name, char *my_name)
                        return False;
                }
 
-               if(!check_bind_response(&hdr_ba, pipe_name, &transfer)) {
-                       DEBUG(0,("rpc_pipe_bind: check_bind_response failed.\n"));
+               if(!check_bind_response(&hdr_ba, pipe_idx, &transfer)) {
+                       DEBUG(2,("rpc_pipe_bind: check_bind_response failed.\n"));
                        prs_mem_free(&rdata);
                        return False;
                }
@@ -1175,45 +1457,42 @@ BOOL rpc_pipe_bind(struct cli_state *cli, const char *pipe_name, char *my_name)
        return True;
 }
 
-/****************************************************************************
- Set ntlmssp negotiation flags.
- ****************************************************************************/
-
-void cli_nt_set_ntlmssp_flgs(struct cli_state *cli, uint32 ntlmssp_flgs)
-{
-       cli->ntlmssp_cli_flgs = ntlmssp_flgs;
-}
-
-
 /****************************************************************************
  Open a session.
  ****************************************************************************/
 
-BOOL cli_nt_session_open(struct cli_state *cli, const char *pipe_name)
+BOOL cli_nt_session_open(struct cli_state *cli, const int pipe_idx)
 {
        int fnum;
 
+       /* At the moment we can't have more than one pipe open over
+           a cli connection. )-: */
+
        SMB_ASSERT(cli->nt_pipe_fnum == 0);
+       
+       /* The pipe index must fall within our array */
+
+       SMB_ASSERT((pipe_idx >= 0) && (pipe_idx < PI_MAX_PIPES));
 
        if (cli->capabilities & CAP_NT_SMBS) {
-               if ((fnum = cli_nt_create(cli, &pipe_name[5], DESIRED_ACCESS_PIPE)) == -1) {
+               if ((fnum = cli_nt_create(cli, &pipe_names[pipe_idx].client_pipe[5], DESIRED_ACCESS_PIPE)) == -1) {
                        DEBUG(0,("cli_nt_session_open: cli_nt_create failed on pipe %s to machine %s.  Error was %s\n",
-                                &pipe_name[5], cli->desthost, cli_errstr(cli)));
+                                &pipe_names[pipe_idx].client_pipe[5], cli->desthost, cli_errstr(cli)));
                        return False;
                }
 
                cli->nt_pipe_fnum = (uint16)fnum;
        } else {
-               if ((fnum = cli_open(cli, pipe_name, O_CREAT|O_RDWR, DENY_NONE)) == -1) {
+               if ((fnum = cli_open(cli, pipe_names[pipe_idx].client_pipe, O_CREAT|O_RDWR, DENY_NONE)) == -1) {
                        DEBUG(0,("cli_nt_session_open: cli_open failed on pipe %s to machine %s.  Error was %s\n",
-                                pipe_name, cli->desthost, cli_errstr(cli)));
+                                pipe_names[pipe_idx].client_pipe, cli->desthost, cli_errstr(cli)));
                        return False;
                }
 
                cli->nt_pipe_fnum = (uint16)fnum;
 
                /**************** Set Named Pipe State ***************/
-               if (!rpc_pipe_set_hnd_state(cli, pipe_name, 0x4300)) {
+               if (!rpc_pipe_set_hnd_state(cli, pipe_names[pipe_idx].client_pipe, 0x4300)) {
                        DEBUG(0,("cli_nt_session_open: pipe hnd state failed.  Error was %s\n",
                                  cli_errstr(cli)));
                        cli_close(cli, cli->nt_pipe_fnum);
@@ -1223,9 +1502,9 @@ BOOL cli_nt_session_open(struct cli_state *cli, const char *pipe_name)
 
        /******************* bind request on pipe *****************/
 
-       if (!rpc_pipe_bind(cli, pipe_name, global_myname)) {
-               DEBUG(0,("cli_nt_session_open: rpc bind failed. Error was %s\n",
-                         cli_errstr(cli)));
+       if (!rpc_pipe_bind(cli, pipe_idx, global_myname(), False)) {
+               DEBUG(2,("cli_nt_session_open: rpc bind to %s failed\n",
+                        get_pipe_name_from_index(pipe_idx)));
                cli_close(cli, cli->nt_pipe_fnum);
                return False;
        }
@@ -1239,22 +1518,136 @@ BOOL cli_nt_session_open(struct cli_state *cli, const char *pipe_name)
        strupper(cli->srv_name_slash);
 
        fstrcpy(cli->clnt_name_slash, "\\\\");
-       fstrcat(cli->clnt_name_slash, global_myname);
+       fstrcat(cli->clnt_name_slash, global_myname());
        strupper(cli->clnt_name_slash);
 
-       fstrcpy(cli->mach_acct, global_myname);
+       fstrcpy(cli->mach_acct, global_myname());
        fstrcat(cli->mach_acct, "$");
        strupper(cli->mach_acct);
 
+       /* Remember which pipe we're talking to */
+       fstrcpy(cli->pipe_name, pipe_names[pipe_idx].client_pipe);
+
+       return True;
+}
+
+
+/****************************************************************************
+ Open a session to the NETLOGON pipe using schannel.
+ ****************************************************************************/
+
+BOOL cli_nt_open_netlogon(struct cli_state *cli, const char *trust_password,
+                         int sec_chan)
+{
+       NTSTATUS result;
+       uint32 neg_flags = 0x000001ff;
+       int fnum;
+
+       if (lp_client_schannel() != False)
+               neg_flags |= NETLOGON_NEG_SCHANNEL;
+
+
+       if (!cli_nt_session_open(cli, PI_NETLOGON)) {
+               return False;
+       }
+
+       if (!secrets_init()) {
+               DEBUG(3,("Failed to init secrets.tdb\n"));
+               return False;
+       }
+
+       result = cli_nt_setup_creds(cli, sec_chan, trust_password,
+                                   &neg_flags, 2);
+
+       if (!NT_STATUS_IS_OK(result)) {
+               cli_nt_session_close(cli);
+               return False;
+       }
+
+       if ((lp_client_schannel() == True) &&
+           ((neg_flags & NETLOGON_NEG_SCHANNEL) == 0)) {
+
+               DEBUG(3, ("Server did not offer schannel\n"));
+               cli_nt_session_close(cli);
+               return False;
+       }
+
+       if ((lp_client_schannel() == False) ||
+           ((neg_flags & NETLOGON_NEG_SCHANNEL) == 0)) {
+               return True;
+       }
+
+       /* Server offered schannel, so try it. */
+
+       cli->auth_info.seq_num = 0;
+       memcpy(cli->auth_info.sess_key, cli->sess_key,
+              sizeof(cli->auth_info.sess_key));
+
+       cli->saved_netlogon_pipe_fnum = cli->nt_pipe_fnum;
+
+       if (cli->capabilities & CAP_NT_SMBS) {
+
+               /* The secure channel connection must be opened on the same 
+                   session (TCP connection) as the one the challenge was
+                   requested from. */
+
+               if ((fnum = cli_nt_create(cli, PIPE_NETLOGON_PLAIN,
+                                         DESIRED_ACCESS_PIPE)) == -1) {
+                       DEBUG(0,("cli_nt_create failed to %s machine %s. "
+                                "Error was %s\n",
+                                PIPE_NETLOGON, cli->desthost,
+                                cli_errstr(cli)));
+                       return False;
+               }
+               
+               cli->nt_pipe_fnum = (uint16)fnum;
+       } else {
+               if ((fnum = cli_open(cli, PIPE_NETLOGON,
+                                    O_CREAT|O_RDWR, DENY_NONE)) == -1) {
+                       DEBUG(0,("cli_open failed on pipe %s to machine %s. "
+                                "Error was %s\n",
+                                PIPE_NETLOGON, cli->desthost,
+                                cli_errstr(cli)));
+                       return False;
+               }
+
+               cli->nt_pipe_fnum = (uint16)fnum;
+
+               /**************** Set Named Pipe State ***************/
+               if (!rpc_pipe_set_hnd_state(cli, PIPE_NETLOGON, 0x4300)) {
+                       DEBUG(0,("Pipe hnd state failed.  Error was %s\n",
+                                 cli_errstr(cli)));
+                       cli_close(cli, cli->nt_pipe_fnum);
+                       return False;
+               }
+       }
+
+       if (!rpc_pipe_bind(cli, PI_NETLOGON, global_myname(), True)) {
+               DEBUG(2,("rpc bind to %s failed\n", PIPE_NETLOGON));
+               cli_close(cli, cli->nt_pipe_fnum);
+               return False;
+       }
+
        return True;
 }
 
+
+const char *cli_pipe_get_name(struct cli_state *cli)
+{
+       return cli->pipe_name;
+}
+
+
 /****************************************************************************
 close the session
 ****************************************************************************/
 
 void cli_nt_session_close(struct cli_state *cli)
 {
+       if (cli->saved_netlogon_pipe_fnum != 0) {
+               cli_close(cli, cli->saved_netlogon_pipe_fnum);
+               cli->saved_netlogon_pipe_fnum = 0;
+       }
        cli_close(cli, cli->nt_pipe_fnum);
        cli->nt_pipe_fnum = 0;
 }