s4:librpc/rpc: add dcerpc_secondary_smb_send/recv()
[mat/samba.git] / source4 / librpc / rpc / dcerpc_smb.c
index 6b43de3358ace0020b7c7bfd0ceec5398bcaa338..c84896f4dca2e467f61f28511daa1af63aa03ab0 100644 (file)
 #include "libcli/raw/libcliraw.h"
 #include "libcli/composite/composite.h"
 #include "librpc/rpc/dcerpc.h"
+#include "librpc/rpc/dcerpc_proto.h"
+#include "librpc/rpc/rpc_common.h"
+#include "../libcli/smb/smbXcli_base.h"
 
 /* transport private information used by SMB pipe transport */
 struct smb_private {
        uint16_t fnum;
        struct smbcli_tree *tree;
+       DATA_BLOB session_key;
        const char *server_name;
        bool dead;
 };
@@ -37,9 +41,9 @@ struct smb_private {
 /*
   tell the dcerpc layer that the transport is dead
 */
-static void pipe_dead(struct dcerpc_connection *c, NTSTATUS status)
+static void pipe_dead(struct dcecli_connection *c, NTSTATUS status)
 {
-       struct smb_private *smb = c->transport.private_data;
+       struct smb_private *smb = (struct smb_private *)c->transport.private_data;
 
        if (smb->dead) {
                return;
@@ -65,7 +69,7 @@ static void pipe_dead(struct dcerpc_connection *c, NTSTATUS status)
    this holds the state of an in-flight call
 */
 struct smb_read_state {
-       struct dcerpc_connection *c;
+       struct dcecli_connection *c;
        struct smbcli_request *req;
        size_t received;
        DATA_BLOB data;
@@ -77,20 +81,22 @@ struct smb_read_state {
 */
 static void smb_read_callback(struct smbcli_request *req)
 {
+       struct dcecli_connection *c;
        struct smb_private *smb;
        struct smb_read_state *state;
        union smb_read *io;
        uint16_t frag_length;
        NTSTATUS status;
 
-       state = talloc_get_type(req->async.private, struct smb_read_state);
+       state = talloc_get_type(req->async.private_data, struct smb_read_state);
        smb = talloc_get_type(state->c->transport.private_data, struct smb_private);
        io = state->io;
+       c = state->c;
 
        status = smb_raw_read_recv(state->req, io);
        if (NT_STATUS_IS_ERR(status)) {
-               pipe_dead(state->c, status);
                talloc_free(state);
+               pipe_dead(c, status);
                return;
        }
 
@@ -99,8 +105,8 @@ static void smb_read_callback(struct smbcli_request *req)
        if (state->received < 16) {
                DEBUG(0,("dcerpc_smb: short packet (length %d) in read callback!\n",
                         (int)state->received));
-               pipe_dead(state->c, NT_STATUS_INFO_LENGTH_MISMATCH);
                talloc_free(state);
+               pipe_dead(c, NT_STATUS_INFO_LENGTH_MISMATCH);
                return;
        }
 
@@ -108,7 +114,6 @@ static void smb_read_callback(struct smbcli_request *req)
 
        if (frag_length <= state->received) {
                DATA_BLOB data = state->data;
-               struct dcerpc_connection *c = state->c;
                data.length = state->received;
                talloc_steal(state->c, data.data);
                talloc_free(state);
@@ -126,22 +131,22 @@ static void smb_read_callback(struct smbcli_request *req)
 
        state->req = smb_raw_read_send(smb->tree, io);
        if (state->req == NULL) {
-               pipe_dead(state->c, NT_STATUS_NO_MEMORY);
                talloc_free(state);
+               pipe_dead(c, NT_STATUS_NO_MEMORY);
                return;
        }
 
        state->req->async.fn = smb_read_callback;
-       state->req->async.private = state;
+       state->req->async.private_data = state;
 }
 
 /*
   trigger a read request from the server, possibly with some initial
   data in the read buffer
 */
-static NTSTATUS send_read_request_continue(struct dcerpc_connection *c, DATA_BLOB *blob)
+static NTSTATUS send_read_request_continue(struct dcecli_connection *c, DATA_BLOB *blob)
 {
-       struct smb_private *smb = c->transport.private_data;
+       struct smb_private *smb = (struct smb_private *)c->transport.private_data;
        union smb_read *io;
        struct smb_read_state *state;
        struct smbcli_request *req;
@@ -176,7 +181,7 @@ static NTSTATUS send_read_request_continue(struct dcerpc_connection *c, DATA_BLO
        io->readx.in.maxcnt = io->readx.in.mincnt;
        io->readx.in.offset = 0;
        io->readx.in.remaining = 0;
-       io->readx.in.read_for_execute = False;
+       io->readx.in.read_for_execute = false;
        io->readx.out.data = state->data.data + state->received;
        req = smb_raw_read_send(smb->tree, io);
        if (req == NULL) {
@@ -184,7 +189,7 @@ static NTSTATUS send_read_request_continue(struct dcerpc_connection *c, DATA_BLO
        }
 
        req->async.fn = smb_read_callback;
-       req->async.private = state;
+       req->async.private_data = state;
 
        state->req = req;
 
@@ -195,9 +200,9 @@ static NTSTATUS send_read_request_continue(struct dcerpc_connection *c, DATA_BLO
 /*
   trigger a read request from the server
 */
-static NTSTATUS send_read_request(struct dcerpc_connection *c)
+static NTSTATUS send_read_request(struct dcecli_connection *c)
 {
-       struct smb_private *smb = c->transport.private_data;
+       struct smb_private *smb = (struct smb_private *)c->transport.private_data;
 
        if (smb->dead) {
                return NT_STATUS_CONNECTION_DISCONNECTED;
@@ -210,7 +215,7 @@ static NTSTATUS send_read_request(struct dcerpc_connection *c)
    this holds the state of an in-flight trans call
 */
 struct smb_trans_state {
-       struct dcerpc_connection *c;
+       struct dcecli_connection *c;
        struct smbcli_request *req;
        struct smb_trans2 *trans;
 };
@@ -220,8 +225,8 @@ struct smb_trans_state {
 */
 static void smb_trans_callback(struct smbcli_request *req)
 {
-       struct smb_trans_state *state = req->async.private;
-       struct dcerpc_connection *c = state->c;
+       struct smb_trans_state *state = (struct smb_trans_state *)req->async.private_data;
+       struct dcecli_connection *c = state->c;
        NTSTATUS status;
 
        status = smb_raw_trans_recv(req, state, state->trans);
@@ -247,14 +252,15 @@ static void smb_trans_callback(struct smbcli_request *req)
 /*
   send a SMBtrans style request
 */
-static NTSTATUS smb_send_trans_request(struct dcerpc_connection *c, DATA_BLOB *blob)
+static NTSTATUS smb_send_trans_request(struct dcecli_connection *c, DATA_BLOB *blob)
 {
-        struct smb_private *smb = c->transport.private_data;
+        struct smb_private *smb = (struct smb_private *)c->transport.private_data;
         struct smb_trans2 *trans;
         uint16_t setup[2];
        struct smb_trans_state *state;
+       uint16_t max_data;
 
-       state = talloc(smb, struct smb_trans_state);
+       state = talloc(c, struct smb_trans_state);
        if (state == NULL) {
                return NT_STATUS_NO_MEMORY;
        }
@@ -269,8 +275,14 @@ static NTSTATUS smb_send_trans_request(struct dcerpc_connection *c, DATA_BLOB *b
         setup[0] = TRANSACT_DCERPCCMD;
         setup[1] = smb->fnum;
 
+       if (c->srv_max_xmit_frag > 0) {
+               max_data = MIN(UINT16_MAX, c->srv_max_xmit_frag);
+       } else {
+               max_data = UINT16_MAX;
+       }
+
         trans->in.max_param = 0;
-        trans->in.max_data = smb_raw_max_trans_data(smb->tree, 0);
+        trans->in.max_data = max_data;
         trans->in.max_setup = 0;
         trans->in.setup_count = 2;
         trans->in.flags = 0;
@@ -285,7 +297,7 @@ static NTSTATUS smb_send_trans_request(struct dcerpc_connection *c, DATA_BLOB *b
        }
 
        state->req->async.fn = smb_trans_callback;
-       state->req->async.private = state;
+       state->req->async.private_data = state;
 
        talloc_steal(state, state->req);
 
@@ -297,26 +309,32 @@ static NTSTATUS smb_send_trans_request(struct dcerpc_connection *c, DATA_BLOB *b
 */
 static void smb_write_callback(struct smbcli_request *req)
 {
-       struct dcerpc_connection *c = req->async.private;
+       struct dcecli_connection *c = (struct dcecli_connection *)req->async.private_data;
+       union smb_write io;
+       NTSTATUS status;
 
-       if (!NT_STATUS_IS_OK(req->status)) {
-               DEBUG(0,("dcerpc_smb: write callback error\n"));
-               pipe_dead(c, req->status);
-       }
+       ZERO_STRUCT(io);
+       io.generic.level = RAW_WRITE_WRITEX;
 
-       smbcli_request_destroy(req);
+       status = smb_raw_write_recv(req, &io);
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(0,("dcerpc_smb: write callback error: %s\n",
+                       nt_errstr(status)));
+               pipe_dead(c, status);
+       }
 }
 
 /* 
    send a packet to the server
 */
-static NTSTATUS smb_send_request(struct dcerpc_connection *c, DATA_BLOB *blob, BOOL trigger_read)
+static NTSTATUS smb_send_request(struct dcecli_connection *c, DATA_BLOB *blob, 
+                                bool trigger_read)
 {
-       struct smb_private *smb = c->transport.private_data;
+       struct smb_private *smb = (struct smb_private *)c->transport.private_data;
        union smb_write io;
        struct smbcli_request *req;
 
-       if (smb->dead) {
+       if (!smb || smb->dead) {
                return NT_STATUS_CONNECTION_DISCONNECTED;
        }
 
@@ -342,21 +360,23 @@ static NTSTATUS smb_send_request(struct dcerpc_connection *c, DATA_BLOB *blob, B
        }
 
        req->async.fn = smb_write_callback;
-       req->async.private = c;
-
-       if (trigger_read) {
-               send_read_request(c);
-       }
+       req->async.private_data = c;
 
        return NT_STATUS_OK;
 }
 
+
+static void free_request(struct smbcli_request *req)
+{
+       talloc_free(req);
+}
+
 /* 
    shutdown SMB pipe connection
 */
-static NTSTATUS smb_shutdown_pipe(struct dcerpc_connection *c, NTSTATUS status)
+static NTSTATUS smb_shutdown_pipe(struct dcecli_connection *c, NTSTATUS status)
 {
-       struct smb_private *smb = c->transport.private_data;
+       struct smb_private *smb = (struct smb_private *)c->transport.private_data;
        union smb_close io;
        struct smbcli_request *req;
 
@@ -369,10 +389,11 @@ static NTSTATUS smb_shutdown_pipe(struct dcerpc_connection *c, NTSTATUS status)
        req = smb_raw_close_send(smb->tree, &io);
        if (req != NULL) {
                /* we don't care if this fails, so just free it if it succeeds */
-               req->async.fn = (void (*)(struct smbcli_request *))talloc_free;
+               req->async.fn = free_request;
        }
 
        talloc_free(smb);
+       c->transport.private_data = NULL;
 
        return status;
 }
@@ -380,38 +401,43 @@ static NTSTATUS smb_shutdown_pipe(struct dcerpc_connection *c, NTSTATUS status)
 /*
   return SMB server name (called name)
 */
-static const char *smb_peer_name(struct dcerpc_connection *c)
+static const char *smb_peer_name(struct dcecli_connection *c)
 {
-       struct smb_private *smb = c->transport.private_data;
+       struct smb_private *smb = (struct smb_private *)c->transport.private_data;
+       if (smb == NULL) return "";
        return smb->server_name;
 }
 
 /*
   return remote name we make the actual connection (good for kerberos) 
 */
-static const char *smb_target_hostname(struct dcerpc_connection *c)
+static const char *smb_target_hostname(struct dcecli_connection *c)
 {
        struct smb_private *smb = talloc_get_type(c->transport.private_data, struct smb_private);
-       return smb->tree->session->transport->socket->hostname;
+       if (smb == NULL) return "";
+       return smbXcli_conn_remote_name(smb->tree->session->transport->conn);
 }
 
 /*
   fetch the user session key 
 */
-static NTSTATUS smb_session_key(struct dcerpc_connection *c, DATA_BLOB *session_key)
+static NTSTATUS smb_session_key(struct dcecli_connection *c, DATA_BLOB *session_key)
 {
-       struct smb_private *smb = c->transport.private_data;
+       struct smb_private *smb = (struct smb_private *)c->transport.private_data;
 
-       if (smb->tree->session->user_session_key.data) {
-               *session_key = smb->tree->session->user_session_key;
-               return NT_STATUS_OK;
+       if (smb == NULL) return NT_STATUS_CONNECTION_DISCONNECTED;
+
+       if (smb->session_key.length == 0) {
+               return NT_STATUS_NO_USER_SESSION_KEY;
        }
-       return NT_STATUS_NO_USER_SESSION_KEY;
+
+       *session_key = smb->session_key;
+       return NT_STATUS_OK;
 }
 
 struct pipe_open_smb_state {
        union smb_open *open;
-       struct dcerpc_connection *c;
+       struct dcecli_connection *c;
        struct smbcli_tree *tree;
        struct composite_context *ctx;
 };
@@ -425,12 +451,15 @@ struct composite_context *dcerpc_pipe_open_smb_send(struct dcerpc_pipe *p,
        struct composite_context *ctx;
        struct pipe_open_smb_state *state;
        struct smbcli_request *req;
-       struct dcerpc_connection *c = p->conn;
+       struct dcecli_connection *c = p->conn;
 
        /* if we don't have a binding on this pipe yet, then create one */
        if (p->binding == NULL) {
                NTSTATUS status;
-               char *s = talloc_asprintf(p, "ncacn_np:%s", tree->session->transport->socket->hostname);
+               const char *r = smbXcli_conn_remote_name(tree->session->transport->conn);
+               char *s;
+               SMB_ASSERT(r != NULL);
+               s = talloc_asprintf(p, "ncacn_np:%s", r);
                if (s == NULL) return NULL;
                status = dcerpc_parse_binding(p, s, &p->binding);
                talloc_free(s);
@@ -455,7 +484,7 @@ struct composite_context *dcerpc_pipe_open_smb_send(struct dcerpc_pipe *p,
 
        state->open->ntcreatex.level = RAW_OPEN_NTCREATEX;
        state->open->ntcreatex.in.flags = 0;
-       state->open->ntcreatex.in.root_fid = 0;
+       state->open->ntcreatex.in.root_fid.fnum = 0;
        state->open->ntcreatex.in.access_mask = 
                SEC_STD_READ_CONTROL |
                SEC_FILE_WRITE_ATTRIBUTE |
@@ -490,10 +519,10 @@ struct composite_context *dcerpc_pipe_open_smb_send(struct dcerpc_pipe *p,
 
 static void pipe_open_recv(struct smbcli_request *req)
 {
-       struct pipe_open_smb_state *state = talloc_get_type(req->async.private,
+       struct pipe_open_smb_state *state = talloc_get_type(req->async.private_data,
                                            struct pipe_open_smb_state);
        struct composite_context *ctx = state->ctx;
-       struct dcerpc_connection *c = state->c;
+       struct dcecli_connection *c = state->c;
        struct smb_private *smb;
        
        ctx->status = smb_raw_open_recv(req, state, state->open);
@@ -521,10 +550,18 @@ static void pipe_open_recv(struct smbcli_request *req)
        smb->fnum       = state->open->ntcreatex.out.file.fnum;
        smb->tree       = talloc_reference(smb, state->tree);
        smb->server_name= strupper_talloc(smb,
-                         state->tree->session->transport->called.name);
+               smbXcli_conn_remote_name(state->tree->session->transport->conn));
        if (composite_nomem(smb->server_name, ctx)) return;
        smb->dead       = false;
 
+       ctx->status = smbXcli_session_application_key(state->tree->session->smbXcli,
+                                                     smb, &smb->session_key);
+       if (NT_STATUS_EQUAL(ctx->status, NT_STATUS_NO_USER_SESSION_KEY)) {
+               smb->session_key = data_blob_null;
+               ctx->status = NT_STATUS_OK;
+       }
+       if (!composite_is_ok(ctx)) return;
+
        c->transport.private_data = smb;
 
        composite_done(ctx);
@@ -537,7 +574,7 @@ NTSTATUS dcerpc_pipe_open_smb_recv(struct composite_context *c)
        return status;
 }
 
-NTSTATUS dcerpc_pipe_open_smb(struct dcerpc_pipe *p,
+_PUBLIC_ NTSTATUS dcerpc_pipe_open_smb(struct dcerpc_pipe *p,
                              struct smbcli_tree *tree,
                              const char *pipe_name)
 {
@@ -549,7 +586,7 @@ NTSTATUS dcerpc_pipe_open_smb(struct dcerpc_pipe *p,
 /*
   return the SMB tree used for a dcerpc over SMB pipe
 */
-struct smbcli_tree *dcerpc_smb_tree(struct dcerpc_connection *c)
+_PUBLIC_ struct smbcli_tree *dcerpc_smb_tree(struct dcecli_connection *c)
 {
        struct smb_private *smb;
 
@@ -561,17 +598,21 @@ struct smbcli_tree *dcerpc_smb_tree(struct dcerpc_connection *c)
        return smb->tree;
 }
 
-/*
-  return the SMB fnum used for a dcerpc over SMB pipe (hack for torture operations)
-*/
-uint16_t dcerpc_smb_fnum(struct dcerpc_connection *c)
+struct composite_context *dcerpc_secondary_smb_send(struct dcecli_connection *c1,
+                                                   struct dcerpc_pipe *p2,
+                                                   const char *pipe_name)
 {
        struct smb_private *smb;
 
-       if (c->transport.transport != NCACN_NP) return 0;
+       if (c1->transport.transport != NCACN_NP) return NULL;
 
-       smb = talloc_get_type(c->transport.private_data, struct smb_private);
-       if (!smb) return 0;
+       smb = talloc_get_type(c1->transport.private_data, struct smb_private);
+       if (!smb) return NULL;
+
+       return dcerpc_pipe_open_smb_send(p2, smb->tree, pipe_name);
+}
 
-       return smb->fnum;
+NTSTATUS dcerpc_secondary_smb_recv(struct composite_context *c)
+{
+       return dcerpc_pipe_open_smb_recv(c);
 }