From 2fddd2e2d5fb32ff15a170acc443218481986b91 Mon Sep 17 00:00:00 2001 From: Jelmer Vernooij Date: Mon, 13 Oct 2008 15:58:45 +0200 Subject: Share ndrdump implementation. --- librpc/tools/ndrdump.1.xml | 83 ++++++++ librpc/tools/ndrdump.c | 461 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 544 insertions(+) create mode 100644 librpc/tools/ndrdump.1.xml create mode 100644 librpc/tools/ndrdump.c (limited to 'librpc/tools') diff --git a/librpc/tools/ndrdump.1.xml b/librpc/tools/ndrdump.1.xml new file mode 100644 index 0000000000..9d66102682 --- /dev/null +++ b/librpc/tools/ndrdump.1.xml @@ -0,0 +1,83 @@ + + + + + + ndrdump + 1 + + + + + ndrdump + DCE/RPC Packet Parser and Dumper + + + + + ndrdump + -c context + pipe + function + in|out + filename + + + ndrdump + pipe + + + ndrdump + + + + + DESCRIPTION + + ndrdump tries to parse the specified filename + using Samba's parser for the specified pipe and function. The + third argument should be + either in or out, depending + on whether the data should be parsed as a request or a reply. + + Running ndrdump without arguments will list the pipes for which + parsers are available. + + Running ndrdump with one argument will list the functions that + Samba can parse for the specified pipe. + + The primary function of ndrdump is debugging Samba's internal + DCE/RPC parsing functions. The file being parsed is usually + one exported by wiresharks Export selected packet bytes + function. + + The context argument can be used to load context data from the request + packet when parsing reply packets (such as array lengths). + + + + + VERSION + + This man page is correct for version 4.0 of the Samba suite. + + + + SEE ALSO + + wireshark, pidl + + + + + AUTHOR + + This utility is part of the Samba suite, which is developed by the global Samba Team. + + ndrdump was written by Andrew Tridgell. + + This manpage was written by Jelmer Vernooij. + + + + diff --git a/librpc/tools/ndrdump.c b/librpc/tools/ndrdump.c new file mode 100644 index 0000000000..3ecf10a167 --- /dev/null +++ b/librpc/tools/ndrdump.c @@ -0,0 +1,461 @@ +/* + Unix SMB/CIFS implementation. + SMB torture tester + Copyright (C) Andrew Tridgell 2003 + Copyright (C) Jelmer Vernooij 2006 + + 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 3 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, see . +*/ + +#include "includes.h" +#if (_SAMBA_BUILD_ >= 4) +#include "lib/cmdline/popt_common.h" +#include "system/filesys.h" +#include "system/locale.h" +#include "librpc/ndr/libndr.h" +#include "librpc/ndr/ndr_table.h" +#include "param/param.h" +#else +#define _NORETURN_ +#endif + +static const struct ndr_interface_call *find_function( + const struct ndr_interface_table *p, + const char *function) +{ + int i; + if (isdigit(function[0])) { + i = strtol(function, NULL, 0); + return &p->calls[i]; + } + for (i=0;inum_calls;i++) { + if (strcmp(p->calls[i].name, function) == 0) { + break; + } + } + if (i == p->num_calls) { + printf("Function '%s' not found\n", function); + exit(1); + } + return &p->calls[i]; +} + +#if (_SAMBA_BUILD_ >= 4) + +_NORETURN_ static void show_pipes(void) +{ + const struct ndr_interface_list *l; + printf("\nYou must specify a pipe\n"); + printf("known pipes are:\n"); + for (l=ndr_table_list();l;l=l->next) { + if(l->table->helpstring) { + printf("\t%s - %s\n", l->table->name, l->table->helpstring); + } else { + printf("\t%s\n", l->table->name); + } + } + exit(1); +} + +#endif + +_NORETURN_ static void show_functions(const struct ndr_interface_table *p) +{ + int i; + printf("\nYou must specify a function\n"); + printf("known functions on '%s' are:\n", p->name); + for (i=0;inum_calls;i++) { + printf("\t0x%02x (%2d) %s\n", i, i, p->calls[i].name); + } + exit(1); +} + +static char *stdin_load(TALLOC_CTX *mem_ctx, size_t *size) +{ + int num_read, total_len = 0; + char buf[255]; + char *result = NULL; + + while((num_read = read(STDIN_FILENO, buf, 255)) > 0) { + + if (result) { + result = talloc_realloc( + mem_ctx, result, char, total_len + num_read); + } else { + result = talloc_array(mem_ctx, char, num_read); + } + + memcpy(result + total_len, buf, num_read); + + total_len += num_read; + } + + if (size) + *size = total_len; + + return result; +} + +static const struct ndr_interface_table *load_iface_from_plugin(const char *plugin, const char *pipe_name) +{ + const struct ndr_interface_table *p; + void *handle; + char *symbol; + + handle = dlopen(plugin, RTLD_NOW); + if (handle == NULL) { + printf("%s: Unable to open: %s\n", plugin, dlerror()); + return NULL; + } + + symbol = talloc_asprintf(NULL, "ndr_table_%s", pipe_name); + p = (const struct ndr_interface_table *)dlsym(handle, symbol); + + if (!p) { + printf("%s: Unable to find DCE/RPC interface table for '%s': %s\n", plugin, pipe_name, dlerror()); + talloc_free(symbol); + return NULL; + } + + talloc_free(symbol); + + return p; +} + +static void ndrdump_data(uint8_t *d, uint32_t l, bool force) +{ + if (force) { + dump_data(0, d, l); + } else { + dump_data_skip_zeros(0, d, l); + } +} + + int main(int argc, const char *argv[]) +{ + const struct ndr_interface_table *p = NULL; + const struct ndr_interface_call *f; + const char *pipe_name, *function, *inout, *filename; + uint8_t *data; + size_t size; + DATA_BLOB blob; + struct ndr_pull *ndr_pull; + struct ndr_print *ndr_print; + TALLOC_CTX *mem_ctx; + int flags; + poptContext pc; + NTSTATUS status; + enum ndr_err_code ndr_err; + void *st; + void *v_st; + const char *ctx_filename = NULL; + const char *plugin = NULL; + bool validate = false; + bool dumpdata = false; + int opt; + enum {OPT_CONTEXT_FILE=1000, OPT_VALIDATE, OPT_DUMP_DATA, OPT_LOAD_DSO}; + struct poptOption long_options[] = { + POPT_AUTOHELP + {"context-file", 'c', POPT_ARG_STRING, NULL, OPT_CONTEXT_FILE, "In-filename to parse first", "CTX-FILE" }, + {"validate", 0, POPT_ARG_NONE, NULL, OPT_VALIDATE, "try to validate the data", NULL }, + {"dump-data", 0, POPT_ARG_NONE, NULL, OPT_DUMP_DATA, "dump the hex data", NULL }, + {"load-dso", 'l', POPT_ARG_STRING, NULL, OPT_LOAD_DSO, "load from shared object file", NULL }, + POPT_COMMON_SAMBA + POPT_COMMON_VERSION + { NULL } + }; + +#if (_SAMBA_BUILD_ >= 4) + ndr_table_init(); +#else + /* Initialise samba stuff */ + load_case_tables(); + + setlinebuf(stdout); + + dbf = x_stderr; + + setup_logging(argv[0],True); +#endif + + pc = poptGetContext("ndrdump", argc, argv, long_options, 0); + + poptSetOtherOptionHelp( + pc, " []"); + + while ((opt = poptGetNextOpt(pc)) != -1) { + switch (opt) { + case OPT_CONTEXT_FILE: + ctx_filename = poptGetOptArg(pc); + break; + case OPT_VALIDATE: + validate = true; + break; + case OPT_DUMP_DATA: + dumpdata = true; + break; + case OPT_LOAD_DSO: + plugin = poptGetOptArg(pc); + break; + } + } + + pipe_name = poptGetArg(pc); + + if (!pipe_name) { + poptPrintUsage(pc, stderr, 0); +#if (_SAMBA_BUILD_ >= 4) + show_pipes(); +#endif + exit(1); + } + + if (plugin != NULL) { + p = load_iface_from_plugin(plugin, pipe_name); + } +#if (_SAMBA_BUILD_ <= 3) + else { + fprintf(stderr, "Only loading from DSO's supported in Samba 3\n"); + exit(1); + } +#else + if (!p) { + p = ndr_table_by_name(pipe_name); + } + + if (!p) { + struct GUID uuid; + + status = GUID_from_string(pipe_name, &uuid); + + if (NT_STATUS_IS_OK(status)) { + p = ndr_table_by_uuid(&uuid); + } + } +#endif + + if (!p) { + printf("Unknown pipe or UUID '%s'\n", pipe_name); + exit(1); + } + + function = poptGetArg(pc); + inout = poptGetArg(pc); + filename = poptGetArg(pc); + + if (!function || !inout) { + poptPrintUsage(pc, stderr, 0); + show_functions(p); + exit(1); + } + + if (strcmp(inout, "in") == 0 || + strcmp(inout, "request") == 0) { + flags = NDR_IN; + } else if (strcmp(inout, "out") == 0 || + strcmp(inout, "response") == 0) { + flags = NDR_OUT; + } else { + printf("Bad inout value '%s'\n", inout); + exit(1); + } + + f = find_function(p, function); + + mem_ctx = talloc_init("ndrdump"); + + st = talloc_zero_size(mem_ctx, f->struct_size); + if (!st) { + printf("Unable to allocate %d bytes\n", (int)f->struct_size); + exit(1); + } + + v_st = talloc_zero_size(mem_ctx, f->struct_size); + if (!v_st) { + printf("Unable to allocate %d bytes\n", (int)f->struct_size); + exit(1); + } + + if (ctx_filename) { + if (flags == NDR_IN) { + printf("Context file can only be used for \"out\" packages\n"); + exit(1); + } + + data = (uint8_t *)file_load(ctx_filename, &size, 0, mem_ctx); + if (!data) { + perror(ctx_filename); + exit(1); + } + + blob.data = data; + blob.length = size; + + ndr_pull = ndr_pull_init_blob(&blob, mem_ctx, lp_iconv_convenience(cmdline_lp_ctx)); + ndr_pull->flags |= LIBNDR_FLAG_REF_ALLOC; + + ndr_err = f->ndr_pull(ndr_pull, NDR_IN, st); + + if (ndr_pull->offset != ndr_pull->data_size) { + printf("WARNING! %d unread bytes while parsing context file\n", ndr_pull->data_size - ndr_pull->offset); + } + + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + status = ndr_map_error2ntstatus(ndr_err); + printf("pull for context file returned %s\n", nt_errstr(status)); + exit(1); + } + memcpy(v_st, st, f->struct_size); + } + + if (filename) + data = (uint8_t *)file_load(filename, &size, 0, mem_ctx); + else + data = (uint8_t *)stdin_load(mem_ctx, &size); + + if (!data) { + if (filename) + perror(filename); + else + perror("stdin"); + exit(1); + } + + blob.data = data; + blob.length = size; + + ndr_pull = ndr_pull_init_blob(&blob, mem_ctx, lp_iconv_convenience(cmdline_lp_ctx)); + ndr_pull->flags |= LIBNDR_FLAG_REF_ALLOC; + + ndr_err = f->ndr_pull(ndr_pull, flags, st); + status = ndr_map_error2ntstatus(ndr_err); + + printf("pull returned %s\n", nt_errstr(status)); + + if (ndr_pull->offset != ndr_pull->data_size) { + printf("WARNING! %d unread bytes\n", ndr_pull->data_size - ndr_pull->offset); + ndrdump_data(ndr_pull->data+ndr_pull->offset, + ndr_pull->data_size - ndr_pull->offset, + dumpdata); + } + + if (dumpdata) { + printf("%d bytes consumed\n", ndr_pull->offset); + ndrdump_data(blob.data, blob.length, dumpdata); + } + + ndr_print = talloc_zero(mem_ctx, struct ndr_print); + ndr_print->print = ndr_print_debug_helper; + ndr_print->depth = 1; + f->ndr_print(ndr_print, function, flags, st); + + if (!NT_STATUS_IS_OK(status)) { + printf("dump FAILED\n"); + exit(1); + } + + if (validate) { + DATA_BLOB v_blob; + struct ndr_push *ndr_v_push; + struct ndr_pull *ndr_v_pull; + struct ndr_print *ndr_v_print; + uint32_t i; + uint8_t byte_a, byte_b; + bool differ; + + ndr_v_push = ndr_push_init_ctx(mem_ctx, lp_iconv_convenience(cmdline_lp_ctx)); + + ndr_err = f->ndr_push(ndr_v_push, flags, st); + status = ndr_map_error2ntstatus(ndr_err); + printf("push returned %s\n", nt_errstr(status)); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + printf("validate push FAILED\n"); + exit(1); + } + + v_blob = ndr_push_blob(ndr_v_push); + + if (dumpdata) { + printf("%ld bytes generated (validate)\n", (long)v_blob.length); + ndrdump_data(v_blob.data, v_blob.length, dumpdata); + } + + ndr_v_pull = ndr_pull_init_blob(&v_blob, mem_ctx, lp_iconv_convenience(cmdline_lp_ctx)); + ndr_v_pull->flags |= LIBNDR_FLAG_REF_ALLOC; + + ndr_err = f->ndr_pull(ndr_v_pull, flags, v_st); + status = ndr_map_error2ntstatus(ndr_err); + printf("pull returned %s\n", nt_errstr(status)); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + printf("validate pull FAILED\n"); + exit(1); + } + + + if (ndr_v_pull->offset != ndr_v_pull->data_size) { + printf("WARNING! %d unread bytes in validation\n", ndr_v_pull->data_size - ndr_v_pull->offset); + ndrdump_data(ndr_v_pull->data+ndr_v_pull->offset, + ndr_v_pull->data_size - ndr_v_pull->offset, + dumpdata); + } + + ndr_v_print = talloc_zero(mem_ctx, struct ndr_print); + ndr_v_print->print = ndr_print_debug_helper; + ndr_v_print->depth = 1; + f->ndr_print(ndr_v_print, function, flags, v_st); + + if (blob.length != v_blob.length) { + printf("WARNING! orig bytes:%llu validated pushed bytes:%llu\n", + (unsigned long long)blob.length, (unsigned long long)v_blob.length); + } + + if (ndr_pull->offset != ndr_v_pull->offset) { + printf("WARNING! orig pulled bytes:%llu validated pulled bytes:%llu\n", + (unsigned long long)ndr_pull->offset, (unsigned long long)ndr_v_pull->offset); + } + + differ = false; + byte_a = 0x00; + byte_b = 0x00; + for (i=0; i < blob.length; i++) { + byte_a = blob.data[i]; + + if (i == v_blob.length) { + byte_b = 0x00; + differ = true; + break; + } + + byte_b = v_blob.data[i]; + + if (byte_a != byte_b) { + differ = true; + break; + } + } + if (differ) { + printf("WARNING! orig and validated differ at byte 0x%02X (%u)\n", i, i); + printf("WARNING! orig byte[0x%02X] = 0x%02X validated byte[0x%02X] = 0x%02X\n", + i, byte_a, i, byte_b); + } + } + + printf("dump OK\n"); + + talloc_free(mem_ctx); + + poptFreeContext(pc); + + return 0; +} -- cgit