1
0
mirror of https://github.com/samba-team/samba.git synced 2024-12-22 13:34:15 +03:00
samba-mirror/source3/rpc_client/wsp_cli.c
Joseph Sutton c095ec02ff s3:rpc_client: Add missing ‘break’ statement
Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
2023-11-02 03:08:37 +00:00

2159 lines
53 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,
uint32_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 %d 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 are adjusted by offset
*
*/
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,
uint32_t offset,
DATA_BLOB *rows_buf,
uint64_t *pcount,
uint64_t **pvec_address/*,
struct wsp_cbasestoragevariant ***variant_array*/)
{
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;
}
/* 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;
}
addr = addr - offset;
if (addr >= rows_buf->length) {
DBG_ERR("offset %"PRIu64" outside buffer range "
"(buf len - %zu)\n",
addr,
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;
}
if (is_vector == false) {
vec_address[0] = addr;
} else {
uint64_t array_addr = addr;
uint64_t i;
for (i = 0; i < count; i++) {
if (is_64bit) {
vec_address[i] =
PULL_LE_I64(rows_buf->data,
array_addr);
array_addr = array_addr + 8;
} else {
vec_address[i] =
(uint32_t)PULL_LE_I32(rows_buf->data,
array_addr);
array_addr = array_addr + 4;
}
/* adjust address */
vec_address[i] -= offset;
}
}
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,
uint32_t offset,
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,
offset,
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 addr;
addr = vec_address[i];
if (addr >= rows_buf->length) {
DBG_ERR("offset %"PRIu64" outside buffer range "
"(buf len - %zu)\n",
addr,
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 - addr;
}
if (!extract_rowbuf_variable_type(ctx,
tablevar->vtype & ~VT_VECTOR,
addr,
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,
uint32_t offset,
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,
offset,
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,
uint32_t cbreserved,
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) {
uint64_t offset = baseaddress + cbreserved;
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,
offset,
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;
for (i = 0; i < rows; i++ ) {
struct wsp_cbasestoragevariant *cols =
talloc_zero_array(ctx,
struct wsp_cbasestoragevariant,
bindingsin->ccolumns);
if (cols == NULL) {
return NDR_ERR_ALLOC;
}
err = process_columns(ctx,
is_64bit,
cbreserved,
baseaddress,
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;
}