Merge: clarify secure channel connection comment.
[kai/samba.git] / source / rpc_client / cli_pipe.c
index 90f08148ef43ecca25df284160b49c6b8f7cc823..a5cb6d425eef64588f87310d9891453faf708255 100644 (file)
@@ -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,6 +294,49 @@ 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;
 }
 
@@ -317,7 +361,7 @@ 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.
 
  ****************************************************************************/
 
@@ -542,7 +586,7 @@ static BOOL rpc_api_pipe(struct cli_state *cli, prs_struct *data, prs_struct *rd
 
  ********************************************************************/
 
-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,
                                 const char *my_name, const char *domain, uint32 neg_flags)
 {
@@ -596,6 +640,43 @@ 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, 0x3, rpc_call_id, 
                RPC_HEADER_LEN + RPC_HDR_RB_LEN + prs_offset(&auth_info),
@@ -747,9 +828,9 @@ static uint32 create_rpc_request(prs_struct *rpc_out, uint8 op_num, int data_len
         */
 
        if (auth_len != 0)
-               alloc_hint = data_left - RPC_HEADER_LEN - RPC_HDR_AUTH_LEN - auth_len;
+               alloc_hint = data_len - RPC_HEADER_LEN - RPC_HDR_AUTH_LEN - auth_len;
        else
-               alloc_hint = data_left - RPC_HEADER_LEN;
+               alloc_hint = data_len - RPC_HEADER_LEN;
 
        DEBUG(10,("create_rpc_request: data_len: %x auth_len: %x alloc_hint: %x\n",
                   data_len, auth_len, alloc_hint));
@@ -771,10 +852,10 @@ static uint32 create_rpc_request(prs_struct *rpc_out, uint8 op_num, int data_len
 }
 
 /*******************************************************************
- Puts an auth header into an rpc request.
+ Puts an NTLMSSP auth header into an rpc request.
  ********************************************************************/
 
-static BOOL create_auth_hdr(prs_struct *outgoing_packet, BOOL auth_verify)
+static BOOL create_ntlmssp_auth_hdr(prs_struct *outgoing_packet, BOOL auth_verify)
 {
        RPC_HDR_AUTH hdr_auth;
 
@@ -789,6 +870,24 @@ static BOOL create_auth_hdr(prs_struct *outgoing_packet, BOOL auth_verify)
        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.
  ********************************************************************/
@@ -825,33 +924,55 @@ BOOL rpc_api_pipe_req(struct cli_state *cli, uint8 op_num,
 {
        uint32 auth_len, max_data, data_left, data_sent;
        BOOL ret = False;
-       BOOL auth_verify, auth_seal;
+       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);
 
-       auth_len = (auth_verify ? RPC_AUTH_NTLMSSP_CHK_LEN : 0);
+       auth_len = 0;
+
+       if (auth_verify)
+               auth_len = RPC_AUTH_NTLMSSP_CHK_LEN;
+
+       if (auth_schannel)
+               auth_len = RPC_AUTH_NETSEC_CHK_LEN;
 
        /*
         * 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;
+               (auth_verify ? RPC_HDR_AUTH_LEN : 0) - auth_len - 8;
 
        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 callid;
+               uint32 auth_padding = 0;
+               RPC_AUTH_NETSEC_CHK verf;
 
                /*
                 * how much will we send this time
                 */
                send_size = MIN(data_left, max_data);
+
+               /*
+                * 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 (auth_schannel)
+                       auth_padding = 8 - (send_size & 7);
+
                data_len = RPC_HEADER_LEN + RPC_HDR_REQ_LEN + send_size +
-                       (auth_verify ? RPC_HDR_AUTH_LEN : 0) + auth_len;
+                       ((auth_verify|auth_schannel) ? RPC_HDR_AUTH_LEN : 0) +
+                       auth_len + auth_padding;
 
                /*
                 * Malloc parse struct to hold it (and enough for alignments).
@@ -862,11 +983,10 @@ BOOL rpc_api_pipe_req(struct cli_state *cli, uint8 op_num,
                        return False;
                }
 
-               if (data_left == prs_offset(data)) {
+               if (data_left == prs_offset(data))
                        flags |= RPC_FLG_FIRST;
-                       callid = 0;
-               }
-               if (data_left < max_data)
+
+               if (data_left <= max_data)
                        flags |= RPC_FLG_LAST;
                /*
                 * Write out the RPC header and the request header.
@@ -892,18 +1012,73 @@ BOOL rpc_api_pipe_req(struct cli_state *cli, uint8 op_num,
                /*
                 * Now copy the data into the outgoing packet.
                 */
-               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;
+
+               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 ((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));
+                       }
+
+                       DEBUG(10,("SCHANNEL seq_num=%d\n", cli->auth_info.seq_num));
+
+                       RSIVAL(sign, 0, cli->auth_info.seq_num);
+                       SIVAL(sign, 4, 0x80);
+
+                       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;
+                       }
+
+                       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_auth_hdr(&outgoing_packet, auth_verify)) {
+                       if(!create_ntlmssp_auth_hdr(&outgoing_packet, auth_verify)) {
                                prs_mem_free(&outgoing_packet);
                                return False;
                        }
@@ -919,6 +1094,21 @@ BOOL rpc_api_pipe_req(struct cli_state *cli, uint8 op_num,
                        }
                }
 
+               if (auth_schannel) {
+
+                       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;
+                       }
+               }
+
                DEBUG(100,("data_len: %x data_calc_len: %x\n", data_len, 
                           prs_offset(&outgoing_packet)));
                
@@ -1007,7 +1197,7 @@ int get_pipe_index( const char *pipe_name )
  check the rpc bind acknowledge response
 ****************************************************************************/
 
-char* get_pipe_name_from_index( const int pipe_index )
+const char* get_pipe_name_from_index( const int pipe_index )
 {
 
        if ( (pipe_index < 0) || (pipe_index >= PI_MAX_PIPES) )
@@ -1192,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, int pipe_idx, const 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;
@@ -1221,7 +1412,7 @@ BOOL rpc_pipe_bind(struct cli_state *cli, int pipe_idx, const 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);
 
@@ -1266,16 +1457,6 @@ BOOL rpc_pipe_bind(struct cli_state *cli, int pipe_idx, const 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.
  ****************************************************************************/
@@ -1284,6 +1465,9 @@ 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 */
@@ -1318,7 +1502,7 @@ BOOL cli_nt_session_open(struct cli_state *cli, const int pipe_idx)
 
        /******************* bind request on pipe *****************/
 
-       if (!rpc_pipe_bind(cli, pipe_idx, global_myname())) {
+       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);
@@ -1348,6 +1532,106 @@ BOOL cli_nt_session_open(struct cli_state *cli, const int pipe_idx)
 }
 
 
+/****************************************************************************
+ 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;
@@ -1360,6 +1644,10 @@ 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;
 }