mirror of
https://github.com/samba-team/samba.git
synced 2025-02-05 21:57:51 +03:00
3fa8da4d26
- added lsa_OpenAccount - handle STATUS_SOME_UNMAPPED (This used to be commit e5d93cf996b3e9647ef0ca45f6aab7601369936b)
809 lines
20 KiB
C
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;
|
|
}
|