r884: convert samba4 to use [u]int32_t instead of [u]int32
[nivanova/samba-autobuild/.git] / source4 / librpc / rpc / dcerpc.c
index cb03ff89707b65d4a796d83247f7423dfb98afe4..0e3ceaf757fa763ee8a9c7885d9f0fd3ed3c503e 100644 (file)
@@ -24,7 +24,7 @@
 
 /* initialise a dcerpc pipe. This currently assumes a SMB named pipe
    transport */
-struct dcerpc_pipe *dcerpc_pipe_init(struct cli_tree *tree)
+struct dcerpc_pipe *dcerpc_pipe_init(void)
 {
        struct dcerpc_pipe *p;
 
@@ -40,10 +40,12 @@ struct dcerpc_pipe *dcerpc_pipe_init(struct cli_tree *tree)
 
        p->reference_count = 0;
        p->mem_ctx = mem_ctx;
-       p->tree = tree;
-       p->tree->reference_count++;
        p->call_id = 1;
-       p->fnum = 0;
+       p->auth_info = NULL;
+       p->security_state = NULL;
+       p->flags = 0;
+       p->srv_max_xmit_frag = 0;
+       p->srv_max_recv_frag = 0;
 
        return p;
 }
@@ -54,541 +56,404 @@ void dcerpc_pipe_close(struct dcerpc_pipe *p)
        if (!p) return;
        p->reference_count--;
        if (p->reference_count <= 0) {
-               cli_tree_close(p->tree);
+               if (p->security_state) {
+                       p->security_state->security_end(p->security_state);
+               }
+               p->transport.shutdown_pipe(p);
                talloc_destroy(p->mem_ctx);
        }
 }
 
-#define BLOB_CHECK_BOUNDS(blob, offset, len) do { \
-       if ((offset) > blob->length || (blob->length - (offset) < (len))) { \
-               return NT_STATUS_INVALID_PARAMETER; \
-       } \
-} while (0)
-
-#define DCERPC_ALIGN(offset, n) do { \
-       (offset) = ((offset) + ((n)-1)) & ~((n)-1); \
-} while (0)
-
-/*
-  pull a wire format uuid into a string. This will consume 16 bytes
-*/
-static char *dcerpc_pull_uuid(char *data, TALLOC_CTX *mem_ctx)
+/* we need to be able to get/set the fragment length without doing a full
+   decode */
+void dcerpc_set_frag_length(DATA_BLOB *blob, uint16 v)
 {
-       uint32 time_low;
-       uint16 time_mid, time_hi_and_version;
-       uint8 clock_seq_hi_and_reserved;
-       uint8 clock_seq_low;
-       uint8 node[6];
-       int i;
-
-       time_low                  = IVAL(data, 0);
-       time_mid                  = SVAL(data, 4);
-       time_hi_and_version       = SVAL(data, 6);
-       clock_seq_hi_and_reserved = CVAL(data, 8);
-       clock_seq_low             = CVAL(data, 9);
-       for (i=0;i<6;i++) {
-               node[i]           = CVAL(data, 10 + i);
-       }
-
-       return talloc_asprintf(mem_ctx, 
-                              "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
-                              time_low, time_mid, time_hi_and_version, 
-                              clock_seq_hi_and_reserved, clock_seq_low,
-                              node[0], node[1], node[2], node[3], node[4], node[5]);
+       if (CVAL(blob->data,DCERPC_DREP_OFFSET) & DCERPC_DREP_LE) {
+               SSVAL(blob->data, DCERPC_FRAG_LEN_OFFSET, v);
+       } else {
+               RSSVAL(blob->data, DCERPC_FRAG_LEN_OFFSET, v);
+       }
 }
 
