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