Make rpc_read() match the control flow normally used in Samba
[jra/samba/.git] / source3 / rpc_client / cli_pipe.c
index b93e8181e96144616844b155ec9cbfb03637fcf7..fbd9b45a3a0884542f6b2e22eb3c4821d18714c8 100644 (file)
@@ -78,23 +78,18 @@ static const struct pipe_id_info {
 };
 
 /****************************************************************************
- Return the pipe name from the index.
+ Return the pipe name from the interface.
  ****************************************************************************/
 
-const char *cli_get_pipe_name(int pipe_idx)
-{
-       return &pipe_names[pipe_idx].client_pipe[5];
-}
-
-static const char *cli_get_pipe_name_from_iface(TALLOC_CTX *mem_ctx,
-                                               struct cli_state *cli,
-                                               const struct ndr_syntax_id *interface)
+const char *cli_get_pipe_name_from_iface(TALLOC_CTX *mem_ctx,
+                                        struct cli_state *cli,
+                                        const struct ndr_syntax_id *interface)
 {
        int i;
        for (i = 0; pipe_names[i].client_pipe; i++) {
                if (ndr_syntax_id_equal(pipe_names[i].abstr_syntax,
                                        interface)) {
-                       return cli_get_pipe_name(i);
+                       return &pipe_names[i].client_pipe[5];
                }
        }
 
@@ -106,30 +101,6 @@ static const char *cli_get_pipe_name_from_iface(TALLOC_CTX *mem_ctx,
        return NULL;
 }
 
-/****************************************************************************
- Return the pipe idx from the syntax.
- ****************************************************************************/
-int cli_get_pipe_idx(const RPC_IFACE *syntax)
-{
-       int i;
-       for (i = 0; pipe_names[i].client_pipe; i++) {
-               if (ndr_syntax_id_equal(pipe_names[i].abstr_syntax, syntax)) {
-                       return i;
-               }
-       }
-
-       return -1;
-}
-
-/********************************************************************
- LEGACY function to ease transition from pipe_idx to interface
- ********************************************************************/
-const struct ndr_syntax_id *cli_get_iface(int pipe_idx)
-{
-       SMB_ASSERT((pipe_idx >= 0) && (pipe_idx < PI_MAX_PIPES));
-       return pipe_names[pipe_idx].abstr_syntax;
-}
-
 /********************************************************************
  Map internal value to wire value.
  ********************************************************************/
@@ -205,15 +176,15 @@ static uint32 get_rpc_call_id(void)
  Read from a RPC named pipe
  ********************************************************************/
 static NTSTATUS rpc_read_np(struct cli_state *cli, const char *pipe_name,
-                           int fnum, char *buf, off_t offset, size_t size,
+                           int fnum, char *buf, size_t size,
                            ssize_t *pnum_read)
 {
        ssize_t num_read;
 
-       num_read = cli_read(cli, fnum, buf, offset, size);
+       num_read = cli_read(cli, fnum, buf, 0, size);
 
-       DEBUG(5,("rpc_read_np: num_read = %d, read offset: %u, to read: %u\n",
-               (int)num_read, (unsigned int)offset, (unsigned int)size));
+       DEBUG(5,("rpc_read_np: num_read = %d, to read: %u\n", (int)num_read,
+               (unsigned int)size));
 
        /*
        * A dos error of ERRDOS/ERRmoredata is not an error.
@@ -252,66 +223,71 @@ static NTSTATUS rpc_read_np(struct cli_state *cli, const char *pipe_name,
        return NT_STATUS_OK;
 }
 
+/*
+ * Realloc pdu to have a least "size" bytes
+ */
+
+static bool rpc_grow_buffer(prs_struct *pdu, size_t size)
+{
+       size_t extra_size;
+
+       if (prs_data_size(pdu) >= size) {
+               return true;
+       }
+
+       extra_size = size - prs_data_size(pdu);
+
+       if (!prs_force_grow(pdu, extra_size)) {
+               DEBUG(0, ("rpc_grow_buffer: Failed to grow parse struct by "
+                         "%d bytes.\n", (int)extra_size));
+               return false;
+       }
+
+       DEBUG(5, ("rpc_grow_buffer: grew buffer by %d bytes to %u\n",
+                 (int)extra_size, prs_data_size(pdu)));
+       return true;
+}
+
 
 /*******************************************************************
  Use SMBreadX to get rest of one fragment's worth of rpc data.
- Will expand the current_pdu struct to the correct size.
+ Reads the whole size or give an error message
  ********************************************************************/
 
 static NTSTATUS rpc_read(struct rpc_pipe_client *cli,
                        prs_struct *current_pdu,
-                       uint32 data_to_read,
-                       uint32 *current_pdu_offset)
+                       size_t size,
+                       uint32 current_pdu_offset)
 {
-       size_t size = (size_t)cli->max_recv_frag;
-       uint32 stream_offset = 0;
        ssize_t num_read = 0;
        char *pdata;
-       ssize_t extra_data_size = ((ssize_t)*current_pdu_offset) + ((ssize_t)data_to_read) - (ssize_t)prs_data_size(current_pdu);
-
-       DEBUG(5,("rpc_read: data_to_read: %u current_pdu offset: %u extra_data_size: %d\n",
-               (unsigned int)data_to_read, (unsigned int)*current_pdu_offset, (int)extra_data_size ));
-
-       /*
-        * Grow the buffer if needed to accommodate the data to be read.
-        */
 
-       if (extra_data_size > 0) {
-               if(!prs_force_grow(current_pdu, (uint32)extra_data_size)) {
-                       DEBUG(0,("rpc_read: Failed to grow parse struct by %d bytes.\n", (int)extra_data_size ));
-                       return NT_STATUS_NO_MEMORY;
-               }
-               DEBUG(5,("rpc_read: grew buffer by %d bytes to %u\n", (int)extra_data_size, prs_data_size(current_pdu) ));
-       }
+       DEBUG(5, ("rpc_read: data_to_read: %u current_pdu offset: %d\n",
+                 (unsigned int)size, (unsigned int)current_pdu_offset));
 
-       pdata = prs_data_p(current_pdu) + *current_pdu_offset;
+       pdata = prs_data_p(current_pdu) + current_pdu_offset;
 
-       do {
+       while (num_read < size) {
+               ssize_t thistime = 0;
                NTSTATUS status;
 
-               /* read data using SMBreadX */
-               if (size > (size_t)data_to_read) {
-                       size = (size_t)data_to_read;
-               }
-
                switch (cli->transport_type) {
                case NCACN_NP:
                        status = rpc_read_np(cli->trans.np.cli,
                                             cli->trans.np.pipe_name,
-                                            cli->trans.np.fnum, pdata,
-                                            (off_t)stream_offset, size,
-                                            &num_read);
+                                            cli->trans.np.fnum,
+                                            pdata + num_read,
+                                            size - num_read, &thistime);
                        break;
                case NCACN_IP_TCP:
                case NCACN_UNIX_STREAM:
                        status = NT_STATUS_OK;
-                       num_read = sys_read(cli->trans.sock.fd, pdata, size);
-                       if (num_read == -1) {
+                       thistime = sys_read(cli->trans.sock.fd,
+                                           pdata + num_read,
+                                           size - num_read);
+                       if (thistime == -1) {
                                status = map_nt_error_from_unix(errno);
                        }
-                       if (num_read == 0) {
-                               status = NT_STATUS_END_OF_FILE;
-                       }
                        break;
                default:
                        DEBUG(0, ("unknown transport type %d\n",
@@ -319,17 +295,18 @@ static NTSTATUS rpc_read(struct rpc_pipe_client *cli,
                        return NT_STATUS_INTERNAL_ERROR;
                }
 
-               data_to_read -= num_read;
-               stream_offset += num_read;
-               pdata += num_read;
+               if (thistime == 0) {
+                       status = NT_STATUS_END_OF_FILE;
+               }
 
-       } while (num_read > 0 && data_to_read > 0);
-       /* && err == (0x80000000 | STATUS_BUFFER_OVERFLOW)); */
+               if (!NT_STATUS_IS_OK(status)) {
+                       return status;
+               }
+
+               num_read += thistime;
+
+       }
 
-       /*
-        * Update the current offset into current_pdu by the amount read.
-        */
-       *current_pdu_offset += stream_offset;
        return NT_STATUS_OK;
 }
 
@@ -345,11 +322,16 @@ static NTSTATUS cli_pipe_get_current_pdu(struct rpc_pipe_client *cli, RPC_HDR *p
 
        /* Ensure we have at least RPC_HEADER_LEN worth of data to parse. */
        if (current_pdu_len < RPC_HEADER_LEN) {
-               /* rpc_read expands the current_pdu struct as neccessary. */
-               ret = rpc_read(cli, current_pdu, RPC_HEADER_LEN - current_pdu_len, &current_pdu_len);
+               if (!rpc_grow_buffer(current_pdu, RPC_HEADER_LEN)) {
+                       return NT_STATUS_NO_MEMORY;
+               }
+               ret = rpc_read(cli, current_pdu,
+                              RPC_HEADER_LEN - current_pdu_len,
+                              current_pdu_len);
                if (!NT_STATUS_IS_OK(ret)) {
                        return ret;
                }
+               current_pdu_len = RPC_HEADER_LEN;
        }
 
        /* This next call sets the endian bit correctly in current_pdu. */
@@ -359,19 +341,26 @@ static NTSTATUS cli_pipe_get_current_pdu(struct rpc_pipe_client *cli, RPC_HDR *p
                return NT_STATUS_BUFFER_TOO_SMALL;
        }
 
+       if (prhdr->frag_len > cli->max_recv_frag) {
+               DEBUG(0, ("cli_pipe_get_current_pdu: Server sent fraglen %d,"
+                         " we only allow %d\n", (int)prhdr->frag_len,
+                         (int)cli->max_recv_frag));
+               return NT_STATUS_BUFFER_TOO_SMALL;
+       }
+
        /* Ensure we have frag_len bytes of data. */
        if (current_pdu_len < prhdr->frag_len) {
-               /* rpc_read expands the current_pdu struct as neccessary. */
-               ret = rpc_read(cli, current_pdu, (uint32)prhdr->frag_len - current_pdu_len, &current_pdu_len);
+               if (!rpc_grow_buffer(current_pdu, prhdr->frag_len)) {
+                       return NT_STATUS_NO_MEMORY;
+               }
+               ret = rpc_read(cli, current_pdu,
+                              (uint32)prhdr->frag_len - current_pdu_len,
+                              current_pdu_len);
                if (!NT_STATUS_IS_OK(ret)) {
                        return ret;
                }
        }
 
-       if (current_pdu_len < prhdr->frag_len) {
-               return NT_STATUS_BUFFER_TOO_SMALL;
-       }
-
        return NT_STATUS_OK;
 }
 
@@ -780,7 +769,7 @@ static NTSTATUS cli_pipe_validate_current_pdu(struct rpc_pipe_client *cli, RPC_H
 
                        DEBUG(1, ("cli_pipe_validate_current_pdu: RPC fault "
                                  "code %s received from %s!\n",
-                               dcerpc_errstr(NT_STATUS_V(fault_resp.status)),
+                               dcerpc_errstr(debug_ctx(), NT_STATUS_V(fault_resp.status)),
                                rpccli_pipe_txt(debug_ctx(), cli)));
                        if (NT_STATUS_IS_OK(fault_resp.status)) {
                                return NT_STATUS_UNSUCCESSFUL;
@@ -862,6 +851,32 @@ static NTSTATUS cli_pipe_reset_current_pdu(struct rpc_pipe_client *cli, RPC_HDR
        return NT_STATUS_OK;
 }
 
+/****************************************************************************
+ Call a remote api on an arbitrary pipe.  takes param, data and setup buffers.
+****************************************************************************/
+
+static bool cli_api_pipe(struct cli_state *cli, const char *pipe_name,
+                        uint16 *setup, uint32 setup_count,
+                        uint32 max_setup_count,
+                        char *params, uint32 param_count,
+                        uint32 max_param_count,
+                        char *data, uint32 data_count,
+                        uint32 max_data_count,
+                        char **rparam, uint32 *rparam_count,
+                        char **rdata, uint32 *rdata_count)
+{
+       cli_send_trans(cli, SMBtrans,
+                 pipe_name,
+                 0,0,                         /* fid, flags */
+                 setup, setup_count, max_setup_count,
+                 params, param_count, max_param_count,
+                 data, data_count, max_data_count);
+
+       return (cli_receive_trans(cli, SMBtrans,
+                            rparam, (unsigned int *)rparam_count,
+                            rdata, (unsigned int *)rdata_count));
+}
+
 /****************************************************************************
  Send data on an rpc pipe via trans. The prs_struct data must be the last
  pdu fragment of an NDR data stream.
@@ -1008,8 +1023,8 @@ static NTSTATUS rpc_api_pipe(struct rpc_pipe_client *cli,
 
        while(1) {
                RPC_HDR rhdr;
-               char *ret_data;
-               uint32 ret_data_len;
+               char *ret_data = NULL;
+               uint32 ret_data_len = 0;
 
                /* Ensure we have enough data for a pdu. */
                ret = cli_pipe_get_current_pdu(cli, &rhdr, &current_pdu);
@@ -1666,6 +1681,7 @@ NTSTATUS rpc_api_pipe_req(struct rpc_pipe_client *cli,
                uint16 frag_len = 0;
                uint8 flags = 0;
                uint32 ss_padding = 0;
+               ssize_t num_written;
 
                data_sent_thistime = calculate_data_len_tosend(cli, data_left,
                                                &frag_len, &auth_len, &ss_padding);
@@ -1753,43 +1769,39 @@ NTSTATUS rpc_api_pipe_req(struct rpc_pipe_client *cli,
                        }
 
                        return ret;
-               } else {
-                       /* More packets to come - write and continue. */
-                       ssize_t num_written;
-
-                       switch (cli->transport_type) {
-                       case NCACN_NP:
-                               num_written = cli_write(cli->trans.np.cli,
-                                                       cli->trans.np.fnum,
-                                                       8, /* 8 means message mode. */
-                                                       prs_data_p(&outgoing_pdu),
-                                                       (off_t)0,
-                                                       (size_t)hdr.frag_len);
-
-                               if (num_written != hdr.frag_len) {
-                                       prs_mem_free(&outgoing_pdu);
-                                       return cli_get_nt_error(
-                                               cli->trans.np.cli);
-                               }
-                               break;
-                       case NCACN_IP_TCP:
-                       case NCACN_UNIX_STREAM:
-                               num_written = write_data(
-                                       cli->trans.sock.fd,
-                                       prs_data_p(&outgoing_pdu),
-                                       (size_t)hdr.frag_len);
-                               if (num_written != hdr.frag_len) {
-                                       NTSTATUS status;
-                                       status = map_nt_error_from_unix(errno);
-                                       prs_mem_free(&outgoing_pdu);
-                                       return status;
-                               }
-                               break;
-                       default:
-                               DEBUG(0, ("unknown transport type %d\n",
-                                         cli->transport_type));
-                               return NT_STATUS_INTERNAL_ERROR;
+               }
+
+               switch (cli->transport_type) {
+               case NCACN_NP:
+                       num_written = cli_write(cli->trans.np.cli,
+                                               cli->trans.np.fnum,
+                                               8, /* 8 means message mode. */
+                                               prs_data_p(&outgoing_pdu),
+                                               (off_t)0,
+                                               (size_t)hdr.frag_len);
+
+                       if (num_written != hdr.frag_len) {
+                               prs_mem_free(&outgoing_pdu);
+                               return cli_get_nt_error(cli->trans.np.cli);
+                       }
+                       break;
+               case NCACN_IP_TCP:
+               case NCACN_UNIX_STREAM:
+                       num_written = write_data(
+                               cli->trans.sock.fd,
+                               prs_data_p(&outgoing_pdu),
+                               (size_t)hdr.frag_len);
+                       if (num_written != hdr.frag_len) {
+                               NTSTATUS status;
+                               status = map_nt_error_from_unix(errno);
+                               prs_mem_free(&outgoing_pdu);
+                               return status;
                        }
+                       break;
+               default:
+                       DEBUG(0, ("unknown transport type %d\n",
+                                 cli->transport_type));
+                       return NT_STATUS_INTERNAL_ERROR;
                }
 
                current_data_offset += data_sent_thistime;
@@ -2355,12 +2367,6 @@ unsigned int rpccli_set_timeout(struct rpc_pipe_client *cli,
        return cli_set_timeout(cli->trans.np.cli, timeout);
 }
 
-bool rpccli_is_pipe_idx(struct rpc_pipe_client *cli, int pipe_idx)
-{
-       return ndr_syntax_id_equal(&cli->abstract_syntax,
-                                  pipe_names[pipe_idx].abstr_syntax);
-}
-
 bool rpccli_get_pwd_hash(struct rpc_pipe_client *cli, uint8_t nt_hash[16])
 {
        if ((cli->auth->auth_type == PIPE_AUTH_TYPE_NTLMSSP)
@@ -2656,9 +2662,8 @@ static NTSTATUS rpc_pipe_open_tcp_port(TALLOC_CTX *mem_ctx, const char *host,
                goto fail;
        }
 
-       result->trans.sock.fd = open_socket_out(SOCK_STREAM, &addr, port, 60);
-       if (result->trans.sock.fd == -1) {
-               status = map_nt_error_from_unix(errno);
+       status = open_socket_out(&addr, port, 60, &result->trans.sock.fd);
+       if (!NT_STATUS_IS_OK(status)) {
                goto fail;
        }
 
@@ -2848,7 +2853,7 @@ NTSTATUS rpc_pipe_open_ncalrpc(TALLOC_CTX *mem_ctx, const char *socket_path,
        result->abstract_syntax = *abstract_syntax;
        result->transfer_syntax = ndr_transfer_syntax;
 
-       result->desthost = get_myname(result);
+       result->desthost = talloc_get_myname(result);
        result->srv_name_slash = talloc_asprintf_strupper_m(
                result, "\\\\%s", result->desthost);
        if ((result->desthost == NULL) || (result->srv_name_slash == NULL)) {
@@ -2936,6 +2941,9 @@ static NTSTATUS rpc_pipe_open_np(struct cli_state *cli,
        result->srv_name_slash = talloc_asprintf_strupper_m(
                result, "\\\\%s", result->desthost);
 
+       result->max_xmit_frag = RPC_MAX_PDU_FRAG_LEN;
+       result->max_recv_frag = RPC_MAX_PDU_FRAG_LEN;
+
        if ((result->desthost == NULL) || (result->srv_name_slash == NULL)) {
                TALLOC_FREE(result);
                return NT_STATUS_NO_MEMORY;
@@ -2944,7 +2952,7 @@ static NTSTATUS rpc_pipe_open_np(struct cli_state *cli,
        fnum = cli_nt_create(cli, result->trans.np.pipe_name,
                             DESIRED_ACCESS_PIPE);
        if (fnum == -1) {
-               DEBUG(1,("rpc_pipe_open_np: cli_nt_create failed on pipe %s "
+               DEBUG(3,("rpc_pipe_open_np: cli_nt_create failed on pipe %s "
                         "to machine %s.  Error was %s\n",
                         result->trans.np.pipe_name, cli->desthost,
                         cli_errstr(cli)));
@@ -3017,6 +3025,9 @@ NTSTATUS cli_rpc_pipe_open_noauth(struct cli_state *cli,
 
        auth->user_name = talloc_strdup(auth, cli->user_name);
        auth->domain = talloc_strdup(auth, cli->domain);
+       auth->user_session_key = data_blob_talloc(auth,
+               cli->user_session_key.data,
+               cli->user_session_key.length);
 
        if ((auth->user_name == NULL) || (auth->domain == NULL)) {
                TALLOC_FREE(result);
@@ -3150,15 +3161,15 @@ NTSTATUS cli_rpc_pipe_open_spnego_ntlmssp(struct cli_state *cli,
 /****************************************************************************
   Get a the schannel session key out of an already opened netlogon pipe.
  ****************************************************************************/
-static bool get_schannel_session_key_common(struct rpc_pipe_client *netlogon_pipe,
-                                           struct cli_state *cli,
-                                           const char *domain,
-                                           uint32 *pneg_flags,
-                                           NTSTATUS *perr)
+static NTSTATUS get_schannel_session_key_common(struct rpc_pipe_client *netlogon_pipe,
+                                               struct cli_state *cli,
+                                               const char *domain,
+                                               uint32 *pneg_flags)
 {
        uint32 sec_chan_type = 0;
        unsigned char machine_pwd[16];
        const char *machine_account;
+       NTSTATUS status;
 
        /* Get the machine account credentials from secrets.tdb. */
        if (!get_trust_pw_hash(domain, machine_pwd, &machine_account,
@@ -3167,11 +3178,10 @@ static bool get_schannel_session_key_common(struct rpc_pipe_client *netlogon_pip
                DEBUG(0, ("get_schannel_session_key: could not fetch "
                        "trust account password for domain '%s'\n",
                        domain));
-               *perr = NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
-               return false;
+               return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
        }
 
-       *perr = rpccli_netlogon_setup_creds(netlogon_pipe,
+       status = rpccli_netlogon_setup_creds(netlogon_pipe,
                                        cli->desthost, /* server name */
                                        domain,        /* domain */
                                        global_myname(), /* client name */
@@ -3180,21 +3190,22 @@ static bool get_schannel_session_key_common(struct rpc_pipe_client *netlogon_pip
                                        sec_chan_type,
                                        pneg_flags);
 
-       if (!NT_STATUS_IS_OK(*perr)) {
-               DEBUG(3,("get_schannel_session_key_common: rpccli_netlogon_setup_creds "
-                       "failed with result %s to server %s, domain %s, machine account %s.\n",
-                       nt_errstr(*perr), cli->desthost, domain, machine_account ));
-               return false;
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(3, ("get_schannel_session_key_common: "
+                         "rpccli_netlogon_setup_creds failed with result %s "
+                         "to server %s, domain %s, machine account %s.\n",
+                         nt_errstr(status), cli->desthost, domain,
+                         machine_account ));
+               return status;
        }
 
        if (((*pneg_flags) & NETLOGON_NEG_SCHANNEL) == 0) {
                DEBUG(3, ("get_schannel_session_key: Server %s did not offer schannel\n",
                        cli->desthost));
-               *perr = NT_STATUS_INVALID_NETWORK_RESPONSE;
-               return false;
+               return NT_STATUS_INVALID_NETWORK_RESPONSE;
        }
 
-       return true;
+       return NT_STATUS_OK;;
 }
 
 /****************************************************************************
@@ -3203,27 +3214,29 @@ static bool get_schannel_session_key_common(struct rpc_pipe_client *netlogon_pip
  ****************************************************************************/
 
 
-struct rpc_pipe_client *get_schannel_session_key(struct cli_state *cli,
-                                                       const char *domain,
-                                                       uint32 *pneg_flags,
-                                                       NTSTATUS *perr)
+NTSTATUS get_schannel_session_key(struct cli_state *cli,
+                                 const char *domain,
+                                 uint32 *pneg_flags,
+                                 struct rpc_pipe_client **presult)
 {
        struct rpc_pipe_client *netlogon_pipe = NULL;
+       NTSTATUS status;
 
-       *perr = cli_rpc_pipe_open_noauth(cli, &ndr_table_netlogon.syntax_id,
-                                        &netlogon_pipe);
-       if (!NT_STATUS_IS_OK(*perr)) {
-               return NULL;
+       status = cli_rpc_pipe_open_noauth(cli, &ndr_table_netlogon.syntax_id,
+                                         &netlogon_pipe);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
        }
 
-       if (!get_schannel_session_key_common(netlogon_pipe, cli, domain,
-                                            pneg_flags, perr))
-       {
+       status = get_schannel_session_key_common(netlogon_pipe, cli, domain,
+                                                pneg_flags);
+       if (!NT_STATUS_IS_OK(status)) {
                TALLOC_FREE(netlogon_pipe);
-               return NULL;
+               return status;
        }
 
-       return netlogon_pipe;
+       *presult = netlogon_pipe;
+       return NT_STATUS_OK;
 }
 
 /****************************************************************************
@@ -3232,37 +3245,38 @@ struct rpc_pipe_client *get_schannel_session_key(struct cli_state *cli,
  using session_key. sign and seal.
  ****************************************************************************/
 
-struct rpc_pipe_client *cli_rpc_pipe_open_schannel_with_key(struct cli_state *cli,
-                                       int pipe_idx,
-                                       enum pipe_auth_level auth_level,
-                                       const char *domain,
-                                       const struct dcinfo *pdc,
-                                       NTSTATUS *perr)
+NTSTATUS cli_rpc_pipe_open_schannel_with_key(struct cli_state *cli,
+                                            const struct ndr_syntax_id *interface,
+                                            enum pipe_auth_level auth_level,
+                                            const char *domain,
+                                            const struct dcinfo *pdc,
+                                            struct rpc_pipe_client **presult)
 {
        struct rpc_pipe_client *result;
        struct cli_pipe_auth_data *auth;
+       NTSTATUS status;
 
-       *perr = cli_rpc_pipe_open(cli, pipe_names[pipe_idx].abstr_syntax,
-                                 &result);
-       if (!NT_STATUS_IS_OK(*perr)) {
-               return NULL;
+       status = cli_rpc_pipe_open(cli, interface, &result);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
        }
 
-       *perr = rpccli_schannel_bind_data(result, domain, auth_level,
-                                         pdc->sess_key, &auth);
-       if (!NT_STATUS_IS_OK(*perr)) {
+       status = rpccli_schannel_bind_data(result, domain, auth_level,
+                                          pdc->sess_key, &auth);
+       if (!NT_STATUS_IS_OK(status)) {
                DEBUG(0, ("rpccli_schannel_bind_data returned %s\n",
-                         nt_errstr(*perr)));
+                         nt_errstr(status)));
                TALLOC_FREE(result);
-               return NULL;
+               return status;
        }
 
-       *perr = rpc_pipe_bind(result, auth);
-       if (!NT_STATUS_IS_OK(*perr)) {
-               DEBUG(0, ("cli_rpc_pipe_open_schannel_with_key: cli_rpc_pipe_bind failed with error %s\n",
-                       nt_errstr(*perr) ));
+       status = rpc_pipe_bind(result, auth);
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(0, ("cli_rpc_pipe_open_schannel_with_key: "
+                         "cli_rpc_pipe_bind failed with error %s\n",
+                         nt_errstr(status) ));
                TALLOC_FREE(result);
-               return NULL;
+               return status;
        }
 
        /*
@@ -3273,7 +3287,7 @@ struct rpc_pipe_client *cli_rpc_pipe_open_schannel_with_key(struct cli_state *cl
        if (result->dc == NULL) {
                DEBUG(0, ("talloc failed\n"));
                TALLOC_FREE(result);
-               return NULL;
+               return NT_STATUS_NO_MEMORY;
        }
 
        DEBUG(10,("cli_rpc_pipe_open_schannel_with_key: opened pipe %s to machine %s "
@@ -3281,7 +3295,8 @@ struct rpc_pipe_client *cli_rpc_pipe_open_schannel_with_key(struct cli_state *cl
                "and bound using schannel.\n",
                result->trans.np.pipe_name, cli->desthost, domain ));
 
-       return result;
+       *presult = result;
+       return NT_STATUS_OK;
 }
 
 /****************************************************************************
@@ -3290,32 +3305,32 @@ struct rpc_pipe_client *cli_rpc_pipe_open_schannel_with_key(struct cli_state *cl
  version uses an ntlmssp auth bound netlogon pipe to get the key.
  ****************************************************************************/
 
-static struct rpc_pipe_client *get_schannel_session_key_auth_ntlmssp(struct cli_state *cli,
-                                                       const char *domain,
-                                                       const char *username,
-                                                       const char *password,
-                                                       uint32 *pneg_flags,
-                                                       NTSTATUS *perr)
+static NTSTATUS get_schannel_session_key_auth_ntlmssp(struct cli_state *cli,
+                                                     const char *domain,
+                                                     const char *username,
+                                                     const char *password,
+                                                     uint32 *pneg_flags,
+                                                     struct rpc_pipe_client **presult)
 {
        struct rpc_pipe_client *netlogon_pipe = NULL;
+       NTSTATUS status;
 
-       *perr = cli_rpc_pipe_open_spnego_ntlmssp(cli,
-                                                &ndr_table_netlogon.syntax_id,
-                                                PIPE_AUTH_LEVEL_PRIVACY,
-                                                domain, username, password,
-                                                &netlogon_pipe);
-       if (!netlogon_pipe) {
-               return NULL;
+       status = cli_rpc_pipe_open_spnego_ntlmssp(
+               cli, &ndr_table_netlogon.syntax_id, PIPE_AUTH_LEVEL_PRIVACY,
+               domain, username, password, &netlogon_pipe);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
        }
 
-       if (!get_schannel_session_key_common(netlogon_pipe, cli, domain,
-                                            pneg_flags, perr))
-       {
+       status = get_schannel_session_key_common(netlogon_pipe, cli, domain,
+                                                pneg_flags);
+       if (!NT_STATUS_IS_OK(status)) {
                TALLOC_FREE(netlogon_pipe);
-               return NULL;
+               return status;
        }
 
-       return netlogon_pipe;
+       *presult = netlogon_pipe;
+       return NT_STATUS_OK;
 }
 
 /****************************************************************************
@@ -3324,35 +3339,39 @@ static struct rpc_pipe_client *get_schannel_session_key_auth_ntlmssp(struct cli_
  uses an ntlmssp bind to get the session key.
  ****************************************************************************/
 
-struct rpc_pipe_client *cli_rpc_pipe_open_ntlmssp_auth_schannel(struct cli_state *cli,
-                                                int pipe_idx,
-                                               enum pipe_auth_level auth_level,
-                                                const char *domain,
-                                               const char *username,
-                                               const char *password,
-                                               NTSTATUS *perr)
+NTSTATUS cli_rpc_pipe_open_ntlmssp_auth_schannel(struct cli_state *cli,
+                                                const struct ndr_syntax_id *interface,
+                                                enum pipe_auth_level auth_level,
+                                                const char *domain,
+                                                const char *username,
+                                                const char *password,
+                                                struct rpc_pipe_client **presult)
 {
        uint32_t neg_flags = NETLOGON_NEG_AUTH2_ADS_FLAGS;
        struct rpc_pipe_client *netlogon_pipe = NULL;
        struct rpc_pipe_client *result = NULL;
+       NTSTATUS status;
 
-       netlogon_pipe = get_schannel_session_key_auth_ntlmssp(cli, domain, username,
-                                                       password, &neg_flags, perr);
-       if (!netlogon_pipe) {
+       status = get_schannel_session_key_auth_ntlmssp(
+               cli, domain, username, password, &neg_flags, &netlogon_pipe);
+       if (!NT_STATUS_IS_OK(status)) {
                DEBUG(0,("cli_rpc_pipe_open_ntlmssp_auth_schannel: failed to get schannel session "
                        "key from server %s for domain %s.\n",
                        cli->desthost, domain ));
-               return NULL;
+               return status;
        }
 
-       result = cli_rpc_pipe_open_schannel_with_key(cli, pipe_idx,
-                               auth_level,
-                               domain, netlogon_pipe->dc, perr);
+       status = cli_rpc_pipe_open_schannel_with_key(
+               cli, interface, auth_level, domain, netlogon_pipe->dc,
+               &result);
 
        /* Now we've bound using the session key we can close the netlog pipe. */
        TALLOC_FREE(netlogon_pipe);
 
-       return result;
+       if (NT_STATUS_IS_OK(status)) {
+               *presult = result;
+       }
+       return status;
 }
 
 /****************************************************************************
@@ -3360,32 +3379,38 @@ struct rpc_pipe_client *cli_rpc_pipe_open_ntlmssp_auth_schannel(struct cli_state
  Fetch the session key ourselves using a temporary netlogon pipe.
  ****************************************************************************/
 
-struct rpc_pipe_client *cli_rpc_pipe_open_schannel(struct cli_state *cli,
-                                                int pipe_idx,
-                                               enum pipe_auth_level auth_level,
-                                                const char *domain,
-                                               NTSTATUS *perr)
+NTSTATUS cli_rpc_pipe_open_schannel(struct cli_state *cli,
+                                   const struct ndr_syntax_id *interface,
+                                   enum pipe_auth_level auth_level,
+                                   const char *domain,
+                                   struct rpc_pipe_client **presult)
 {
        uint32_t neg_flags = NETLOGON_NEG_AUTH2_ADS_FLAGS;
        struct rpc_pipe_client *netlogon_pipe = NULL;
        struct rpc_pipe_client *result = NULL;
+       NTSTATUS status;
 
-       netlogon_pipe = get_schannel_session_key(cli, domain, &neg_flags, perr);
-       if (!netlogon_pipe) {
+       status = get_schannel_session_key(cli, domain, &neg_flags,
+                                         &netlogon_pipe);
+       if (!NT_STATUS_IS_OK(status)) {
                DEBUG(0,("cli_rpc_pipe_open_schannel: failed to get schannel session "
                        "key from server %s for domain %s.\n",
                        cli->desthost, domain ));
-               return NULL;
+               return status;
        }
 
-       result = cli_rpc_pipe_open_schannel_with_key(cli, pipe_idx,
-                               auth_level,
-                               domain, netlogon_pipe->dc, perr);
+       status = cli_rpc_pipe_open_schannel_with_key(
+               cli, interface, auth_level, domain, netlogon_pipe->dc,
+               &result);
 
        /* Now we've bound using the session key we can close the netlog pipe. */
        TALLOC_FREE(netlogon_pipe);
 
-       return result;
+       if (NT_STATUS_IS_OK(status)) {
+               *presult = result;
+       }
+
+       return NT_STATUS_OK;
 }
 
 /****************************************************************************
@@ -3394,45 +3419,46 @@ struct rpc_pipe_client *cli_rpc_pipe_open_schannel(struct cli_state *cli,
  NULL so long as the caller has a TGT.
  ****************************************************************************/
 
-struct rpc_pipe_client *cli_rpc_pipe_open_krb5(struct cli_state *cli,
-                                               int pipe_idx,
-                                               enum pipe_auth_level auth_level,
-                                               const char *service_princ,
-                                               const char *username,
-                                               const char *password,
-                                               NTSTATUS *perr)
+NTSTATUS cli_rpc_pipe_open_krb5(struct cli_state *cli,
+                               const struct ndr_syntax_id *interface,
+                               enum pipe_auth_level auth_level,
+                               const char *service_princ,
+                               const char *username,
+                               const char *password,
+                               struct rpc_pipe_client **presult)
 {
 #ifdef HAVE_KRB5
        struct rpc_pipe_client *result;
        struct cli_pipe_auth_data *auth;
+       NTSTATUS status;
 
-       *perr = cli_rpc_pipe_open(cli, pipe_names[pipe_idx].abstr_syntax,
-                                 &result);
-       if (!NT_STATUS_IS_OK(*perr)) {
-               return NULL;
+       status = cli_rpc_pipe_open(cli, interface, &result);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
        }
 
-       *perr = rpccli_kerberos_bind_data(result, auth_level, service_princ,
-                                         username, password, &auth);
-       if (!NT_STATUS_IS_OK(*perr)) {
+       status = rpccli_kerberos_bind_data(result, auth_level, service_princ,
+                                          username, password, &auth);
+       if (!NT_STATUS_IS_OK(status)) {
                DEBUG(0, ("rpccli_kerberos_bind_data returned %s\n",
-                         nt_errstr(*perr)));
+                         nt_errstr(status)));
                TALLOC_FREE(result);
-               return NULL;
+               return status;
        }
 
-       *perr = rpc_pipe_bind(result, auth);
-       if (!NT_STATUS_IS_OK(*perr)) {
-               DEBUG(0, ("cli_rpc_pipe_open_krb5: cli_rpc_pipe_bind failed with error %s\n",
-                       nt_errstr(*perr) ));
+       status = rpc_pipe_bind(result, auth);
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(0, ("cli_rpc_pipe_open_krb5: cli_rpc_pipe_bind failed "
+                         "with error %s\n", nt_errstr(status)));
                TALLOC_FREE(result);
-               return NULL;
+               return status;
        }
 
-       return result;
+       *presult = result;
+       return NT_STATUS_OK;
 #else
        DEBUG(0,("cli_rpc_pipe_open_krb5: kerberos not found at compile time.\n"));
-       return NULL;
+       return NT_STATUS_NOT_IMPLEMENTED;
 #endif
 }
 
@@ -3466,6 +3492,10 @@ NTSTATUS cli_get_session_key(TALLOC_CTX *mem_ctx,
                                cli->auth->a_u.kerberos_auth->session_key.length);
                        break;
                case PIPE_AUTH_TYPE_NONE:
+                       *session_key = data_blob_talloc(mem_ctx,
+                               cli->auth->user_session_key.data,
+                               cli->auth->user_session_key.length);
+                       break;
                default:
                        return NT_STATUS_NO_USER_SESSION_KEY;
        }