-/*
-  push a uuid_str into wire format. It will consume 16 bytes
-*/
-static NTSTATUS push_uuid_str(char *data, const char *uuid_str)
+uint16 dcerpc_get_frag_length(const DATA_BLOB *blob)
 {
-       uint32 time_low;
-       uint32 time_mid, time_hi_and_version;
-       uint32 clock_seq_hi_and_reserved;
-       uint32 clock_seq_low;
-       uint32 node[6];
-       int i;
-
-       if (11 != sscanf(uuid_str, "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
-                        &time_low, &time_mid, &time_hi_and_version, 
-                        &clock_seq_hi_and_reserved, &clock_seq_low,
-                        &node[0], &node[1], &node[2], &node[3], &node[4], &node[5])) {
-               return NT_STATUS_INVALID_PARAMETER;
-       }
-
-       SIVAL(data, 0, time_low);
-       SSVAL(data, 4, time_mid);
-       SSVAL(data, 6, time_hi_and_version);
-       SCVAL(data, 8, clock_seq_hi_and_reserved);
-       SCVAL(data, 9, clock_seq_low);
-       for (i=0;i<6;i++) {
-               SCVAL(data, 10 + i, node[i]);
+       if (CVAL(blob->data,DCERPC_DREP_OFFSET) & DCERPC_DREP_LE) {
+               return SVAL(blob->data, DCERPC_FRAG_LEN_OFFSET);
+       } else {
+               return RSVAL(blob->data, DCERPC_FRAG_LEN_OFFSET);
        }
-
-       return NT_STATUS_OK;
 }
 
-/*
-  pull a dcerpc syntax id from a blob
-*/
-static NTSTATUS dcerpc_pull_syntax_id(DATA_BLOB *blob, TALLOC_CTX *mem_ctx, 
-                                     uint32 *offset, 
-                                     struct dcerpc_syntax_id *syntax)
+void dcerpc_set_auth_length(DATA_BLOB *blob, uint16 v)
 {
-       syntax->uuid_str = dcerpc_pull_uuid(blob->data + (*offset), mem_ctx);
-       if (!syntax->uuid_str) {
-               return NT_STATUS_NO_MEMORY;
+       if (CVAL(blob->data,DCERPC_DREP_OFFSET) & DCERPC_DREP_LE) {
+               SSVAL(blob->data, DCERPC_AUTH_LEN_OFFSET, v);
+       } else {
+               RSSVAL(blob->data, DCERPC_AUTH_LEN_OFFSET, v);
        }
-       (*offset) += 16;
-       syntax->if_version = IVAL(blob->data, *offset);
-       (*offset) += 4;
-       return NT_STATUS_OK;
 }
 
-/*
-  push a syntax id onto the wire. It will consume 20 bytes
-*/
-static NTSTATUS push_syntax_id(char *data, const struct dcerpc_syntax_id *syntax)
-{
-       NTSTATUS status;
-
-       status = push_uuid_str(data, syntax->uuid_str);
-       SIVAL(data, 16, syntax->if_version);
-
-       return status;
-}
 
-/*
-  pull an auth verifier from a packet
+/* 
+   parse a data blob into a dcerpc_packet structure. This handles both
+   input and output packets
 */
-static NTSTATUS dcerpc_pull_auth_verifier(DATA_BLOB *blob, TALLOC_CTX *mem_ctx, 
-                                         uint32 *offset, 
-                                         struct dcerpc_hdr *hdr,
-                                         DATA_BLOB *auth)
+static NTSTATUS dcerpc_pull(DATA_BLOB *blob, TALLOC_CTX *mem_ctx, 
+                           struct dcerpc_packet *pkt)
 {
-       if (hdr->auth_length == 0) {
-               return NT_STATUS_OK;
-       }
+       struct ndr_pull *ndr;
 
-       BLOB_CHECK_BOUNDS(blob, *offset, hdr->auth_length);
-       *auth = data_blob_talloc(mem_ctx, blob->data + (*offset), hdr->auth_length);
-       if (!auth->data) {
+       ndr = ndr_pull_init_blob(blob, mem_ctx);
+       if (!ndr) {
                return NT_STATUS_NO_MEMORY;
        }
-       (*offset) += hdr->auth_length;
-       return NT_STATUS_OK;
-}
-
-/* 
-   parse a struct dcerpc_response
-*/
-static NTSTATUS dcerpc_pull_response(DATA_BLOB *blob, TALLOC_CTX *mem_ctx, 
-                                    uint32 *offset, 
-                                    struct dcerpc_hdr *hdr,
-                                    struct dcerpc_response *pkt)
-{
-       uint32 stub_len;
-       
-       BLOB_CHECK_BOUNDS(blob, *offset, 8);
-
-       pkt->alloc_hint   = IVAL(blob->data, (*offset) + 0);
-       pkt->context_id   = SVAL(blob->data, (*offset) + 4);
-       pkt->cancel_count = CVAL(blob->data, (*offset) + 6);
 
-       (*offset) += 8;
-
-       stub_len = blob->length - ((*offset) + hdr->auth_length);
-       BLOB_CHECK_BOUNDS(blob, *offset, stub_len);
-       pkt->stub_data = data_blob_talloc(mem_ctx, blob->data + (*offset), stub_len);
-       if (stub_len != 0 && !pkt->stub_data.data) {
-               return NT_STATUS_NO_MEMORY;
+       if (! (CVAL(blob->data, DCERPC_DREP_OFFSET) & DCERPC_DREP_LE)) {
+               ndr->flags |= LIBNDR_FLAG_BIGENDIAN;
        }
-       (*offset) += stub_len;
 
-       return dcerpc_pull_auth_verifier(blob, mem_ctx, offset, hdr, &pkt->auth_verifier);      
+       return ndr_pull_dcerpc_packet(ndr, NDR_SCALARS|NDR_BUFFERS, pkt);
 }
 
-
 /* 
-   parse a struct bind_ack
+   parse a possibly signed blob into a dcerpc request packet structure
 */
-static NTSTATUS dcerpc_pull_bind_ack(DATA_BLOB *blob, TALLOC_CTX *mem_ctx, 
-                                    uint32 *offset, 
-                                    struct dcerpc_hdr *hdr,
-                                    struct dcerpc_bind_ack *pkt)
+static NTSTATUS dcerpc_pull_request_sign(struct dcerpc_pipe *p, 
+                                        DATA_BLOB *blob, TALLOC_CTX *mem_ctx, 
+                                        struct dcerpc_packet *pkt)
 {
-       uint16 len;
-       int i;
-       
-       BLOB_CHECK_BOUNDS(blob, *offset, 10);
-       pkt->max_xmit_frag  = SVAL(blob->data, (*offset) + 0);
-       pkt->max_recv_frag  = SVAL(blob->data, (*offset) + 2);
-       pkt->assoc_group_id = IVAL(blob->data, (*offset) + 4);
-       len                 = SVAL(blob->data, (*offset) + 8);
-       (*offset) += 10;
-
-       if (len) {
-               BLOB_CHECK_BOUNDS(blob, *offset, len);
-               pkt->secondary_address = talloc_strndup(mem_ctx, blob->data + (*offset), len);
-               if (!pkt->secondary_address) {
-                       return NT_STATUS_NO_MEMORY;
-               }
-               (*offset) += len;
-       }
-
-       DCERPC_ALIGN(*offset, 4);
-       BLOB_CHECK_BOUNDS(blob, *offset, 4);
-       pkt->num_results = CVAL(blob->data, *offset); 
-       (*offset) += 4;
+       struct ndr_pull *ndr;
+       NTSTATUS status;
+       struct dcerpc_auth auth;
+       DATA_BLOB auth_blob;
 
-       if (pkt->num_results > 0) {
-               pkt->ctx_list = talloc(mem_ctx, sizeof(pkt->ctx_list[0]) * pkt->num_results);
-               if (!pkt->ctx_list) {
-                       return NT_STATUS_NO_MEMORY;
-               }
+       /* non-signed packets are simpler */
+       if (!p->auth_info || !p->security_state) {
+               return dcerpc_pull(blob, mem_ctx, pkt);
        }
 
-       for (i=0;i<pkt->num_results;i++) {
-               NTSTATUS status;
-
-               BLOB_CHECK_BOUNDS(blob, *offset, 24);
-               pkt->ctx_list[i].result = SVAL(blob->data, *offset);
-               pkt->ctx_list[i].reason = SVAL(blob->data, 2 + *offset);
-               (*offset) += 4;
-               status = dcerpc_pull_syntax_id(blob, mem_ctx, offset, &pkt->ctx_list[i].syntax);
-               if (!NT_STATUS_IS_OK(status)) {
-                       return status;
-               }
+       ndr = ndr_pull_init_blob(blob, mem_ctx);
+       if (!ndr) {
+               return NT_STATUS_NO_MEMORY;
        }
 
-       return dcerpc_pull_auth_verifier(blob, mem_ctx, offset, hdr, &pkt->auth_verifier);
-}
-
+       if (! (CVAL(blob->data, DCERPC_DREP_OFFSET) & DCERPC_DREP_LE)) {
+               ndr->flags |= LIBNDR_FLAG_BIGENDIAN;
+       }
 
-/* 
-   parse a dcerpc header
-*/
-static NTSTATUS dcerpc_pull_hdr(DATA_BLOB *blob, uint32 *offset, struct dcerpc_hdr *hdr)
-{
-       BLOB_CHECK_BOUNDS(blob, *offset, 16);
-       
-       hdr->rpc_vers       = CVAL(blob->data, (*offset) + 0);
-       hdr->rpc_vers_minor = CVAL(blob->data, (*offset) + 1);
-       hdr->ptype          = CVAL(blob->data, (*offset) + 2);
-       hdr->pfc_flags      = CVAL(blob->data, (*offset) + 3);
-       memcpy(hdr->drep, blob->data + (*offset) + 4, 4);
-       hdr->frag_length    = SVAL(blob->data, (*offset) + 8);
-       hdr->auth_length    = SVAL(blob->data, (*offset) + 10);
-       hdr->call_id        = IVAL(blob->data, (*offset) + 12);
+       /* pull the basic packet */
+       status = ndr_pull_dcerpc_packet(ndr, NDR_SCALARS|NDR_BUFFERS, pkt);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
 
-       (*offset) += 16;
+       if (pkt->ptype != DCERPC_PKT_RESPONSE) {
+               return status;
+       }
 
-       return NT_STATUS_OK;
-}
+       auth_blob.length = 8 + pkt->auth_length;
 
-/* 
-   parse a dcerpc header. It consumes 16 bytes
-*/
-static void dcerpc_push_hdr(char *data, struct dcerpc_hdr *hdr)
-{
-       SCVAL(data, 0, hdr->rpc_vers);
-       SCVAL(data, 1, hdr->rpc_vers_minor);
-       SCVAL(data, 2, hdr->ptype);
-       SCVAL(data, 3, hdr->pfc_flags);
-       memcpy(data + 4, hdr->drep, 4);
-       SSVAL(data, 8, hdr->frag_length);
-       SSVAL(data, 12, hdr->call_id);
-}
+       /* check for a valid length */
+       if (pkt->u.response.stub_and_verifier.length < auth_blob.length) {
+               return NT_STATUS_INFO_LENGTH_MISMATCH;
+       }
 
+       auth_blob.data = 
+               pkt->u.response.stub_and_verifier.data + 
+               pkt->u.response.stub_and_verifier.length - auth_blob.length;
+       pkt->u.response.stub_and_verifier.length -= auth_blob.length;
 
+       /* pull the auth structure */
+       ndr = ndr_pull_init_blob(&auth_blob, mem_ctx);
+       if (!ndr) {
+               return NT_STATUS_NO_MEMORY;
+       }
 
-/* 
-   parse a data blob into a dcerpc_packet structure. This handles both
-   input and output packets
-*/
-NTSTATUS dcerpc_pull(DATA_BLOB *blob, TALLOC_CTX *mem_ctx, struct dcerpc_packet *pkt)
-{
-       NTSTATUS status;
-       uint32 offset = 0;
+       if (! (CVAL(blob->data, DCERPC_DREP_OFFSET) & DCERPC_DREP_LE)) {
+               ndr->flags |= LIBNDR_FLAG_BIGENDIAN;
+       }
 
-       status = dcerpc_pull_hdr(blob, &offset, &pkt->hdr);
+       status = ndr_pull_dcerpc_auth(ndr, NDR_SCALARS|NDR_BUFFERS, &auth);
        if (!NT_STATUS_IS_OK(status)) {
                return status;
        }
 
-       switch (pkt->hdr.ptype) {
-       case DCERPC_PKT_BIND_ACK:
-               status = dcerpc_pull_bind_ack(blob, mem_ctx, &offset, &pkt->hdr, &pkt->out.bind_ack);
-               if (!NT_STATUS_IS_OK(status)) {
-                       return status;
-               }               
+
+       /* check signature or unseal the packet */
+       switch (p->auth_info->auth_level) {
+       case DCERPC_AUTH_LEVEL_PRIVACY:
+               status = p->security_state->unseal_packet(p->security_state, 
+                                                         mem_ctx, 
+                                                         pkt->u.response.stub_and_verifier.data, 
+                                                         pkt->u.response.stub_and_verifier.length, 
+                                                         &auth.credentials);
                break;
 
-       case DCERPC_PKT_RESPONSE:
-               status = dcerpc_pull_response(blob, mem_ctx, &offset, &pkt->hdr, &pkt->out.response);
-               if (!NT_STATUS_IS_OK(status)) {
-                       return status;
-               }               
+       case DCERPC_AUTH_LEVEL_INTEGRITY:
+               status = p->security_state->check_packet(p->security_state, 
+                                                        mem_ctx, 
+                                                        pkt->u.response.stub_and_verifier.data, 
+                                                        pkt->u.response.stub_and_verifier.length, 
+                                                        &auth.credentials);
+               break;
+
+       case DCERPC_AUTH_LEVEL_NONE:
                break;
 
        default:
-               return NT_STATUS_NET_WRITE_FAULT;
+               status = NT_STATUS_INVALID_LEVEL;
+               break;
+       }
+
+       /* remove the indicated amount of paddiing */
+       if (pkt->u.response.stub_and_verifier.length < auth.auth_pad_length) {
+               return NT_STATUS_INFO_LENGTH_MISMATCH;
        }
+       pkt->u.response.stub_and_verifier.length -= auth.auth_pad_length;
 
        return status;
 }
 
 
 /* 
-   push a dcerpc_bind into a blob
+   push a dcerpc request packet into a blob, possibly signing it.
 */
-static NTSTATUS dcerpc_push_bind(DATA_BLOB *blob, uint32 *offset,
-                                struct dcerpc_hdr *hdr,
-                                struct dcerpc_bind *pkt)
+static NTSTATUS dcerpc_push_request_sign(struct dcerpc_pipe *p, 
+                                        DATA_BLOB *blob, TALLOC_CTX *mem_ctx, 
+                                        struct dcerpc_packet *pkt)
 {
-       int i, j;
-
-       SSVAL(blob->data, (*offset) + 0, pkt->max_xmit_frag);
-       SSVAL(blob->data, (*offset) + 2, pkt->max_recv_frag);
-       SIVAL(blob->data, (*offset) + 4, pkt->assoc_group_id);
-       SCVAL(blob->data, (*offset) + 8, pkt->num_contexts);
-       (*offset) += 12;
-
-       for (i=0;i<pkt->num_contexts;i++) {
-               NTSTATUS status;
+       NTSTATUS status;
+       struct ndr_push *ndr;
 
-               SSVAL(blob->data, (*offset) + 0, pkt->ctx_list[i].context_id);
-               SCVAL(blob->data, (*offset) + 2, pkt->ctx_list[i].num_transfer_syntaxes);
-               status = push_syntax_id(blob->data + (*offset) + 4, &pkt->ctx_list[i].abstract_syntax);
-               if (!NT_STATUS_IS_OK(status)) {
-                       return status;
-               }
-               (*offset) += 24;
-               for (j=0;j<pkt->ctx_list[i].num_transfer_syntaxes;j++) {
-                       status = push_syntax_id(blob->data + (*offset), 
-                                               &pkt->ctx_list[i].transfer_syntaxes[j]);
-                       if (!NT_STATUS_IS_OK(status)) {
-                               return status;
-                       }
-                       (*offset) += 20;
-               }
+       /* non-signed packets are simpler */
+       if (!p->auth_info || !p->security_state) {
+               return dcerpc_push_auth(blob, mem_ctx, pkt, p->auth_info);
        }
 
-       return NT_STATUS_OK;
-}
-
-/* 
-   push a dcerpc_request into a blob
-*/
-static NTSTATUS dcerpc_push_request(DATA_BLOB *blob, uint32 *offset,
-                                   struct dcerpc_hdr *hdr,
-                                   struct dcerpc_request *pkt)
-{
-       SIVAL(blob->data, (*offset) + 0, pkt->alloc_hint);
-       SSVAL(blob->data, (*offset) + 4, pkt->context_id);
-       SSVAL(blob->data, (*offset) + 6, pkt->opnum);
-
-       (*offset) += 8;
-
-       memcpy(blob->data + (*offset), pkt->stub_data.data, pkt->stub_data.length);
-       (*offset) += pkt->stub_data.length;
-
-       memcpy(blob->data + (*offset), pkt->auth_verifier.data, pkt->auth_verifier.length);
-       (*offset) += pkt->auth_verifier.length;
-
-       return NT_STATUS_OK;
-}
+       ndr = ndr_push_init_ctx(mem_ctx);
+       if (!ndr) {
+               return NT_STATUS_NO_MEMORY;
+       }
 
+       if (p->flags & DCERPC_PUSH_BIGENDIAN) {
+               ndr->flags |= LIBNDR_FLAG_BIGENDIAN;
+       }
 
-/*
-  work out the wire size of a dcerpc packet 
-*/
-static uint32 dcerpc_wire_size(struct dcerpc_packet *pkt)
-{
-       int i;
-       uint32 size = 0;
+       status = ndr_push_dcerpc_packet(ndr, NDR_SCALARS|NDR_BUFFERS, pkt);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
 
-       size += 16; /* header */
+       /* pad to 8 byte multiple */
+       p->auth_info->auth_pad_length = NDR_ALIGN(ndr, 8);
+       ndr_push_zero(ndr, p->auth_info->auth_pad_length);
 
-       switch (pkt->hdr.ptype) {
-       case DCERPC_PKT_REQUEST:
-               size += 8;
-               size += pkt->in.request.stub_data.length;
-               size += pkt->in.request.auth_verifier.length;
+       /* sign or seal the packet */
+       switch (p->auth_info->auth_level) {
+       case DCERPC_AUTH_LEVEL_PRIVACY:
+               status = p->security_state->seal_packet(p->security_state, 
+                                                       mem_ctx, 
+                                                       ndr->data + DCERPC_REQUEST_LENGTH, 
+                                                       ndr->offset - DCERPC_REQUEST_LENGTH,
+                                                       &p->auth_info->credentials);
                break;
 
-       case DCERPC_PKT_RESPONSE:
-               size += 8;
-               size += pkt->out.response.stub_data.length;
-               size += pkt->hdr.auth_length;
+       case DCERPC_AUTH_LEVEL_INTEGRITY:
+               status = p->security_state->sign_packet(p->security_state, 
+                                                       mem_ctx, 
+                                                       ndr->data + DCERPC_REQUEST_LENGTH, 
+                                                       ndr->offset - DCERPC_REQUEST_LENGTH,
+                                                       &p->auth_info->credentials);
                break;
 
-       case DCERPC_PKT_BIND:
-               size += 12;
-               for (i=0;i<pkt->in.bind.num_contexts;i++) {
-                       size += 24;
-                       size += pkt->in.bind.ctx_list[i].num_transfer_syntaxes * 20;
-               }
-               size += pkt->hdr.auth_length;
+       case DCERPC_AUTH_LEVEL_NONE:
+               p->auth_info->credentials = data_blob(NULL, 0);
                break;
 
-       case DCERPC_PKT_BIND_ACK:
-               size += 10;
-               if (pkt->out.bind_ack.secondary_address) {
-                       size += strlen(pkt->out.bind_ack.secondary_address) + 1;
-               }
-               size += 4;
-               size += pkt->out.bind_ack.num_results * 24;
-               size += pkt->hdr.auth_length;
+       default:
+               status = NT_STATUS_INVALID_LEVEL;
                break;
        }
 
-       return size;
-}
-
-/* 
-   push a dcerpc_packet into a blob. This handles both input and
-   output packets
-*/
-NTSTATUS dcerpc_push(DATA_BLOB *blob, TALLOC_CTX *mem_ctx, struct dcerpc_packet *pkt)
-{
-       uint32 offset = 0;
-       uint32 wire_size;
-       NTSTATUS status;
-
-       /* work out how big the packet will be on the wire */
-       wire_size = dcerpc_wire_size(pkt);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }       
 
-       (*blob) = data_blob_talloc(mem_ctx, NULL, wire_size);
-       if (!blob->data) {
-               return NT_STATUS_NO_MEMORY;
+       /* add the auth verifier */
+       status = ndr_push_dcerpc_auth(ndr, NDR_SCALARS|NDR_BUFFERS, p->auth_info);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
        }
 
-       pkt->hdr.frag_length = wire_size;
+       /* extract the whole packet as a blob */
+       *blob = ndr_push_blob(ndr);
 
-       dcerpc_push_hdr(blob->data + offset, &pkt->hdr);
-       offset += 16;
+       /* fill in the fragment length and auth_length, we can't fill
+          in these earlier as we don't know the signature length (it
+          could be variable length) */
+       dcerpc_set_frag_length(blob, blob->length);
+       dcerpc_set_auth_length(blob, p->auth_info->credentials.length);
 
-       switch (pkt->hdr.ptype) {
-       case DCERPC_PKT_BIND:
-               status = dcerpc_push_bind(blob, &offset, &pkt->hdr, &pkt->in.bind);
-               break;
+       data_blob_free(&p->auth_info->credentials);
 
-       case DCERPC_PKT_REQUEST:
-               status = dcerpc_push_request(blob, &offset, &pkt->hdr, &pkt->in.request);
-               break;
-               
-       default:
-               status = NT_STATUS_NET_WRITE_FAULT;
-       }
-
-       return status;
+       return NT_STATUS_OK;
 }
 
 
-
-
 /* 
    fill in the fixed values in a dcerpc header 
 */
-static void init_dcerpc_hdr(struct dcerpc_hdr *hdr)
+static void init_dcerpc_hdr(struct dcerpc_pipe *p, struct dcerpc_packet *pkt)
 {
-        hdr->rpc_vers = 5;
-        hdr->rpc_vers_minor = 0;
-        hdr->drep[0] = 0x10; /* Little endian */
-        hdr->drep[1] = 0;
-        hdr->drep[2] = 0;
-        hdr->drep[3] = 0;
+       pkt->rpc_vers = 5;
+       pkt->rpc_vers_minor = 0;
+       if (p->flags & DCERPC_PUSH_BIGENDIAN) {
+               pkt->drep[0] = 0;
+       } else {
+               pkt->drep[0] = DCERPC_DREP_LE;
+       }
+       pkt->drep[1] = 0;
+       pkt->drep[2] = 0;
+       pkt->drep[3] = 0;
 }
 
 
 /* 
    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)
 {
-       TALLOC_CTX *mem_ctx;
-        struct dcerpc_packet pkt;
+       struct dcerpc_packet pkt;
        NTSTATUS status;
        DATA_BLOB blob;
-       DATA_BLOB blob_out;
+       struct dcerpc_syntax_id tsyntax;
 
-       mem_ctx = talloc_init("dcerpc_bind");
-       if (!mem_ctx) {
-               return NT_STATUS_NO_MEMORY;
-       }
-
-       init_dcerpc_hdr(&pkt.hdr);
+       init_dcerpc_hdr(p, &pkt);
 
-       pkt.hdr.ptype = DCERPC_PKT_BIND;
-       pkt.hdr.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
-       pkt.hdr.call_id = p->call_id++;
-       pkt.hdr.auth_length = 0;
+       pkt.ptype = DCERPC_PKT_BIND;
+       pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
+       pkt.call_id = p->call_id;
+       pkt.auth_length = 0;
 
-        pkt.in.bind.max_xmit_frag = 0x2000;
-        pkt.in.bind.max_recv_frag = 0x2000;
-        pkt.in.bind.assoc_group_id = 0;
-        pkt.in.bind.num_contexts = 1;
-       pkt.in.bind.ctx_list = talloc(mem_ctx, sizeof(pkt.in.bind.ctx_list[0]));
-       if (!pkt.in.bind.ctx_list) {
-               talloc_destroy(mem_ctx);
+       pkt.u.bind.max_xmit_frag = 0x2000;
+       pkt.u.bind.max_recv_frag = 0x2000;
+       pkt.u.bind.assoc_group_id = 0;
+       pkt.u.bind.num_contexts = 1;
+       pkt.u.bind.ctx_list = talloc(mem_ctx, sizeof(pkt.u.bind.ctx_list[0]));
+       if (!pkt.u.bind.ctx_list) {
                return NT_STATUS_NO_MEMORY;
        }
-       pkt.in.bind.ctx_list[0].context_id = 0;
-       pkt.in.bind.ctx_list[0].num_transfer_syntaxes = 1;
-       pkt.in.bind.ctx_list[0].abstract_syntax = *syntax;
-       pkt.in.bind.ctx_list[0].transfer_syntaxes = transfer_syntax;
-
-       pkt.in.bind.auth_verifier = data_blob(NULL, 0);
+       pkt.u.bind.ctx_list[0].context_id = 0;
+       pkt.u.bind.ctx_list[0].num_transfer_syntaxes = 1;
+       pkt.u.bind.ctx_list[0].abstract_syntax = *syntax;
+       tsyntax = *transfer_syntax;
+       pkt.u.bind.ctx_list[0].transfer_syntaxes = &tsyntax;
+       pkt.u.bind.auth_info = data_blob(NULL, 0);
 
-       status = dcerpc_push(&blob, mem_ctx, &pkt);
+       /* construct the NDR form of the packet */
+       status = dcerpc_push_auth(&blob, mem_ctx, &pkt, p->auth_info);
        if (!NT_STATUS_IS_OK(status)) {
-               talloc_destroy(mem_ctx);
                return status;
        }
 
-       status = dcerpc_raw_packet(p, mem_ctx, &blob, &blob_out);
+       /* send it on its way */
+       status = p->transport.full_request(p, mem_ctx, &blob, &blob);
        if (!NT_STATUS_IS_OK(status)) {
-               talloc_destroy(mem_ctx);
                return status;
        }
 
-       status = dcerpc_pull(&blob_out, mem_ctx, &pkt);
+       /* unmarshall the NDR */
+       status = dcerpc_pull(&blob, mem_ctx, &pkt);
        if (!NT_STATUS_IS_OK(status)) {
-               talloc_destroy(mem_ctx);
                return status;
        }
 
-       if (pkt.hdr.ptype != DCERPC_PKT_BIND_ACK ||
-           pkt.out.bind_ack.num_results == 0 ||
-           pkt.out.bind_ack.ctx_list[0].result != 0) {
+       if ((pkt.ptype != DCERPC_PKT_BIND_ACK && pkt.ptype != DCERPC_PKT_ALTER_ACK) ||
+           pkt.u.bind_ack.num_results == 0 ||
+           pkt.u.bind_ack.ctx_list[0].result != 0) {
                status = NT_STATUS_UNSUCCESSFUL;
        }
 
-       p->srv_max_xmit_frag = pkt.out.bind_ack.max_xmit_frag;
-       p->srv_max_recv_frag = pkt.out.bind_ack.max_recv_frag;
+       if (pkt.ptype != DCERPC_PKT_ALTER_ACK) {
+               p->srv_max_xmit_frag = pkt.u.bind_ack.max_xmit_frag;
+               p->srv_max_recv_frag = pkt.u.bind_ack.max_recv_frag;
+       }
 
-       talloc_destroy(mem_ctx);
+       /* the bind_ack might contain a reply set of credentials */
+       if (p->auth_info && pkt.u.bind_ack.auth_info.length) {
+               status = ndr_pull_struct_blob(&pkt.u.bind_ack.auth_info,
+                                             mem_ctx,
+                                             p->auth_info,
+                                             (ndr_pull_flags_fn_t)ndr_pull_dcerpc_auth);
+       }
 
        return status;  
 }
 
-/* Perform a bind using the given UUID and version */
+/* 
+   perform a continued bind (and auth3)
+*/
+NTSTATUS dcerpc_auth3(struct dcerpc_pipe *p, 
+                     TALLOC_CTX *mem_ctx)
+{
+       struct dcerpc_packet pkt;
+       NTSTATUS status;
+       DATA_BLOB blob;
+
+       init_dcerpc_hdr(p, &pkt);
+
+       pkt.ptype = DCERPC_PKT_AUTH3;
+       pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
+       pkt.call_id = p->call_id++;
+       pkt.auth_length = 0;
+       pkt.u.auth._pad = 0;
+       pkt.u.auth.auth_info = data_blob(NULL, 0);
+
+       /* construct the NDR form of the packet */
+       status = dcerpc_push_auth(&blob, mem_ctx, &pkt, p->auth_info);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+
+       /* send it on its way */
+       status = p->transport.initial_request(p, mem_ctx, &blob);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+
+       return status;  
+}
+
+
+/* perform a dcerpc bind, using the uuid as the key */
 NTSTATUS dcerpc_bind_byuuid(struct dcerpc_pipe *p, 
+                           TALLOC_CTX *mem_ctx,
                            const char *uuid, unsigned version)
 {
        struct dcerpc_syntax_id syntax;
        struct dcerpc_syntax_id transfer_syntax;
+       NTSTATUS status;
 
-       syntax.uuid_str = uuid;
+       status = GUID_from_string(uuid, &syntax.uuid);
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(2,("Invalid uuid string in dcerpc_bind_byuuid\n"));
+               return status;
+       }
        syntax.if_version = version;
 
-       transfer_syntax.uuid_str = "8a885d04-1ceb-11c9-9fe8-08002b104860";
-       transfer_syntax.if_version = 2;
+       status = GUID_from_string(NDR_GUID, &transfer_syntax.uuid);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+       transfer_syntax.if_version = NDR_GUID_VERSION;
 
-       return dcerpc_bind(p, &syntax, &transfer_syntax);
+       return dcerpc_bind(p, mem_ctx, &syntax, &transfer_syntax);
 }
 
 /*
@@ -603,44 +468,46 @@ NTSTATUS dcerpc_request(struct dcerpc_pipe *p,
        
        struct dcerpc_packet pkt;
        NTSTATUS status;
-       DATA_BLOB blob_in, blob_out, payload;
-       uint32 remaining, chunk_size;
+       DATA_BLOB blob, payload;
+       uint32_t remaining, chunk_size;
+
+       /* allow the application to tell when a fault has happened */
+       p->last_fault_code = 0;
 
-       init_dcerpc_hdr(&pkt.hdr);
+       init_dcerpc_hdr(p, &pkt);
 
        remaining = stub_data_in->length;
 
        /* we can write a full max_recv_frag size, minus the dcerpc
           request header size */
-       chunk_size = p->srv_max_recv_frag - 24;
+       chunk_size = p->srv_max_recv_frag - (DCERPC_MAX_SIGN_SIZE+DCERPC_REQUEST_LENGTH);
 
-       pkt.hdr.ptype = DCERPC_PKT_REQUEST;
-       pkt.hdr.call_id = p->call_id++;
-       pkt.hdr.auth_length = 0;
-       pkt.in.request.alloc_hint = remaining;
-       pkt.in.request.context_id = 0;
-       pkt.in.request.opnum = opnum;
-       pkt.in.request.auth_verifier = data_blob(NULL, 0);
+       pkt.ptype = DCERPC_PKT_REQUEST;
+       pkt.call_id = p->call_id++;
+       pkt.auth_length = 0;
+       pkt.u.request.alloc_hint = remaining;
+       pkt.u.request.context_id = 0;
+       pkt.u.request.opnum = opnum;
 
        /* we send a series of pdus without waiting for a reply until
           the last pdu */
        while (remaining > chunk_size) {
                if (remaining == stub_data_in->length) {
-                       pkt.hdr.pfc_flags = DCERPC_PFC_FLAG_FIRST;
+                       pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST;
                } else {
-                       pkt.hdr.pfc_flags = 0;
+                       pkt.pfc_flags = 0;
                }
 
-               pkt.in.request.stub_data.data = stub_data_in->data + 
+               pkt.u.request.stub_and_verifier.data = stub_data_in->data + 
                        (stub_data_in->length - remaining);
-               pkt.in.request.stub_data.length = chunk_size;
+               pkt.u.request.stub_and_verifier.length = chunk_size;
 
-               status = dcerpc_push(&blob_in, mem_ctx, &pkt);
+               status = dcerpc_push_request_sign(p, &blob, mem_ctx, &pkt);
                if (!NT_STATUS_IS_OK(status)) {
                        return status;
                }
                
-               status = dcerpc_raw_packet_initial(p, mem_ctx, &blob_in);
+               status = p->transport.initial_request(p, mem_ctx, &blob);
                if (!NT_STATUS_IS_OK(status)) {
                        return status;
                }               
@@ -651,62 +518,75 @@ NTSTATUS dcerpc_request(struct dcerpc_pipe *p,
        /* now we send a pdu with LAST_FRAG sent and get the first
           part of the reply */
        if (remaining == stub_data_in->length) {
-               pkt.hdr.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
+               pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
        } else {
-               pkt.hdr.pfc_flags = DCERPC_PFC_FLAG_LAST;
+               pkt.pfc_flags = DCERPC_PFC_FLAG_LAST;
        }
-       pkt.in.request.stub_data.data = stub_data_in->data + 
+       pkt.u.request.stub_and_verifier.data = stub_data_in->data + 
                (stub_data_in->length - remaining);
-       pkt.in.request.stub_data.length = remaining;
-       
-       status = dcerpc_push(&blob_in, mem_ctx, &pkt);
+       pkt.u.request.stub_and_verifier.length = remaining;
+
+       status = dcerpc_push_request_sign(p, &blob, mem_ctx, &pkt);
        if (!NT_STATUS_IS_OK(status)) {
                return status;
        }
 
        /* send the pdu and get the initial response pdu */
-       status = dcerpc_raw_packet(p, mem_ctx, &blob_in, &blob_out);
+       status = p->transport.full_request(p, mem_ctx, &blob, &blob);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
 
-       status = dcerpc_pull(&blob_out, mem_ctx, &pkt);
+       status = dcerpc_pull_request_sign(p, &blob, mem_ctx, &pkt);
        if (!NT_STATUS_IS_OK(status)) {
                return status;
        }
 
-       if (pkt.hdr.ptype != DCERPC_PKT_RESPONSE) {
+       if (pkt.ptype == DCERPC_PKT_FAULT) {
+               p->last_fault_code = pkt.u.fault.status;
+               return NT_STATUS_NET_WRITE_FAULT;
+       }
+
+       if (pkt.ptype != DCERPC_PKT_RESPONSE) {
                return NT_STATUS_UNSUCCESSFUL;
        }
 
-       if (!(pkt.hdr.pfc_flags & DCERPC_PFC_FLAG_FIRST)) {
+       if (!(pkt.pfc_flags & DCERPC_PFC_FLAG_FIRST)) {
                /* something is badly wrong! */
                return NT_STATUS_UNSUCCESSFUL;
        }
 
-       payload = pkt.out.response.stub_data;
+       payload = pkt.u.response.stub_and_verifier;
 
        /* continue receiving fragments */
-       while (!(pkt.hdr.pfc_flags & DCERPC_PFC_FLAG_LAST)) {
-               uint32 length;
+       while (!(pkt.pfc_flags & DCERPC_PFC_FLAG_LAST)) {
+               uint32_t length;
 
-               status = dcerpc_raw_packet_secondary(p, mem_ctx, &blob_out);
+               status = p->transport.secondary_request(p, mem_ctx, &blob);
                if (!NT_STATUS_IS_OK(status)) {
                        return status;
                }
 
-               status = dcerpc_pull(&blob_out, mem_ctx, &pkt);
+               status = dcerpc_pull_request_sign(p, &blob, mem_ctx, &pkt);
                if (!NT_STATUS_IS_OK(status)) {
                        return status;
                }
 
-               if (pkt.hdr.pfc_flags & DCERPC_PFC_FLAG_FIRST) {
+               if (pkt.pfc_flags & DCERPC_PFC_FLAG_FIRST) {
                        /* start of another packet!? */
                        return NT_STATUS_UNSUCCESSFUL;
                }
 
-               if (pkt.hdr.ptype != DCERPC_PKT_RESPONSE) {
+               if (pkt.ptype == DCERPC_PKT_FAULT) {
+                       p->last_fault_code = pkt.u.fault.status;
+                       return NT_STATUS_NET_WRITE_FAULT;
+               }
+
+               if (pkt.ptype != DCERPC_PKT_RESPONSE) {
                        return NT_STATUS_UNSUCCESSFUL;
                }
 
-               length = pkt.out.response.stub_data.length;
+               length = pkt.u.response.stub_and_verifier.length;
 
                payload.data = talloc_realloc(mem_ctx, 
                                              payload.data, 
@@ -716,7 +596,7 @@ NTSTATUS dcerpc_request(struct dcerpc_pipe *p,
                }
 
                memcpy(payload.data + payload.length,
-                      pkt.out.response.stub_data.data,
+                      pkt.u.response.stub_and_verifier.data,
                       length);
 
                payload.length += length;
@@ -726,6 +606,12 @@ NTSTATUS dcerpc_request(struct dcerpc_pipe *p,
                *stub_data_out = payload;
        }
 
+       if (!(pkt.drep[0] & DCERPC_DREP_LE)) {
+               p->flags |= DCERPC_PULL_BIGENDIAN;
+       } else {
+               p->flags &= ~DCERPC_PULL_BIGENDIAN;
+       }
+
        return status;
 }
 
@@ -761,7 +647,7 @@ static NTSTATUS dcerpc_ndr_validate_in(TALLOC_CTX *mem_ctx,
        status = ndr_pull(pull, NDR_IN, st);
        if (!NT_STATUS_IS_OK(status)) {
                return ndr_pull_error(pull, NDR_ERR_VALIDATE, 
-                                     "Error in input validation pull - %s",
+                                     "failed input validation pull - %s",
                                      nt_errstr(status));
        }
 
@@ -773,15 +659,19 @@ static NTSTATUS dcerpc_ndr_validate_in(TALLOC_CTX *mem_ctx,
        status = ndr_push(push, NDR_IN, st);
        if (!NT_STATUS_IS_OK(status)) {
                return ndr_push_error(push, NDR_ERR_VALIDATE, 
-                                     "Error in input validation push - %s",
+                                     "failed input validation push - %s",
                                      nt_errstr(status));
        }
 
        blob2 = ndr_push_blob(push);
 
        if (!data_blob_equal(&blob, &blob2)) {
+               DEBUG(3,("original:\n"));
+               dump_data(3, blob.data, blob.length);
+               DEBUG(3,("secondary:\n"));
+               dump_data(3, blob2.data, blob2.length);
                return ndr_push_error(push, NDR_ERR_VALIDATE, 
-                                     "Error in input validation data - %s",
+                                     "failed input validation data - %s",
                                      nt_errstr(status));
        }
 
@@ -821,7 +711,7 @@ static NTSTATUS dcerpc_ndr_validate_out(TALLOC_CTX *mem_ctx,
        status = ndr_push(push, NDR_OUT, struct_ptr);
        if (!NT_STATUS_IS_OK(status)) {
                return ndr_push_error(push, NDR_ERR_VALIDATE, 
-                                     "Error in output validation push - %s",
+                                     "failed output validation push - %s",
                                      nt_errstr(status));
        }
 
@@ -836,7 +726,7 @@ static NTSTATUS dcerpc_ndr_validate_out(TALLOC_CTX *mem_ctx,
        status = ndr_pull(pull, NDR_OUT, st);
        if (!NT_STATUS_IS_OK(status)) {
                return ndr_pull_error(pull, NDR_ERR_VALIDATE, 
-                                     "Error in output validation pull - %s",
+                                     "failed output validation pull - %s",
                                      nt_errstr(status));
        }
 
@@ -848,15 +738,19 @@ static NTSTATUS dcerpc_ndr_validate_out(TALLOC_CTX *mem_ctx,
        status = ndr_push(push, NDR_OUT, st);
        if (!NT_STATUS_IS_OK(status)) {
                return ndr_push_error(push, NDR_ERR_VALIDATE, 
-                                     "Error in output validation push2 - %s",
+                                     "failed output validation push2 - %s",
                                      nt_errstr(status));
        }
 
        blob2 = ndr_push_blob(push);
 
        if (!data_blob_equal(&blob, &blob2)) {
+               DEBUG(3,("original:\n"));
+               dump_data(3, blob.data, blob.length);
+               DEBUG(3,("secondary:\n"));
+               dump_data(3, blob2.data, blob2.length);
                return ndr_push_error(push, NDR_ERR_VALIDATE, 
-                                     "Error in output validation data - %s",
+                                     "failed output validation data - %s",
                                      nt_errstr(status));
        }
 
@@ -870,7 +764,7 @@ static NTSTATUS dcerpc_ndr_validate_out(TALLOC_CTX *mem_ctx,
   standard format
 */
 NTSTATUS dcerpc_ndr_request(struct dcerpc_pipe *p,
-                           uint32 opnum,
+                           uint32_t opnum,
                            TALLOC_CTX *mem_ctx,
                            NTSTATUS (*ndr_push)(struct ndr_push *, int, void *),
                            NTSTATUS (*ndr_pull)(struct ndr_pull *, int, void *),
@@ -889,6 +783,10 @@ NTSTATUS dcerpc_ndr_request(struct dcerpc_pipe *p,
                return NT_STATUS_NO_MEMORY;
        }
 
+       if (p->flags & DCERPC_PUSH_BIGENDIAN) {
+               push->flags |= LIBNDR_FLAG_BIGENDIAN;
+       }
+
        /* push the structure into a blob */
        status = ndr_push(push, NDR_IN, struct_ptr);
        if (!NT_STATUS_IS_OK(status)) {
@@ -921,6 +819,10 @@ NTSTATUS dcerpc_ndr_request(struct dcerpc_pipe *p,
                goto failed;
        }
 
+       if (p->flags & DCERPC_PULL_BIGENDIAN) {
+               pull->flags |= LIBNDR_FLAG_BIGENDIAN;
+       }
+
        DEBUG(10,("rpc reply data:\n"));
        dump_data(10, pull->data, pull->data_size);
 
@@ -930,6 +832,9 @@ NTSTATUS dcerpc_ndr_request(struct dcerpc_pipe *p,
                goto failed;
        }
 
+       /* possibly check the packet signature */
+       
+
        if (p->flags & DCERPC_DEBUG_VALIDATE_OUT) {
                status = dcerpc_ndr_validate_out(mem_ctx, struct_ptr, struct_size, 
                                                 ndr_push, ndr_pull);
@@ -955,5 +860,8 @@ failed:
 */
 const char *dcerpc_server_name(struct dcerpc_pipe *p)
 {
-       return p->tree->session->transport->called.name;
+       if (!p->transport.peer_name) {
+               return "";
+       }
+       return p->transport.peer_name(p);
 }