1
0
mirror of https://github.com/samba-team/samba.git synced 2024-12-24 21:34:56 +03:00
samba-mirror/source3/rpc_client/wsp_cli.c
Noel Power 885850b6aa s3/rpc_client: Fix array offset check
Previous to this commit we were modifying the offset before
the array offset check. This was causing a spurious debug
message indicating the offset was out of bounds. An second
problem is that upon detecting the error we don't exit the loop.
A third problem was that when reading the offset the check
didn't cater for the size of the integer address about to be read.

This commit moves the offset check to before the first read,
additionally when an error is detected now we actually exit the loop
and the offset have been corrected to include the size of the
integer to be read

BUG: https://bugzilla.samba.org/show_bug.cgi?id=15579
Signed-off-by: Noel Power <noel.power@suse.com>
Reviewed-by: Volker Lendecke <vl@samba.org>

Autobuild-User(master): Volker Lendecke <vl@samba.org>
Autobuild-Date(master): Sat Feb 17 17:58:43 UTC 2024 on atb-devel-224
2024-02-17 17:58:43 +00:00

2222 lines
55 KiB
C

/*
* Unix SMB/CIFS implementation.
*
* Window Search Service
*
* Copyright (c) Noel Power
*
* 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 "client.h"
#include "rpc_client/wsp_cli.h"
#include "rpc_client/rpc_client.h"
#include "param/param.h"
#include "auth/credentials/credentials.h"
#include <tevent.h>
#include <util/tevent_ntstatus.h>
#include "libcli/tstream_binding_handle/tstream_binding_handle.h"
#include "lib/tsocket/tsocket.h"
#include "librpc/wsp/wsp_util.h"
#include "librpc/gen_ndr/ndr_wsp.h"
#include "rpc_client/cli_pipe.h"
#include "libcli/smb/smbXcli_base.h"
#define MSG_HDR_SIZE 16
#define USED 1
/*
* 32-bit Windows XP operating system, 32-bit Windows Server 2003 operating
* system, 32-bit Windows Home Server server software, 32-bit Windows Vista
* with Windows Search 4.0, 32-bit Windows Server 2003 with Windows
* Search 4.0. All of these versions of Windows are running
* Windows Search 4.0.
*/
static const uint32_t CLIENTVERSION = 0x00010700;
/*
* DBPROP_CI_SCOPE_FLAGS
* containing QUERY_DEEP
* QUERY_DEEP (0x1) indicates that files in the scope directory and all
* subdirectories are included in the results. If clear, only files in
* the scope directory are included in the results.
*/
static int32_t scope_flags_vector[] = {0x00000001};
/*
* Search everywhere "\\" is the root scope
*/
static const char * root_scope_string_vector[] = {"\\"};
/* sets sensible defaults */
static void init_wsp_prop(struct wsp_cdbprop *prop)
{
*prop = (struct wsp_cdbprop){0};
prop->colid.ekind = DBKIND_GUID_PROPID;
}
static bool create_restriction_array(TALLOC_CTX *ctx,
struct wsp_crestriction **pelements,
uint32_t nnodes)
{
struct wsp_crestriction *elements = talloc_zero_array(ctx,
struct wsp_crestriction,
nnodes);
if (elements == NULL) {
return false;
}
*pelements = elements;
return true;
}
static bool create_noderestriction(TALLOC_CTX *ctx,
struct wsp_cnoderestriction *pnode,
uint32_t nnodes)
{
bool ok;
pnode->cnode = nnodes;
ok = create_restriction_array(ctx, &pnode->panode, nnodes);
return ok;
}
static bool fill_sortarray(TALLOC_CTX *ctx, struct wsp_csort **dest,
struct wsp_csort *src, uint32_t num)
{
uint32_t i;
struct wsp_csort *psort = talloc_zero_array(ctx, struct wsp_csort,
num);
if (psort == NULL) {
return false;
}
for (i = 0; i < num; i++) {
psort[i] = src[i];
}
*dest = psort;
return true;
}
static bool set_fullpropspec(TALLOC_CTX *ctx, struct wsp_cfullpropspec *prop,
const char* propname, uint32_t kind)
{
struct GUID guid = {0};
const struct full_propset_info *prop_info = NULL;
prop_info = get_propset_info_with_guid(propname, &guid);
if (!prop_info) {
DBG_ERR("Failed to handle property named %s\n",
propname);
return false;
}
prop->guidpropset = guid;
prop->ulkind = kind;
if (kind == PRSPEC_LPWSTR) {
prop->name_or_id.propname.vstring = talloc_strdup(ctx,
propname);
if (prop->name_or_id.propname.vstring == NULL) {
DBG_ERR("out of memory");
return false;
}
prop->name_or_id.propname.len = strlen(propname);
} else {
prop->name_or_id.prspec = prop_info->id;
}
return true;
}
struct binding
{
uint32_t status_off;
uint32_t value_off;
uint32_t len_off;
};
static bool set_ctablecolumn(TALLOC_CTX *ctx, struct wsp_ctablecolumn *tablecol,
const char* propname, struct binding *offsets,
uint32_t value_size)
{
struct wsp_cfullpropspec *prop = &tablecol->propspec;
if (!set_fullpropspec(ctx, prop, propname, PRSPEC_PROPID)) {
return false;
}
tablecol->vtype =VT_VARIANT ;
tablecol->aggregateused = USED;
tablecol->valueused = USED;
tablecol->valueoffset.value = offsets->value_off;
tablecol->valuesize.value = value_size;
tablecol->statusused = USED;
tablecol->statusoffset.value = offsets->status_off;
tablecol->lengthused = USED;
tablecol->lengthoffset.value = offsets->len_off;
return true;
}
static bool fill_uint32_vec(TALLOC_CTX* ctx,
uint32_t **pdest,
uint32_t* ivector, uint32_t elems)
{
uint32_t i;
uint32_t *dest = talloc_zero_array(ctx, uint32_t, elems);
if (dest == NULL) {
return false;
}
for ( i = 0; i < elems; i++ ) {
dest[ i ] = ivector[ i ];
}
*pdest = dest;
return true;
}
static bool init_propset1(TALLOC_CTX* tmp_ctx,
struct wsp_cdbpropset *propertyset)
{
uint32_t i;
GUID_from_string(DBPROPSET_FSCIFRMWRK_EXT,
&propertyset->guidpropertyset);
propertyset->cproperties = 4;
propertyset->aprops =
talloc_zero_array(tmp_ctx, struct wsp_cdbprop,
propertyset->cproperties);
if (propertyset->aprops == NULL) {
return false;
}
/* initialise first 4 props */
for( i = 0; i < propertyset->cproperties; i++) {
init_wsp_prop(&propertyset->aprops[i]);
}
/*
* see MS-WSP 2.2.1.31.1 & 4.1 Protocol examples, Example 1
* and also as seen in various windows network traces
* set value prop[0] - 'catalog to search'
*/
propertyset->aprops[0].dbpropid = DBPROP_CI_CATALOG_NAME;
/* The name of the Catalog to Query */
set_variant_lpwstr(tmp_ctx, &propertyset->aprops[0].vvalue,
"Windows\\SystemIndex");
/*
* set value prop[1] 'Regular Query'
*/
propertyset->aprops[1].dbpropid = DBPROP_CI_QUERY_TYPE;
set_variant_i4(tmp_ctx, &propertyset->aprops[1].vvalue,
CINORMAL);
/*
* set value prop[2] 'search subfolders'
*/
propertyset->aprops[2].dbpropid = DBPROP_CI_SCOPE_FLAGS;
set_variant_i4_vector(tmp_ctx, &propertyset->aprops[2].vvalue,
scope_flags_vector, ARRAY_SIZE(scope_flags_vector));
/*
* set value prop[3] 'root scope'
*/
propertyset->aprops[3].dbpropid = DBPROP_CI_INCLUDE_SCOPES;
set_variant_lpwstr_vector(tmp_ctx,
&propertyset->aprops[3].vvalue,
root_scope_string_vector,
ARRAY_SIZE(root_scope_string_vector));
return true;
}
static bool init_propset2(TALLOC_CTX* tmp_ctx,
struct wsp_cdbpropset *propertyset,
const char* server)
{
uint32_t i;
GUID_from_string(DBPROPSET_CIFRMWRKCORE_EXT,
&propertyset->guidpropertyset);
propertyset->cproperties = 1;
propertyset->aprops =
talloc_zero_array(tmp_ctx, struct wsp_cdbprop,
propertyset->cproperties);
if (propertyset->aprops == NULL) {
return false;
}
/* initialise first 1 props */
for( i = 0; i < propertyset->cproperties; i++) {
init_wsp_prop(&propertyset->aprops[i]);
}
/*
* see MS-WSP 2.2.1.31.1 & 4.1 Protocol examples, Example 1
* and also as seen in various windows network traces
* set value prop[0] - 'machines to search'
*/
propertyset->aprops[0].dbpropid = DBPROP_MACHINE;
set_variant_bstr(tmp_ctx, &propertyset->aprops[0].vvalue,
server);
return true;
}
static bool init_apropset0(TALLOC_CTX* tmp_ctx,
struct wsp_cdbpropset *propertyset)
{
uint32_t i;
GUID_from_string(DBPROPSET_MSIDXS_ROWSETEXT,
&propertyset->guidpropertyset);
propertyset->cproperties = 7;
propertyset->aprops =
talloc_zero_array(tmp_ctx, struct wsp_cdbprop,
propertyset->cproperties);
if (propertyset->aprops == NULL) {
return false;
}
/* initialise props */
for( i = 0; i < propertyset->cproperties; i++) {
init_wsp_prop(&propertyset->aprops[i]);
}
/*
* see MS-WSP 2.2.1.31.1 & 4.1 Protocol examples, Example 1
* set value prop[0]
* MSIDXSPROP_ROWSETQUERYSTATUS - 'ignored'
*/
propertyset->aprops[0].dbpropid = MSIDXSPROP_ROWSETQUERYSTATUS;
set_variant_i4(tmp_ctx, &propertyset->aprops[0].vvalue, 0x00000000);
/*
* set value prop[1]
* MSIDXSPROP_COMMAND_LOCALE_STRING - 'EN'
*/
propertyset->aprops[1].dbpropid = MSIDXSPROP_COMMAND_LOCALE_STRING;
set_variant_bstr(tmp_ctx, &propertyset->aprops[1].vvalue,
"en-us");
/*
* set value prop[2]
* MSIDXSPROP_QUERY_RESTRICTION - 'ignored'
*/
propertyset->aprops[2].dbpropid = MSIDXSPROP_QUERY_RESTRICTION;
set_variant_bstr(tmp_ctx, &propertyset->aprops[2].vvalue,
"");
/*
* set value prop[3]
* MSIDXSPROP_PARSE_TREE - 'ignored'
*/
propertyset->aprops[3].dbpropid = MSIDXSPROP_PARSE_TREE;
set_variant_bstr(tmp_ctx, &propertyset->aprops[3].vvalue,
"");
/*
* set value prop[4]
* MSIDXSPROP_MAX_RANK - 'ignored'
*/
propertyset->aprops[4].dbpropid = MSIDXSPROP_MAX_RANK;
set_variant_i4(tmp_ctx, &propertyset->aprops[4].vvalue, 0x00000000);
/*
* set value prop[5]
* MSIDXSPROP_RESULTS_FOUND - 'ignored'
*/
propertyset->aprops[5].dbpropid = MSIDXSPROP_RESULTS_FOUND;
set_variant_i4(tmp_ctx, &propertyset->aprops[5].vvalue, 0x00000000);
/*
* set value prop[6]
* ? - '' (unknown property id)
*/
propertyset->aprops[6].dbpropid = 0x00000008;
set_variant_i4(tmp_ctx, &propertyset->aprops[6].vvalue, 0x00000000);
return true;
}
static bool init_apropset1(TALLOC_CTX* tmp_ctx,
struct wsp_cdbpropset *propertyset)
{
uint32_t i;
GUID_from_string(DBPROPSET_QUERYEXT,
&propertyset->guidpropertyset);
propertyset->cproperties = 11;
propertyset->aprops =
talloc_zero_array(tmp_ctx, struct wsp_cdbprop,
propertyset->cproperties);
if (propertyset->aprops == NULL) {
return false;
}
/* init properties */
for( i = 0; i < propertyset->cproperties; i++) {
init_wsp_prop(&propertyset->aprops[i]);
}
/*
* see MS-WSP 2.2.1.31.1 & 4.1 Protocol examples, Example 1
* set value prop[0]
* DBPROP_USECONTENTINDEX - 'forced use of the full text index
* is false.'
*/
propertyset->aprops[0].dbpropid = DBPROP_USECONTENTINDEX;
set_variant_vt_bool(tmp_ctx, &propertyset->aprops[0].vvalue, false);
/*
* set value prop[1]
* DBPROP_DEFERNONINDEXEDTRIMMING - 'trimming of security
* results will not be deferred'
*/
propertyset->aprops[1].dbpropid = DBPROP_DEFERNONINDEXEDTRIMMING;
set_variant_vt_bool(tmp_ctx, &propertyset->aprops[1].vvalue, false);
/*
* set value prop[2]
* DBPROP_USEEXTENDEDDBTYPES - 'extended DB types are not used'
*/
propertyset->aprops[2].dbpropid = DBPROP_USEEXTENDEDDBTYPES;
set_variant_vt_bool(tmp_ctx, &propertyset->aprops[2].vvalue, false);
/*
* set value prop[3]
* DBPROP_IGNORENOISEONLYCLAUSES = 'full text clauses consisting
* entirely of noise words will
* result in an error being returned'
*/
propertyset->aprops[3].dbpropid = DBPROP_IGNORENOISEONLYCLAUSES;
set_variant_vt_bool(tmp_ctx, &propertyset->aprops[3].vvalue, false);
/*
* set value prop[4]
* DBPROP_GENERICOPTIONS_STRING - 'no generic options set'
*/
propertyset->aprops[4].dbpropid = DBPROP_GENERICOPTIONS_STRING;
set_variant_bstr(tmp_ctx, &propertyset->aprops[4].vvalue, "");
/*
* set value prop[5]
* DBPROP_DEFERCATALOGVERIFICATION - 'catalog verification is not
* deferred.'
*/
propertyset->aprops[5].dbpropid = DBPROP_DEFERCATALOGVERIFICATION;
set_variant_vt_bool(tmp_ctx, &propertyset->aprops[5].vvalue, false);
/*
* set value prop[6]
* DBPROP_IGNORESBRI - 'query can use the sort-by-rank index
* optimization'
*/
propertyset->aprops[6].dbpropid = DBPROP_IGNORESBRI;
set_variant_vt_bool(tmp_ctx, &propertyset->aprops[6].vvalue, false);
/*
* set value prop[7]
* DBPROP_GENERATEPARSETREE - 'a parse tree is not generated for
* debugging.'
*/
propertyset->aprops[7].dbpropid = DBPROP_GENERATEPARSETREE;
set_variant_vt_bool(tmp_ctx, &propertyset->aprops[7].vvalue, false);
/*
* set value prop[8]
* DBPROP_FREETEXTANYTERM - 'all terms from a FREETEXT clause
* appear in every matching document'
*/
propertyset->aprops[8].dbpropid = DBPROP_FREETEXTANYTERM;
set_variant_vt_bool(tmp_ctx, &propertyset->aprops[8].vvalue, false);
/*
* set value prop[9]
* DBPROP_FREETEXTUSESTEMMING - 'stemming is not used when interpreting
* a FREETEXT clause'
*/
propertyset->aprops[9].dbpropid = DBPROP_FREETEXTUSESTEMMING;
set_variant_vt_bool(tmp_ctx, &propertyset->aprops[9].vvalue, false);
/*
* set value prop[10]
* ? - ''
*/
propertyset->aprops[10].dbpropid = 0x0000000f; /* ??? */
set_variant_vt_bool(tmp_ctx, &propertyset->aprops[10].vvalue, false);
return true;
}
static bool init_apropset2(TALLOC_CTX* tmp_ctx,
struct wsp_cdbpropset *propertyset,
const char* server)
{
uint32_t i;
GUID_from_string(DBPROPSET_CIFRMWRKCORE_EXT,
&propertyset->guidpropertyset);
propertyset->cproperties = 1;
propertyset->aprops =
talloc_zero_array(tmp_ctx, struct wsp_cdbprop,
propertyset->cproperties);
if (propertyset->aprops == NULL) {
return false;
}
/* init properties */
for( i = 0; i < propertyset->cproperties; i++) {
init_wsp_prop(&propertyset->aprops[i]);
}
/*
* see MS-WSP 2.2.1.31.1 & 4.1 Protocol examples, Example 1
* and also as seen in various windows network traces
* set value prop[0]
* DBPROP_MACHINE - 'target server'
*/
propertyset->aprops[0].dbpropid = DBPROP_MACHINE;
set_variant_bstr(tmp_ctx, &propertyset->aprops[0].vvalue, server);
return true;
}
static bool init_apropset3(TALLOC_CTX* tmp_ctx,
struct wsp_cdbpropset *propertyset)
{
uint32_t i;
GUID_from_string(DBPROPSET_FSCIFRMWRK_EXT,
&propertyset->guidpropertyset);
propertyset->cproperties = 3;
propertyset->aprops =
talloc_zero_array(tmp_ctx, struct wsp_cdbprop,
propertyset->cproperties);
if (propertyset->aprops == NULL) {
return false;
}
/* init properties */
for( i = 0; i < propertyset->cproperties; i++) {
init_wsp_prop(&propertyset->aprops[i]);
}
/*
* see MS-WSP 2.2.1.31.1 & 4.1 Protocol examples, Example 1
* and also as seen in various windows network traces
* set value prop[0]
* DBPROP_CI_INCLUDE_SCOPES - 'search everywhere'
*/
propertyset->aprops[0].dbpropid = DBPROP_CI_INCLUDE_SCOPES;
set_variant_array_bstr(tmp_ctx, &propertyset->aprops[0].vvalue,
root_scope_string_vector,
ARRAY_SIZE(root_scope_string_vector));
/*
* set value prop[1]
* DBPROP_CI_SCOPE_FLAGS - 'QUERY_DEEP'
*/
propertyset->aprops[1].dbpropid = DBPROP_CI_SCOPE_FLAGS;
set_variant_array_i4(tmp_ctx, &propertyset->aprops[1].vvalue,
scope_flags_vector,
ARRAY_SIZE(scope_flags_vector));
/*
* set value prop[2]
* DBPROP_CI_CATALOG_NAME - 'index to use' (always the same)
*/
propertyset->aprops[2].dbpropid = DBPROP_CI_CATALOG_NAME;
set_variant_bstr(tmp_ctx, &propertyset->aprops[2].vvalue,
"Windows\\SystemIndex");
return true;
}
bool init_connectin_request(TALLOC_CTX *ctx,
struct wsp_request* request,
const char* clientmachine,
const char* clientuser,
const char* server)
{
enum ndr_err_code err;
struct connectin_propsets *props = NULL;
struct connectin_extpropsets *ext_props = NULL;
DATA_BLOB props_blob = data_blob_null;
struct ndr_push *ndr_props = NULL;
ndr_flags_type ndr_flags = NDR_SCALARS | NDR_BUFFERS;
bool result;
struct wsp_cpmconnectin *connectin =
&request->message.cpmconnect;
props = talloc_zero(ctx, struct connectin_propsets);
if (props == NULL) {
result = false;
DBG_ERR("out of memory\n");
goto out;
}
ext_props = talloc_zero(ctx, struct connectin_extpropsets) ;
if (ext_props == NULL) {
result = false;
DBG_ERR("out of memory\n");
goto out;
}
request->header.msg = CPMCONNECT;
connectin->iclientversion = CLIENTVERSION;
/*
* hmm just say the client is remote, if we
* are talking to windows it is, if not does
* it really matter?
*/
connectin->fclientisremote = 0x00000001;
connectin->machinename = clientmachine;
connectin->username = clientuser;
props->cpropsets = 2;
/* =================== */
/* set up PropertySet1 */
/* =================== */
if (!init_propset1(ctx, &props->propertyset1)) {
result = false;
DBG_ERR("initialising propset1 failed\n");
goto out;
}
/* =================== */
/* set up PropertySet2 */
/* =================== */
if (!init_propset2(ctx, &props->propertyset2, server)) {
result = false;
DBG_ERR("initialising propset2 failed\n");
goto out;
}
/* 4 ExtPropSets */
ext_props->cextpropset = 4;
ext_props->apropertysets = talloc_zero_array(ctx, struct wsp_cdbpropset,
ext_props->cextpropset);
if (ext_props->apropertysets == NULL) {
result = false;
DBG_ERR("out of memory\n");
goto out;
}
/* ======================= */
/* set up aPropertySets[0] */
/* ======================= */
if (!init_apropset0(ctx, &ext_props->apropertysets[0])) {
result = false;
DBG_ERR("initialisation of apropset0 failed\n");
goto out;
}
/* ======================= */
/* set up aPropertySets[1] */
/* ======================= */
if (!init_apropset1(ctx, &ext_props->apropertysets[1])) {
result = false;
DBG_ERR("initialisation of apropset1 failed\n");
goto out;
}
/* ======================= */
/* set up aPropertySets[2] */
/* ======================= */
if (!init_apropset2(ctx, &ext_props->apropertysets[2], server)) {
result = false;
DBG_ERR("initialisation of apropset2 failed\n");
goto out;
}
/* ======================= */
/* set up aPropertySets[3] */
/* ======================= */
if (!init_apropset3(ctx, &ext_props->apropertysets[3])) {
result = false;
DBG_ERR("initialisation of apropset3 failed\n");
goto out;
}
/* we also have to fill the opaque blobs that contain the propsets */
ndr_props = ndr_push_init_ctx(ctx);
if (ndr_props == NULL) {
result = false;
DBG_ERR("out of memory\n");
goto out;
}
/* first connectin_propsets */
err = ndr_push_connectin_propsets(ndr_props, ndr_flags, props);
if (err) {
DBG_ERR("Failed to push propset, error %d\n", err);
result = false;
goto out;
}
props_blob = ndr_push_blob(ndr_props);
connectin->cbblob1 = props_blob.length;
connectin->propsets = talloc_zero_array(ctx, uint8_t,
connectin->cbblob1);
if (connectin->propsets == NULL) {
result = false;
DBG_ERR("out of memory\n");
goto out;
}
memcpy(connectin->propsets, props_blob.data, props_blob.length);
/* then connectin_extpropsets */
TALLOC_FREE(ndr_props);
ndr_props = ndr_push_init_ctx(ctx);
if (ndr_props == NULL) {
result = false;
DBG_ERR("out of memory\n");
goto out;
}
err = ndr_push_connectin_extpropsets(ndr_props, ndr_flags, ext_props);
if (err) {
DBG_ERR("Failed to push extpropset, error %d\n", err);
result = false;
goto out;
}
props_blob = ndr_push_blob(ndr_props);
connectin->cbblob2 = props_blob.length;
connectin->extpropsets = talloc_zero_array(ctx, uint8_t,
connectin->cbblob2);
if (connectin->extpropsets == NULL) {
result = false;
DBG_ERR("out of memory\n");
goto out;
}
memcpy(connectin->extpropsets, props_blob.data, props_blob.length);
TALLOC_FREE(ndr_props);
result = true;
out:
return result;
}
void create_seekat_getrows_request(TALLOC_CTX * ctx,
struct wsp_request* request,
uint32_t cursor,
uint32_t bookmark,
uint32_t skip,
uint32_t rows,
uint32_t cbreserved,
uint32_t ulclientbase,
uint32_t cbrowwidth,
uint32_t fbwdfetch)
{
struct wsp_cpmgetrowsin *getrows =
&request->message.cpmgetrows;
/* msg type */
request->header.msg = CPMGETROWS;
/* position */
getrows->hcursor = cursor;
/* max no. rows to receive */
getrows->crowstotransfer = rows;
/*
* size (length) of row in bytes, determined from value set
* by CPMSetBindings message
*/
getrows->cbrowWidth = cbrowwidth;
/*
* according to we should calculate this (see MS-WSP 3.2.4.2.4)
* but it seems window always sets this to the max 16KB limit
* (most likely when any row value is variable size e.g. like a
* string/path)
*/
getrows->cbreadbuffer = 0x00004000;
/*
* base value of buffer pointer
*/
getrows->ulclientbase = ulclientbase;
getrows->cbreserved = cbreserved;
/* fetch rows in forward order */
getrows->fbwdfetch = fbwdfetch;
/* eRowSeekAt */
getrows->etype = EROWSEEKAT;
/* we don't handle chapters */
getrows->chapt = 0;
/* CRowsSeekAt (MS-WSP 2.2.1.37) */
getrows->seekdescription.crowseekat.bmkoffset = bookmark;
getrows->seekdescription.crowseekat.cskip = skip;
getrows->seekdescription.crowseekat.hregion = 0;
}
static bool extract_rowbuf_variable_type(TALLOC_CTX *ctx,
uint16_t type,
uint64_t offset,
DATA_BLOB *rows_buf, uint32_t len,
struct wsp_cbasestoragevariant *val)
{
enum ndr_err_code err;
struct ndr_pull *ndr_pull = NULL;
ndr_flags_type ndr_flags = NDR_SCALARS | NDR_BUFFERS;
DATA_BLOB variant_blob = data_blob_null;
if (offset >= rows_buf->length) {
DBG_ERR("offset %"PRIu64" outside buffer range (buf len - %zu)",
offset,
rows_buf->length);
return false;
}
variant_blob.data = rows_buf->data + offset;
variant_blob.length = len;
ndr_pull = ndr_pull_init_blob(&variant_blob, ctx);
if (ndr_pull == NULL) {
DBG_ERR("out of memory\n");
return false;
}
switch (type) {
case VT_LPWSTR: {
const char *string = NULL;
ndr_set_flags(&ndr_pull->flags, LIBNDR_FLAG_STR_NULLTERM);
err = ndr_pull_string(ndr_pull, ndr_flags, &string);
if (err) {
DBG_ERR("error unmarshalling string from %p\n", variant_blob.data );
} else {
DBG_INFO("\tstring val ->%s<-\n", string );
val->vtype = type;
val->vvalue.vt_lpwstr.value = string;
}
break;
}
default:
DBG_ERR("#FIXME Unhandled variant type %s\n", get_vtype_name(type));
break;
}
return true;
}
static bool convert_variant_array_to_vector(TALLOC_CTX *ctx,
uint64_t count,
struct wsp_cbasestoragevariant **variant_array,
struct wsp_cbasestoragevariant *outval)
{
uint64_t i;
uint16_t vtype;
union variant_types vvalue = {0};
vtype = variant_array[0]->vtype;
if (outval == NULL) {
return false;
}
if (count) {
switch (vtype) {
case VT_BSTR:
vvalue.vt_bstr_v.vvector_elements = count;
vvalue.vt_bstr_v.vvector_data =
talloc_zero_array(ctx,
struct vt_bstr, count);
if (vvalue.vt_bstr_v.vvector_data == NULL) {
return false;
}
break;
case VT_LPWSTR:
vvalue.vt_lpwstr_v.vvector_elements = count;
vvalue.vt_lpwstr_v.vvector_data =
talloc_zero_array(ctx,
struct vt_lpwstr, count);
if (vvalue.vt_lpwstr_v.vvector_data == NULL) {
return false;
}
break;
case VT_COMPRESSED_LPWSTR:
vvalue.vt_compresseed_lpwstr_v.vvector_elements
= count;
vvalue.vt_compresseed_lpwstr_v.vvector_data =
talloc_zero_array(ctx,
struct vt_compressed_lpwstr,
count);
if (vvalue.vt_compresseed_lpwstr_v.vvector_data == NULL) {
return false;
}
break;
default:
DBG_ERR("Can't convert array of %s to VECTOR\n",
get_vtype_name(vtype));
return false;
}
}
for (i = 0; i < count; i++) {
if (variant_array[i]->vtype != vtype) {
DBG_ERR("array item type %s doesn't match extpected "
"type %s\n",
get_vtype_name(variant_array[i]->vtype),
get_vtype_name(vtype));
return false;
}
switch (variant_array[i]->vtype) {
case VT_BSTR:
vvalue.vt_bstr_v.vvector_data[i]
= variant_array[i]->vvalue.vt_bstr;
break;
case VT_LPWSTR:
vvalue.vt_lpwstr_v.vvector_data[i]
= variant_array[i]->vvalue.vt_lpwstr;
break;
case VT_COMPRESSED_LPWSTR:
vvalue.vt_compresseed_lpwstr_v.vvector_data[i]
= variant_array[i]->vvalue.vt_compressed_lpwstr;
break;
default:
DBG_ERR("Can't convert array of %s to VECTOR\n",
get_vtype_name(vtype));
return false;
}
}
outval->vtype = vtype | VT_VECTOR;
outval->vvalue = vvalue;
return true;
}
/*
* get the addresses in rowbuf of variants to read from
* pvec_address will point to addresses,
* an array of n elements for a vector or array of 1 element
* if non-vector item.
*
* addresses stored in pvec_address
*
*/
static enum ndr_err_code extract_variant_addresses(TALLOC_CTX *ctx,
struct wsp_ctablevariant *tablevar,
bool is_64bit,
struct ndr_pull *ndr_pull,
ndr_flags_type flags,
uint64_t baseaddress,
DATA_BLOB *rows_buf,
uint64_t *pcount,
uint64_t **pvec_address)
{
bool is_vector = tablevar->vtype & VT_VECTOR;
uint64_t count;
uint64_t addr;
uint64_t *vec_address = NULL;
enum ndr_err_code err;
/* read count (only if this is a vector) */
if (is_vector) {
if (is_64bit) {
err = ndr_pull_udlong(ndr_pull,
flags,
&count);
if (err) {
DBG_ERR("Failed to extract count\n");
goto out;
}
} else {
uint32_t count_32;
err = ndr_pull_uint32(ndr_pull,
flags,
&count_32);
if (err) {
DBG_ERR("Failed to extract count\n");
goto out;
}
count = (uint64_t)count_32;
}
} else {
count = 1;
}
/* ensure count is at least within buffer range */
if (count >= MAX_ROW_BUFF_SIZE || count >= rows_buf->length) {
DBG_ERR("count %"PRIu64" either exceeds max buffer size "
"or buffer size (%zu)",
count, rows_buf->length);
err = NDR_ERR_VALIDATE;
goto out;
}
/* read address */
if (is_64bit) {
err = ndr_pull_udlong(ndr_pull,
flags,
&addr);
if (err) {
DBG_ERR("Failed to extract address\n");
goto out;
}
} else {
uint32_t addr_32;
err = ndr_pull_uint32(ndr_pull, flags, &addr_32);
if (err) {
DBG_ERR("Failed to extract address\n");
goto out;
}
addr = addr_32;
}
if ((addr - baseaddress) >= rows_buf->length) {
DBG_ERR("offset %"PRIu64" outside buffer range "
"(buf len - %zu)\n",
addr - baseaddress,
rows_buf->length);
err = NDR_ERR_VALIDATE;
goto out;
}
vec_address = talloc_zero_array(ctx,
uint64_t, count);
if (vec_address == NULL) {
err = NDR_ERR_ALLOC;
goto out;
}
/*
* non vector case addr points to value
* otherwise addr points to list of addresses
* for the values in vector
*/
if (is_vector == false) {
vec_address[0] = addr;
} else {
uint64_t array_offset = addr - baseaddress;
uint64_t i;
uint32_t intsize;
if (is_64bit) {
intsize = 8;
} else {
intsize = 4;
}
if (array_offset >= MAX_ROW_BUFF_SIZE
|| array_offset >= rows_buf->length) {
DBG_ERR("offset %"PRIu64" either exceeds max buf size "
"or buffer size (%zu)",
array_offset, rows_buf->length);
err = NDR_ERR_VALIDATE;
goto out;
}
/* addr points to a list of int32 or int64 addresses */
for (i = 0; i < count; i++) {
/*
* read the addresses of the vector elements
* note: we can safely convert the uint64_t
* values here to uint32_t values as
* we are sure they are within range
* due to previous checks above.
*/
if (smb_buffer_oob((uint32_t)rows_buf->length,
(uint32_t)array_offset,
intsize)) {
DBG_ERR("offset %"PRIu64" will be outside "
"buffer range (buf len - %zu) after "
"reading %s address\n",
array_offset,
rows_buf->length,
is_64bit ? "64 bit" : "32 bit");
err = NDR_ERR_VALIDATE;
goto out;
}
if (is_64bit) {
vec_address[i] =
PULL_LE_I64(rows_buf->data,
array_offset);
} else {
vec_address[i] =
(uint32_t)PULL_LE_I32(rows_buf->data,
array_offset);
}
array_offset += intsize;
}
}
err = NDR_ERR_SUCCESS;
*pcount = count;
*pvec_address = vec_address;
out:
return err;
}
static enum ndr_err_code extract_crowvariant_variable(TALLOC_CTX *ctx,
struct wsp_ctablevariant *tablevar,
bool is_64bit,
struct ndr_pull *ndr_pull,
ndr_flags_type flags,
uint64_t baseaddress,
DATA_BLOB *rows_buf,
uint32_t len,
struct wsp_cbasestoragevariant *val)
{
enum ndr_err_code err;
bool is_vector = tablevar->vtype & VT_VECTOR;
uint64_t count = 0;
uint64_t *vec_address = NULL;
struct wsp_cbasestoragevariant **variant_array = NULL;
int i;
err = extract_variant_addresses(ctx,
tablevar,
is_64bit,
ndr_pull,
flags,
baseaddress,
rows_buf,
&count,
&vec_address);
if (err) {
DBG_ERR("Failed to extract address and/or count\n");
goto out;
}
variant_array = talloc_zero_array(ctx,
struct wsp_cbasestoragevariant*,
count);
if (variant_array == NULL) {
err = NDR_ERR_ALLOC;
goto out;
}
if (is_vector == false) {
variant_array[0] = val;
} else {
for (i = 0; i < count; i++) {
variant_array[i] = talloc_zero(ctx,
struct wsp_cbasestoragevariant);
if (variant_array[i] == NULL) {
err = NDR_ERR_ALLOC;
goto out;
}
}
}
for (i = 0; i < count; i++) {
uint32_t tmplen = len;
uint64_t buf_offset;
buf_offset = vec_address[i] - baseaddress;
if (buf_offset >= rows_buf->length) {
DBG_ERR("offset %"PRIu64" outside buffer range "
"(buf len - %zu)\n",
buf_offset,
rows_buf->length);
err = NDR_ERR_VALIDATE;
goto out;
}
if (is_64bit
&& (tablevar->vtype & ~(VT_VECTOR)) == VT_LPWSTR) {
/*
* we can't trust len if 64 bit mode
* (in 32 bit mode the length reported at len offset
* seem consistent and correct)
* So in this case instead of using the len
* at len offset we just use the full buffer
* from the point the value is stored at
* till the end of the buffer
*/
tmplen = rows_buf->length - buf_offset;
}
if (!extract_rowbuf_variable_type(ctx,
tablevar->vtype & ~VT_VECTOR,
buf_offset,
rows_buf,
tmplen,
variant_array[i])) {
err = NDR_ERR_VALIDATE;
goto out;
}
}
if (is_vector) {
if (!convert_variant_array_to_vector(ctx,
count,
variant_array,
val)) {
err = NDR_ERR_VALIDATE;
goto out;
}
}
err = NDR_ERR_SUCCESS;
out:
return err;
}
static enum ndr_err_code extract_crowvariant(TALLOC_CTX *ctx,
struct wsp_ctablevariant *tablevar,
bool is_64bit,
struct ndr_pull *ndr_pull,
ndr_flags_type flags,
uint64_t baseaddress,
DATA_BLOB *rows_buf, uint32_t len,
struct wsp_cbasestoragevariant *val)
{
enum ndr_err_code err = NDR_ERR_SUCCESS;
bool is_vector = tablevar->vtype & VT_VECTOR;
bool is_array = tablevar->vtype & VT_ARRAY;
if (is_array) {
DBG_ERR("Not handling ARRAYs!!!\n");
err = NDR_ERR_VALIDATE;
goto out;
}
if (is_variable_size((tablevar->vtype & ~(VT_VECTOR)))) {
err = extract_crowvariant_variable(ctx,
tablevar,
is_64bit,
ndr_pull,
flags,
baseaddress,
rows_buf,
len,
val);
} else {
if (is_vector) {
DBG_ERR("Not handling VECTORs of fixed size values!!!\n");
err = NDR_ERR_VALIDATE;
goto out;
}
NDR_CHECK(ndr_pull_set_switch_value(ndr_pull,
&val->vvalue,
tablevar->vtype));
NDR_CHECK(ndr_pull_variant_types(ndr_pull, NDR_SCALARS, &val->vvalue));
val->vtype = tablevar->vtype;
}
out:
return err;
}
static enum ndr_err_code process_columns(TALLOC_CTX *ctx,
bool is_64bit,
uint64_t baseaddress,
struct wsp_cpmsetbindingsin *bindingin,
DATA_BLOB *rows_buf,
uint32_t nrow,
struct wsp_cbasestoragevariant *cols)
{
uint32_t i;
enum ndr_err_code err = NDR_ERR_SUCCESS;
struct ndr_pull *ndr_pull = NULL;
ndr_flags_type ndr_flags = NDR_SCALARS | NDR_BUFFERS;
uint64_t nrow_offset = (uint64_t)nrow * bindingin->brow;
if (nrow_offset >= rows_buf->length) {
DBG_ERR("offset %"PRIu64" outside buffer range (buf len - %zu)\n",
nrow_offset,
rows_buf->length);
err = NDR_ERR_ALLOC;
goto out;
}
/*
* process columns, column info is contained in cpmsetbindings
* for more information see 'Rows' description MS-WSP 2.2.4.1.2
* which describes how the server fills the buffer.
*/
for (i = 0; i < bindingin->ccolumns; i++) {
struct wsp_ctablecolumn *tab_col = &bindingin->acolumns[i];
DATA_BLOB col_val_blob = data_blob_null;
uint64_t val_offset;
struct wsp_ctablevariant tablevariant = {0};
DBG_INFO("\nRow[%d]Col[%d] property %s type %s\n",nrow, i,
prop_from_fullprop(ctx, &tab_col->propspec),
get_vtype_name(tab_col->vtype));
if (tab_col->statusused) {
val_offset = nrow_offset + tab_col->statusoffset.value;
if (val_offset >= rows_buf->length) {
DBG_ERR("offset %"PRIu64" outside buffer range "
"(buf len - %zu)\n",
val_offset,
rows_buf->length);
err = NDR_ERR_ALLOC;
goto out;
}
DBG_INFO("\n\tstatusoffset 0x%x status is %s\n",
tab_col->statusoffset.value,
get_store_status(
(uint8_t)*(rows_buf->data
+ val_offset)));
}
if (tab_col->lengthused) {
val_offset = nrow_offset + tab_col->lengthoffset.value;
if (val_offset >= rows_buf->length) {
DBG_ERR("offset %"PRIu64" outside buffer range "
"(buf len - %zu)\n",
val_offset,
rows_buf->length);
err = NDR_ERR_ALLOC;
goto out;
}
DBG_INFO("\n\tlen offset 0x%x value at length is 0x%x\n",
tab_col->lengthoffset.value,
PULL_LE_I32(rows_buf->data,
val_offset));
}
if (tab_col->valueused) {
uint32_t len = 0;
val_offset = nrow_offset + tab_col->valueoffset.value;
if (val_offset >= rows_buf->length) {
DBG_ERR("offset %"PRIu64" outside buffer range "
"(buf len - %zu)\n",
val_offset,
rows_buf->length);
err = NDR_ERR_ALLOC;
goto out;
}
DBG_INFO("\n\tvalueoffset:valuesize 0x%x:0x%x "
"crowvariant address = 0x%"PRIx64"\n",
tab_col->valueoffset.value,
tab_col->valuesize.value,
val_offset);
col_val_blob.data = rows_buf->data + val_offset;
col_val_blob.length = tab_col->valuesize.value;
if (tab_col->vtype != VT_VARIANT) {
DBG_ERR("Not handling non variant column "
"values\n");
err = NDR_ERR_VALIDATE;
goto out;
}
ndr_pull = ndr_pull_init_blob(&col_val_blob, ctx);
if (ndr_pull == NULL) {
err = NDR_ERR_ALLOC;
DBG_ERR("out of memory\n");
goto out;
}
err = ndr_pull_wsp_ctablevariant(ndr_pull,
ndr_flags,
&tablevariant);
if (err) {
DBG_ERR("!!! failed to pull fixed part of variant data for col data\n");
goto out;
}
DBG_INFO("\n");
DBG_INFO("\tcrowvariant contains %s \n",
get_vtype_name(tablevariant.vtype));
if (tab_col->lengthused) {
/*
* it seems the size is what's at
* lengthoffset - tab_col->valuesize.value
*/
len = PULL_LE_I32(rows_buf->data,
nrow_offset
+ tab_col->lengthoffset.value);
len = len - tab_col->valuesize.value;
}
err = extract_crowvariant(ctx,
&tablevariant,
is_64bit,
ndr_pull,
ndr_flags,
baseaddress,
rows_buf,
len,
&cols[i]);
}
}
out:
return err;
}
/*
* extracts values from rows_buf into rowsarray
* based on the information in bindingsin
*/
enum ndr_err_code extract_rowsarray(
TALLOC_CTX * ctx,
DATA_BLOB *rows_buf,
bool is_64bit,
struct wsp_cpmsetbindingsin *bindingsin,
uint32_t cbreserved,
uint64_t baseaddress,
uint32_t rows,
struct wsp_cbasestoragevariant **rowsarray)
{
uint32_t i;
enum ndr_err_code err = NDR_ERR_SUCCESS;
/*
* limit check the size of rows_buf
* see MS-WSP 2.2.3.11 which describes the size
* of the rows buffer MUST not exceed 0x0004000 bytes.
* This limit will ensure we can safely check
* limits based on uint32_t offsets
*/
if (rows_buf->length > MAX_ROW_BUFF_SIZE) {
DBG_ERR("Buffer size 0x%zx exceeds 0x%x max buffer size\n",
rows_buf->length, MAX_ROW_BUFF_SIZE);
return NDR_ERR_BUFSIZE;
}
for (i = 0; i < rows; i++ ) {
struct wsp_cbasestoragevariant *cols =
talloc_zero_array(ctx,
struct wsp_cbasestoragevariant,
bindingsin->ccolumns);
uint64_t adjusted_address;
if (cols == NULL) {
return NDR_ERR_ALLOC;
}
/*
* cater for paddingrows (see MS-WSP 2.2.3.12)
* Rows buffer starts cbreserved bytes into messages
*/
adjusted_address = baseaddress + cbreserved;
err = process_columns(ctx,
is_64bit,
adjusted_address,
bindingsin,
rows_buf,
i,
cols);
if (err) {
break;
}
rowsarray[i] = cols;
}
return err;
}
static bool process_query_node(TALLOC_CTX *ctx,
struct wsp_crestriction *crestriction,
t_query *node);
static bool process_andornot_node(TALLOC_CTX *ctx,
struct wsp_crestriction *crestr,
t_query *node,
struct wsp_crestriction **left,
struct wsp_crestriction **right)
{
struct wsp_cnoderestriction *restriction_node = NULL;
*left = NULL;
*right = NULL;
restriction_node =
&crestr->restriction.cnoderestriction;
crestr->weight = 1000;
if (node->type == eAND || node->type == eOR) {
if (node->type == eAND) {
crestr->ultype = RTAND;
} else {
crestr->ultype = RTOR;
}
if (!create_noderestriction(ctx, restriction_node, 2)) {
return false;
}
*left = &restriction_node->panode[0];
*right = &restriction_node->panode[1];
} else {
crestr->ultype = RTNOT;
crestr->restriction.restriction.restriction =
talloc_zero(ctx, struct wsp_crestriction);
if (crestr->restriction.restriction.restriction == NULL) {
DBG_ERR("out of memory\n");
return false;
}
crestr =
crestr->restriction.restriction.restriction;
}
if (*left == NULL) {
*left = crestr;
}
if (*right == NULL) {
*right = crestr;
}
return true;
}
static void process_value_node(TALLOC_CTX *ctx,
struct wsp_crestriction *crestriction,
t_query *node)
{
*crestriction = *node->restriction;
}
static bool process_query_node(TALLOC_CTX *ctx,
struct wsp_crestriction *crestriction,
t_query *node)
{
struct wsp_crestriction *left = NULL, *right = NULL;
if (node == NULL) {
return true;
}
switch (node->type) {
case eAND:
case eOR:
case eNOT:
if (!process_andornot_node(ctx, crestriction, node,
&left, &right)) {
return false;
}
break;
case eVALUE:
process_value_node(ctx, crestriction, node);
break;
default:
break;
}
if (!process_query_node(ctx, left, node->left)) {
return false;
}
if (!process_query_node(ctx, right, node->right)) {
return false;
}
return true;
}
bool create_querysearch_request(TALLOC_CTX * ctx,
struct wsp_request* request,
t_select_stmt *sql)
{
uint32_t indices[sql->cols->num_cols];
uint32_t i;
uint32_t j;
struct wsp_cpmcreatequeryin *createquery =
&request->message.cpmcreatequery;
for (i = 0; i < sql->cols->num_cols; i++) {
indices[i] = i;
}
request->header.msg = CPMCREATEQUERY;
createquery->ccolumnsetpresent = 1;
createquery->columnset.columnset.count = sql->cols->num_cols;
if (!fill_uint32_vec(ctx, &createquery->columnset.columnset.indexes,
indices,
sql->cols->num_cols)) {
return false;
}
/* handle restrictions */
createquery->crestrictionpresent = 1;
createquery->restrictionarray.restrictionarray.count = 1;
createquery->restrictionarray.restrictionarray.ispresent = 1;
if (!create_restriction_array(ctx,
&createquery->restrictionarray.restrictionarray.restrictions,
createquery->restrictionarray.restrictionarray.count)) {
return false;
}
if (!process_query_node(ctx,
&createquery->restrictionarray.restrictionarray.restrictions[0],
sql->where)) {
return false;
}
/* handle rest */
createquery->csortsetpresent = 1;
if (createquery->csortsetpresent) {
/* sort on first column */
struct wsp_csort data[] = {
{0x00000000, 0x00000000, 0x00000000, WSP_DEFAULT_LCID},
};
struct wsp_csortset *sortset = NULL;
struct wsp_cingroupsortaggregsets *aggregsets = NULL;
aggregsets = &createquery->sortset.groupsortaggregsets;
aggregsets->ccount = 1;
aggregsets->sortsets =
talloc_zero_array(ctx,
struct wsp_cingroupsortaggregset,
aggregsets->ccount);
sortset = &aggregsets->sortsets[0].sortaggregset;
sortset->count = ARRAY_SIZE(data);
if (!fill_sortarray(ctx,
&sortset->sortarray,
data,sortset->count)) {
return false;
}
}
createquery->ccategorizationsetpresent = 0;
createquery->rowsetproperties.ubooleanoptions = 0x00000203;
createquery->rowsetproperties.ulmaxopenrows = 0x00000000;
createquery->rowsetproperties.ulmemoryusage = 0x00000000;
createquery->rowsetproperties.cmaxresults = 0x00000000;
createquery->rowsetproperties.ccmdtimeout = 0x00000005;
createquery->pidmapper.count = sql->cols->num_cols;
createquery->pidmapper.apropspec = talloc_zero_array(ctx,
struct wsp_cfullpropspec,
createquery->pidmapper.count);
if (createquery->pidmapper.apropspec == NULL) {
DBG_ERR("out of memory\n");
return false;
}
for(i = 0, j = 0; i < sql->cols->num_cols; i++) {
struct wsp_cfullpropspec *prop =
&createquery->pidmapper.apropspec[j];
char *propname = sql->cols->cols[i];
/*
* don't put RowID in pidmapper or windows will reject
* the query.
*/
if (strequal(propname, "System.Search.RowID")) {
continue;
}
if (!set_fullpropspec(ctx,
prop, sql->cols->cols[i],
PRSPEC_PROPID)) {
DBG_ERR("Failed to handle property named %s\n",
sql->cols->cols[i]);
continue;
}
j++;
}
createquery->columnset.columnset.count = j;
createquery->pidmapper.count = j;
createquery->lcid = WSP_DEFAULT_LCID;
return true;
}
static int32_t getNextAddress(int32_t value_off,
int32_t status_off,
int32_t len_off,
int32_t max_value_size)
{
return MAX(MAX(value_off + max_value_size, status_off + 1), len_off + 2);
}
static void create_binding_offsets(struct binding *binding, int no_cols,
int max_value_size)
{
uint32_t buf_addr = 0x0;
uint32_t i;
uint32_t value_off = 0;
uint32_t len_off = 0;
/* initial state this will get incremented to the desired 0x2 */
uint32_t status_off = 0x1;
uint32_t avail = 0x4;
int status_remain = 0x2;
int len_remain = -1;
const static uint32_t WINDOW = 0x8;
const static uint32_t LEN_STAT_SIZE = 0x4;
for (i = 0; i < no_cols; i++) {
buf_addr = buf_addr + WINDOW;
value_off = buf_addr;
if (status_remain <= 0) {
if (avail) {
status_off = avail;
status_remain = LEN_STAT_SIZE;
avail = 0;
} else {
/*
* we prepare the address to allocate
* another block from here. It will
* be allocated automatically when we
* re-enter the loop
*/
status_off = getNextAddress(value_off,
status_off,
len_off,
max_value_size) + WINDOW;
status_remain = LEN_STAT_SIZE;
buf_addr = status_off;
avail = buf_addr + LEN_STAT_SIZE;
}
} else {
status_off++;
buf_addr = getNextAddress(value_off,
status_off,
len_off,
max_value_size);
}
if (len_remain <= 0) {
if (avail) {
len_off = avail;
len_remain = LEN_STAT_SIZE;
avail = 0;
} else {
/*
* we prepare the address to allocate
* another block from here. It will
* be allocated automatically when we
* re-enter the loop
*/
len_off = getNextAddress(value_off,
status_off,
len_off,
max_value_size) + WINDOW;
len_remain = LEN_STAT_SIZE;
buf_addr = len_off;
avail = buf_addr + LEN_STAT_SIZE;
}
} else {
len_off += 0x4;
buf_addr = getNextAddress(value_off,
status_off,
len_off,
max_value_size);
}
status_remain--;
len_remain -= LEN_STAT_SIZE;
binding[i].value_off = value_off;
binding[i].status_off = status_off;
binding[i].len_off = len_off;
}
}
static bool fill_bindings(TALLOC_CTX *ctx,
struct wsp_cpmsetbindingsin *bindingsin,
char **col_names,
bool is_64bit)
{
uint32_t i;
struct binding *offsets = NULL;
uint32_t num_cols;
int maxvalue = is_64bit ? 0x18 : 0x10;
struct wsp_ctablecolumn *tablecols = bindingsin->acolumns;
bindingsin->brow = 0x0;
num_cols = bindingsin->ccolumns;
offsets = talloc_zero_array(ctx, struct binding, num_cols);
if (offsets == NULL) {
DBG_ERR("out of memory\n");
return false;
}
create_binding_offsets(offsets,
num_cols,
maxvalue);
for (i = 0; i < num_cols; i++) {
uint32_t max_off;
if (!set_ctablecolumn(ctx, &tablecols[i], col_names[i],
&offsets[i], maxvalue)) {
DBG_ERR("Failed to handle property named %s\n",
col_names[i]);
continue;
}
max_off = MAX(offsets[i].value_off + maxvalue,
offsets[i].status_off + 1);
max_off = MAX(max_off, offsets[i].len_off + 2);
if (max_off > bindingsin->brow) {
bindingsin->brow = max_off;
}
}
/* important */
bindingsin->brow += ndr_align_size(bindingsin->brow,4);
return true;
}
bool create_setbindings_request(TALLOC_CTX * ctx,
struct wsp_request* request,
t_select_stmt *sql,
uint32_t cursor,
bool is_64bit)
{
struct wsp_cpmsetbindingsin *bindingsin =
&request->message.cpmsetbindings;
request->header.msg = CPMSETBINDINGSIN;
bindingsin->hcursor = cursor;
bindingsin->ccolumns = sql->cols->num_cols;
bindingsin->acolumns = talloc_zero_array(ctx,
struct wsp_ctablecolumn,
bindingsin->ccolumns);
if (bindingsin->acolumns == NULL) {
DBG_ERR("out of memory\n");
return false;
}
if (!fill_bindings(ctx, bindingsin, sql->cols->cols, is_64bit)) {
return false;
}
return true;
}
enum search_kind get_kind(const char* kind_str)
{
enum search_kind result = UNKNOWN;
int i;
const static struct {
const char* str;
enum search_kind search_kind;
} kind_map[] = {
{"Calendar", CALENDAR},
{"Communication", COMMUNICATION},
{"Contact", CONTACT},
{"Document", DOCUMENT},
{"Email", EMAIL},
{"Feed", FEED},
{"Folder", FOLDER},
{"Game", GAME},
{"InstantMessage", INSTANTMESSAGE},
{"Journal", JOURNAL},
{"Link", LINK},
{"Movie", MOVIE},
{"Music", MUSIC},
{"Note", NOTE},
{"Picture", PICTURE},
{"Program", PROGRAM},
{"RecordedTV", RECORDEDTV},
{"SearchFolder", SEARCHFOLDER},
{"Task", TASK},
{"Video", VIDEO},
{"WebHistory", WEBHISTORY},
};
for (i = 0; i < ARRAY_SIZE(kind_map); i++) {
if (strequal(kind_str, kind_map[i].str)) {
result = kind_map[i].search_kind;
break;
}
}
return result;
}
struct wsp_client_ctx
{
struct rpc_pipe_client *rpccli;
struct cli_state *cli_state;
struct dcerpc_binding_handle *h;
};
static NTSTATUS wsp_resp_pdu_complete(struct tstream_context *stream,
void *private_data,
DATA_BLOB blob,
size_t *packet_size)
{
ssize_t to_read;
to_read = tstream_pending_bytes(stream);
if (to_read == -1) {
return NT_STATUS_IO_DEVICE_ERROR;
}
if (to_read > 0) {
*packet_size = blob.length + to_read;
return STATUS_MORE_ENTRIES;
}
return NT_STATUS_OK;
}
NTSTATUS wsp_server_connect(TALLOC_CTX *mem_ctx,
const char *servername,
struct tevent_context *ev_ctx,
struct loadparm_context *lp_ctx,
struct cli_credentials *credentials,
struct cli_state *cli,
struct wsp_client_ctx **wsp_ctx)
{
struct wsp_client_ctx *ctx = NULL;
struct dcerpc_binding_handle *h = NULL;
struct tstream_context *stream = NULL;
NTSTATUS status;
bool smb2_or_greater =
(lpcfg_client_max_protocol(lp_ctx) >= PROTOCOL_SMB2_02);
if (!smb2_or_greater) {
return NT_STATUS_PROTOCOL_NOT_SUPPORTED;
}
ctx = talloc_zero(mem_ctx, struct wsp_client_ctx);
if (ctx == NULL) {
return NT_STATUS_NO_MEMORY;
}
ctx->cli_state = cli;
status = smb2cli_ioctl_pipe_wait(
cli->conn,
cli->timeout,
cli->smb2.session,
cli->smb2.tcon,
"MsFteWds",
1);
if (!NT_STATUS_IS_OK(status)) {
DBG_ERR("wait for pipe failed: %s)\n",
nt_errstr(status));
return status;
}
status = rpc_pipe_open_np(cli,
&ndr_table_msftewds,
&ctx->rpccli);
if (!NT_STATUS_IS_OK(status)) {
DBG_ERR("failed to int the pipe)\n");
return status;
}
stream = rpc_transport_get_tstream(ctx->rpccli->transport);
h = tstream_binding_handle_create(ctx->rpccli,
NULL,
&stream,
MSG_HDR_SIZE,
wsp_resp_pdu_complete,
ctx, 42280);
if (!h) {
DBG_ERR("failed to create the pipe handle)\n");
return NT_STATUS_UNSUCCESSFUL;
}
ctx->rpccli->binding_handle = h;
*wsp_ctx = ctx;
return status;
}
static NTSTATUS write_something(TALLOC_CTX* ctx,
struct rpc_pipe_client *p,
DATA_BLOB *blob_in,
DATA_BLOB *blob_out)
{
uint32_t outflags;
struct dcerpc_binding_handle *handle = p->binding_handle;
NTSTATUS status;
status = dcerpc_binding_handle_raw_call(handle,
NULL,
0,
0,
blob_in->data,
blob_in->length,
ctx,
&blob_out->data,
&blob_out->length,
&outflags);
return status;
}
/* msg is expected to be created on the heap with talloc */
static enum ndr_err_code parse_blob(TALLOC_CTX *ctx, DATA_BLOB *blob,
struct wsp_request *request,
struct wsp_response *response,
DATA_BLOB *unread)
{
struct ndr_pull *ndr = NULL;
enum ndr_err_code err;
ndr_flags_type ndr_flags = NDR_SCALARS | NDR_BUFFERS;
uint32_t status = 0;
ndr = ndr_pull_init_blob(blob, ctx);
if (ndr == NULL) {
return NDR_ERR_ALLOC;
}
/* peek at the status */
status = PULL_LE_I32(blob->data, 4);
/* is hard error ?*/
if (status & 0x80000000 && blob->length == MSG_HDR_SIZE) {
/* just pull the header */
err = ndr_pull_wsp_header(ndr, ndr_flags, &response->header);
DBG_ERR("error: %s\n", nt_errstr(NT_STATUS(status)));
goto out;
}
err = ndr_pull_wsp_response(ndr, ndr_flags, response);
if (err) {
DBG_ERR("Failed to pull header from response blob error %d\n", err);
goto out;
}
if (DEBUGLEVEL >=6) {
NDR_PRINT_DEBUG(wsp_response, response);
}
if (response->header.msg == CPMGETROWS) {
if (request) {
/* point to rows buffer */
struct wsp_cpmgetrowsin *getrows =
&request->message.cpmgetrows;
ndr->offset = getrows->cbreserved;
}
}
if (ndr->offset < blob->length) {
int bytes = blob->length - ndr->offset;
*unread = data_blob_named(blob->data + ndr->offset,
bytes, "UNREAD");
DBG_WARNING("\nThere are unprocessed bytes (len 0x%x) "
"at end of message\n", bytes);
}
out:
return err;
}
static void set_msg_checksum(DATA_BLOB *blob, struct wsp_header *hdr)
{
/* point at payload */
uint32_t i;
uint8_t *buffer = blob->data + MSG_HDR_SIZE;
uint32_t buf_size = blob->length - MSG_HDR_SIZE;
uint32_t nwords = buf_size/4;
uint32_t offset = 0;
uint32_t checksum = 0;
static const uint32_t xor_const = 0x59533959;
for(i = 0; i < nwords; i++) {
checksum += PULL_LE_I32(buffer, offset);
offset += 4;
}
checksum ^= xor_const;
checksum -= hdr->msg;
hdr->checksum = checksum;
}
static enum ndr_err_code insert_header_and_checksum(TALLOC_CTX *ctx,
DATA_BLOB* blob,
struct wsp_header *header)
{
enum ndr_err_code err;
ndr_flags_type ndr_flags = NDR_SCALARS | NDR_BUFFERS;
struct ndr_push *header_ndr = ndr_push_init_ctx(ctx);
if (header_ndr == NULL) {
return NDR_ERR_ALLOC;
}
if (header->msg == CPMCONNECT
|| header->msg == CPMCREATEQUERY
|| header->msg == CPMSETBINDINGSIN
|| header->msg == CPMGETROWS
|| header->msg == CPMFETCHVALUE) {
set_msg_checksum(blob, header);
}
err = ndr_push_wsp_header(header_ndr, ndr_flags, header);
if (err) {
DBG_ERR("Failed to push header, error %d\n", err);
return err;
}
memcpy(blob->data, header_ndr->data, MSG_HDR_SIZE);
return err;
}
NTSTATUS wsp_request_response(TALLOC_CTX* ctx,
struct wsp_client_ctx *wsp_ctx,
struct wsp_request* request,
struct wsp_response *response,
DATA_BLOB *unread)
{
struct rpc_pipe_client *p = wsp_ctx->rpccli;
NTSTATUS status = NT_STATUS_OK;
ndr_flags_type ndr_flags = NDR_SCALARS | NDR_BUFFERS;
struct ndr_push* push_ndr = NULL;
enum ndr_err_code err;
DATA_BLOB req_blob;
DATA_BLOB resp_blob;
ZERO_STRUCT(req_blob);
ZERO_STRUCT(resp_blob);
push_ndr = ndr_push_init_ctx(ctx);
if (push_ndr == NULL) {
return NT_STATUS_NO_MEMORY;
}
/* write message payload first */
push_ndr->offset = MSG_HDR_SIZE;
DBG_INFO("\n");
switch(request->header.msg) {
case CPMCONNECT:
{
struct wsp_cpmconnectin *connectin =
&request->message.cpmconnect;
err = ndr_push_wsp_cpmconnectin(push_ndr, ndr_flags,
connectin);
break;
}
case CPMCREATEQUERY:
{
struct wsp_cpmcreatequeryin* createquery =
&request->message.cpmcreatequery;
err = ndr_push_wsp_cpmcreatequeryin(push_ndr,
ndr_flags,
createquery);
req_blob = ndr_push_blob(push_ndr);
/* we need to set cpmcreatequery.size */
createquery->size =
req_blob.length - MSG_HDR_SIZE;
PUSH_LE_U32(req_blob.data, MSG_HDR_SIZE,
createquery->size);
break;
}
case CPMSETBINDINGSIN:
{
struct wsp_cpmsetbindingsin *bindingsin =
&request->message.cpmsetbindings;
err = ndr_push_wsp_cpmsetbindingsin(push_ndr, ndr_flags,
bindingsin);
req_blob = ndr_push_blob(push_ndr);
/* we need to set cpmsetbindings.bbindingdesc (size) */
bindingsin->bbindingdesc =
req_blob.length - MSG_HDR_SIZE - 16;
PUSH_LE_U32(req_blob.data, MSG_HDR_SIZE + 8,
bindingsin->bbindingdesc);
break;
}
case CPMGETROWS:
{
struct wsp_cpmgetrowsin *getrows =
&request->message.cpmgetrows;
err = ndr_push_wsp_cpmgetrowsin(push_ndr, ndr_flags,
getrows);
req_blob = ndr_push_blob(push_ndr);
getrows->cbseek = req_blob.length - MSG_HDR_SIZE - 32;
/* we need to set cpmgetrowsin.cbseek (size) */
PUSH_LE_U32(req_blob.data, MSG_HDR_SIZE + 12,
getrows->cbseek);
PUSH_LE_U32(req_blob.data, MSG_HDR_SIZE + 16,
getrows->cbreserved);
break;
}
case CPMGETQUERYSTATUS:
{
struct wsp_cpmgetquerystatusin *querystatus =
&request->message.cpmgetquerystatus;
err = ndr_push_wsp_cpmgetquerystatusin(
push_ndr,
ndr_flags,
querystatus);
break;
}
case CPMGETQUERYSTATUSEX:
{
struct wsp_cpmgetquerystatusexin *statusexin =
&request->message.cpmgetquerystatusex;
err = ndr_push_wsp_cpmgetquerystatusexin(
push_ndr,
ndr_flags,
statusexin);
break;
}
case CPMFREECURSOR:
{
struct wsp_cpmfreecursorin *freecursor =
&request->message.cpmfreecursor;
err = ndr_push_wsp_cpmfreecursorin(
push_ndr,
ndr_flags,
freecursor);
break;
}
case CPMFETCHVALUE:
{
struct wsp_cpmfetchvaluein *fetchvalue =
&request->message.cpmfetchvalue;
err = ndr_push_wsp_cpmfetchvaluein(
push_ndr,
ndr_flags,
fetchvalue);
break;
}
case CPMGETAPPROXIMATEPOSITION:
{
struct wsp_cpmgetapproximatepositionin *position =
&request->message.getapproximateposition;
err = ndr_push_wsp_cpmgetapproximatepositionin(
push_ndr,
ndr_flags,
position);
break;
}
default:
status = NT_STATUS_MESSAGE_NOT_FOUND;
goto out;
break;
}
if (err) {
DBG_ERR("failed to serialise message! (%d)\n", err);
status = NT_STATUS_UNSUCCESSFUL;
goto out;
}
if (!req_blob.data) {
req_blob = ndr_push_blob(push_ndr);
}
err = insert_header_and_checksum(ctx, &req_blob, &request->header);
DBG_NOTICE("\nsending raw message from client len %d\n", (int)req_blob.length);
DBG_NOTICE("\nsending raw message from client\n");
DBG_NOTICE( "===============================\n");
dump_data(5, req_blob.data, req_blob.length);
status = write_something(ctx, p, &req_blob, &resp_blob);
if (!NT_STATUS_IS_OK(status)) {
DBG_ERR("Failed to write message\n");
goto out;
}
DBG_NOTICE("\nraw response from server\n");
DBG_NOTICE( "========================\n");
dump_data(5, resp_blob.data, resp_blob.length);
err = parse_blob(ctx,
&resp_blob,
request,
response,
unread);
if (err) {
DBG_ERR("Failed to parse response error %d\n", err);
status = NT_STATUS_UNSUCCESSFUL;
goto out;
}
DBG_NOTICE("response status is 0x%x\n", response->header.status);
/* propagate error status to return status */
if (response->header.status & 0x80000000) {
status = NT_STATUS_UNSUCCESSFUL;
}
out:
return status;
}
struct dcerpc_binding_handle* get_wsp_pipe(struct wsp_client_ctx *ctx)
{
return ctx->rpccli->binding_handle;
}