1
0
mirror of https://github.com/samba-team/samba.git synced 2025-02-05 21:57:51 +03:00
Andrew Tridgell 3fa8da4d26 - fixed lsa_EnumTrustDom
- added lsa_OpenAccount

- handle STATUS_SOME_UNMAPPED
(This used to be commit e5d93cf996b3e9647ef0ca45f6aab7601369936b)
2003-11-09 11:01:39 +00:00

809 lines
20 KiB
C

/*
Unix SMB/CIFS implementation.
raw dcerpc operations
Copyright (C) Tim Potter 2003
Copyright (C) Andrew Tridgell 2003
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
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
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.
*/
#include "includes.h"
/* 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 *p;
TALLOC_CTX *mem_ctx = talloc_init("dcerpc_tree");
if (mem_ctx == NULL)
return NULL;
p = talloc(mem_ctx, sizeof(*p));
if (!p) {
talloc_destroy(mem_ctx);
return NULL;
}
p->reference_count = 0;
p->mem_ctx = mem_ctx;
p->tree = tree;
p->tree->reference_count++;
p->call_id = 1;
p->fnum = 0;
return p;
}
/* close down a dcerpc over SMB pipe */
void dcerpc_pipe_close(struct dcerpc_pipe *p)
{
if (!p) return;
p->reference_count--;
if (p->reference_count <= 0) {
cli_tree_close(p->tree);
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)
{
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]);
}
/*
push a uuid_str into wire format. It will consume 16 bytes
*/
static NTSTATUS push_uuid_str(char *data, const char *uuid_str)
{
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]);
}
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)
{
syntax->uuid_str = dcerpc_pull_uuid(blob->data + (*offset), mem_ctx);
if (!syntax->uuid_str) {
return NT_STATUS_NO_MEMORY;
}
(*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
*/
static NTSTATUS dcerpc_pull_auth_verifier(DATA_BLOB *blob, TALLOC_CTX *mem_ctx,
uint32 *offset,
struct dcerpc_hdr *hdr,
DATA_BLOB *auth)
{
if (hdr->auth_length == 0) {
return NT_STATUS_OK;
}
BLOB_CHECK_BOUNDS(blob, *offset, hdr->auth_length);
*auth = data_blob_talloc(mem_ctx, blob->data + (*offset), hdr->auth_length);
if (!auth->data) {
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;
}
(*offset) += stub_len;
return dcerpc_pull_auth_verifier(blob, mem_ctx, offset, hdr, &pkt->auth_verifier);
}
/*
parse a struct bind_ack
*/
static NTSTATUS dcerpc_pull_bind_ack(DATA_BLOB *blob, TALLOC_CTX *mem_ctx,
uint32 *offset,
struct dcerpc_hdr *hdr,
struct dcerpc_bind_ack *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;
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;
}
}
for (i=0;i<pkt->num_results;i++) {
NTSTATUS status;
BLOB_CHECK_BOUNDS(blob, *offset, 24);
pkt->ctx_list[i].result = IVAL(blob->data, *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;
}
}
return dcerpc_pull_auth_verifier(blob, mem_ctx, offset, hdr, &pkt->auth_verifier);
}
/*
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);
(*offset) += 16;
return NT_STATUS_OK;
}
/*
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);
}
/*
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;
status = dcerpc_pull_hdr(blob, &offset, &pkt->hdr);
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;
}
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;
}
break;
default:
return NT_STATUS_INVALID_LEVEL;
}
return status;
}
/*
push a dcerpc_bind into a blob
*/
static NTSTATUS dcerpc_push_bind(DATA_BLOB *blob, uint32 *offset,
struct dcerpc_hdr *hdr,
struct dcerpc_bind *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;
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;
}
}
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;
}
/*
work out the wire size of a dcerpc packet
*/
static uint32 dcerpc_wire_size(struct dcerpc_packet *pkt)
{
int i;
uint32 size = 0;
size += 16; /* header */
switch (pkt->hdr.ptype) {
case DCERPC_PKT_REQUEST:
size += 8;
size += pkt->in.request.stub_data.length;
size += pkt->in.request.auth_verifier.length;
break;
case DCERPC_PKT_RESPONSE:
size += 8;
size += pkt->out.response.stub_data.length;
size += pkt->hdr.auth_length;
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;
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;
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);
(*blob) = data_blob_talloc(mem_ctx, NULL, wire_size);
if (!blob->data) {
return NT_STATUS_NO_MEMORY;
}
pkt->hdr.frag_length = wire_size;
dcerpc_push_hdr(blob->data + offset, &pkt->hdr);
offset += 16;
switch (pkt->hdr.ptype) {
case DCERPC_PKT_BIND:
status = dcerpc_push_bind(blob, &offset, &pkt->hdr, &pkt->in.bind);
break;
case DCERPC_PKT_REQUEST:
status = dcerpc_push_request(blob, &offset, &pkt->hdr, &pkt->in.request);
break;
default:
status = NT_STATUS_INVALID_LEVEL;
}
return status;
}
/*
fill in the fixed values in a dcerpc header
*/
static void init_dcerpc_hdr(struct dcerpc_hdr *hdr)
{
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;
}
/*
perform a bind using the given syntax
*/
NTSTATUS dcerpc_bind(struct dcerpc_pipe *p,
const struct dcerpc_syntax_id *syntax,
const struct dcerpc_syntax_id *transfer_syntax)
{
TALLOC_CTX *mem_ctx;
struct dcerpc_packet pkt;
NTSTATUS status;
DATA_BLOB blob;
DATA_BLOB blob_out;
mem_ctx = talloc_init("dcerpc_bind");
if (!mem_ctx) {
return NT_STATUS_NO_MEMORY;
}
init_dcerpc_hdr(&pkt.hdr);
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.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);
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);
status = dcerpc_push(&blob, mem_ctx, &pkt);
if (!NT_STATUS_IS_OK(status)) {
talloc_destroy(mem_ctx);
return status;
}
status = dcerpc_raw_packet(p, mem_ctx, &blob, &blob_out);
if (!NT_STATUS_IS_OK(status)) {
talloc_destroy(mem_ctx);
return status;
}
status = dcerpc_pull(&blob_out, mem_ctx, &pkt);
if (!NT_STATUS_IS_OK(status)) {
talloc_destroy(mem_ctx);
return status;
}
if (pkt.hdr.ptype != DCERPC_PKT_BIND_ACK) {
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;
talloc_destroy(mem_ctx);
return status;
}
static const struct {
const char *name;
struct dcerpc_syntax_id syntax;
const struct dcerpc_syntax_id transfer_syntax;
} known_pipes[] = {
{ "lsarpc" , { "12345778-1234-abcd-ef00-0123456789ab", 0 }, DCERPC_TRANSFER_SYNTAX_V2 },
{ "samr" , { "12345778-1234-abcd-ef00-0123456789ac", 1 }, DCERPC_TRANSFER_SYNTAX_V2 },
{ "netlogon", { "12345778-1234-abcd-ef00-01234567cffb", 1 }, DCERPC_TRANSFER_SYNTAX_V2 },
{ "srvsvc" , { "4b324fc8-1670-01d3-1278-5a47bf6ee188", 3 }, DCERPC_TRANSFER_SYNTAX_V2 },
{ "wkssvc" , { "6bffd098-a112-3610-9833-46c3f87e345a", 1 }, DCERPC_TRANSFER_SYNTAX_V2 },
{ "winreg" , { "338cd001-2244-31f1-aaaa-900038001003", 1 }, DCERPC_TRANSFER_SYNTAX_V2 },
{ "spoolss" , { "12345678-1234-abcd-ef00-0123456789ab", 1 }, DCERPC_TRANSFER_SYNTAX_V2 },
{ "netdfs" , { "4fc742e0-4a10-11cf-8273-00aa004ae673", 3 }, DCERPC_TRANSFER_SYNTAX_V2 },
{ "rpcecho" , { "60a15ec5-4de8-11d7-a637-005056a20182", 1 }, DCERPC_TRANSFER_SYNTAX_V2 },
{ NULL , }
};
/* Perform a bind using the given well-known pipe name */
NTSTATUS dcerpc_bind_byname(struct dcerpc_pipe *p, const char *pipe_name)
{
int i;
for (i=0; known_pipes[i].name; i++) {
if (strcasecmp(known_pipes[i].name, pipe_name) == 0)
break;
}
if (known_pipes[i].name == NULL) {
return NT_STATUS_OBJECT_NAME_NOT_FOUND;
}
return dcerpc_bind(p, &known_pipes[i].syntax, &known_pipes[i].transfer_syntax);
}
/*
perform a full request/response pair on a dcerpc pipe
*/
NTSTATUS dcerpc_request(struct dcerpc_pipe *p,
uint16 opnum,
TALLOC_CTX *mem_ctx,
DATA_BLOB *stub_data_in,
DATA_BLOB *stub_data_out)
{
struct dcerpc_packet pkt;
NTSTATUS status;
DATA_BLOB blob_in, blob_out, payload;
uint32 remaining, chunk_size;
init_dcerpc_hdr(&pkt.hdr);
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;
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);
/* 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;
} else {
pkt.hdr.pfc_flags = 0;
}
pkt.in.request.stub_data.data = stub_data_in->data +
(stub_data_in->length - remaining);
pkt.in.request.stub_data.length = chunk_size;
status = dcerpc_push(&blob_in, mem_ctx, &pkt);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
status = dcerpc_raw_packet_initial(p, mem_ctx, &blob_in);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
remaining -= chunk_size;
}
/* 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;
} else {
pkt.hdr.pfc_flags = DCERPC_PFC_FLAG_LAST;
}
pkt.in.request.stub_data.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);
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 = dcerpc_pull(&blob_out, mem_ctx, &pkt);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
if (pkt.hdr.ptype != DCERPC_PKT_RESPONSE) {
return NT_STATUS_UNSUCCESSFUL;
}
if (!(pkt.hdr.pfc_flags & DCERPC_PFC_FLAG_FIRST)) {
/* something is badly wrong! */
return NT_STATUS_UNSUCCESSFUL;
}
payload = pkt.out.response.stub_data;
/* continue receiving fragments */
while (!(pkt.hdr.pfc_flags & DCERPC_PFC_FLAG_LAST)) {
uint32 length;
status = dcerpc_raw_packet_secondary(p, mem_ctx, &blob_out);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
status = dcerpc_pull(&blob_out, mem_ctx, &pkt);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
if (pkt.hdr.pfc_flags & DCERPC_PFC_FLAG_FIRST) {
/* start of another packet!? */
return NT_STATUS_UNSUCCESSFUL;
}
if (pkt.hdr.ptype != DCERPC_PKT_RESPONSE) {
return NT_STATUS_UNSUCCESSFUL;
}
length = pkt.out.response.stub_data.length;
payload.data = talloc_realloc(mem_ctx,
payload.data,
payload.length + length);
if (!payload.data) {
return NT_STATUS_NO_MEMORY;
}
memcpy(payload.data + payload.length,
pkt.out.response.stub_data.data,
length);
payload.length += length;
}
if (stub_data_out) {
*stub_data_out = payload;
}
return status;
}
/*
a useful helper function for synchronous rpc requests
this can be used when you have ndr push/pull functions in the
standard format
*/
NTSTATUS dcerpc_ndr_request(struct dcerpc_pipe *p,
uint32 opnum,
TALLOC_CTX *mem_ctx,
NTSTATUS (*ndr_push)(struct ndr_push *, void *),
NTSTATUS (*ndr_pull)(struct ndr_pull *, void *),
void *struct_ptr)
{
struct ndr_push *push;
struct ndr_pull *pull;
NTSTATUS status;
DATA_BLOB request, response;
/* setup for a ndr_push_* call */
push = ndr_push_init();
if (!push) {
talloc_destroy(mem_ctx);
return NT_STATUS_NO_MEMORY;
}
/* push the structure into a blob */
status = ndr_push(push, struct_ptr);
if (!NT_STATUS_IS_OK(status)) {
goto failed;
}
/* retrieve the blob */
request = ndr_push_blob(push);
/* make the actual dcerpc request */
status = dcerpc_request(p, opnum, mem_ctx, &request, &response);
if (!NT_STATUS_IS_OK(status)) {
goto failed;
}
/* prepare for ndr_pull_* */
pull = ndr_pull_init_blob(&response, mem_ctx);
if (!pull) {
goto failed;
}
/* pull the structure from the blob */
status = ndr_pull(pull, struct_ptr);
if (!NT_STATUS_IS_OK(status)) {
goto failed;
}
if (pull->offset != pull->data_size) {
DEBUG(0,("Warning! %d unread bytes\n", pull->data_size - pull->offset));
}
failed:
ndr_push_free(push);
return status;
}