r23792: convert Samba4 to GPLv3
[sfrench/samba-autobuild/.git] / source4 / librpc / rpc / dcerpc.c
index 53d0e1b65b53897cf8f83cd90399c005bfc39ab4..20b4dd241b1d16fbdd974f79a05756421a381507 100644 (file)
@@ -8,7 +8,7 @@
    
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 2 of the License, or
+   the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.
    
    This program is distributed in the hope that it will be useful,
@@ -17,8 +17,7 @@
    GNU General Public License for more details.
    
    You should have received a copy of the GNU General Public License
-   along with this program; if not, write to the Free Software
-   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
 #include "includes.h"
@@ -37,14 +36,17 @@ NTSTATUS dcerpc_init(void)
        return NT_STATUS_OK;
 }
 
+static void dcerpc_connection_dead(struct dcerpc_connection *conn, NTSTATUS status);
 static void dcerpc_ship_next_request(struct dcerpc_connection *c);
 
 /* destroy a dcerpc connection */
-static int dcerpc_connection_destructor(struct dcerpc_connection *c)
+static int dcerpc_connection_destructor(struct dcerpc_connection *conn)
 {
-       if (c->transport.shutdown_pipe) {
-               c->transport.shutdown_pipe(c);
+       if (conn->dead) {
+               conn->free_skipped = True;
+               return -1;
        }
+       dcerpc_connection_dead(conn, NT_STATUS_LOCAL_DISCONNECT);
        return 0;
 }
 
@@ -52,7 +54,7 @@ static int dcerpc_connection_destructor(struct dcerpc_connection *c)
 /* initialise a dcerpc connection. 
    the event context is optional
 */
-struct dcerpc_connection *dcerpc_connection_init(TALLOC_CTX *mem_ctx, 
+static struct dcerpc_connection *dcerpc_connection_init(TALLOC_CTX *mem_ctx, 
                                                 struct event_context *ev)
 {
        struct dcerpc_connection *c;
@@ -110,6 +112,7 @@ struct dcerpc_pipe *dcerpc_pipe_init(TALLOC_CTX *mem_ctx, struct event_context *
        p->last_fault_code = 0;
        p->context_id = 0;
        p->request_timeout = DCERPC_REQUEST_TIMEOUT;
+       p->binding = NULL;
 
        ZERO_STRUCT(p->syntax);
        ZERO_STRUCT(p->transfer_syntax);
@@ -272,7 +275,6 @@ static NTSTATUS ncacn_pull_request_auth(struct dcerpc_connection *c, TALLOC_CTX
                return status;
        }
        
-       
        /* check signature or unseal the packet */
        switch (c->security_state.auth_info->auth_level) {
        case DCERPC_AUTH_LEVEL_PRIVACY:
@@ -347,7 +349,7 @@ static NTSTATUS ncacn_push_request_sign(struct dcerpc_connection *c,
                ndr->flags |= LIBNDR_FLAG_BIGENDIAN;
        }
 
-       if (pkt->pfc_flags & DCERPC_PFC_FLAG_ORPC) {
+       if (pkt->pfc_flags & DCERPC_PFC_FLAG_OBJECT_UUID) {
                ndr->flags |= LIBNDR_FLAG_OBJECT_PRESENT;
        }
 
@@ -426,20 +428,20 @@ static NTSTATUS ncacn_push_request_sign(struct dcerpc_connection *c,
                if (!NT_STATUS_IS_OK(status)) {
                        return status;
                }
-               status = data_blob_realloc(mem_ctx, blob,
-                                          blob->length - c->security_state.auth_info->credentials.length + 
-                                          creds2.length);
-
-               if (!NT_STATUS_IS_OK(status)) {
-                       return status;
-               }
-               memcpy(blob->data + blob->length - c->security_state.auth_info->credentials.length,
-                      creds2.data, creds2.length);
-
+               blob->length -= c->security_state.auth_info->credentials.length;
+               status = data_blob_append(mem_ctx, blob,
+                                         creds2.data, creds2.length);
                if (!NT_STATUS_IS_OK(status)) {
                        return status;
                }
                dcerpc_set_auth_length(blob, creds2.length);
+               if (c->security_state.auth_info->credentials.length == 0) {
+                       /* this is needed for krb5 only, to correct the total packet
+                          length */
+                       dcerpc_set_frag_length(blob, 
+                                              dcerpc_get_frag_length(blob)
+                                              +creds2.length);
+               }
                break;
 
        case DCERPC_AUTH_LEVEL_INTEGRITY:
@@ -454,20 +456,20 @@ static NTSTATUS ncacn_push_request_sign(struct dcerpc_connection *c,
                if (!NT_STATUS_IS_OK(status)) {
                        return status;
                }
-               status = data_blob_realloc(mem_ctx, blob,
-                                          blob->length - c->security_state.auth_info->credentials.length + 
-                                          creds2.length);
-
-               if (!NT_STATUS_IS_OK(status)) {
-                       return status;
-               }
-               memcpy(blob->data + blob->length - c->security_state.auth_info->credentials.length,
-                      creds2.data, creds2.length);
-
+               blob->length -= c->security_state.auth_info->credentials.length;
+               status = data_blob_append(mem_ctx, blob,
+                                         creds2.data, creds2.length);
                if (!NT_STATUS_IS_OK(status)) {
                        return status;
                }
                dcerpc_set_auth_length(blob, creds2.length);
+               if (c->security_state.auth_info->credentials.length == 0) {
+                       /* this is needed for krb5 only, to correct the total packet
+                          length */
+                       dcerpc_set_frag_length(blob, 
+                                              dcerpc_get_frag_length(blob)
+                                              +creds2.length);
+               }
                break;
 
        case DCERPC_AUTH_LEVEL_CONNECT:
@@ -524,26 +526,58 @@ static NTSTATUS dcerpc_map_reason(uint16_t reason)
 */
 static void dcerpc_composite_fail(struct rpc_request *req)
 {
-       struct composite_context *c = talloc_get_type(req->async.private, 
+       struct composite_context *c = talloc_get_type(req->async.private_data
                                                      struct composite_context);
        composite_error(c, req->status);
 }
 
+/*
+  remove requests from the pending or queued queues
+ */
+static int dcerpc_req_dequeue(struct rpc_request *req)
+{
+       switch (req->state) {
+       case RPC_REQUEST_QUEUED:
+               DLIST_REMOVE(req->p->conn->request_queue, req);
+               break;
+       case RPC_REQUEST_PENDING:
+               DLIST_REMOVE(req->p->conn->pending, req);
+               break;
+       case RPC_REQUEST_DONE:
+               break;
+       }
+       return 0;
+}
+
+
 /*
   mark the dcerpc connection dead. All outstanding requests get an error
 */
 static void dcerpc_connection_dead(struct dcerpc_connection *conn, NTSTATUS status)
 {
+       if (conn->dead) return;
+
+       conn->dead = true;
+
+       if (conn->transport.shutdown_pipe) {
+               conn->transport.shutdown_pipe(conn, status);
+       }
+
        /* all pending requests get the error */
        while (conn->pending) {
                struct rpc_request *req = conn->pending;
+               dcerpc_req_dequeue(req);
                req->state = RPC_REQUEST_DONE;
                req->status = status;
-               DLIST_REMOVE(conn->pending, req);
                if (req->async.callback) {
                        req->async.callback(req);
                }
        }       
+
+       talloc_set_destructor(conn, NULL);
+       if (conn->free_skipped) {
+               talloc_free(conn);
+       }
 }
 
 /*
@@ -594,7 +628,7 @@ static void dcerpc_bind_recv_handler(struct rpc_request *req,
        struct composite_context *c;
        struct dcerpc_connection *conn;
 
-       c = talloc_get_type(req->async.private, struct composite_context);
+       c = talloc_get_type(req->async.private_data, struct composite_context);
 
        if (pkt->ptype == DCERPC_PKT_BIND_NAK) {
                DEBUG(2,("dcerpc: bind_nak reason %d\n",
@@ -626,6 +660,8 @@ static void dcerpc_bind_recv_handler(struct rpc_request *req,
                if (!composite_is_ok(c)) return;
        }
 
+       req->p->assoc_group_id = pkt->u.bind_ack.assoc_group_id;
+
        composite_done(c);
 }
 
@@ -637,19 +673,19 @@ static void dcerpc_timeout_handler(struct event_context *ev, struct timed_event
 {
        struct rpc_request *req = talloc_get_type(private, struct rpc_request);
 
-       if (req->state != RPC_REQUEST_PENDING) {
+       if (req->ignore_timeout) {
+               dcerpc_req_dequeue(req);
+               req->state = RPC_REQUEST_DONE;
+               req->status = NT_STATUS_IO_TIMEOUT;
+               if (req->async.callback) {
+                       req->async.callback(req);
+               }
                return;
        }
 
-       req->status = NT_STATUS_IO_TIMEOUT;
-       req->state = RPC_REQUEST_DONE;
-       DLIST_REMOVE(req->p->conn->pending, req);
-       if (req->async.callback) {
-               req->async.callback(req);
-       }
+       dcerpc_connection_dead(req->p->conn, NT_STATUS_IO_TIMEOUT);
 }
 
-
 /*
   send a async dcerpc bind request
 */
@@ -678,9 +714,13 @@ struct composite_context *dcerpc_bind_send(struct dcerpc_pipe *p,
        pkt.call_id = p->conn->call_id;
        pkt.auth_length = 0;
 
+       if (p->binding->flags & DCERPC_CONCURRENT_MULTIPLEX) {
+               pkt.pfc_flags |= DCERPC_PFC_FLAG_CONC_MPX;
+       }
+
        pkt.u.bind.max_xmit_frag = 5840;
        pkt.u.bind.max_recv_frag = 5840;
-       pkt.u.bind.assoc_group_id = 0;
+       pkt.u.bind.assoc_group_id = p->binding->assoc_group_id;
        pkt.u.bind.num_contexts = 1;
        pkt.u.bind.ctx_list = talloc_array(mem_ctx, struct dcerpc_ctx_list, 1);
        if (composite_nomem(pkt.u.bind.ctx_list, c)) return c;
@@ -706,11 +746,12 @@ struct composite_context *dcerpc_bind_send(struct dcerpc_pipe *p,
 
        req->state = RPC_REQUEST_PENDING;
        req->call_id = pkt.call_id;
-       req->async.private = c;
+       req->async.private_data = c;
        req->async.callback = dcerpc_composite_fail;
        req->p = p;
        req->recv_handler = dcerpc_bind_recv_handler;
        DLIST_ADD_END(p->conn->pending, req, struct rpc_request *);
+       talloc_set_destructor(req, dcerpc_req_dequeue);
 
        c->status = p->conn->transport.send_request(p->conn, &blob,
                                                    True);
@@ -733,22 +774,6 @@ NTSTATUS dcerpc_bind_recv(struct composite_context *ctx)
        return result;
 }
 
-/* 
-   perform a bind using the given syntax 
-
-   the auth_info structure is updated with the reply authentication info
-   on success
-*/
-NTSTATUS dcerpc_bind(struct dcerpc_pipe *p, 
-                    TALLOC_CTX *mem_ctx,
-                    const struct dcerpc_syntax_id *syntax,
-                    const struct dcerpc_syntax_id *transfer_syntax)
-{
-       struct composite_context *creq;
-       creq = dcerpc_bind_send(p, mem_ctx, syntax, transfer_syntax);
-       return dcerpc_bind_recv(creq);
-}
-
 /* 
    perform a continued bind (and auth3)
 */
@@ -784,41 +809,6 @@ NTSTATUS dcerpc_auth3(struct dcerpc_connection *c,
 }
 
 
-/*
-  return the rpc syntax and transfer syntax given the pipe uuid and version
-*/
-NTSTATUS dcerpc_init_syntaxes(const struct dcerpc_interface_table *table,
-                             struct dcerpc_syntax_id *syntax,
-                             struct dcerpc_syntax_id *transfer_syntax)
-{
-       syntax->uuid = table->syntax_id.uuid;
-       syntax->if_version = table->syntax_id.if_version;
-
-       *transfer_syntax = ndr_transfer_syntax;
-
-       return NT_STATUS_OK;
-}
-
-/* perform a dcerpc bind, using the uuid as the key */
-NTSTATUS dcerpc_bind_byuuid(struct dcerpc_pipe *p, 
-                           TALLOC_CTX *mem_ctx,
-                           const struct dcerpc_interface_table *table)
-{
-       struct dcerpc_syntax_id syntax;
-       struct dcerpc_syntax_id transfer_syntax;
-       NTSTATUS status;
-
-       status = dcerpc_init_syntaxes(table,
-                                     &syntax, &transfer_syntax);
-       if (!NT_STATUS_IS_OK(status)) {
-               DEBUG(2,("Invalid uuid string in dcerpc_bind_byuuid\n"));
-               return status;
-       }
-
-       return dcerpc_bind(p, mem_ctx, &syntax, &transfer_syntax);
-}
-
-
 /*
   process a fragment received from the transport layer during a
   request
@@ -867,8 +857,8 @@ static void dcerpc_request_recv_data(struct dcerpc_connection *c,
        talloc_steal(req, raw_packet->data);
 
        if (req->recv_handler != NULL) {
+               dcerpc_req_dequeue(req);
                req->state = RPC_REQUEST_DONE;
-               DLIST_REMOVE(c->pending, req);
                req->recv_handler(req, raw_packet, pkt);
                return;
        }
@@ -939,15 +929,6 @@ req_done:
        }
 }
 
-/*
-  make sure requests are cleaned up 
- */
-static int dcerpc_req_destructor(struct rpc_request *req)
-{
-       DLIST_REMOVE(req->p->conn->pending, req);
-       return 0;
-}
-
 /*
   perform the send side of a async dcerpc request
 */
@@ -969,13 +950,14 @@ static struct rpc_request *dcerpc_request_send(struct dcerpc_pipe *p,
        req->p = p;
        req->call_id = next_call_id(p->conn);
        req->status = NT_STATUS_OK;
-       req->state = RPC_REQUEST_PENDING;
+       req->state = RPC_REQUEST_QUEUED;
        req->payload = data_blob(NULL, 0);
        req->flags = 0;
        req->fault_code = 0;
        req->async_call = async;
+       req->ignore_timeout = False;
        req->async.callback = NULL;
-       req->async.private = NULL;
+       req->async.private_data = NULL;
        req->recv_handler = NULL;
 
        if (object != NULL) {
@@ -991,11 +973,12 @@ static struct rpc_request *dcerpc_request_send(struct dcerpc_pipe *p,
        req->opnum = opnum;
        req->request_data.length = stub_data->length;
        req->request_data.data = talloc_reference(req, stub_data->data);
-       if (req->request_data.data == NULL) {
+       if (req->request_data.length && req->request_data.data == NULL) {
                return NULL;
        }
 
        DLIST_ADD_END(p->conn->request_queue, req, struct rpc_request *);
+       talloc_set_destructor(req, dcerpc_req_dequeue);
 
        dcerpc_ship_next_request(p->conn);
 
@@ -1005,7 +988,6 @@ static struct rpc_request *dcerpc_request_send(struct dcerpc_pipe *p,
                                dcerpc_timeout_handler, req);
        }
 
-       talloc_set_destructor(req, dcerpc_req_destructor);
        return req;
 }
 
@@ -1037,6 +1019,7 @@ static void dcerpc_ship_next_request(struct dcerpc_connection *c)
 
        DLIST_REMOVE(c->request_queue, req);
        DLIST_ADD(c->pending, req);
+       req->state = RPC_REQUEST_PENDING;
 
        init_ncacn_hdr(p->conn, &pkt);
 
@@ -1056,7 +1039,7 @@ static void dcerpc_ship_next_request(struct dcerpc_connection *c)
 
        if (req->object) {
                pkt.u.request.object.object = *req->object;
-               pkt.pfc_flags |= DCERPC_PFC_FLAG_ORPC;
+               pkt.pfc_flags |= DCERPC_PFC_FLAG_OBJECT_UUID;
                chunk_size -= ndr_size_GUID(req->object,0);
        }
 
@@ -1118,7 +1101,7 @@ NTSTATUS dcerpc_request_recv(struct rpc_request *req,
 {
        NTSTATUS status;
 
-       while (req->state == RPC_REQUEST_PENDING) {
+       while (req->state != RPC_REQUEST_DONE) {
                struct event_context *ctx = dcerpc_event_context(req->p);
                if (event_loop_once(ctx) != 0) {
                        return NT_STATUS_CONNECTION_DISCONNECTED;
@@ -1209,7 +1192,7 @@ static NTSTATUS dcerpc_ndr_validate_in(struct dcerpc_connection *c,
 
        blob2 = ndr_push_blob(push);
 
-       if (!data_blob_equal(&blob, &blob2)) {
+       if (data_blob_cmp(&blob, &blob2) != 0) {
                DEBUG(3,("original:\n"));
                dump_data(3, blob.data, blob.length);
                DEBUG(3,("secondary:\n"));
@@ -1292,7 +1275,7 @@ static NTSTATUS dcerpc_ndr_validate_out(struct dcerpc_connection *c,
 
        blob2 = ndr_push_blob(push);
 
-       if (!data_blob_equal(&blob, &blob2)) {
+       if (data_blob_cmp(&blob, &blob2) != 0) {
                DEBUG(3,("original:\n"));
                dump_data(3, blob.data, blob.length);
                DEBUG(3,("secondary:\n"));
@@ -1397,7 +1380,7 @@ struct rpc_request *dcerpc_ndr_request_send(struct dcerpc_pipe *p,
 /*
   receive the answer from a dcerpc_ndr_request_send()
 */
-NTSTATUS dcerpc_ndr_request_recv(struct rpc_request *req)
+_PUBLIC_ NTSTATUS dcerpc_ndr_request_recv(struct rpc_request *req)
 {
        struct dcerpc_pipe *p = req->p;
        NTSTATUS status;
@@ -1412,10 +1395,13 @@ NTSTATUS dcerpc_ndr_request_recv(struct rpc_request *req)
 
        /* make sure the recv code doesn't free the request, as we
           need to grab the flags element before it is freed */
-       talloc_increase_ref_count(req);
+       if (talloc_reference(p, req) == NULL) {
+               return NT_STATUS_NO_MEMORY;
+       }
 
        status = dcerpc_request_recv(req, mem_ctx, &response);
        if (!NT_STATUS_IS_OK(status)) {
+               talloc_unlink(p, req);
                return status;
        }
 
@@ -1424,14 +1410,14 @@ NTSTATUS dcerpc_ndr_request_recv(struct rpc_request *req)
        /* prepare for ndr_pull_* */
        pull = ndr_pull_init_flags(p->conn, &response, mem_ctx);
        if (!pull) {
-               talloc_free(req);
+               talloc_unlink(p, req);
                return NT_STATUS_NO_MEMORY;
        }
 
        if (pull->data) {
                pull->data = talloc_steal(pull, pull->data);
        }
-       talloc_free(req);
+       talloc_unlink(p, req);
 
        if (flags & DCERPC_PULL_BIGENDIAN) {
                pull->flags |= LIBNDR_FLAG_BIGENDIAN;
@@ -1539,7 +1525,7 @@ static void dcerpc_alter_recv_handler(struct rpc_request *req,
        struct composite_context *c;
        struct dcerpc_pipe *recv_pipe;
 
-       c = talloc_get_type(req->async.private, struct composite_context);
+       c = talloc_get_type(req->async.private_data, struct composite_context);
        recv_pipe = talloc_get_type(c->private_data, struct dcerpc_pipe);
 
        if (pkt->ptype == DCERPC_PKT_ALTER_RESP &&
@@ -1599,9 +1585,13 @@ struct composite_context *dcerpc_alter_context_send(struct dcerpc_pipe *p,
        pkt.call_id = p->conn->call_id;
        pkt.auth_length = 0;
 
+       if (p->binding->flags & DCERPC_CONCURRENT_MULTIPLEX) {
+               pkt.pfc_flags |= DCERPC_PFC_FLAG_CONC_MPX;
+       }
+
        pkt.u.alter.max_xmit_frag = 5840;
        pkt.u.alter.max_recv_frag = 5840;
-       pkt.u.alter.assoc_group_id = 0;
+       pkt.u.alter.assoc_group_id = p->binding->assoc_group_id;
        pkt.u.alter.num_contexts = 1;
        pkt.u.alter.ctx_list = talloc_array(c, struct dcerpc_ctx_list, 1);
        if (composite_nomem(pkt.u.alter.ctx_list, c)) return c;
@@ -1627,11 +1617,12 @@ struct composite_context *dcerpc_alter_context_send(struct dcerpc_pipe *p,
 
        req->state = RPC_REQUEST_PENDING;
        req->call_id = pkt.call_id;
-       req->async.private = c;
+       req->async.private_data = c;
        req->async.callback = dcerpc_composite_fail;
        req->p = p;
        req->recv_handler = dcerpc_alter_recv_handler;
        DLIST_ADD_END(p->conn->pending, req, struct rpc_request *);
+       talloc_set_destructor(req, dcerpc_req_dequeue);
 
        c->status = p->conn->transport.send_request(p->conn, &blob, True);
        if (!composite_is_ok(c)) return c;