mirror of
https://github.com/samba-team/samba.git
synced 2024-12-22 13:34:15 +03:00
c095ec02ff
Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abartlet@samba.org>
2159 lines
53 KiB
C
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;
|
|
}
|