diff options
Diffstat (limited to 'librpc')
-rw-r--r-- | librpc/ndr/ndr.c | 1118 | ||||
-rw-r--r-- | librpc/ndr/ndr_basic.c | 833 | ||||
-rw-r--r-- | librpc/ndr/ndr_misc.c | 46 | ||||
-rw-r--r-- | librpc/ndr/uuid.c | 227 | ||||
-rw-r--r-- | librpc/tools/ndrdump.1.xml | 83 | ||||
-rw-r--r-- | librpc/tools/ndrdump.c | 461 |
6 files changed, 2768 insertions, 0 deletions
diff --git a/librpc/ndr/ndr.c b/librpc/ndr/ndr.c new file mode 100644 index 0000000000..2f1daeaeb5 --- /dev/null +++ b/librpc/ndr/ndr.c @@ -0,0 +1,1118 @@ +/* + Unix SMB/CIFS implementation. + + libndr interface + + 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 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 <http://www.gnu.org/licenses/>. +*/ + +/* + this provides the core routines for NDR parsing functions + + see http://www.opengroup.org/onlinepubs/9629399/chap14.htm for details + of NDR encoding rules +*/ + +#include "includes.h" +#include "librpc/ndr/libndr.h" +#include "../lib/util/dlinklist.h" +#if _SAMBA_BUILD_ == 4 +#include "param/param.h" +#endif + +#define NDR_BASE_MARSHALL_SIZE 1024 + +/* this guid indicates NDR encoding in a protocol tower */ +const struct ndr_syntax_id ndr_transfer_syntax = { + { 0x8a885d04, 0x1ceb, 0x11c9, {0x9f, 0xe8}, {0x08,0x00,0x2b,0x10,0x48,0x60} }, + 2 +}; + +const struct ndr_syntax_id ndr64_transfer_syntax = { + { 0x71710533, 0xbeba, 0x4937, {0x83, 0x19}, {0xb5,0xdb,0xef,0x9c,0xcc,0x36} }, + 1 +}; + +/* + work out the number of bytes needed to align on a n byte boundary +*/ +_PUBLIC_ size_t ndr_align_size(uint32_t offset, size_t n) +{ + if ((offset & (n-1)) == 0) return 0; + return n - (offset & (n-1)); +} + +/* + initialise a ndr parse structure from a data blob +*/ +_PUBLIC_ struct ndr_pull *ndr_pull_init_blob(const DATA_BLOB *blob, TALLOC_CTX *mem_ctx, struct smb_iconv_convenience *iconv_convenience) +{ + struct ndr_pull *ndr; + + ndr = talloc_zero(mem_ctx, struct ndr_pull); + if (!ndr) return NULL; + ndr->current_mem_ctx = mem_ctx; + + ndr->data = blob->data; + ndr->data_size = blob->length; + ndr->iconv_convenience = talloc_reference(ndr, iconv_convenience); + + return ndr; +} + +/* + advance by 'size' bytes +*/ +_PUBLIC_ enum ndr_err_code ndr_pull_advance(struct ndr_pull *ndr, uint32_t size) +{ + ndr->offset += size; + if (ndr->offset > ndr->data_size) { + return ndr_pull_error(ndr, NDR_ERR_BUFSIZE, + "ndr_pull_advance by %u failed", + size); + } + return NDR_ERR_SUCCESS; +} + +/* + set the parse offset to 'ofs' +*/ +static enum ndr_err_code ndr_pull_set_offset(struct ndr_pull *ndr, uint32_t ofs) +{ + ndr->offset = ofs; + if (ndr->offset > ndr->data_size) { + return ndr_pull_error(ndr, NDR_ERR_BUFSIZE, + "ndr_pull_set_offset %u failed", + ofs); + } + return NDR_ERR_SUCCESS; +} + +/* create a ndr_push structure, ready for some marshalling */ +_PUBLIC_ struct ndr_push *ndr_push_init_ctx(TALLOC_CTX *mem_ctx, struct smb_iconv_convenience *iconv_convenience) +{ + struct ndr_push *ndr; + + ndr = talloc_zero(mem_ctx, struct ndr_push); + if (!ndr) { + return NULL; + } + + ndr->flags = 0; + ndr->alloc_size = NDR_BASE_MARSHALL_SIZE; + ndr->data = talloc_array(ndr, uint8_t, ndr->alloc_size); + if (!ndr->data) { + return NULL; + } + ndr->iconv_convenience = talloc_reference(ndr, iconv_convenience); + + return ndr; +} + +/* return a DATA_BLOB structure for the current ndr_push marshalled data */ +_PUBLIC_ DATA_BLOB ndr_push_blob(struct ndr_push *ndr) +{ + DATA_BLOB blob; + blob = data_blob_const(ndr->data, ndr->offset); + if (ndr->alloc_size > ndr->offset) { + ndr->data[ndr->offset] = 0; + } + return blob; +} + + +/* + expand the available space in the buffer to ndr->offset + extra_size +*/ +_PUBLIC_ enum ndr_err_code ndr_push_expand(struct ndr_push *ndr, uint32_t extra_size) +{ + uint32_t size = extra_size + ndr->offset; + + if (size < ndr->offset) { + /* extra_size overflowed the offset */ + return ndr_push_error(ndr, NDR_ERR_BUFSIZE, "Overflow in push_expand to %u", + size); + } + + if (ndr->alloc_size > size) { + return NDR_ERR_SUCCESS; + } + + ndr->alloc_size += NDR_BASE_MARSHALL_SIZE; + if (size+1 > ndr->alloc_size) { + ndr->alloc_size = size+1; + } + ndr->data = talloc_realloc(ndr, ndr->data, uint8_t, ndr->alloc_size); + if (!ndr->data) { + return ndr_push_error(ndr, NDR_ERR_ALLOC, "Failed to push_expand to %u", + ndr->alloc_size); + } + + return NDR_ERR_SUCCESS; +} + +_PUBLIC_ void ndr_print_debug_helper(struct ndr_print *ndr, const char *format, ...) +{ + va_list ap; + char *s = NULL; + int i, ret; + + va_start(ap, format); + ret = vasprintf(&s, format, ap); + va_end(ap); + + if (ret == -1) { + return; + } + + for (i=0;i<ndr->depth;i++) { + DEBUGADD(0,(" ")); + } + + DEBUGADD(0,("%s\n", s)); + free(s); +} + +_PUBLIC_ void ndr_print_string_helper(struct ndr_print *ndr, const char *format, ...) +{ + va_list ap; + int i; + + for (i=0;i<ndr->depth;i++) { + ndr->private_data = talloc_asprintf_append_buffer( + (char *)ndr->private_data, " "); + } + + va_start(ap, format); + ndr->private_data = talloc_vasprintf_append_buffer((char *)ndr->private_data, + format, ap); + va_end(ap); + ndr->private_data = talloc_asprintf_append_buffer((char *)ndr->private_data, + "\n"); +} + +/* + a useful helper function for printing idl structures via DEBUG() +*/ +_PUBLIC_ void ndr_print_debug(ndr_print_fn_t fn, const char *name, void *ptr) +{ + struct ndr_print *ndr; + + ndr = talloc_zero(NULL, struct ndr_print); + if (!ndr) return; + ndr->print = ndr_print_debug_helper; + ndr->depth = 1; + ndr->flags = 0; + fn(ndr, name, ptr); + talloc_free(ndr); +} + +/* + a useful helper function for printing idl unions via DEBUG() +*/ +_PUBLIC_ void ndr_print_union_debug(ndr_print_fn_t fn, const char *name, uint32_t level, void *ptr) +{ + struct ndr_print *ndr; + + ndr = talloc_zero(NULL, struct ndr_print); + if (!ndr) return; + ndr->print = ndr_print_debug_helper; + ndr->depth = 1; + ndr->flags = 0; + ndr_print_set_switch_value(ndr, ptr, level); + fn(ndr, name, ptr); + talloc_free(ndr); +} + +/* + a useful helper function for printing idl function calls via DEBUG() +*/ +_PUBLIC_ void ndr_print_function_debug(ndr_print_function_t fn, const char *name, int flags, void *ptr) +{ + struct ndr_print *ndr; + + ndr = talloc_zero(NULL, struct ndr_print); + if (!ndr) return; + ndr->print = ndr_print_debug_helper; + ndr->depth = 1; + ndr->flags = 0; + fn(ndr, name, flags, ptr); + talloc_free(ndr); +} + +/* + a useful helper function for printing idl structures to a string +*/ +_PUBLIC_ char *ndr_print_struct_string(TALLOC_CTX *mem_ctx, ndr_print_fn_t fn, const char *name, void *ptr) +{ + struct ndr_print *ndr; + char *ret = NULL; + + ndr = talloc_zero(mem_ctx, struct ndr_print); + if (!ndr) return NULL; + ndr->private_data = talloc_strdup(ndr, ""); + if (!ndr->private_data) { + goto failed; + } + ndr->print = ndr_print_string_helper; + ndr->depth = 1; + ndr->flags = 0; + fn(ndr, name, ptr); + ret = talloc_steal(mem_ctx, (char *)ndr->private_data); +failed: + talloc_free(ndr); + return ret; +} + +/* + a useful helper function for printing idl unions to a string +*/ +_PUBLIC_ char *ndr_print_union_string(TALLOC_CTX *mem_ctx, ndr_print_fn_t fn, const char *name, uint32_t level, void *ptr) +{ + struct ndr_print *ndr; + char *ret = NULL; + + ndr = talloc_zero(mem_ctx, struct ndr_print); + if (!ndr) return NULL; + ndr->private_data = talloc_strdup(ndr, ""); + if (!ndr->private_data) { + goto failed; + } + ndr->print = ndr_print_string_helper; + ndr->depth = 1; + ndr->flags = 0; + ndr_print_set_switch_value(ndr, ptr, level); + fn(ndr, name, ptr); + ret = talloc_steal(mem_ctx, (char *)ndr->private_data); +failed: + talloc_free(ndr); + return ret; +} + +/* + a useful helper function for printing idl function calls to a string +*/ +_PUBLIC_ char *ndr_print_function_string(TALLOC_CTX *mem_ctx, + ndr_print_function_t fn, const char *name, + int flags, void *ptr) +{ + struct ndr_print *ndr; + char *ret = NULL; + + ndr = talloc_zero(mem_ctx, struct ndr_print); + if (!ndr) return NULL; + ndr->private_data = talloc_strdup(ndr, ""); + if (!ndr->private_data) { + goto failed; + } + ndr->print = ndr_print_string_helper; + ndr->depth = 1; + ndr->flags = 0; + fn(ndr, name, flags, ptr); + ret = talloc_steal(mem_ctx, (char *)ndr->private_data); +failed: + talloc_free(ndr); + return ret; +} + +_PUBLIC_ void ndr_set_flags(uint32_t *pflags, uint32_t new_flags) +{ + /* the big/little endian flags are inter-dependent */ + if (new_flags & LIBNDR_FLAG_LITTLE_ENDIAN) { + (*pflags) &= ~LIBNDR_FLAG_BIGENDIAN; + } + if (new_flags & LIBNDR_FLAG_BIGENDIAN) { + (*pflags) &= ~LIBNDR_FLAG_LITTLE_ENDIAN; + } + if (new_flags & LIBNDR_FLAG_REMAINING) { + (*pflags) &= ~LIBNDR_ALIGN_FLAGS; + } + if (new_flags & LIBNDR_ALIGN_FLAGS) { + (*pflags) &= ~LIBNDR_FLAG_REMAINING; + } + (*pflags) |= new_flags; +} + +/* + return and possibly log an NDR error +*/ +_PUBLIC_ enum ndr_err_code ndr_pull_error(struct ndr_pull *ndr, + enum ndr_err_code ndr_err, + const char *format, ...) +{ + char *s=NULL; + va_list ap; + int ret; + + va_start(ap, format); + ret = vasprintf(&s, format, ap); + va_end(ap); + + if (ret == -1) { + return NDR_ERR_ALLOC; + } + + DEBUG(1,("ndr_pull_error(%u): %s\n", ndr_err, s)); + + free(s); + + return ndr_err; +} + +/* + return and possibly log an NDR error +*/ +_PUBLIC_ enum ndr_err_code ndr_push_error(struct ndr_push *ndr, + enum ndr_err_code ndr_err, + const char *format, ...) +{ + char *s=NULL; + va_list ap; + int ret; + + va_start(ap, format); + ret = vasprintf(&s, format, ap); + va_end(ap); + + if (ret == -1) { + return NDR_ERR_ALLOC; + } + + DEBUG(1,("ndr_push_error(%u): %s\n", ndr_err, s)); + + free(s); + + return ndr_err; +} + +/* + handle subcontext buffers, which in midl land are user-marshalled, but + we use magic in pidl to make them easier to cope with +*/ +_PUBLIC_ enum ndr_err_code ndr_pull_subcontext_start(struct ndr_pull *ndr, + struct ndr_pull **_subndr, + size_t header_size, + ssize_t size_is) +{ + struct ndr_pull *subndr; + uint32_t r_content_size; + bool force_le = false; + bool force_be = false; + + switch (header_size) { + case 0: { + uint32_t content_size = ndr->data_size - ndr->offset; + if (size_is >= 0) { + content_size = size_is; + } + r_content_size = content_size; + break; + } + + case 2: { + uint16_t content_size; + NDR_CHECK(ndr_pull_uint16(ndr, NDR_SCALARS, &content_size)); + if (size_is >= 0 && size_is != content_size) { + return ndr_pull_error(ndr, NDR_ERR_SUBCONTEXT, "Bad subcontext (PULL) size_is(%d) mismatch content_size %d", + (int)size_is, (int)content_size); + } + r_content_size = content_size; + break; + } + + case 4: { + uint32_t content_size; + NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, &content_size)); + if (size_is >= 0 && size_is != content_size) { + return ndr_pull_error(ndr, NDR_ERR_SUBCONTEXT, "Bad subcontext (PULL) size_is(%d) mismatch content_size %d", + (int)size_is, (int)content_size); + } + r_content_size = content_size; + break; + } + case 0xFFFFFC01: { + /* + * Common Type Header for the Serialization Stream + * See [MS-RPCE] 2.2.6 Type Serialization Version 1 + */ + uint8_t version; + uint8_t drep; + uint16_t hdrlen; + uint32_t filler; + uint32_t content_size; + uint32_t reserved; + + /* version */ + NDR_CHECK(ndr_pull_uint8(ndr, NDR_SCALARS, &version)); + + if (version != 1) { + return ndr_pull_error(ndr, NDR_ERR_SUBCONTEXT, + "Bad subcontext (PULL) Common Type Header version %d != 1", + (int)version); + } + + /* + * 0x10 little endian + * 0x00 big endian + */ + NDR_CHECK(ndr_pull_uint8(ndr, NDR_SCALARS, &drep)); + if (drep == 0x10) { + force_le = true; + } else if (drep == 0x00) { + force_be = true; + } else { + return ndr_pull_error(ndr, NDR_ERR_SUBCONTEXT, + "Bad subcontext (PULL) Common Type Header invalid drep 0x%02X", + (unsigned int)drep); + } + + /* length of the "Private Header for Constructed Type" */ + NDR_CHECK(ndr_pull_uint16(ndr, NDR_SCALARS, &hdrlen)); + if (hdrlen != 8) { + return ndr_pull_error(ndr, NDR_ERR_SUBCONTEXT, + "Bad subcontext (PULL) Common Type Header length %d != 8", + (int)hdrlen); + } + + /* filler should be ignored */ + NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, &filler)); + + /* + * Private Header for Constructed Type + */ + /* length - will be updated latter */ + NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, &content_size)); + if (size_is >= 0 && size_is != content_size) { + return ndr_pull_error(ndr, NDR_ERR_SUBCONTEXT, "Bad subcontext (PULL) size_is(%d) mismatch content_size %d", + (int)size_is, (int)content_size); + } + /* the content size must be a multiple of 8 */ + if ((content_size % 8) != 0) { + return ndr_pull_error(ndr, NDR_ERR_SUBCONTEXT, + "Bad subcontext (PULL) size_is(%d) not padded to 8 content_size %d", + (int)size_is, (int)content_size); + } + r_content_size = content_size; + + /* reserved */ + NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, &reserved)); + break; + } + default: + return ndr_pull_error(ndr, NDR_ERR_SUBCONTEXT, "Bad subcontext (PULL) header_size %d", + (int)header_size); + } + + NDR_PULL_NEED_BYTES(ndr, r_content_size); + + subndr = talloc_zero(ndr, struct ndr_pull); + NDR_ERR_HAVE_NO_MEMORY(subndr); + subndr->flags = ndr->flags; + subndr->current_mem_ctx = ndr->current_mem_ctx; + + subndr->data = ndr->data + ndr->offset; + subndr->offset = 0; + subndr->data_size = r_content_size; + subndr->iconv_convenience = talloc_reference(subndr, ndr->iconv_convenience); + + if (force_le) { + ndr_set_flags(&ndr->flags, LIBNDR_FLAG_LITTLE_ENDIAN); + } else if (force_be) { + ndr_set_flags(&ndr->flags, LIBNDR_FLAG_BIGENDIAN); + } + + *_subndr = subndr; + return NDR_ERR_SUCCESS; +} + +_PUBLIC_ enum ndr_err_code ndr_pull_subcontext_end(struct ndr_pull *ndr, + struct ndr_pull *subndr, + size_t header_size, + ssize_t size_is) +{ + uint32_t advance; + if (size_is >= 0) { + advance = size_is; + } else if (header_size > 0) { + advance = subndr->data_size; + } else { + advance = subndr->offset; + } + NDR_CHECK(ndr_pull_advance(ndr, advance)); + return NDR_ERR_SUCCESS; +} + +_PUBLIC_ enum ndr_err_code ndr_push_subcontext_start(struct ndr_push *ndr, + struct ndr_push **_subndr, + size_t header_size, + ssize_t size_is) +{ + struct ndr_push *subndr; + + subndr = ndr_push_init_ctx(ndr, ndr->iconv_convenience); + NDR_ERR_HAVE_NO_MEMORY(subndr); + subndr->flags = ndr->flags; + + *_subndr = subndr; + return NDR_ERR_SUCCESS; +} + +/* + push a subcontext header +*/ +_PUBLIC_ enum ndr_err_code ndr_push_subcontext_end(struct ndr_push *ndr, + struct ndr_push *subndr, + size_t header_size, + ssize_t size_is) +{ + ssize_t padding_len; + + if (size_is >= 0) { + padding_len = size_is - subndr->offset; + if (padding_len > 0) { + NDR_CHECK(ndr_push_zero(subndr, padding_len)); + } else if (padding_len < 0) { + return ndr_push_error(ndr, NDR_ERR_SUBCONTEXT, "Bad subcontext (PUSH) content_size %d is larger than size_is(%d)", + (int)subndr->offset, (int)size_is); + } + } + + switch (header_size) { + case 0: + break; + + case 2: + NDR_CHECK(ndr_push_uint16(ndr, NDR_SCALARS, subndr->offset)); + break; + + case 4: + NDR_CHECK(ndr_push_uint32(ndr, NDR_SCALARS, subndr->offset)); + break; + + case 0xFFFFFC01: + /* + * Common Type Header for the Serialization Stream + * See [MS-RPCE] 2.2.6 Type Serialization Version 1 + */ + padding_len = NDR_ROUND(subndr->offset, 8) - subndr->offset; + if (padding_len > 0) { + NDR_CHECK(ndr_push_zero(subndr, padding_len)); + } + + /* version */ + NDR_CHECK(ndr_push_uint8(ndr, NDR_SCALARS, 1)); + + /* + * 0x10 little endian + * 0x00 big endian + */ + NDR_CHECK(ndr_push_uint8(ndr, NDR_SCALARS, NDR_BE(ndr)?0x00:0x10)); + + /* length of the "Private Header for Constructed Type" */ + NDR_CHECK(ndr_push_uint16(ndr, NDR_SCALARS, 8)); + + /* filler */ + NDR_CHECK(ndr_push_uint32(ndr, NDR_SCALARS, 0xCCCCCCCC)); + + /* + * Private Header for Constructed Type + */ + /* length - will be updated latter */ + NDR_CHECK(ndr_push_uint32(ndr, NDR_SCALARS, subndr->offset)); + + /* reserved */ + NDR_CHECK(ndr_push_uint32(ndr, NDR_SCALARS, 0)); + break; + + default: + return ndr_push_error(ndr, NDR_ERR_SUBCONTEXT, "Bad subcontext header size %d", + (int)header_size); + } + + NDR_CHECK(ndr_push_bytes(ndr, subndr->data, subndr->offset)); + return NDR_ERR_SUCCESS; +} + +/* + store a token in the ndr context, for later retrieval +*/ +_PUBLIC_ enum ndr_err_code ndr_token_store(TALLOC_CTX *mem_ctx, + struct ndr_token_list **list, + const void *key, + uint32_t value) +{ + struct ndr_token_list *tok; + tok = talloc(mem_ctx, struct ndr_token_list); + NDR_ERR_HAVE_NO_MEMORY(tok); + tok->key = key; + tok->value = value; + DLIST_ADD((*list), tok); + return NDR_ERR_SUCCESS; +} + +/* + retrieve a token from a ndr context, using cmp_fn to match the tokens +*/ +_PUBLIC_ enum ndr_err_code ndr_token_retrieve_cmp_fn(struct ndr_token_list **list, const void *key, uint32_t *v, + comparison_fn_t _cmp_fn, bool _remove_tok) +{ + struct ndr_token_list *tok; + for (tok=*list;tok;tok=tok->next) { + if (_cmp_fn && _cmp_fn(tok->key,key)==0) goto found; + else if (!_cmp_fn && tok->key == key) goto found; + } + return NDR_ERR_TOKEN; +found: + *v = tok->value; + if (_remove_tok) { + DLIST_REMOVE((*list), tok); + talloc_free(tok); + } + return NDR_ERR_SUCCESS; +} + +/* + retrieve a token from a ndr context +*/ +_PUBLIC_ enum ndr_err_code ndr_token_retrieve(struct ndr_token_list **list, const void *key, uint32_t *v) +{ + return ndr_token_retrieve_cmp_fn(list, key, v, NULL, true); +} + +/* + peek at but don't removed a token from a ndr context +*/ +_PUBLIC_ uint32_t ndr_token_peek(struct ndr_token_list **list, const void *key) +{ + enum ndr_err_code status; + uint32_t v; + + status = ndr_token_retrieve_cmp_fn(list, key, &v, NULL, false); + if (!NDR_ERR_CODE_IS_SUCCESS(status)) { + return 0; + } + + return v; +} + +/* + pull an array size field and add it to the array_size_list token list +*/ +_PUBLIC_ enum ndr_err_code ndr_pull_array_size(struct ndr_pull *ndr, const void *p) +{ + uint32_t size; + NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, &size)); + return ndr_token_store(ndr, &ndr->array_size_list, p, size); +} + +/* + get the stored array size field +*/ +_PUBLIC_ uint32_t ndr_get_array_size(struct ndr_pull *ndr, const void *p) +{ + return ndr_token_peek(&ndr->array_size_list, p); +} + +/* + check the stored array size field +*/ +_PUBLIC_ enum ndr_err_code ndr_check_array_size(struct ndr_pull *ndr, void *p, uint32_t size) +{ + uint32_t stored; + stored = ndr_token_peek(&ndr->array_size_list, p); + if (stored != size) { + return ndr_pull_error(ndr, NDR_ERR_ARRAY_SIZE, + "Bad array size - got %u expected %u\n", + stored, size); + } + return NDR_ERR_SUCCESS; +} + +/* + pull an array length field and add it to the array_length_list token list +*/ +_PUBLIC_ enum ndr_err_code ndr_pull_array_length(struct ndr_pull *ndr, const void *p) +{ + uint32_t length, offset; + NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, &offset)); + if (offset != 0) { + return ndr_pull_error(ndr, NDR_ERR_ARRAY_SIZE, + "non-zero array offset %u\n", offset); + } + NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, &length)); + return ndr_token_store(ndr, &ndr->array_length_list, p, length); +} + +/* + get the stored array length field +*/ +_PUBLIC_ uint32_t ndr_get_array_length(struct ndr_pull *ndr, const void *p) +{ + return ndr_token_peek(&ndr->array_length_list, p); +} + +/* + check the stored array length field +*/ +_PUBLIC_ enum ndr_err_code ndr_check_array_length(struct ndr_pull *ndr, void *p, uint32_t length) +{ + uint32_t stored; + stored = ndr_token_peek(&ndr->array_length_list, p); + if (stored != length) { + return ndr_pull_error(ndr, NDR_ERR_ARRAY_SIZE, + "Bad array length - got %u expected %u\n", + stored, length); + } + return NDR_ERR_SUCCESS; +} + +/* + store a switch value + */ +_PUBLIC_ enum ndr_err_code ndr_push_set_switch_value(struct ndr_push *ndr, const void *p, uint32_t val) +{ + return ndr_token_store(ndr, &ndr->switch_list, p, val); +} + +_PUBLIC_ enum ndr_err_code ndr_pull_set_switch_value(struct ndr_pull *ndr, const void *p, uint32_t val) +{ + return ndr_token_store(ndr, &ndr->switch_list, p, val); +} + +_PUBLIC_ enum ndr_err_code ndr_print_set_switch_value(struct ndr_print *ndr, const void *p, uint32_t val) +{ + return ndr_token_store(ndr, &ndr->switch_list, p, val); +} + +/* + retrieve a switch value + */ +_PUBLIC_ uint32_t ndr_push_get_switch_value(struct ndr_push *ndr, const void *p) +{ + return ndr_token_peek(&ndr->switch_list, p); +} + +_PUBLIC_ uint32_t ndr_pull_get_switch_value(struct ndr_pull *ndr, const void *p) +{ + return ndr_token_peek(&ndr->switch_list, p); +} + +_PUBLIC_ uint32_t ndr_print_get_switch_value(struct ndr_print *ndr, const void *p) +{ + return ndr_token_peek(&ndr->switch_list, p); +} + +/* + pull a struct from a blob using NDR +*/ +_PUBLIC_ enum ndr_err_code ndr_pull_struct_blob(const DATA_BLOB *blob, TALLOC_CTX *mem_ctx, struct smb_iconv_convenience *iconv_convenience, void *p, + ndr_pull_flags_fn_t fn) +{ + struct ndr_pull *ndr; + ndr = ndr_pull_init_blob(blob, mem_ctx, iconv_convenience); + NDR_ERR_HAVE_NO_MEMORY(ndr); + NDR_CHECK(fn(ndr, NDR_SCALARS|NDR_BUFFERS, p)); + return NDR_ERR_SUCCESS; +} + +/* + pull a struct from a blob using NDR - failing if all bytes are not consumed +*/ +_PUBLIC_ enum ndr_err_code ndr_pull_struct_blob_all(const DATA_BLOB *blob, TALLOC_CTX *mem_ctx, + struct smb_iconv_convenience *iconv_convenience, + void *p, ndr_pull_flags_fn_t fn) +{ + struct ndr_pull *ndr; + ndr = ndr_pull_init_blob(blob, mem_ctx, iconv_convenience); + NDR_ERR_HAVE_NO_MEMORY(ndr); + NDR_CHECK(fn(ndr, NDR_SCALARS|NDR_BUFFERS, p)); + if (ndr->offset < ndr->data_size) { + return ndr_pull_error(ndr, NDR_ERR_UNREAD_BYTES, + "not all bytes consumed ofs[%u] size[%u]", + ndr->offset, ndr->data_size); + } + return NDR_ERR_SUCCESS; +} + +/* + pull a union from a blob using NDR, given the union discriminator +*/ +_PUBLIC_ enum ndr_err_code ndr_pull_union_blob(const DATA_BLOB *blob, TALLOC_CTX *mem_ctx, + struct smb_iconv_convenience *iconv_convenience, void *p, + uint32_t level, ndr_pull_flags_fn_t fn) +{ + struct ndr_pull *ndr; + ndr = ndr_pull_init_blob(blob, mem_ctx, iconv_convenience); + NDR_ERR_HAVE_NO_MEMORY(ndr); + NDR_CHECK(ndr_pull_set_switch_value(ndr, p, level)); + NDR_CHECK(fn(ndr, NDR_SCALARS|NDR_BUFFERS, p)); + return NDR_ERR_SUCCESS; +} + +/* + pull a union from a blob using NDR, given the union discriminator, + failing if all bytes are not consumed +*/ +_PUBLIC_ enum ndr_err_code ndr_pull_union_blob_all(const DATA_BLOB *blob, TALLOC_CTX *mem_ctx, + struct smb_iconv_convenience *iconv_convenience, void *p, + uint32_t level, ndr_pull_flags_fn_t fn) +{ + struct ndr_pull *ndr; + ndr = ndr_pull_init_blob(blob, mem_ctx, iconv_convenience); + NDR_ERR_HAVE_NO_MEMORY(ndr); + NDR_CHECK(ndr_pull_set_switch_value(ndr, p, level)); + NDR_CHECK(fn(ndr, NDR_SCALARS|NDR_BUFFERS, p)); + if (ndr->offset < ndr->data_size) { + return ndr_pull_error(ndr, NDR_ERR_UNREAD_BYTES, + "not all bytes consumed ofs[%u] size[%u]", + ndr->offset, ndr->data_size); + } + return NDR_ERR_SUCCESS; +} + +/* + push a struct to a blob using NDR +*/ +_PUBLIC_ enum ndr_err_code ndr_push_struct_blob(DATA_BLOB *blob, TALLOC_CTX *mem_ctx, struct smb_iconv_convenience *iconv_convenience, const void *p, ndr_push_flags_fn_t fn) +{ + struct ndr_push *ndr; + ndr = ndr_push_init_ctx(mem_ctx, iconv_convenience); + NDR_ERR_HAVE_NO_MEMORY(ndr); + + NDR_CHECK(fn(ndr, NDR_SCALARS|NDR_BUFFERS, p)); + + *blob = ndr_push_blob(ndr); + talloc_steal(mem_ctx, blob->data); + talloc_free(ndr); + + return NDR_ERR_SUCCESS; +} + +/* + push a union to a blob using NDR +*/ +_PUBLIC_ enum ndr_err_code ndr_push_union_blob(DATA_BLOB *blob, TALLOC_CTX *mem_ctx, struct smb_iconv_convenience *iconv_convenience, void *p, + uint32_t level, ndr_push_flags_fn_t fn) +{ + struct ndr_push *ndr; + ndr = ndr_push_init_ctx(mem_ctx, iconv_convenience); + NDR_ERR_HAVE_NO_MEMORY(ndr); + + NDR_CHECK(ndr_push_set_switch_value(ndr, p, level)); + NDR_CHECK(fn(ndr, NDR_SCALARS|NDR_BUFFERS, p)); + + *blob = ndr_push_blob(ndr); + talloc_steal(mem_ctx, blob->data); + talloc_free(ndr); + + return NDR_ERR_SUCCESS; +} + +/* + generic ndr_size_*() handler for structures +*/ +_PUBLIC_ size_t ndr_size_struct(const void *p, int flags, ndr_push_flags_fn_t push) +{ + struct ndr_push *ndr; + enum ndr_err_code status; + size_t ret; + + /* avoid recursion */ + if (flags & LIBNDR_FLAG_NO_NDR_SIZE) return 0; + + ndr = ndr_push_init_ctx(NULL, lp_iconv_convenience(global_loadparm)); + if (!ndr) return 0; + ndr->flags |= flags | LIBNDR_FLAG_NO_NDR_SIZE; + status = push(ndr, NDR_SCALARS|NDR_BUFFERS, discard_const(p)); + if (!NDR_ERR_CODE_IS_SUCCESS(status)) { + talloc_free(ndr); + return 0; + } + ret = ndr->offset; + talloc_free(ndr); + return ret; +} + +/* + generic ndr_size_*() handler for unions +*/ +_PUBLIC_ size_t ndr_size_union(const void *p, int flags, uint32_t level, ndr_push_flags_fn_t push) +{ + struct ndr_push *ndr; + enum ndr_err_code status; + size_t ret; + + /* avoid recursion */ + if (flags & LIBNDR_FLAG_NO_NDR_SIZE) return 0; + + ndr = ndr_push_init_ctx(NULL, lp_iconv_convenience(global_loadparm)); + if (!ndr) return 0; + ndr->flags |= flags | LIBNDR_FLAG_NO_NDR_SIZE; + + status = ndr_push_set_switch_value(ndr, p, level); + if (!NDR_ERR_CODE_IS_SUCCESS(status)) { + talloc_free(ndr); + return 0; + } + status = push(ndr, NDR_SCALARS|NDR_BUFFERS, p); + if (!NDR_ERR_CODE_IS_SUCCESS(status)) { + talloc_free(ndr); + return 0; + } + ret = ndr->offset; + talloc_free(ndr); + return ret; +} + +/* + get the current base for relative pointers for the push +*/ +_PUBLIC_ uint32_t ndr_push_get_relative_base_offset(struct ndr_push *ndr) +{ + return ndr->relative_base_offset; +} + +/* + restore the old base for relative pointers for the push +*/ +_PUBLIC_ void ndr_push_restore_relative_base_offset(struct ndr_push *ndr, uint32_t offset) +{ + ndr->relative_base_offset = offset; +} + +/* + setup the current base for relative pointers for the push + called in the NDR_SCALAR stage +*/ +_PUBLIC_ enum ndr_err_code ndr_push_setup_relative_base_offset1(struct ndr_push *ndr, const void *p, uint32_t offset) +{ + ndr->relative_base_offset = offset; + return ndr_token_store(ndr, &ndr->relative_base_list, p, offset); +} + +/* + setup the current base for relative pointers for the push + called in the NDR_BUFFERS stage +*/ +_PUBLIC_ enum ndr_err_code ndr_push_setup_relative_base_offset2(struct ndr_push *ndr, const void *p) +{ + return ndr_token_retrieve(&ndr->relative_base_list, p, &ndr->relative_base_offset); +} + +/* + push a relative object - stage1 + this is called during SCALARS processing +*/ +_PUBLIC_ enum ndr_err_code ndr_push_relative_ptr1(struct ndr_push *ndr, const void *p) +{ + if (p == NULL) { + NDR_CHECK(ndr_push_uint32(ndr, NDR_SCALARS, 0)); + return NDR_ERR_SUCCESS; + } + NDR_CHECK(ndr_push_align(ndr, 4)); + NDR_CHECK(ndr_token_store(ndr, &ndr->relative_list, p, ndr->offset)); + return ndr_push_uint32(ndr, NDR_SCALARS, 0xFFFFFFFF); +} + +/* + push a relative object - stage2 + this is called during buffers processing +*/ +_PUBLIC_ enum ndr_err_code ndr_push_relative_ptr2(struct ndr_push *ndr, const void *p) +{ + uint32_t save_offset; + uint32_t ptr_offset = 0xFFFFFFFF; + if (p == NULL) { + return NDR_ERR_SUCCESS; + } + save_offset = ndr->offset; + NDR_CHECK(ndr_token_retrieve(&ndr->relative_list, p, &ptr_offset)); + if (ptr_offset > ndr->offset) { + return ndr_push_error(ndr, NDR_ERR_BUFSIZE, + "ndr_push_relative_ptr2 ptr_offset(%u) > ndr->offset(%u)", + ptr_offset, ndr->offset); + } + ndr->offset = ptr_offset; + if (save_offset < ndr->relative_base_offset) { + return ndr_push_error(ndr, NDR_ERR_BUFSIZE, + "ndr_push_relative_ptr2 save_offset(%u) < ndr->relative_base_offset(%u)", + save_offset, ndr->relative_base_offset); + } + NDR_CHECK(ndr_push_uint32(ndr, NDR_SCALARS, save_offset - ndr->relative_base_offset)); + ndr->offset = save_offset; + return NDR_ERR_SUCCESS; +} + +/* + get the current base for relative pointers for the pull +*/ +_PUBLIC_ uint32_t ndr_pull_get_relative_base_offset(struct ndr_pull *ndr) +{ + return ndr->relative_base_offset; +} + +/* + restore the old base for relative pointers for the pull +*/ +_PUBLIC_ void ndr_pull_restore_relative_base_offset(struct ndr_pull *ndr, uint32_t offset) +{ + ndr->relative_base_offset = offset; +} + +/* + setup the current base for relative pointers for the pull + called in the NDR_SCALAR stage +*/ +_PUBLIC_ enum ndr_err_code ndr_pull_setup_relative_base_offset1(struct ndr_pull *ndr, const void *p, uint32_t offset) +{ + ndr->relative_base_offset = offset; + return ndr_token_store(ndr, &ndr->relative_base_list, p, offset); +} + +/* + setup the current base for relative pointers for the pull + called in the NDR_BUFFERS stage +*/ +_PUBLIC_ enum ndr_err_code ndr_pull_setup_relative_base_offset2(struct ndr_pull *ndr, const void *p) +{ + return ndr_token_retrieve(&ndr->relative_base_list, p, &ndr->relative_base_offset); +} + +/* + pull a relative object - stage1 + called during SCALARS processing +*/ +_PUBLIC_ enum ndr_err_code ndr_pull_relative_ptr1(struct ndr_pull *ndr, const void *p, uint32_t rel_offset) +{ + rel_offset += ndr->relative_base_offset; + if (rel_offset > ndr->data_size) { + return ndr_pull_error(ndr, NDR_ERR_BUFSIZE, + "ndr_pull_relative_ptr1 rel_offset(%u) > ndr->data_size(%u)", + rel_offset, ndr->data_size); + } + return ndr_token_store(ndr, &ndr->relative_list, p, rel_offset); +} + +/* + pull a relative object - stage2 + called during BUFFERS processing +*/ +_PUBLIC_ enum ndr_err_code ndr_pull_relative_ptr2(struct ndr_pull *ndr, const void *p) +{ + uint32_t rel_offset; + NDR_CHECK(ndr_token_retrieve(&ndr->relative_list, p, &rel_offset)); + return ndr_pull_set_offset(ndr, rel_offset); +} diff --git a/librpc/ndr/ndr_basic.c b/librpc/ndr/ndr_basic.c new file mode 100644 index 0000000000..1d2b47c850 --- /dev/null +++ b/librpc/ndr/ndr_basic.c @@ -0,0 +1,833 @@ +/* + Unix SMB/CIFS implementation. + + routines for marshalling/unmarshalling basic types + + 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 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 <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "system/network.h" +#include "librpc/ndr/libndr.h" + +#define NDR_SVAL(ndr, ofs) (NDR_BE(ndr)?RSVAL(ndr->data,ofs):SVAL(ndr->data,ofs)) +#define NDR_IVAL(ndr, ofs) (NDR_BE(ndr)?RIVAL(ndr->data,ofs):IVAL(ndr->data,ofs)) +#define NDR_IVALS(ndr, ofs) (NDR_BE(ndr)?RIVALS(ndr->data,ofs):IVALS(ndr->data,ofs)) +#define NDR_SSVAL(ndr, ofs, v) do { if (NDR_BE(ndr)) { RSSVAL(ndr->data,ofs,v); } else SSVAL(ndr->data,ofs,v); } while (0) +#define NDR_SIVAL(ndr, ofs, v) do { if (NDR_BE(ndr)) { RSIVAL(ndr->data,ofs,v); } else SIVAL(ndr->data,ofs,v); } while (0) +#define NDR_SIVALS(ndr, ofs, v) do { if (NDR_BE(ndr)) { RSIVALS(ndr->data,ofs,v); } else SIVALS(ndr->data,ofs,v); } while (0) + + +/* + check for data leaks from the server by looking for non-zero pad bytes + these could also indicate that real structure elements have been + mistaken for padding in the IDL +*/ +_PUBLIC_ void ndr_check_padding(struct ndr_pull *ndr, size_t n) +{ + size_t ofs2 = (ndr->offset + (n-1)) & ~(n-1); + int i; + for (i=ndr->offset;i<ofs2;i++) { + if (ndr->data[i] != 0) { + break; + } + } + if (i<ofs2) { + DEBUG(0,("WARNING: Non-zero padding to %d: ", (int)n)); + for (i=ndr->offset;i<ofs2;i++) { + DEBUG(0,("%02x ", ndr->data[i])); + } + DEBUG(0,("\n")); + } + +} + +/* + parse a int8_t +*/ +_PUBLIC_ enum ndr_err_code ndr_pull_int8(struct ndr_pull *ndr, int ndr_flags, int8_t *v) +{ + NDR_PULL_NEED_BYTES(ndr, 1); + *v = (int8_t)CVAL(ndr->data, ndr->offset); + ndr->offset += 1; + return NDR_ERR_SUCCESS; +} + +/* + parse a uint8_t +*/ +_PUBLIC_ enum ndr_err_code ndr_pull_uint8(struct ndr_pull *ndr, int ndr_flags, uint8_t *v) +{ + NDR_PULL_NEED_BYTES(ndr, 1); + *v = CVAL(ndr->data, ndr->offset); + ndr->offset += 1; + return NDR_ERR_SUCCESS; +} + +/* + parse a int16_t +*/ +_PUBLIC_ enum ndr_err_code ndr_pull_int16(struct ndr_pull *ndr, int ndr_flags, int16_t *v) +{ + NDR_PULL_ALIGN(ndr, 2); + NDR_PULL_NEED_BYTES(ndr, 2); + *v = (uint16_t)NDR_SVAL(ndr, ndr->offset); + ndr->offset += 2; + return NDR_ERR_SUCCESS; +} + +/* + parse a uint16_t +*/ +_PUBLIC_ enum ndr_err_code ndr_pull_uint16(struct ndr_pull *ndr, int ndr_flags, uint16_t *v) +{ + NDR_PULL_ALIGN(ndr, 2); + NDR_PULL_NEED_BYTES(ndr, 2); + *v = NDR_SVAL(ndr, ndr->offset); + ndr->offset += 2; + return NDR_ERR_SUCCESS; +} + +/* + parse a int32_t +*/ +_PUBLIC_ enum ndr_err_code ndr_pull_int32(struct ndr_pull *ndr, int ndr_flags, int32_t *v) +{ + NDR_PULL_ALIGN(ndr, 4); + NDR_PULL_NEED_BYTES(ndr, 4); + *v = NDR_IVALS(ndr, ndr->offset); + ndr->offset += 4; + return NDR_ERR_SUCCESS; +} + +/* + parse a uint32_t +*/ +_PUBLIC_ enum ndr_err_code ndr_pull_uint32(struct ndr_pull *ndr, int ndr_flags, uint32_t *v) +{ + NDR_PULL_ALIGN(ndr, 4); + NDR_PULL_NEED_BYTES(ndr, 4); + *v = NDR_IVAL(ndr, ndr->offset); + ndr->offset += 4; + return NDR_ERR_SUCCESS; +} + +/* + parse a pointer referent identifier +*/ +_PUBLIC_ enum ndr_err_code ndr_pull_generic_ptr(struct ndr_pull *ndr, uint32_t *v) +{ + NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, v)); + if (*v != 0) { + ndr->ptr_count++; + } + return NDR_ERR_SUCCESS; +} + +/* + parse a ref pointer referent identifier +*/ +_PUBLIC_ enum ndr_err_code ndr_pull_ref_ptr(struct ndr_pull *ndr, uint32_t *v) +{ + NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, v)); + /* ref pointers always point to data */ + *v = 1; + return NDR_ERR_SUCCESS; +} + +/* + parse a udlong +*/ +_PUBLIC_ enum ndr_err_code ndr_pull_udlong(struct ndr_pull *ndr, int ndr_flags, uint64_t *v) +{ + NDR_PULL_ALIGN(ndr, 4); + NDR_PULL_NEED_BYTES(ndr, 8); + *v = NDR_IVAL(ndr, ndr->offset); + *v |= (uint64_t)(NDR_IVAL(ndr, ndr->offset+4)) << 32; + ndr->offset += 8; + return NDR_ERR_SUCCESS; +} + +/* + parse a udlongr +*/ +_PUBLIC_ enum ndr_err_code ndr_pull_udlongr(struct ndr_pull *ndr, int ndr_flags, uint64_t *v) +{ + NDR_PULL_ALIGN(ndr, 4); + NDR_PULL_NEED_BYTES(ndr, 8); + *v = ((uint64_t)NDR_IVAL(ndr, ndr->offset)) << 32; + *v |= NDR_IVAL(ndr, ndr->offset+4); + ndr->offset += 8; + return NDR_ERR_SUCCESS; +} + +/* + parse a dlong +*/ +_PUBLIC_ enum ndr_err_code ndr_pull_dlong(struct ndr_pull *ndr, int ndr_flags, int64_t *v) +{ + return ndr_pull_udlong(ndr, ndr_flags, (uint64_t *)v); +} + +/* + parse a hyper +*/ +_PUBLIC_ enum ndr_err_code ndr_pull_hyper(struct ndr_pull *ndr, int ndr_flags, uint64_t *v) +{ + NDR_PULL_ALIGN(ndr, 8); + return ndr_pull_udlong(ndr, ndr_flags, v); +} + +/* + parse a pointer +*/ +_PUBLIC_ enum ndr_err_code ndr_pull_pointer(struct ndr_pull *ndr, int ndr_flags, void* *v) +{ + uintptr_t h; + NDR_PULL_ALIGN(ndr, sizeof(h)); + NDR_PULL_NEED_BYTES(ndr, sizeof(h)); + memcpy(&h, ndr->data+ndr->offset, sizeof(h)); + ndr->offset += sizeof(h); + *v = (void *)h; + return NDR_ERR_SUCCESS; +} + +/* + pull a NTSTATUS +*/ +_PUBLIC_ enum ndr_err_code ndr_pull_NTSTATUS(struct ndr_pull *ndr, int ndr_flags, NTSTATUS *status) +{ + uint32_t v; + NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, &v)); + *status = NT_STATUS(v); + return NDR_ERR_SUCCESS; +} + +/* + push a NTSTATUS +*/ +_PUBLIC_ enum ndr_err_code ndr_push_NTSTATUS(struct ndr_push *ndr, int ndr_flags, NTSTATUS status) +{ + return ndr_push_uint32(ndr, ndr_flags, NT_STATUS_V(status)); +} + +_PUBLIC_ void ndr_print_NTSTATUS(struct ndr_print *ndr, const char *name, NTSTATUS r) +{ + ndr->print(ndr, "%-25s: %s", name, nt_errstr(r)); +} + +/* + pull a WERROR +*/ +_PUBLIC_ enum ndr_err_code ndr_pull_WERROR(struct ndr_pull *ndr, int ndr_flags, WERROR *status) +{ + uint32_t v; + NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, &v)); + *status = W_ERROR(v); + return NDR_ERR_SUCCESS; +} + +/* + push a WERROR +*/ +_PUBLIC_ enum ndr_err_code ndr_push_WERROR(struct ndr_push *ndr, int ndr_flags, WERROR status) +{ + return ndr_push_uint32(ndr, NDR_SCALARS, W_ERROR_V(status)); +} + +_PUBLIC_ void ndr_print_WERROR(struct ndr_print *ndr, const char *name, WERROR r) +{ + ndr->print(ndr, "%-25s: %s", name, win_errstr(r)); +} + +/* + parse a set of bytes +*/ +_PUBLIC_ enum ndr_err_code ndr_pull_bytes(struct ndr_pull *ndr, uint8_t *data, uint32_t n) +{ + NDR_PULL_NEED_BYTES(ndr, n); + memcpy(data, ndr->data + ndr->offset, n); + ndr->offset += n; + return NDR_ERR_SUCCESS; +} + +/* + pull an array of uint8 +*/ +_PUBLIC_ enum ndr_err_code ndr_pull_array_uint8(struct ndr_pull *ndr, int ndr_flags, uint8_t *data, uint32_t n) +{ + if (!(ndr_flags & NDR_SCALARS)) { + return NDR_ERR_SUCCESS; + } + return ndr_pull_bytes(ndr, data, n); +} + +/* + push a int8_t +*/ +_PUBLIC_ enum ndr_err_code ndr_push_int8(struct ndr_push *ndr, int ndr_flags, int8_t v) +{ + NDR_PUSH_NEED_BYTES(ndr, 1); + SCVAL(ndr->data, ndr->offset, (uint8_t)v); + ndr->offset += 1; + return NDR_ERR_SUCCESS; +} + +/* + push a uint8_t +*/ +_PUBLIC_ enum ndr_err_code ndr_push_uint8(struct ndr_push *ndr, int ndr_flags, uint8_t v) +{ + NDR_PUSH_NEED_BYTES(ndr, 1); + SCVAL(ndr->data, ndr->offset, v); + ndr->offset += 1; + return NDR_ERR_SUCCESS; +} + +/* + push a int16_t +*/ +_PUBLIC_ enum ndr_err_code ndr_push_int16(struct ndr_push *ndr, int ndr_flags, int16_t v) +{ + NDR_PUSH_ALIGN(ndr, 2); + NDR_PUSH_NEED_BYTES(ndr, 2); + NDR_SSVAL(ndr, ndr->offset, (uint16_t)v); + ndr->offset += 2; + return NDR_ERR_SUCCESS; +} + +/* + push a uint16_t +*/ +_PUBLIC_ enum ndr_err_code ndr_push_uint16(struct ndr_push *ndr, int ndr_flags, uint16_t v) +{ + NDR_PUSH_ALIGN(ndr, 2); + NDR_PUSH_NEED_BYTES(ndr, 2); + NDR_SSVAL(ndr, ndr->offset, v); + ndr->offset += 2; + return NDR_ERR_SUCCESS; +} + +/* + push a int32_t +*/ +_PUBLIC_ enum ndr_err_code ndr_push_int32(struct ndr_push *ndr, int ndr_flags, int32_t v) +{ + NDR_PUSH_ALIGN(ndr, 4); + NDR_PUSH_NEED_BYTES(ndr, 4); + NDR_SIVALS(ndr, ndr->offset, v); + ndr->offset += 4; + return NDR_ERR_SUCCESS; +} + +/* + push a uint32_t +*/ +_PUBLIC_ enum ndr_err_code ndr_push_uint32(struct ndr_push *ndr, int ndr_flags, uint32_t v) +{ + NDR_PUSH_ALIGN(ndr, 4); + NDR_PUSH_NEED_BYTES(ndr, 4); + NDR_SIVAL(ndr, ndr->offset, v); + ndr->offset += 4; + return NDR_ERR_SUCCESS; +} + +/* + push a udlong +*/ +_PUBLIC_ enum ndr_err_code ndr_push_udlong(struct ndr_push *ndr, int ndr_flags, uint64_t v) +{ + NDR_PUSH_ALIGN(ndr, 4); + NDR_PUSH_NEED_BYTES(ndr, 8); + NDR_SIVAL(ndr, ndr->offset, (v & 0xFFFFFFFF)); + NDR_SIVAL(ndr, ndr->offset+4, (v>>32)); + ndr->offset += 8; + return NDR_ERR_SUCCESS; +} + +/* + push a udlongr +*/ +_PUBLIC_ enum ndr_err_code ndr_push_udlongr(struct ndr_push *ndr, int ndr_flags, uint64_t v) +{ + NDR_PUSH_ALIGN(ndr, 4); + NDR_PUSH_NEED_BYTES(ndr, 8); + NDR_SIVAL(ndr, ndr->offset, (v>>32)); + NDR_SIVAL(ndr, ndr->offset+4, (v & 0xFFFFFFFF)); + ndr->offset += 8; + return NDR_ERR_SUCCESS; +} + +/* + push a dlong +*/ +_PUBLIC_ enum ndr_err_code ndr_push_dlong(struct ndr_push *ndr, int ndr_flags, int64_t v) +{ + return ndr_push_udlong(ndr, NDR_SCALARS, (uint64_t)v); +} + +/* + push a hyper +*/ +_PUBLIC_ enum ndr_err_code ndr_push_hyper(struct ndr_push *ndr, int ndr_flags, uint64_t v) +{ + NDR_PUSH_ALIGN(ndr, 8); + return ndr_push_udlong(ndr, NDR_SCALARS, v); +} + +/* + push a pointer +*/ +_PUBLIC_ enum ndr_err_code ndr_push_pointer(struct ndr_push *ndr, int ndr_flags, void* v) +{ + uintptr_t h = (intptr_t)v; + NDR_PUSH_ALIGN(ndr, sizeof(h)); + NDR_PUSH_NEED_BYTES(ndr, sizeof(h)); + memcpy(ndr->data+ndr->offset, &h, sizeof(h)); + ndr->offset += sizeof(h); + return NDR_ERR_SUCCESS; +} + +_PUBLIC_ enum ndr_err_code ndr_push_align(struct ndr_push *ndr, size_t size) +{ + NDR_PUSH_ALIGN(ndr, size); + return NDR_ERR_SUCCESS; +} + +_PUBLIC_ enum ndr_err_code ndr_pull_align(struct ndr_pull *ndr, size_t size) +{ + NDR_PULL_ALIGN(ndr, size); + return NDR_ERR_SUCCESS; +} + +/* + push some bytes +*/ +_PUBLIC_ enum ndr_err_code ndr_push_bytes(struct ndr_push *ndr, const uint8_t *data, uint32_t n) +{ + NDR_PUSH_NEED_BYTES(ndr, n); + memcpy(ndr->data + ndr->offset, data, n); + ndr->offset += n; + return NDR_ERR_SUCCESS; +} + +/* + push some zero bytes +*/ +_PUBLIC_ enum ndr_err_code ndr_push_zero(struct ndr_push *ndr, uint32_t n) +{ + NDR_PUSH_NEED_BYTES(ndr, n); + memset(ndr->data + ndr->offset, 0, n); + ndr->offset += n; + return NDR_ERR_SUCCESS; +} + +/* + push an array of uint8 +*/ +_PUBLIC_ enum ndr_err_code ndr_push_array_uint8(struct ndr_push *ndr, int ndr_flags, const uint8_t *data, uint32_t n) +{ + if (!(ndr_flags & NDR_SCALARS)) { + return NDR_ERR_SUCCESS; + } + return ndr_push_bytes(ndr, data, n); +} + +/* + push a unique non-zero value if a pointer is non-NULL, otherwise 0 +*/ +_PUBLIC_ enum ndr_err_code ndr_push_unique_ptr(struct ndr_push *ndr, const void *p) +{ + uint32_t ptr = 0; + if (p) { + ptr = ndr->ptr_count * 4; + ptr |= 0x00020000; + ndr->ptr_count++; + } + return ndr_push_uint32(ndr, NDR_SCALARS, ptr); +} + +/* + push a 'simple' full non-zero value if a pointer is non-NULL, otherwise 0 +*/ +_PUBLIC_ enum ndr_err_code ndr_push_full_ptr(struct ndr_push *ndr, const void *p) +{ + uint32_t ptr = 0; + if (p) { + /* Check if the pointer already exists and has an id */ + ptr = ndr_token_peek(&ndr->full_ptr_list, p); + if (ptr == 0) { + ndr->ptr_count++; + ptr = ndr->ptr_count; + ndr_token_store(ndr, &ndr->full_ptr_list, p, ptr); + } + } + return ndr_push_uint32(ndr, NDR_SCALARS, ptr); +} + +/* + push always a 0, if a pointer is NULL it's a fatal error +*/ +_PUBLIC_ enum ndr_err_code ndr_push_ref_ptr(struct ndr_push *ndr) +{ + return ndr_push_uint32(ndr, NDR_SCALARS, 0xAEF1AEF1); +} + + +/* + push a NTTIME +*/ +_PUBLIC_ enum ndr_err_code ndr_push_NTTIME(struct ndr_push *ndr, int ndr_flags, NTTIME t) +{ + NDR_CHECK(ndr_push_udlong(ndr, ndr_flags, t)); + return NDR_ERR_SUCCESS; +} + +/* + pull a NTTIME +*/ +_PUBLIC_ enum ndr_err_code ndr_pull_NTTIME(struct ndr_pull *ndr, int ndr_flags, NTTIME *t) +{ + NDR_CHECK(ndr_pull_udlong(ndr, ndr_flags, t)); + return NDR_ERR_SUCCESS; +} + +/* + push a NTTIME +*/ +_PUBLIC_ enum ndr_err_code ndr_push_NTTIME_1sec(struct ndr_push *ndr, int ndr_flags, NTTIME t) +{ + t /= 10000000; + NDR_CHECK(ndr_push_hyper(ndr, ndr_flags, t)); + return NDR_ERR_SUCCESS; +} + +/* + pull a NTTIME_1sec +*/ +_PUBLIC_ enum ndr_err_code ndr_pull_NTTIME_1sec(struct ndr_pull *ndr, int ndr_flags, NTTIME *t) +{ + NDR_CHECK(ndr_pull_hyper(ndr, ndr_flags, t)); + (*t) *= 10000000; + return NDR_ERR_SUCCESS; +} + +/* + pull a NTTIME_hyper +*/ +_PUBLIC_ enum ndr_err_code ndr_pull_NTTIME_hyper(struct ndr_pull *ndr, int ndr_flags, NTTIME *t) +{ + NDR_CHECK(ndr_pull_hyper(ndr, ndr_flags, t)); + return NDR_ERR_SUCCESS; +} + +/* + push a NTTIME_hyper +*/ +_PUBLIC_ enum ndr_err_code ndr_push_NTTIME_hyper(struct ndr_push *ndr, int ndr_flags, NTTIME t) +{ + NDR_CHECK(ndr_push_hyper(ndr, ndr_flags, t)); + return NDR_ERR_SUCCESS; +} + +/* + push a time_t +*/ +_PUBLIC_ enum ndr_err_code ndr_push_time_t(struct ndr_push *ndr, int ndr_flags, time_t t) +{ + return ndr_push_uint32(ndr, ndr_flags, t); +} + +/* + pull a time_t +*/ +_PUBLIC_ enum ndr_err_code ndr_pull_time_t(struct ndr_pull *ndr, int ndr_flags, time_t *t) +{ + uint32_t tt; + NDR_CHECK(ndr_pull_uint32(ndr, ndr_flags, &tt)); + *t = tt; + return NDR_ERR_SUCCESS; +} + + +/* + pull a ipv4address +*/ +_PUBLIC_ enum ndr_err_code ndr_pull_ipv4address(struct ndr_pull *ndr, int ndr_flags, const char **address) +{ + struct in_addr in; + NDR_CHECK(ndr_pull_uint32(ndr, ndr_flags, &in.s_addr)); + in.s_addr = htonl(in.s_addr); + *address = talloc_strdup(ndr->current_mem_ctx, inet_ntoa(in)); + NDR_ERR_HAVE_NO_MEMORY(*address); + return NDR_ERR_SUCCESS; +} + +/* + push a ipv4address +*/ +_PUBLIC_ enum ndr_err_code ndr_push_ipv4address(struct ndr_push *ndr, int ndr_flags, const char *address) +{ + uint32_t addr; + if (!is_ipaddress(address)) { + return ndr_push_error(ndr, NDR_ERR_IPV4ADDRESS, + "Invalid IPv4 address: '%s'", + address); + } + addr = inet_addr(address); + NDR_CHECK(ndr_push_uint32(ndr, ndr_flags, htonl(addr))); + return NDR_ERR_SUCCESS; +} + +/* + print a ipv4address +*/ +_PUBLIC_ void ndr_print_ipv4address(struct ndr_print *ndr, const char *name, + const char *address) +{ + ndr->print(ndr, "%-25s: %s", name, address); +} + + +_PUBLIC_ void ndr_print_struct(struct ndr_print *ndr, const char *name, const char *type) +{ + ndr->print(ndr, "%s: struct %s", name, type); +} + +_PUBLIC_ void ndr_print_enum(struct ndr_print *ndr, const char *name, const char *type, + const char *val, uint32_t value) +{ + if (ndr->flags & LIBNDR_PRINT_ARRAY_HEX) { + ndr->print(ndr, "%-25s: %s (0x%X)", name, val?val:"UNKNOWN_ENUM_VALUE", value); + } else { + ndr->print(ndr, "%-25s: %s (%d)", name, val?val:"UNKNOWN_ENUM_VALUE", value); + } +} + +_PUBLIC_ void ndr_print_bitmap_flag(struct ndr_print *ndr, size_t size, const char *flag_name, uint32_t flag, uint32_t value) +{ + /* this is an attempt to support multi-bit bitmap masks */ + value &= flag; + + while (!(flag & 1)) { + flag >>= 1; + value >>= 1; + } + if (flag == 1) { + ndr->print(ndr, " %d: %-25s", value, flag_name); + } else { + ndr->print(ndr, "0x%02x: %-25s (%d)", value, flag_name, value); + } +} + +_PUBLIC_ void ndr_print_int8(struct ndr_print *ndr, const char *name, int8_t v) +{ + ndr->print(ndr, "%-25s: %d", name, v); +} + +_PUBLIC_ void ndr_print_uint8(struct ndr_print *ndr, const char *name, uint8_t v) +{ + ndr->print(ndr, "%-25s: 0x%02x (%u)", name, v, v); +} + +_PUBLIC_ void ndr_print_int16(struct ndr_print *ndr, const char *name, int16_t v) +{ + ndr->print(ndr, "%-25s: %d", name, v); +} + +_PUBLIC_ void ndr_print_uint16(struct ndr_print *ndr, const char *name, uint16_t v) +{ + ndr->print(ndr, "%-25s: 0x%04x (%u)", name, v, v); +} + +_PUBLIC_ void ndr_print_int32(struct ndr_print *ndr, const char *name, int32_t v) +{ + ndr->print(ndr, "%-25s: %d", name, v); +} + +_PUBLIC_ void ndr_print_uint32(struct ndr_print *ndr, const char *name, uint32_t v) +{ + ndr->print(ndr, "%-25s: 0x%08x (%u)", name, v, v); +} + +_PUBLIC_ void ndr_print_udlong(struct ndr_print *ndr, const char *name, uint64_t v) +{ + ndr->print(ndr, "%-25s: 0x%016llx (%llu)", name, (unsigned long long)v, (unsigned long long)v); +} + +_PUBLIC_ void ndr_print_udlongr(struct ndr_print *ndr, const char *name, uint64_t v) +{ + ndr_print_udlong(ndr, name, v); +} + +_PUBLIC_ void ndr_print_dlong(struct ndr_print *ndr, const char *name, int64_t v) +{ + ndr->print(ndr, "%-25s: 0x%016llx (%lld)", name, (unsigned long long)v, (long long)v); +} + +_PUBLIC_ void ndr_print_hyper(struct ndr_print *ndr, const char *name, uint64_t v) +{ + ndr_print_dlong(ndr, name, v); +} + +_PUBLIC_ void ndr_print_pointer(struct ndr_print *ndr, const char *name, void *v) +{ + ndr->print(ndr, "%-25s: %p", name, v); +} + +_PUBLIC_ void ndr_print_ptr(struct ndr_print *ndr, const char *name, const void *p) +{ + if (p) { + ndr->print(ndr, "%-25s: *", name); + } else { + ndr->print(ndr, "%-25s: NULL", name); + } +} + +_PUBLIC_ void ndr_print_NTTIME(struct ndr_print *ndr, const char *name, NTTIME t) +{ + ndr->print(ndr, "%-25s: %s", name, nt_time_string(ndr, t)); +} + +_PUBLIC_ void ndr_print_NTTIME_1sec(struct ndr_print *ndr, const char *name, NTTIME t) +{ + /* this is a standard NTTIME here + * as it's already converted in the pull/push code + */ + ndr_print_NTTIME(ndr, name, t); +} + +_PUBLIC_ void ndr_print_NTTIME_hyper(struct ndr_print *ndr, const char *name, NTTIME t) +{ + ndr_print_NTTIME(ndr, name, t); +} + +_PUBLIC_ void ndr_print_time_t(struct ndr_print *ndr, const char *name, time_t t) +{ + if (t == (time_t)-1 || t == 0) { + ndr->print(ndr, "%-25s: (time_t)%d", name, (int)t); + } else { + ndr->print(ndr, "%-25s: %s", name, timestring(ndr, t)); + } +} + +_PUBLIC_ void ndr_print_union(struct ndr_print *ndr, const char *name, int level, const char *type) +{ + if (ndr->flags & LIBNDR_PRINT_ARRAY_HEX) { + ndr->print(ndr, "%-25s: union %s(case 0x%X)", name, type, level); + } else { + ndr->print(ndr, "%-25s: union %s(case %d)", name, type, level); + } +} + +_PUBLIC_ void ndr_print_bad_level(struct ndr_print *ndr, const char *name, uint16_t level) +{ + ndr->print(ndr, "UNKNOWN LEVEL %u", level); +} + +_PUBLIC_ void ndr_print_array_uint8(struct ndr_print *ndr, const char *name, + const uint8_t *data, uint32_t count) +{ + int i; + + if (count <= 600 && (ndr->flags & LIBNDR_PRINT_ARRAY_HEX)) { + char s[1202]; + for (i=0;i<count;i++) { + snprintf(&s[i*2], 3, "%02x", data[i]); + } + s[i*2] = 0; + ndr->print(ndr, "%-25s: %s", name, s); + return; + } + + ndr->print(ndr, "%s: ARRAY(%d)", name, count); + ndr->depth++; + for (i=0;i<count;i++) { + char *idx=NULL; + asprintf(&idx, "[%d]", i); + if (idx) { + ndr_print_uint8(ndr, idx, data[i]); + free(idx); + } + } + ndr->depth--; +} + +_PUBLIC_ void ndr_print_DATA_BLOB(struct ndr_print *ndr, const char *name, DATA_BLOB r) +{ + ndr->print(ndr, "%-25s: DATA_BLOB length=%u", name, (unsigned)r.length); + if (r.length) { + dump_data(10, r.data, r.length); + } +} + + +/* + push a DATA_BLOB onto the wire. +*/ +_PUBLIC_ enum ndr_err_code ndr_push_DATA_BLOB(struct ndr_push *ndr, int ndr_flags, DATA_BLOB blob) +{ + if (ndr->flags & LIBNDR_ALIGN_FLAGS) { + if (ndr->flags & LIBNDR_FLAG_ALIGN2) { + blob.length = NDR_ALIGN(ndr, 2); + } else if (ndr->flags & LIBNDR_FLAG_ALIGN4) { + blob.length = NDR_ALIGN(ndr, 4); + } else if (ndr->flags & LIBNDR_FLAG_ALIGN8) { + blob.length = NDR_ALIGN(ndr, 8); + } + NDR_PUSH_ALLOC_SIZE(ndr, blob.data, blob.length); + data_blob_clear(&blob); + } else if (!(ndr->flags & LIBNDR_FLAG_REMAINING)) { + NDR_CHECK(ndr_push_uint32(ndr, NDR_SCALARS, blob.length)); + } + NDR_CHECK(ndr_push_bytes(ndr, blob.data, blob.length)); + return NDR_ERR_SUCCESS; +} + +/* + pull a DATA_BLOB from the wire. +*/ +_PUBLIC_ enum ndr_err_code ndr_pull_DATA_BLOB(struct ndr_pull *ndr, int ndr_flags, DATA_BLOB *blob) +{ + uint32_t length = 0; + + if (ndr->flags & LIBNDR_ALIGN_FLAGS) { + if (ndr->flags & LIBNDR_FLAG_ALIGN2) { + length = NDR_ALIGN(ndr, 2); + } else if (ndr->flags & LIBNDR_FLAG_ALIGN4) { + length = NDR_ALIGN(ndr, 4); + } else if (ndr->flags & LIBNDR_FLAG_ALIGN8) { + length = NDR_ALIGN(ndr, 8); + } + if (ndr->data_size - ndr->offset < length) { + length = ndr->data_size - ndr->offset; + } + } else if (ndr->flags & LIBNDR_FLAG_REMAINING) { + length = ndr->data_size - ndr->offset; + } else { + NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, &length)); + } + NDR_PULL_NEED_BYTES(ndr, length); + *blob = data_blob_talloc(ndr->current_mem_ctx, ndr->data+ndr->offset, length); + ndr->offset += length; + return NDR_ERR_SUCCESS; +} + +_PUBLIC_ uint32_t ndr_size_DATA_BLOB(int ret, const DATA_BLOB *data, int flags) +{ + if (!data) return ret; + return ret + data->length; +} diff --git a/librpc/ndr/ndr_misc.c b/librpc/ndr/ndr_misc.c new file mode 100644 index 0000000000..2e5ecbac6d --- /dev/null +++ b/librpc/ndr/ndr_misc.c @@ -0,0 +1,46 @@ +/* + Unix SMB/CIFS implementation. + + UUID/GUID/policy_handle functions + + Copyright (C) Andrew Tridgell 2003. + Copyright (C) Stefan (metze) Metzmacher 2004. + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "system/network.h" +#include "librpc/ndr/libndr.h" + +_PUBLIC_ void ndr_print_in_addr(struct ndr_print *ndr, const char *name, const struct in_addr *_ip) +{ + struct in_addr ip; + + ip.s_addr = htonl(_ip->s_addr); + + ndr->print(ndr, "%-25s: %s", name, inet_ntoa(ip)); +} + +_PUBLIC_ void ndr_print_GUID(struct ndr_print *ndr, const char *name, const struct GUID *guid) +{ + ndr->print(ndr, "%-25s: %s", name, GUID_string(ndr, guid)); +} + +bool ndr_syntax_id_equal(const struct ndr_syntax_id *i1, + const struct ndr_syntax_id *i2) +{ + return GUID_equal(&i1->uuid, &i2->uuid) + && (i1->if_version == i2->if_version); +} diff --git a/librpc/ndr/uuid.c b/librpc/ndr/uuid.c new file mode 100644 index 0000000000..1e6ee0a3db --- /dev/null +++ b/librpc/ndr/uuid.c @@ -0,0 +1,227 @@ +/* + Unix SMB/CIFS implementation. + + UUID/GUID functions + + Copyright (C) Theodore Ts'o 1996, 1997, + Copyright (C) Jim McDonough 2002. + 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 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 <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "librpc/ndr/libndr.h" + +/** + build a GUID from a string +*/ +_PUBLIC_ NTSTATUS GUID_from_string(const char *s, struct GUID *guid) +{ + NTSTATUS status = NT_STATUS_INVALID_PARAMETER; + uint32_t time_low; + uint32_t time_mid, time_hi_and_version; + uint32_t clock_seq[2]; + uint32_t node[6]; + int i; + + if (s == NULL) { + return NT_STATUS_INVALID_PARAMETER; + } + + if (11 == sscanf(s, "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", + &time_low, &time_mid, &time_hi_and_version, + &clock_seq[0], &clock_seq[1], + &node[0], &node[1], &node[2], &node[3], &node[4], &node[5])) { + status = NT_STATUS_OK; + } else if (11 == sscanf(s, "{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}", + &time_low, &time_mid, &time_hi_and_version, + &clock_seq[0], &clock_seq[1], + &node[0], &node[1], &node[2], &node[3], &node[4], &node[5])) { + status = NT_STATUS_OK; + } + + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + guid->time_low = time_low; + guid->time_mid = time_mid; + guid->time_hi_and_version = time_hi_and_version; + guid->clock_seq[0] = clock_seq[0]; + guid->clock_seq[1] = clock_seq[1]; + for (i=0;i<6;i++) { + guid->node[i] = node[i]; + } + + return NT_STATUS_OK; +} + +/** + build a GUID from a string +*/ +_PUBLIC_ NTSTATUS NS_GUID_from_string(const char *s, struct GUID *guid) +{ + NTSTATUS status = NT_STATUS_INVALID_PARAMETER; + uint32_t time_low; + uint32_t time_mid, time_hi_and_version; + uint32_t clock_seq[2]; + uint32_t node[6]; + int i; + + if (s == NULL) { + return NT_STATUS_INVALID_PARAMETER; + } + + if (11 == sscanf(s, "%08x-%04x%04x-%02x%02x%02x%02x-%02x%02x%02x%02x", + &time_low, &time_mid, &time_hi_and_version, + &clock_seq[0], &clock_seq[1], + &node[0], &node[1], &node[2], &node[3], &node[4], &node[5])) { + status = NT_STATUS_OK; + } + + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + guid->time_low = time_low; + guid->time_mid = time_mid; + guid->time_hi_and_version = time_hi_and_version; + guid->clock_seq[0] = clock_seq[0]; + guid->clock_seq[1] = clock_seq[1]; + for (i=0;i<6;i++) { + guid->node[i] = node[i]; + } + + return NT_STATUS_OK; +} + +/** + * generate a random GUID + */ +_PUBLIC_ struct GUID GUID_random(void) +{ + struct GUID guid; + + generate_random_buffer((uint8_t *)&guid, sizeof(guid)); + guid.clock_seq[0] = (guid.clock_seq[0] & 0x3F) | 0x80; + guid.time_hi_and_version = (guid.time_hi_and_version & 0x0FFF) | 0x4000; + + return guid; +} + +/** + * generate an empty GUID + */ +_PUBLIC_ struct GUID GUID_zero(void) +{ + struct GUID guid; + + ZERO_STRUCT(guid); + + return guid; +} + +_PUBLIC_ bool GUID_all_zero(const struct GUID *u) +{ + if (u->time_low != 0 || + u->time_mid != 0 || + u->time_hi_and_version != 0 || + u->clock_seq[0] != 0 || + u->clock_seq[1] != 0 || + !all_zero(u->node, 6)) { + return false; + } + return true; +} + +_PUBLIC_ bool GUID_equal(const struct GUID *u1, const struct GUID *u2) +{ + if (u1->time_low != u2->time_low || + u1->time_mid != u2->time_mid || + u1->time_hi_and_version != u2->time_hi_and_version || + u1->clock_seq[0] != u2->clock_seq[0] || + u1->clock_seq[1] != u2->clock_seq[1] || + memcmp(u1->node, u2->node, 6) != 0) { + return false; + } + return true; +} + +_PUBLIC_ int GUID_compare(const struct GUID *u1, const struct GUID *u2) +{ + if (u1->time_low != u2->time_low) { + return u1->time_low - u2->time_low; + } + + if (u1->time_mid != u2->time_mid) { + return u1->time_mid - u2->time_mid; + } + + if (u1->time_hi_and_version != u2->time_hi_and_version) { + return u1->time_hi_and_version - u2->time_hi_and_version; + } + + if (u1->clock_seq[0] != u2->clock_seq[0]) { + return u1->clock_seq[0] - u2->clock_seq[0]; + } + + if (u1->clock_seq[1] != u2->clock_seq[1]) { + return u1->clock_seq[1] - u2->clock_seq[1]; + } + + return memcmp(u1->node, u2->node, 6); +} + +/** + its useful to be able to display these in debugging messages +*/ +_PUBLIC_ char *GUID_string(TALLOC_CTX *mem_ctx, const struct GUID *guid) +{ + return talloc_asprintf(mem_ctx, + "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", + guid->time_low, guid->time_mid, + guid->time_hi_and_version, + guid->clock_seq[0], + guid->clock_seq[1], + guid->node[0], guid->node[1], + guid->node[2], guid->node[3], + guid->node[4], guid->node[5]); +} + +_PUBLIC_ char *GUID_string2(TALLOC_CTX *mem_ctx, const struct GUID *guid) +{ + char *ret, *s = GUID_string(mem_ctx, guid); + ret = talloc_asprintf(mem_ctx, "{%s}", s); + talloc_free(s); + return ret; +} + +_PUBLIC_ char *NS_GUID_string(TALLOC_CTX *mem_ctx, const struct GUID *guid) +{ + return talloc_asprintf(mem_ctx, + "%08x-%04x%04x-%02x%02x%02x%02x-%02x%02x%02x%02x", + guid->time_low, guid->time_mid, + guid->time_hi_and_version, + guid->clock_seq[0], + guid->clock_seq[1], + guid->node[0], guid->node[1], + guid->node[2], guid->node[3], + guid->node[4], guid->node[5]); +} + +_PUBLIC_ bool policy_handle_empty(struct policy_handle *h) +{ + return (h->handle_type == 0 && GUID_all_zero(&h->uuid)); +} 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 @@ +<?xml version="1.0" encoding="iso-8859-1"?> +<!DOCTYPE refentry PUBLIC "-//Samba-Team//DTD DocBook V4.2-Based Variant V1.0//EN" "http://www.samba.org/samba/DTD/samba-doc"> +<refentry id="ndrdump.1"> + +<refmeta> + <refentrytitle>ndrdump</refentrytitle> + <manvolnum>1</manvolnum> +</refmeta> + + +<refnamediv> + <refname>ndrdump</refname> + <refpurpose>DCE/RPC Packet Parser and Dumper</refpurpose> +</refnamediv> + +<refsynopsisdiv> + <cmdsynopsis> + <command>ndrdump</command> + <arg choice="opt">-c context</arg> + <arg choice="req">pipe</arg> + <arg choice="req">function</arg> + <arg choice="req">in|out</arg> + <arg choice="req">filename</arg> + </cmdsynopsis> + <cmdsynopsis> + <command>ndrdump</command> + <arg choice="opt">pipe</arg> + </cmdsynopsis> + <cmdsynopsis> + <command>ndrdump</command> + </cmdsynopsis> +</refsynopsisdiv> + +<refsect1> + <title>DESCRIPTION</title> + + <para>ndrdump tries to parse the specified <replaceable>filename</replaceable> + using Samba's parser for the specified pipe and function. The + third argument should be + either <emphasis>in</emphasis> or <emphasis>out</emphasis>, depending + on whether the data should be parsed as a request or a reply.</para> + + <para>Running ndrdump without arguments will list the pipes for which + parsers are available.</para> + + <para>Running ndrdump with one argument will list the functions that + Samba can parse for the specified pipe.</para> + + <para>The primary function of ndrdump is debugging Samba's internal + DCE/RPC parsing functions. The file being parsed is usually + one exported by wiresharks <quote>Export selected packet bytes</quote> + function.</para> + + <para>The context argument can be used to load context data from the request + packet when parsing reply packets (such as array lengths).</para> + +</refsect1> + +<refsect1> + <title>VERSION</title> + + <para>This man page is correct for version 4.0 of the Samba suite.</para> +</refsect1> + +<refsect1> + <title>SEE ALSO</title> + + <para>wireshark, pidl</para> + +</refsect1> + +<refsect1> + <title>AUTHOR</title> + + <para>This utility is part of the <ulink url="http://www.samba.org/">Samba</ulink> suite, which is developed by the global <ulink url="http://www.samba.org/samba/team/">Samba Team</ulink>.</para> + + <para>ndrdump was written by Andrew Tridgell. </para> + + <para>This manpage was written by Jelmer Vernooij. </para> + +</refsect1> + +</refentry> 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 <http://www.gnu.org/licenses/>. +*/ + +#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;i<p->num_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;i<p->num_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, "<pipe|uuid> <function> <inout> [<filename>]"); + + 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; +} |