2 * Unix SMB/CIFS implementation.
4 * Window Search Service
6 * Copyright (c) Noel Power
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 3 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, see <http://www.gnu.org/licenses/>.
24 #include "rpc_client/wsp_cli.h"
25 #include "rpc_client/rpc_client.h"
26 #include "param/param.h"
27 #include "auth/credentials/credentials.h"
29 #include <util/tevent_ntstatus.h>
30 #include "libcli/tstream_binding_handle/tstream_binding_handle.h"
31 #include "lib/tsocket/tsocket.h"
32 #include "librpc/wsp/wsp_util.h"
33 #include "librpc/gen_ndr/ndr_wsp.h"
34 #include "rpc_client/cli_pipe.h"
35 #include "libcli/smb/smbXcli_base.h"
37 #define MSG_HDR_SIZE 16
40 * 32-bit Windows XP operating system, 32-bit Windows Server 2003 operating
41 * system, 32-bit Windows Home Server server software, 32-bit Windows Vista
42 * with Windows Search 4.0, 32-bit Windows Server 2003 with Windows
43 * Search 4.0. All of these versions of Windows are running
47 static const uint32_t CLIENTVERSION
= 0x00010700;
50 * DBPROP_CI_SCOPE_FLAGS
51 * containing QUERY_DEEP
52 * QUERY_DEEP (0x1) indicates that files in the scope directory and all
53 * subdirectories are included in the results. If clear, only files in
54 * the scope directory are included in the results.
56 static int32_t scope_flags_vector
[] = {0x00000001};
58 * Search everywhere "\\" is the root scope
60 static const char * root_scope_string_vector
[] = {"\\"};
62 /* sets sensible defaults */
63 static void init_wsp_prop(struct wsp_cdbprop
*prop
)
65 *prop
= (struct wsp_cdbprop
){0};
66 prop
->colid
.ekind
= DBKIND_GUID_PROPID
;
70 static bool create_restriction_array(TALLOC_CTX
*ctx
,
71 struct wsp_crestriction
**pelements
,
74 struct wsp_crestriction
*elements
= talloc_zero_array(ctx
,
75 struct wsp_crestriction
,
77 if (elements
== NULL
) {
80 *pelements
= elements
;
85 static bool create_noderestriction(TALLOC_CTX
*ctx
,
86 struct wsp_cnoderestriction
*pnode
,
90 pnode
->cnode
= nnodes
;
91 ok
= create_restriction_array(ctx
, &pnode
->panode
, nnodes
);
95 static bool fill_sortarray(TALLOC_CTX
*ctx
, struct wsp_csort
**dest
,
96 struct wsp_csort
*src
, uint32_t num
)
99 struct wsp_csort
*psort
= talloc_zero_array(ctx
, struct wsp_csort
,
104 for (i
= 0; i
< num
; i
++) {
113 static bool set_fullpropspec(TALLOC_CTX
*ctx
, struct wsp_cfullpropspec
*prop
,
114 const char* propname
, uint32_t kind
)
116 struct GUID guid
= {0};
117 const struct full_propset_info
*prop_info
= NULL
;
119 prop_info
= get_propset_info_with_guid(propname
, &guid
);
121 DBG_ERR("Failed to handle property named %s\n",
125 prop
->guidpropset
= guid
;
127 if (kind
== PRSPEC_LPWSTR
) {
128 prop
->name_or_id
.propname
.vstring
= talloc_strdup(ctx
,
130 if (prop
->name_or_id
.propname
.vstring
== NULL
) {
131 DBG_ERR("out of memory");
134 prop
->name_or_id
.propname
.len
= strlen(propname
);
136 prop
->name_or_id
.prspec
= prop_info
->id
;
148 static bool set_ctablecolumn(TALLOC_CTX
*ctx
, struct wsp_ctablecolumn
*tablecol
,
149 const char* propname
, struct binding
*offsets
,
152 struct wsp_cfullpropspec
*prop
= &tablecol
->propspec
;
154 if (!set_fullpropspec(ctx
, prop
, propname
, PRSPEC_PROPID
)) {
157 tablecol
->vtype
=VT_VARIANT
;
158 tablecol
->aggregateused
= USED
;
159 tablecol
->valueused
= USED
;
160 tablecol
->valueoffset
.value
= offsets
->value_off
;
161 tablecol
->valuesize
.value
= value_size
;
162 tablecol
->statusused
= USED
;
163 tablecol
->statusoffset
.value
= offsets
->status_off
;
164 tablecol
->lengthused
= USED
;
165 tablecol
->lengthoffset
.value
= offsets
->len_off
;
170 static bool fill_uint32_vec(TALLOC_CTX
* ctx
,
172 uint32_t* ivector
, uint32_t elems
)
175 uint32_t *dest
= talloc_zero_array(ctx
, uint32_t, elems
);
180 for ( i
= 0; i
< elems
; i
++ ) {
181 dest
[ i
] = ivector
[ i
];
187 static bool init_propset1(TALLOC_CTX
* tmp_ctx
,
188 struct wsp_cdbpropset
*propertyset
)
191 GUID_from_string(DBPROPSET_FSCIFRMWRK_EXT
,
192 &propertyset
->guidpropertyset
);
194 propertyset
->cproperties
= 4;
195 propertyset
->aprops
=
196 talloc_zero_array(tmp_ctx
, struct wsp_cdbprop
,
197 propertyset
->cproperties
);
198 if (propertyset
->aprops
== NULL
) {
202 /* initialise first 4 props */
203 for( i
= 0; i
< propertyset
->cproperties
; i
++) {
204 init_wsp_prop(&propertyset
->aprops
[i
]);
208 * see MS-WSP 2.2.1.31.1 & 4.1 Protocol examples, Example 1
209 * and also as seen in various windows network traces
210 * set value prop[0] - 'catalog to search'
213 propertyset
->aprops
[0].dbpropid
= DBPROP_CI_CATALOG_NAME
;
214 /* The name of the Catalog to Query */
215 set_variant_lpwstr(tmp_ctx
, &propertyset
->aprops
[0].vvalue
,
216 "Windows\\SystemIndex");
218 * set value prop[1] 'Regular Query'
221 propertyset
->aprops
[1].dbpropid
= DBPROP_CI_QUERY_TYPE
;
222 set_variant_i4(tmp_ctx
, &propertyset
->aprops
[1].vvalue
,
226 * set value prop[2] 'search subfolders'
228 propertyset
->aprops
[2].dbpropid
= DBPROP_CI_SCOPE_FLAGS
;
229 set_variant_i4_vector(tmp_ctx
, &propertyset
->aprops
[2].vvalue
,
230 scope_flags_vector
, ARRAY_SIZE(scope_flags_vector
));
233 * set value prop[3] 'root scope'
235 propertyset
->aprops
[3].dbpropid
= DBPROP_CI_INCLUDE_SCOPES
;
236 set_variant_lpwstr_vector(tmp_ctx
,
237 &propertyset
->aprops
[3].vvalue
,
238 root_scope_string_vector
,
239 ARRAY_SIZE(root_scope_string_vector
));
243 static bool init_propset2(TALLOC_CTX
* tmp_ctx
,
244 struct wsp_cdbpropset
*propertyset
,
249 GUID_from_string(DBPROPSET_CIFRMWRKCORE_EXT
,
250 &propertyset
->guidpropertyset
);
252 propertyset
->cproperties
= 1;
253 propertyset
->aprops
=
254 talloc_zero_array(tmp_ctx
, struct wsp_cdbprop
,
255 propertyset
->cproperties
);
256 if (propertyset
->aprops
== NULL
) {
260 /* initialise first 1 props */
261 for( i
= 0; i
< propertyset
->cproperties
; i
++) {
262 init_wsp_prop(&propertyset
->aprops
[i
]);
266 * see MS-WSP 2.2.1.31.1 & 4.1 Protocol examples, Example 1
267 * and also as seen in various windows network traces
268 * set value prop[0] - 'machines to search'
270 propertyset
->aprops
[0].dbpropid
= DBPROP_MACHINE
;
271 set_variant_bstr(tmp_ctx
, &propertyset
->aprops
[0].vvalue
,
276 static bool init_apropset0(TALLOC_CTX
* tmp_ctx
,
277 struct wsp_cdbpropset
*propertyset
)
281 GUID_from_string(DBPROPSET_MSIDXS_ROWSETEXT
,
282 &propertyset
->guidpropertyset
);
284 propertyset
->cproperties
= 7;
285 propertyset
->aprops
=
286 talloc_zero_array(tmp_ctx
, struct wsp_cdbprop
,
287 propertyset
->cproperties
);
288 if (propertyset
->aprops
== NULL
) {
292 /* initialise props */
293 for( i
= 0; i
< propertyset
->cproperties
; i
++) {
294 init_wsp_prop(&propertyset
->aprops
[i
]);
298 * see MS-WSP 2.2.1.31.1 & 4.1 Protocol examples, Example 1
300 * MSIDXSPROP_ROWSETQUERYSTATUS - 'ignored'
302 propertyset
->aprops
[0].dbpropid
= MSIDXSPROP_ROWSETQUERYSTATUS
;
303 set_variant_i4(tmp_ctx
, &propertyset
->aprops
[0].vvalue
, 0x00000000);
307 * MSIDXSPROP_COMMAND_LOCALE_STRING - 'EN'
309 propertyset
->aprops
[1].dbpropid
= MSIDXSPROP_COMMAND_LOCALE_STRING
;
310 set_variant_bstr(tmp_ctx
, &propertyset
->aprops
[1].vvalue
,
315 * MSIDXSPROP_QUERY_RESTRICTION - 'ignored'
317 propertyset
->aprops
[2].dbpropid
= MSIDXSPROP_QUERY_RESTRICTION
;
318 set_variant_bstr(tmp_ctx
, &propertyset
->aprops
[2].vvalue
,
323 * MSIDXSPROP_PARSE_TREE - 'ignored'
325 propertyset
->aprops
[3].dbpropid
= MSIDXSPROP_PARSE_TREE
;
326 set_variant_bstr(tmp_ctx
, &propertyset
->aprops
[3].vvalue
,
331 * MSIDXSPROP_MAX_RANK - 'ignored'
333 propertyset
->aprops
[4].dbpropid
= MSIDXSPROP_MAX_RANK
;
334 set_variant_i4(tmp_ctx
, &propertyset
->aprops
[4].vvalue
, 0x00000000);
338 * MSIDXSPROP_RESULTS_FOUND - 'ignored'
340 propertyset
->aprops
[5].dbpropid
= MSIDXSPROP_RESULTS_FOUND
;
341 set_variant_i4(tmp_ctx
, &propertyset
->aprops
[5].vvalue
, 0x00000000);
345 * ? - '' (unknown property id)
347 propertyset
->aprops
[6].dbpropid
= 0x00000008;
348 set_variant_i4(tmp_ctx
, &propertyset
->aprops
[6].vvalue
, 0x00000000);
352 static bool init_apropset1(TALLOC_CTX
* tmp_ctx
,
353 struct wsp_cdbpropset
*propertyset
)
356 GUID_from_string(DBPROPSET_QUERYEXT
,
357 &propertyset
->guidpropertyset
);
359 propertyset
->cproperties
= 11;
360 propertyset
->aprops
=
361 talloc_zero_array(tmp_ctx
, struct wsp_cdbprop
,
362 propertyset
->cproperties
);
363 if (propertyset
->aprops
== NULL
) {
367 /* init properties */
368 for( i
= 0; i
< propertyset
->cproperties
; i
++) {
369 init_wsp_prop(&propertyset
->aprops
[i
]);
373 * see MS-WSP 2.2.1.31.1 & 4.1 Protocol examples, Example 1
375 * DBPROP_USECONTENTINDEX - 'forced use of the full text index
378 propertyset
->aprops
[0].dbpropid
= DBPROP_USECONTENTINDEX
;
379 set_variant_vt_bool(tmp_ctx
, &propertyset
->aprops
[0].vvalue
, false);
383 * DBPROP_DEFERNONINDEXEDTRIMMING - 'trimming of security
384 * results will not be deferred'
386 propertyset
->aprops
[1].dbpropid
= DBPROP_DEFERNONINDEXEDTRIMMING
;
387 set_variant_vt_bool(tmp_ctx
, &propertyset
->aprops
[1].vvalue
, false);
391 * DBPROP_USEEXTENDEDDBTYPES - 'extended DB types are not used'
393 propertyset
->aprops
[2].dbpropid
= DBPROP_USEEXTENDEDDBTYPES
;
394 set_variant_vt_bool(tmp_ctx
, &propertyset
->aprops
[2].vvalue
, false);
398 * DBPROP_IGNORENOISEONLYCLAUSES = 'full text clauses consisting
399 * entirely of noise words will
400 * result in an error being returned'
402 propertyset
->aprops
[3].dbpropid
= DBPROP_IGNORENOISEONLYCLAUSES
;
403 set_variant_vt_bool(tmp_ctx
, &propertyset
->aprops
[3].vvalue
, false);
407 * DBPROP_GENERICOPTIONS_STRING - 'no generic options set'
409 propertyset
->aprops
[4].dbpropid
= DBPROP_GENERICOPTIONS_STRING
;
410 set_variant_bstr(tmp_ctx
, &propertyset
->aprops
[4].vvalue
, "");
414 * DBPROP_DEFERCATALOGVERIFICATION - 'catalog verification is not
417 propertyset
->aprops
[5].dbpropid
= DBPROP_DEFERCATALOGVERIFICATION
;
418 set_variant_vt_bool(tmp_ctx
, &propertyset
->aprops
[5].vvalue
, false);
422 * DBPROP_IGNORESBRI - 'query can use the sort-by-rank index
425 propertyset
->aprops
[6].dbpropid
= DBPROP_IGNORESBRI
;
426 set_variant_vt_bool(tmp_ctx
, &propertyset
->aprops
[6].vvalue
, false);
430 * DBPROP_GENERATEPARSETREE - 'a parse tree is not generated for
433 propertyset
->aprops
[7].dbpropid
= DBPROP_GENERATEPARSETREE
;
434 set_variant_vt_bool(tmp_ctx
, &propertyset
->aprops
[7].vvalue
, false);
438 * DBPROP_FREETEXTANYTERM - 'all terms from a FREETEXT clause
439 * appear in every matching document'
441 propertyset
->aprops
[8].dbpropid
= DBPROP_FREETEXTANYTERM
;
442 set_variant_vt_bool(tmp_ctx
, &propertyset
->aprops
[8].vvalue
, false);
445 * DBPROP_FREETEXTUSESTEMMING - 'stemming is not used when interpreting
448 propertyset
->aprops
[9].dbpropid
= DBPROP_FREETEXTUSESTEMMING
;
449 set_variant_vt_bool(tmp_ctx
, &propertyset
->aprops
[9].vvalue
, false);
455 propertyset
->aprops
[10].dbpropid
= 0x0000000f; /* ??? */
456 set_variant_vt_bool(tmp_ctx
, &propertyset
->aprops
[10].vvalue
, false);
460 static bool init_apropset2(TALLOC_CTX
* tmp_ctx
,
461 struct wsp_cdbpropset
*propertyset
,
465 GUID_from_string(DBPROPSET_CIFRMWRKCORE_EXT
,
466 &propertyset
->guidpropertyset
);
468 propertyset
->cproperties
= 1;
469 propertyset
->aprops
=
470 talloc_zero_array(tmp_ctx
, struct wsp_cdbprop
,
471 propertyset
->cproperties
);
472 if (propertyset
->aprops
== NULL
) {
476 /* init properties */
477 for( i
= 0; i
< propertyset
->cproperties
; i
++) {
478 init_wsp_prop(&propertyset
->aprops
[i
]);
482 * see MS-WSP 2.2.1.31.1 & 4.1 Protocol examples, Example 1
483 * and also as seen in various windows network traces
485 * DBPROP_MACHINE - 'target server'
487 propertyset
->aprops
[0].dbpropid
= DBPROP_MACHINE
;
488 set_variant_bstr(tmp_ctx
, &propertyset
->aprops
[0].vvalue
, server
);
493 static bool init_apropset3(TALLOC_CTX
* tmp_ctx
,
494 struct wsp_cdbpropset
*propertyset
)
498 GUID_from_string(DBPROPSET_FSCIFRMWRK_EXT
,
499 &propertyset
->guidpropertyset
);
501 propertyset
->cproperties
= 3;
502 propertyset
->aprops
=
503 talloc_zero_array(tmp_ctx
, struct wsp_cdbprop
,
504 propertyset
->cproperties
);
505 if (propertyset
->aprops
== NULL
) {
509 /* init properties */
510 for( i
= 0; i
< propertyset
->cproperties
; i
++) {
511 init_wsp_prop(&propertyset
->aprops
[i
]);
515 * see MS-WSP 2.2.1.31.1 & 4.1 Protocol examples, Example 1
516 * and also as seen in various windows network traces
518 * DBPROP_CI_INCLUDE_SCOPES - 'search everywhere'
520 propertyset
->aprops
[0].dbpropid
= DBPROP_CI_INCLUDE_SCOPES
;
521 set_variant_array_bstr(tmp_ctx
, &propertyset
->aprops
[0].vvalue
,
522 root_scope_string_vector
,
523 ARRAY_SIZE(root_scope_string_vector
));
527 * DBPROP_CI_SCOPE_FLAGS - 'QUERY_DEEP'
529 propertyset
->aprops
[1].dbpropid
= DBPROP_CI_SCOPE_FLAGS
;
530 set_variant_array_i4(tmp_ctx
, &propertyset
->aprops
[1].vvalue
,
532 ARRAY_SIZE(scope_flags_vector
));
536 * DBPROP_CI_CATALOG_NAME - 'index to use' (always the same)
538 propertyset
->aprops
[2].dbpropid
= DBPROP_CI_CATALOG_NAME
;
539 set_variant_bstr(tmp_ctx
, &propertyset
->aprops
[2].vvalue
,
540 "Windows\\SystemIndex");
544 bool init_connectin_request(TALLOC_CTX
*ctx
,
545 struct wsp_request
* request
,
546 const char* clientmachine
,
547 const char* clientuser
,
550 enum ndr_err_code err
;
551 struct connectin_propsets
*props
= NULL
;
552 struct connectin_extpropsets
*ext_props
= NULL
;
553 DATA_BLOB props_blob
= data_blob_null
;
554 struct ndr_push
*ndr_props
= NULL
;
555 ndr_flags_type ndr_flags
= NDR_SCALARS
| NDR_BUFFERS
;
557 struct wsp_cpmconnectin
*connectin
=
558 &request
->message
.cpmconnect
;
560 props
= talloc_zero(ctx
, struct connectin_propsets
);
563 DBG_ERR("out of memory\n");
567 ext_props
= talloc_zero(ctx
, struct connectin_extpropsets
) ;
568 if (ext_props
== NULL
) {
570 DBG_ERR("out of memory\n");
574 request
->header
.msg
= CPMCONNECT
;
575 connectin
->iclientversion
= CLIENTVERSION
;
577 * hmm just say the client is remote, if we
578 * are talking to windows it is, if not does
581 connectin
->fclientisremote
= 0x00000001;
582 connectin
->machinename
= clientmachine
;
583 connectin
->username
= clientuser
;
584 props
->cpropsets
= 2;
586 /* =================== */
587 /* set up PropertySet1 */
588 /* =================== */
589 if (!init_propset1(ctx
, &props
->propertyset1
)) {
591 DBG_ERR("initialising propset1 failed\n");
595 /* =================== */
596 /* set up PropertySet2 */
597 /* =================== */
598 if (!init_propset2(ctx
, &props
->propertyset2
, server
)) {
600 DBG_ERR("initialising propset2 failed\n");
605 ext_props
->cextpropset
= 4;
606 ext_props
->apropertysets
= talloc_zero_array(ctx
, struct wsp_cdbpropset
,
607 ext_props
->cextpropset
);
609 if (ext_props
->apropertysets
== NULL
) {
611 DBG_ERR("out of memory\n");
615 /* ======================= */
616 /* set up aPropertySets[0] */
617 /* ======================= */
618 if (!init_apropset0(ctx
, &ext_props
->apropertysets
[0])) {
620 DBG_ERR("initialisation of apropset0 failed\n");
624 /* ======================= */
625 /* set up aPropertySets[1] */
626 /* ======================= */
627 if (!init_apropset1(ctx
, &ext_props
->apropertysets
[1])) {
629 DBG_ERR("initialisation of apropset1 failed\n");
633 /* ======================= */
634 /* set up aPropertySets[2] */
635 /* ======================= */
636 if (!init_apropset2(ctx
, &ext_props
->apropertysets
[2], server
)) {
638 DBG_ERR("initialisation of apropset2 failed\n");
642 /* ======================= */
643 /* set up aPropertySets[3] */
644 /* ======================= */
645 if (!init_apropset3(ctx
, &ext_props
->apropertysets
[3])) {
647 DBG_ERR("initialisation of apropset3 failed\n");
651 /* we also have to fill the opaque blobs that contain the propsets */
652 ndr_props
= ndr_push_init_ctx(ctx
);
653 if (ndr_props
== NULL
) {
655 DBG_ERR("out of memory\n");
659 /* first connectin_propsets */
660 err
= ndr_push_connectin_propsets(ndr_props
, ndr_flags
, props
);
662 DBG_ERR("Failed to push propset, error %d\n", err
);
666 props_blob
= ndr_push_blob(ndr_props
);
667 connectin
->cbblob1
= props_blob
.length
;
668 connectin
->propsets
= talloc_zero_array(ctx
, uint8_t,
670 if (connectin
->propsets
== NULL
) {
672 DBG_ERR("out of memory\n");
676 memcpy(connectin
->propsets
, props_blob
.data
, props_blob
.length
);
678 /* then connectin_extpropsets */
679 TALLOC_FREE(ndr_props
);
680 ndr_props
= ndr_push_init_ctx(ctx
);
682 if (ndr_props
== NULL
) {
684 DBG_ERR("out of memory\n");
688 err
= ndr_push_connectin_extpropsets(ndr_props
, ndr_flags
, ext_props
);
691 DBG_ERR("Failed to push extpropset, error %d\n", err
);
696 props_blob
= ndr_push_blob(ndr_props
);
697 connectin
->cbblob2
= props_blob
.length
;
698 connectin
->extpropsets
= talloc_zero_array(ctx
, uint8_t,
701 if (connectin
->extpropsets
== NULL
) {
703 DBG_ERR("out of memory\n");
707 memcpy(connectin
->extpropsets
, props_blob
.data
, props_blob
.length
);
708 TALLOC_FREE(ndr_props
);
714 void create_seekat_getrows_request(TALLOC_CTX
* ctx
,
715 struct wsp_request
* request
,
721 uint32_t ulclientbase
,
725 struct wsp_cpmgetrowsin
*getrows
=
726 &request
->message
.cpmgetrows
;
728 request
->header
.msg
= CPMGETROWS
;
730 getrows
->hcursor
= cursor
;
731 /* max no. rows to receive */
732 getrows
->crowstotransfer
= rows
;
734 * size (length) of row in bytes, determined from value set
735 * by CPMSetBindings message
737 getrows
->cbrowWidth
= cbrowwidth
;
739 * according to we should calculate this (see MS-WSP 3.2.4.2.4)
740 * but it seems window always sets this to the max 16KB limit
741 * (most likely when any row value is variable size e.g. like a
744 getrows
->cbreadbuffer
= 0x00004000;
746 * base value of buffer pointer
748 getrows
->ulclientbase
= ulclientbase
;
749 getrows
->cbreserved
= cbreserved
;
750 /* fetch rows in forward order */
751 getrows
->fbwdfetch
= fbwdfetch
;
753 getrows
->etype
= EROWSEEKAT
;
754 /* we don't handle chapters */
756 /* CRowsSeekAt (MS-WSP 2.2.1.37) */
757 getrows
->seekdescription
.crowseekat
.bmkoffset
= bookmark
;
758 getrows
->seekdescription
.crowseekat
.cskip
= skip
;
759 getrows
->seekdescription
.crowseekat
.hregion
= 0;
762 static bool extract_rowbuf_variable_type(TALLOC_CTX
*ctx
,
765 DATA_BLOB
*rows_buf
, uint32_t len
,
766 struct wsp_cbasestoragevariant
*val
)
768 enum ndr_err_code err
;
769 struct ndr_pull
*ndr_pull
= NULL
;
770 ndr_flags_type ndr_flags
= NDR_SCALARS
| NDR_BUFFERS
;
771 DATA_BLOB variant_blob
= data_blob_null
;
772 if (offset
>= rows_buf
->length
) {
773 DBG_ERR("offset %"PRIu64
" outside buffer range (buf len - %zu)",
778 variant_blob
.data
= rows_buf
->data
+ offset
;
779 variant_blob
.length
= len
;
780 ndr_pull
= ndr_pull_init_blob(&variant_blob
, ctx
);
782 if (ndr_pull
== NULL
) {
783 DBG_ERR("out of memory\n");
789 const char *string
= NULL
;
790 ndr_set_flags(&ndr_pull
->flags
, LIBNDR_FLAG_STR_NULLTERM
);
791 err
= ndr_pull_string(ndr_pull
, ndr_flags
, &string
);
793 DBG_ERR("error unmarshalling string from %p\n", variant_blob
.data
);
795 DBG_INFO("\tstring val ->%s<-\n", string
);
797 val
->vvalue
.vt_lpwstr
.value
= string
;
802 DBG_ERR("#FIXME Unhandled variant type %s\n", get_vtype_name(type
));
808 static bool convert_variant_array_to_vector(TALLOC_CTX
*ctx
,
810 struct wsp_cbasestoragevariant
**variant_array
,
811 struct wsp_cbasestoragevariant
*outval
)
815 union variant_types vvalue
= {0};
816 vtype
= variant_array
[0]->vtype
;
818 if (outval
== NULL
) {
825 vvalue
.vt_bstr_v
.vvector_elements
= count
;
826 vvalue
.vt_bstr_v
.vvector_data
=
827 talloc_zero_array(ctx
,
828 struct vt_bstr
, count
);
829 if (vvalue
.vt_bstr_v
.vvector_data
== NULL
) {
834 vvalue
.vt_lpwstr_v
.vvector_elements
= count
;
835 vvalue
.vt_lpwstr_v
.vvector_data
=
836 talloc_zero_array(ctx
,
837 struct vt_lpwstr
, count
);
838 if (vvalue
.vt_lpwstr_v
.vvector_data
== NULL
) {
842 case VT_COMPRESSED_LPWSTR
:
843 vvalue
.vt_compresseed_lpwstr_v
.vvector_elements
845 vvalue
.vt_compresseed_lpwstr_v
.vvector_data
=
846 talloc_zero_array(ctx
,
847 struct vt_compressed_lpwstr
,
849 if (vvalue
.vt_compresseed_lpwstr_v
.vvector_data
== NULL
) {
854 DBG_ERR("Can't convert array of %s to VECTOR\n",
855 get_vtype_name(vtype
));
859 for (i
= 0; i
< count
; i
++) {
860 if (variant_array
[i
]->vtype
!= vtype
) {
861 DBG_ERR("array item type %s doesn't match extpected "
863 get_vtype_name(variant_array
[i
]->vtype
),
864 get_vtype_name(vtype
));
867 switch (variant_array
[i
]->vtype
) {
869 vvalue
.vt_bstr_v
.vvector_data
[i
]
870 = variant_array
[i
]->vvalue
.vt_bstr
;
873 vvalue
.vt_lpwstr_v
.vvector_data
[i
]
874 = variant_array
[i
]->vvalue
.vt_lpwstr
;
876 case VT_COMPRESSED_LPWSTR
:
877 vvalue
.vt_compresseed_lpwstr_v
.vvector_data
[i
]
878 = variant_array
[i
]->vvalue
.vt_compressed_lpwstr
;
881 DBG_ERR("Can't convert array of %s to VECTOR\n",
882 get_vtype_name(vtype
));
886 outval
->vtype
= vtype
| VT_VECTOR
;
887 outval
->vvalue
= vvalue
;
892 * get the addresses in rowbuf of variants to read from
893 * pvec_address will point to addresses,
894 * an array of n elements for a vector or array of 1 element
895 * if non-vector item.
897 * addresses stored in pvec_address
900 static enum ndr_err_code
extract_variant_addresses(TALLOC_CTX
*ctx
,
901 struct wsp_ctablevariant
*tablevar
,
903 struct ndr_pull
*ndr_pull
,
904 ndr_flags_type flags
,
905 uint64_t baseaddress
,
908 uint64_t **pvec_address
)
910 bool is_vector
= tablevar
->vtype
& VT_VECTOR
;
913 uint64_t *vec_address
= NULL
;
914 enum ndr_err_code err
;
916 /* read count (only if this is a vector) */
919 err
= ndr_pull_udlong(ndr_pull
,
923 DBG_ERR("Failed to extract count\n");
928 err
= ndr_pull_uint32(ndr_pull
,
932 DBG_ERR("Failed to extract count\n");
935 count
= (uint64_t)count_32
;
941 /* ensure count is at least within buffer range */
942 if (count
>= MAX_ROW_BUFF_SIZE
|| count
>= rows_buf
->length
) {
943 DBG_ERR("count %"PRIu64
" either exceeds max buffer size "
944 "or buffer size (%zu)",
945 count
, rows_buf
->length
);
946 err
= NDR_ERR_VALIDATE
;
952 err
= ndr_pull_udlong(ndr_pull
,
956 DBG_ERR("Failed to extract address\n");
961 err
= ndr_pull_uint32(ndr_pull
, flags
, &addr_32
);
963 DBG_ERR("Failed to extract address\n");
969 if ((addr
- baseaddress
) >= rows_buf
->length
) {
970 DBG_ERR("offset %"PRIu64
" outside buffer range "
974 err
= NDR_ERR_VALIDATE
;
978 vec_address
= talloc_zero_array(ctx
,
981 if (vec_address
== NULL
) {
987 * non vector case addr points to value
988 * otherwise addr points to list of addresses
989 * for the values in vector
991 if (is_vector
== false) {
992 vec_address
[0] = addr
;
994 uint64_t array_offset
= addr
- baseaddress
;
1004 if (array_offset
>= MAX_ROW_BUFF_SIZE
1005 || array_offset
>= rows_buf
->length
) {
1006 DBG_ERR("offset %"PRIu64
" either exceeds max buf size "
1007 "or buffer size (%zu)",
1008 array_offset
, rows_buf
->length
);
1009 err
= NDR_ERR_VALIDATE
;
1013 /* addr points to a list of int32 or int64 addresses */
1014 for (i
= 0; i
< count
; i
++) {
1016 * read the addresses of the vector elements
1017 * note: we can safely convert the uint64_t
1018 * values here to uint32_t values as
1019 * we are sure they are within range
1020 * due to previous checks above.
1022 if (smb_buffer_oob((uint32_t)rows_buf
->length
,
1023 (uint32_t)array_offset
,
1025 DBG_ERR("offset %"PRIu64
" will be outside "
1026 "buffer range (buf len - %zu) after "
1027 "reading %s address\n",
1030 is_64bit
? "64 bit" : "32 bit");
1031 err
= NDR_ERR_VALIDATE
;
1036 PULL_LE_I64(rows_buf
->data
,
1040 (uint32_t)PULL_LE_I32(rows_buf
->data
,
1043 array_offset
+= intsize
;
1046 err
= NDR_ERR_SUCCESS
;
1048 *pvec_address
= vec_address
;
1053 static enum ndr_err_code
extract_crowvariant_variable(TALLOC_CTX
*ctx
,
1054 struct wsp_ctablevariant
*tablevar
,
1056 struct ndr_pull
*ndr_pull
,
1057 ndr_flags_type flags
,
1058 uint64_t baseaddress
,
1059 DATA_BLOB
*rows_buf
,
1061 struct wsp_cbasestoragevariant
*val
)
1063 enum ndr_err_code err
;
1064 bool is_vector
= tablevar
->vtype
& VT_VECTOR
;
1067 uint64_t *vec_address
= NULL
;
1068 struct wsp_cbasestoragevariant
**variant_array
= NULL
;
1072 err
= extract_variant_addresses(ctx
,
1083 DBG_ERR("Failed to extract address and/or count\n");
1087 variant_array
= talloc_zero_array(ctx
,
1088 struct wsp_cbasestoragevariant
*,
1091 if (variant_array
== NULL
) {
1092 err
= NDR_ERR_ALLOC
;
1096 if (is_vector
== false) {
1097 variant_array
[0] = val
;
1099 for (i
= 0; i
< count
; i
++) {
1100 variant_array
[i
] = talloc_zero(ctx
,
1101 struct wsp_cbasestoragevariant
);
1102 if (variant_array
[i
] == NULL
) {
1103 err
= NDR_ERR_ALLOC
;
1109 for (i
= 0; i
< count
; i
++) {
1110 uint32_t tmplen
= len
;
1111 uint64_t buf_offset
;
1112 buf_offset
= vec_address
[i
] - baseaddress
;
1113 if (buf_offset
>= rows_buf
->length
) {
1114 DBG_ERR("offset %"PRIu64
" outside buffer range "
1115 "(buf len - %zu)\n",
1118 err
= NDR_ERR_VALIDATE
;
1122 && (tablevar
->vtype
& ~(VT_VECTOR
)) == VT_LPWSTR
) {
1124 * we can't trust len if 64 bit mode
1125 * (in 32 bit mode the length reported at len offset
1126 * seem consistent and correct)
1127 * So in this case instead of using the len
1128 * at len offset we just use the full buffer
1129 * from the point the value is stored at
1130 * till the end of the buffer
1132 tmplen
= rows_buf
->length
- buf_offset
;
1134 if (!extract_rowbuf_variable_type(ctx
,
1135 tablevar
->vtype
& ~VT_VECTOR
,
1139 variant_array
[i
])) {
1140 err
= NDR_ERR_VALIDATE
;
1146 if (!convert_variant_array_to_vector(ctx
,
1150 err
= NDR_ERR_VALIDATE
;
1154 err
= NDR_ERR_SUCCESS
;
1159 static enum ndr_err_code
extract_crowvariant(TALLOC_CTX
*ctx
,
1160 struct wsp_ctablevariant
*tablevar
,
1162 struct ndr_pull
*ndr_pull
,
1163 ndr_flags_type flags
,
1164 uint64_t baseaddress
,
1165 DATA_BLOB
*rows_buf
, uint32_t len
,
1166 struct wsp_cbasestoragevariant
*val
)
1168 enum ndr_err_code err
= NDR_ERR_SUCCESS
;
1169 bool is_vector
= tablevar
->vtype
& VT_VECTOR
;
1170 bool is_array
= tablevar
->vtype
& VT_ARRAY
;
1173 DBG_ERR("Not handling ARRAYs!!!\n");
1174 err
= NDR_ERR_VALIDATE
;
1178 if (is_variable_size((tablevar
->vtype
& ~(VT_VECTOR
)))) {
1179 err
= extract_crowvariant_variable(ctx
,
1191 DBG_ERR("Not handling VECTORs of fixed size values!!!\n");
1192 err
= NDR_ERR_VALIDATE
;
1195 NDR_CHECK(ndr_pull_set_switch_value(ndr_pull
,
1198 NDR_CHECK(ndr_pull_variant_types(ndr_pull
, NDR_SCALARS
, &val
->vvalue
));
1199 val
->vtype
= tablevar
->vtype
;
1205 static enum ndr_err_code
process_columns(TALLOC_CTX
*ctx
,
1207 uint64_t baseaddress
,
1208 struct wsp_cpmsetbindingsin
*bindingin
,
1209 DATA_BLOB
*rows_buf
,
1211 struct wsp_cbasestoragevariant
*cols
)
1214 enum ndr_err_code err
= NDR_ERR_SUCCESS
;
1215 struct ndr_pull
*ndr_pull
= NULL
;
1216 ndr_flags_type ndr_flags
= NDR_SCALARS
| NDR_BUFFERS
;
1217 uint64_t nrow_offset
= (uint64_t)nrow
* bindingin
->brow
;
1219 if (nrow_offset
>= rows_buf
->length
) {
1220 DBG_ERR("offset %"PRIu64
" outside buffer range (buf len - %zu)\n",
1223 err
= NDR_ERR_ALLOC
;
1228 * process columns, column info is contained in cpmsetbindings
1229 * for more information see 'Rows' description MS-WSP 2.2.4.1.2
1230 * which describes how the server fills the buffer.
1232 for (i
= 0; i
< bindingin
->ccolumns
; i
++) {
1233 struct wsp_ctablecolumn
*tab_col
= &bindingin
->acolumns
[i
];
1234 DATA_BLOB col_val_blob
= data_blob_null
;
1235 uint64_t val_offset
;
1236 struct wsp_ctablevariant tablevariant
= {0};
1237 DBG_INFO("\nRow[%d]Col[%d] property %s type %s\n",nrow
, i
,
1238 prop_from_fullprop(ctx
, &tab_col
->propspec
),
1239 get_vtype_name(tab_col
->vtype
));
1240 if (tab_col
->statusused
) {
1241 val_offset
= nrow_offset
+ tab_col
->statusoffset
.value
;
1242 if (val_offset
>= rows_buf
->length
) {
1243 DBG_ERR("offset %"PRIu64
" outside buffer range "
1244 "(buf len - %zu)\n",
1247 err
= NDR_ERR_ALLOC
;
1250 DBG_INFO("\n\tstatusoffset 0x%x status is %s\n",
1251 tab_col
->statusoffset
.value
,
1253 (uint8_t)*(rows_buf
->data
1256 if (tab_col
->lengthused
) {
1257 val_offset
= nrow_offset
+ tab_col
->lengthoffset
.value
;
1258 if (val_offset
>= rows_buf
->length
) {
1259 DBG_ERR("offset %"PRIu64
" outside buffer range "
1260 "(buf len - %zu)\n",
1263 err
= NDR_ERR_ALLOC
;
1266 DBG_INFO("\n\tlen offset 0x%x value at length is 0x%x\n",
1267 tab_col
->lengthoffset
.value
,
1268 PULL_LE_I32(rows_buf
->data
,
1271 if (tab_col
->valueused
) {
1273 val_offset
= nrow_offset
+ tab_col
->valueoffset
.value
;
1274 if (val_offset
>= rows_buf
->length
) {
1275 DBG_ERR("offset %"PRIu64
" outside buffer range "
1276 "(buf len - %zu)\n",
1279 err
= NDR_ERR_ALLOC
;
1282 DBG_INFO("\n\tvalueoffset:valuesize 0x%x:0x%x "
1283 "crowvariant address = 0x%"PRIx64
"\n",
1284 tab_col
->valueoffset
.value
,
1285 tab_col
->valuesize
.value
,
1288 col_val_blob
.data
= rows_buf
->data
+ val_offset
;
1289 col_val_blob
.length
= tab_col
->valuesize
.value
;
1292 if (tab_col
->vtype
!= VT_VARIANT
) {
1293 DBG_ERR("Not handling non variant column "
1295 err
= NDR_ERR_VALIDATE
;
1298 ndr_pull
= ndr_pull_init_blob(&col_val_blob
, ctx
);
1299 if (ndr_pull
== NULL
) {
1300 err
= NDR_ERR_ALLOC
;
1301 DBG_ERR("out of memory\n");
1305 err
= ndr_pull_wsp_ctablevariant(ndr_pull
,
1309 DBG_ERR("!!! failed to pull fixed part of variant data for col data\n");
1313 DBG_INFO("\tcrowvariant contains %s \n",
1314 get_vtype_name(tablevariant
.vtype
));
1316 if (tab_col
->lengthused
) {
1318 * it seems the size is what's at
1319 * lengthoffset - tab_col->valuesize.value
1321 len
= PULL_LE_I32(rows_buf
->data
,
1323 + tab_col
->lengthoffset
.value
);
1324 len
= len
- tab_col
->valuesize
.value
;
1326 err
= extract_crowvariant(ctx
,
1342 * extracts values from rows_buf into rowsarray
1343 * based on the information in bindingsin
1345 enum ndr_err_code
extract_rowsarray(
1347 DATA_BLOB
*rows_buf
,
1349 struct wsp_cpmsetbindingsin
*bindingsin
,
1350 uint32_t cbreserved
,
1351 uint64_t baseaddress
,
1353 struct wsp_cbasestoragevariant
**rowsarray
)
1356 enum ndr_err_code err
= NDR_ERR_SUCCESS
;
1358 * limit check the size of rows_buf
1359 * see MS-WSP 2.2.3.11 which describes the size
1360 * of the rows buffer MUST not exceed 0x0004000 bytes.
1361 * This limit will ensure we can safely check
1362 * limits based on uint32_t offsets
1365 if (rows_buf
->length
> MAX_ROW_BUFF_SIZE
) {
1366 DBG_ERR("Buffer size 0x%zx exceeds 0x%x max buffer size\n",
1367 rows_buf
->length
, MAX_ROW_BUFF_SIZE
);
1368 return NDR_ERR_BUFSIZE
;
1371 for (i
= 0; i
< rows
; i
++ ) {
1372 struct wsp_cbasestoragevariant
*cols
=
1373 talloc_zero_array(ctx
,
1374 struct wsp_cbasestoragevariant
,
1375 bindingsin
->ccolumns
);
1376 uint64_t adjusted_address
;
1378 return NDR_ERR_ALLOC
;
1382 * cater for paddingrows (see MS-WSP 2.2.3.12)
1383 * Rows buffer starts cbreserved bytes into messages
1385 adjusted_address
= baseaddress
+ cbreserved
;
1387 err
= process_columns(ctx
,
1397 rowsarray
[i
] = cols
;
1402 static bool process_query_node(TALLOC_CTX
*ctx
,
1403 struct wsp_crestriction
*crestriction
,
1406 static bool process_andornot_node(TALLOC_CTX
*ctx
,
1407 struct wsp_crestriction
*crestr
,
1409 struct wsp_crestriction
**left
,
1410 struct wsp_crestriction
**right
)
1412 struct wsp_cnoderestriction
*restriction_node
= NULL
;
1418 &crestr
->restriction
.cnoderestriction
;
1420 crestr
->weight
= 1000;
1422 if (node
->type
== eAND
|| node
->type
== eOR
) {
1423 if (node
->type
== eAND
) {
1424 crestr
->ultype
= RTAND
;
1426 crestr
->ultype
= RTOR
;
1428 if (!create_noderestriction(ctx
, restriction_node
, 2)) {
1431 *left
= &restriction_node
->panode
[0];
1432 *right
= &restriction_node
->panode
[1];
1434 crestr
->ultype
= RTNOT
;
1435 crestr
->restriction
.restriction
.restriction
=
1436 talloc_zero(ctx
, struct wsp_crestriction
);
1437 if (crestr
->restriction
.restriction
.restriction
== NULL
) {
1438 DBG_ERR("out of memory\n");
1442 crestr
->restriction
.restriction
.restriction
;
1444 if (*left
== NULL
) {
1447 if (*right
== NULL
) {
1453 static void process_value_node(TALLOC_CTX
*ctx
,
1454 struct wsp_crestriction
*crestriction
,
1457 *crestriction
= *node
->restriction
;
1460 static bool process_query_node(TALLOC_CTX
*ctx
,
1461 struct wsp_crestriction
*crestriction
,
1464 struct wsp_crestriction
*left
= NULL
, *right
= NULL
;
1468 switch (node
->type
) {
1472 if (!process_andornot_node(ctx
, crestriction
, node
,
1478 process_value_node(ctx
, crestriction
, node
);
1483 if (!process_query_node(ctx
, left
, node
->left
)) {
1486 if (!process_query_node(ctx
, right
, node
->right
)) {
1492 bool create_querysearch_request(TALLOC_CTX
* ctx
,
1493 struct wsp_request
* request
,
1496 uint32_t indices
[sql
->cols
->num_cols
];
1499 struct wsp_cpmcreatequeryin
*createquery
=
1500 &request
->message
.cpmcreatequery
;
1502 for (i
= 0; i
< sql
->cols
->num_cols
; i
++) {
1506 request
->header
.msg
= CPMCREATEQUERY
;
1507 createquery
->ccolumnsetpresent
= 1;
1508 createquery
->columnset
.columnset
.count
= sql
->cols
->num_cols
;
1509 if (!fill_uint32_vec(ctx
, &createquery
->columnset
.columnset
.indexes
,
1511 sql
->cols
->num_cols
)) {
1515 /* handle restrictions */
1516 createquery
->crestrictionpresent
= 1;
1517 createquery
->restrictionarray
.restrictionarray
.count
= 1;
1518 createquery
->restrictionarray
.restrictionarray
.ispresent
= 1;
1520 if (!create_restriction_array(ctx
,
1521 &createquery
->restrictionarray
.restrictionarray
.restrictions
,
1522 createquery
->restrictionarray
.restrictionarray
.count
)) {
1527 if (!process_query_node(ctx
,
1528 &createquery
->restrictionarray
.restrictionarray
.restrictions
[0],
1535 createquery
->csortsetpresent
= 1;
1536 if (createquery
->csortsetpresent
) {
1537 /* sort on first column */
1538 struct wsp_csort data
[] = {
1539 {0x00000000, 0x00000000, 0x00000000, WSP_DEFAULT_LCID
},
1541 struct wsp_csortset
*sortset
= NULL
;
1542 struct wsp_cingroupsortaggregsets
*aggregsets
= NULL
;
1544 aggregsets
= &createquery
->sortset
.groupsortaggregsets
;
1545 aggregsets
->ccount
= 1;
1546 aggregsets
->sortsets
=
1547 talloc_zero_array(ctx
,
1548 struct wsp_cingroupsortaggregset
,
1549 aggregsets
->ccount
);
1550 sortset
= &aggregsets
->sortsets
[0].sortaggregset
;
1551 sortset
->count
= ARRAY_SIZE(data
);
1552 if (!fill_sortarray(ctx
,
1553 &sortset
->sortarray
,
1554 data
,sortset
->count
)) {
1559 createquery
->ccategorizationsetpresent
= 0;
1561 createquery
->rowsetproperties
.ubooleanoptions
= 0x00000203;
1562 createquery
->rowsetproperties
.ulmaxopenrows
= 0x00000000;
1563 createquery
->rowsetproperties
.ulmemoryusage
= 0x00000000;
1564 createquery
->rowsetproperties
.cmaxresults
= 0x00000000;
1565 createquery
->rowsetproperties
.ccmdtimeout
= 0x00000005;
1567 createquery
->pidmapper
.count
= sql
->cols
->num_cols
;
1568 createquery
->pidmapper
.apropspec
= talloc_zero_array(ctx
,
1569 struct wsp_cfullpropspec
,
1570 createquery
->pidmapper
.count
);
1572 if (createquery
->pidmapper
.apropspec
== NULL
) {
1573 DBG_ERR("out of memory\n");
1577 for(i
= 0, j
= 0; i
< sql
->cols
->num_cols
; i
++) {
1578 struct wsp_cfullpropspec
*prop
=
1579 &createquery
->pidmapper
.apropspec
[j
];
1580 char *propname
= sql
->cols
->cols
[i
];
1582 * don't put RowID in pidmapper or windows will reject
1585 if (strequal(propname
, "System.Search.RowID")) {
1588 if (!set_fullpropspec(ctx
,
1589 prop
, sql
->cols
->cols
[i
],
1591 DBG_ERR("Failed to handle property named %s\n",
1592 sql
->cols
->cols
[i
]);
1597 createquery
->columnset
.columnset
.count
= j
;
1598 createquery
->pidmapper
.count
= j
;
1599 createquery
->lcid
= WSP_DEFAULT_LCID
;
1603 static int32_t getNextAddress(int32_t value_off
,
1606 int32_t max_value_size
)
1608 return MAX(MAX(value_off
+ max_value_size
, status_off
+ 1), len_off
+ 2);
1611 static void create_binding_offsets(struct binding
*binding
, int no_cols
,
1614 uint32_t buf_addr
= 0x0;
1617 uint32_t value_off
= 0;
1618 uint32_t len_off
= 0;
1620 /* initial state this will get incremented to the desired 0x2 */
1621 uint32_t status_off
= 0x1;
1622 uint32_t avail
= 0x4;
1623 int status_remain
= 0x2;
1624 int len_remain
= -1;
1626 const static uint32_t WINDOW
= 0x8;
1627 const static uint32_t LEN_STAT_SIZE
= 0x4;
1628 for (i
= 0; i
< no_cols
; i
++) {
1629 buf_addr
= buf_addr
+ WINDOW
;
1630 value_off
= buf_addr
;
1632 if (status_remain
<= 0) {
1635 status_remain
= LEN_STAT_SIZE
;
1639 * we prepare the address to allocate
1640 * another block from here. It will
1641 * be allocated automatically when we
1644 status_off
= getNextAddress(value_off
,
1647 max_value_size
) + WINDOW
;
1648 status_remain
= LEN_STAT_SIZE
;
1649 buf_addr
= status_off
;
1650 avail
= buf_addr
+ LEN_STAT_SIZE
;
1654 buf_addr
= getNextAddress(value_off
,
1660 if (len_remain
<= 0) {
1663 len_remain
= LEN_STAT_SIZE
;
1667 * we prepare the address to allocate
1668 * another block from here. It will
1669 * be allocated automatically when we
1672 len_off
= getNextAddress(value_off
,
1675 max_value_size
) + WINDOW
;
1676 len_remain
= LEN_STAT_SIZE
;
1678 avail
= buf_addr
+ LEN_STAT_SIZE
;
1682 buf_addr
= getNextAddress(value_off
,
1688 len_remain
-= LEN_STAT_SIZE
;
1689 binding
[i
].value_off
= value_off
;
1690 binding
[i
].status_off
= status_off
;
1691 binding
[i
].len_off
= len_off
;
1695 static bool fill_bindings(TALLOC_CTX
*ctx
,
1696 struct wsp_cpmsetbindingsin
*bindingsin
,
1701 struct binding
*offsets
= NULL
;
1703 int maxvalue
= is_64bit
? 0x18 : 0x10;
1705 struct wsp_ctablecolumn
*tablecols
= bindingsin
->acolumns
;
1706 bindingsin
->brow
= 0x0;
1707 num_cols
= bindingsin
->ccolumns
;
1709 offsets
= talloc_zero_array(ctx
, struct binding
, num_cols
);
1711 if (offsets
== NULL
) {
1712 DBG_ERR("out of memory\n");
1716 create_binding_offsets(offsets
,
1720 for (i
= 0; i
< num_cols
; i
++) {
1722 if (!set_ctablecolumn(ctx
, &tablecols
[i
], col_names
[i
],
1723 &offsets
[i
], maxvalue
)) {
1724 DBG_ERR("Failed to handle property named %s\n",
1728 max_off
= MAX(offsets
[i
].value_off
+ maxvalue
,
1729 offsets
[i
].status_off
+ 1);
1730 max_off
= MAX(max_off
, offsets
[i
].len_off
+ 2);
1731 if (max_off
> bindingsin
->brow
) {
1732 bindingsin
->brow
= max_off
;
1736 bindingsin
->brow
+= ndr_align_size(bindingsin
->brow
,4);
1740 bool create_setbindings_request(TALLOC_CTX
* ctx
,
1741 struct wsp_request
* request
,
1746 struct wsp_cpmsetbindingsin
*bindingsin
=
1747 &request
->message
.cpmsetbindings
;
1749 request
->header
.msg
= CPMSETBINDINGSIN
;
1750 bindingsin
->hcursor
= cursor
;
1751 bindingsin
->ccolumns
= sql
->cols
->num_cols
;
1753 bindingsin
->acolumns
= talloc_zero_array(ctx
,
1754 struct wsp_ctablecolumn
,
1755 bindingsin
->ccolumns
);
1757 if (bindingsin
->acolumns
== NULL
) {
1758 DBG_ERR("out of memory\n");
1762 if (!fill_bindings(ctx
, bindingsin
, sql
->cols
->cols
, is_64bit
)) {
1769 enum search_kind
get_kind(const char* kind_str
)
1771 enum search_kind result
= UNKNOWN
;
1773 const static struct {
1775 enum search_kind search_kind
;
1777 {"Calendar", CALENDAR
},
1778 {"Communication", COMMUNICATION
},
1779 {"Contact", CONTACT
},
1780 {"Document", DOCUMENT
},
1785 {"InstantMessage", INSTANTMESSAGE
},
1786 {"Journal", JOURNAL
},
1791 {"Picture", PICTURE
},
1792 {"Program", PROGRAM
},
1793 {"RecordedTV", RECORDEDTV
},
1794 {"SearchFolder", SEARCHFOLDER
},
1797 {"WebHistory", WEBHISTORY
},
1799 for (i
= 0; i
< ARRAY_SIZE(kind_map
); i
++) {
1800 if (strequal(kind_str
, kind_map
[i
].str
)) {
1801 result
= kind_map
[i
].search_kind
;
1808 struct wsp_client_ctx
1810 struct dcerpc_binding_handle
*h
;
1813 static NTSTATUS
wsp_resp_pdu_complete(struct tstream_context
*stream
,
1816 size_t *packet_size
)
1820 to_read
= tstream_pending_bytes(stream
);
1821 if (to_read
== -1) {
1822 return NT_STATUS_IO_DEVICE_ERROR
;
1826 *packet_size
= blob
.length
+ to_read
;
1827 return STATUS_MORE_ENTRIES
;
1830 return NT_STATUS_OK
;
1833 static NTSTATUS
wsp_rpc_transport_np_connect(struct cli_state
*cli
,
1834 TALLOC_CTX
*mem_ctx
,
1835 struct rpc_cli_transport
**presult
)
1837 struct tevent_context
*ev
= NULL
;
1838 struct tevent_req
*req
= NULL
;
1839 NTSTATUS status
= NT_STATUS_NO_MEMORY
;
1841 ev
= samba_tevent_context_init(mem_ctx
);
1845 req
= rpc_transport_np_init_send(ev
, ev
, cli
, "MsFteWds");
1849 if (!tevent_req_poll_ntstatus(req
, ev
, &status
)) {
1852 status
= rpc_transport_np_init_recv(req
, mem_ctx
, presult
);
1859 NTSTATUS
wsp_server_connect(TALLOC_CTX
*mem_ctx
,
1860 const char *servername
,
1861 struct tevent_context
*ev_ctx
,
1862 struct loadparm_context
*lp_ctx
,
1863 struct cli_credentials
*credentials
,
1864 struct cli_state
*cli
,
1865 struct wsp_client_ctx
**wsp_ctx
)
1867 struct wsp_client_ctx
*ctx
= NULL
;
1868 struct rpc_cli_transport
*transport
= NULL
;
1869 struct tstream_context
*stream
= NULL
;
1872 bool smb2_or_greater
=
1873 (lpcfg_client_max_protocol(lp_ctx
) >= PROTOCOL_SMB2_02
);
1875 if (!smb2_or_greater
) {
1876 return NT_STATUS_PROTOCOL_NOT_SUPPORTED
;
1879 ctx
= talloc_zero(mem_ctx
, struct wsp_client_ctx
);
1881 return NT_STATUS_NO_MEMORY
;
1884 status
= smb2cli_ioctl_pipe_wait(
1891 if (!NT_STATUS_IS_OK(status
)) {
1892 DBG_ERR("wait for pipe failed: %s)\n",
1898 status
= wsp_rpc_transport_np_connect(cli
,
1901 if (!NT_STATUS_IS_OK(status
)) {
1902 DBG_ERR("failed to int the pipe)\n");
1907 stream
= rpc_transport_get_tstream(transport
);
1908 ctx
->h
= tstream_binding_handle_create(ctx
,
1912 wsp_resp_pdu_complete
,
1914 if (ctx
->h
== NULL
) {
1915 DBG_ERR("failed to create the pipe handle)\n");
1917 return NT_STATUS_UNSUCCESSFUL
;
1925 static NTSTATUS
write_something(TALLOC_CTX
* ctx
,
1926 struct dcerpc_binding_handle
*handle
,
1928 DATA_BLOB
*blob_out
)
1933 status
= dcerpc_binding_handle_raw_call(handle
,
1946 /* msg is expected to be created on the heap with talloc */
1947 static enum ndr_err_code
parse_blob(TALLOC_CTX
*ctx
, DATA_BLOB
*blob
,
1948 struct wsp_request
*request
,
1949 struct wsp_response
*response
,
1952 struct ndr_pull
*ndr
= NULL
;
1953 enum ndr_err_code err
;
1954 ndr_flags_type ndr_flags
= NDR_SCALARS
| NDR_BUFFERS
;
1955 uint32_t status
= 0;
1957 ndr
= ndr_pull_init_blob(blob
, ctx
);
1960 return NDR_ERR_ALLOC
;
1963 /* peek at the status */
1964 status
= PULL_LE_I32(blob
->data
, 4);
1966 /* is hard error ?*/
1967 if (status
& 0x80000000 && blob
->length
== MSG_HDR_SIZE
) {
1968 /* just pull the header */
1969 err
= ndr_pull_wsp_header(ndr
, ndr_flags
, &response
->header
);
1970 DBG_ERR("error: %s\n", nt_errstr(NT_STATUS(status
)));
1973 err
= ndr_pull_wsp_response(ndr
, ndr_flags
, response
);
1975 DBG_ERR("Failed to pull header from response blob error %d\n", err
);
1978 if (DEBUGLEVEL
>=6) {
1979 NDR_PRINT_DEBUG(wsp_response
, response
);
1981 if (response
->header
.msg
== CPMGETROWS
) {
1983 /* point to rows buffer */
1984 struct wsp_cpmgetrowsin
*getrows
=
1985 &request
->message
.cpmgetrows
;
1986 ndr
->offset
= getrows
->cbreserved
;
1990 if (ndr
->offset
< blob
->length
) {
1991 int bytes
= blob
->length
- ndr
->offset
;
1992 *unread
= data_blob_named(blob
->data
+ ndr
->offset
,
1994 DBG_WARNING("\nThere are unprocessed bytes (len 0x%x) "
1995 "at end of message\n", bytes
);
2002 static void set_msg_checksum(DATA_BLOB
*blob
, struct wsp_header
*hdr
)
2004 /* point at payload */
2006 uint8_t *buffer
= blob
->data
+ MSG_HDR_SIZE
;
2007 uint32_t buf_size
= blob
->length
- MSG_HDR_SIZE
;
2008 uint32_t nwords
= buf_size
/4;
2009 uint32_t offset
= 0;
2010 uint32_t checksum
= 0;
2012 static const uint32_t xor_const
= 0x59533959;
2013 for(i
= 0; i
< nwords
; i
++) {
2014 checksum
+= PULL_LE_I32(buffer
, offset
);
2018 checksum
^= xor_const
;
2019 checksum
-= hdr
->msg
;
2020 hdr
->checksum
= checksum
;
2023 static enum ndr_err_code
insert_header_and_checksum(TALLOC_CTX
*ctx
,
2025 struct wsp_header
*header
)
2027 enum ndr_err_code err
;
2028 ndr_flags_type ndr_flags
= NDR_SCALARS
| NDR_BUFFERS
;
2029 struct ndr_push
*header_ndr
= ndr_push_init_ctx(ctx
);
2031 if (header_ndr
== NULL
) {
2032 return NDR_ERR_ALLOC
;
2035 if (header
->msg
== CPMCONNECT
2036 || header
->msg
== CPMCREATEQUERY
2037 || header
->msg
== CPMSETBINDINGSIN
2038 || header
->msg
== CPMGETROWS
2039 || header
->msg
== CPMFETCHVALUE
) {
2041 set_msg_checksum(blob
, header
);
2043 err
= ndr_push_wsp_header(header_ndr
, ndr_flags
, header
);
2045 DBG_ERR("Failed to push header, error %d\n", err
);
2048 memcpy(blob
->data
, header_ndr
->data
, MSG_HDR_SIZE
);
2052 NTSTATUS
wsp_request_response(TALLOC_CTX
* ctx
,
2053 struct wsp_client_ctx
*wsp_ctx
,
2054 struct wsp_request
* request
,
2055 struct wsp_response
*response
,
2058 NTSTATUS status
= NT_STATUS_OK
;
2060 ndr_flags_type ndr_flags
= NDR_SCALARS
| NDR_BUFFERS
;
2061 struct ndr_push
* push_ndr
= NULL
;
2063 enum ndr_err_code err
;
2066 DATA_BLOB resp_blob
;
2068 ZERO_STRUCT(req_blob
);
2069 ZERO_STRUCT(resp_blob
);
2071 push_ndr
= ndr_push_init_ctx(ctx
);
2072 if (push_ndr
== NULL
) {
2073 return NT_STATUS_NO_MEMORY
;
2076 /* write message payload first */
2077 push_ndr
->offset
= MSG_HDR_SIZE
;
2080 switch(request
->header
.msg
) {
2083 struct wsp_cpmconnectin
*connectin
=
2084 &request
->message
.cpmconnect
;
2085 err
= ndr_push_wsp_cpmconnectin(push_ndr
, ndr_flags
,
2089 case CPMCREATEQUERY
:
2091 struct wsp_cpmcreatequeryin
* createquery
=
2092 &request
->message
.cpmcreatequery
;
2093 err
= ndr_push_wsp_cpmcreatequeryin(push_ndr
,
2096 req_blob
= ndr_push_blob(push_ndr
);
2097 /* we need to set cpmcreatequery.size */
2099 req_blob
.length
- MSG_HDR_SIZE
;
2100 PUSH_LE_U32(req_blob
.data
, MSG_HDR_SIZE
,
2105 case CPMSETBINDINGSIN
:
2107 struct wsp_cpmsetbindingsin
*bindingsin
=
2108 &request
->message
.cpmsetbindings
;
2109 err
= ndr_push_wsp_cpmsetbindingsin(push_ndr
, ndr_flags
,
2111 req_blob
= ndr_push_blob(push_ndr
);
2112 /* we need to set cpmsetbindings.bbindingdesc (size) */
2113 bindingsin
->bbindingdesc
=
2114 req_blob
.length
- MSG_HDR_SIZE
- 16;
2115 PUSH_LE_U32(req_blob
.data
, MSG_HDR_SIZE
+ 8,
2116 bindingsin
->bbindingdesc
);
2121 struct wsp_cpmgetrowsin
*getrows
=
2122 &request
->message
.cpmgetrows
;
2123 err
= ndr_push_wsp_cpmgetrowsin(push_ndr
, ndr_flags
,
2125 req_blob
= ndr_push_blob(push_ndr
);
2126 getrows
->cbseek
= req_blob
.length
- MSG_HDR_SIZE
- 32;
2127 /* we need to set cpmgetrowsin.cbseek (size) */
2128 PUSH_LE_U32(req_blob
.data
, MSG_HDR_SIZE
+ 12,
2130 PUSH_LE_U32(req_blob
.data
, MSG_HDR_SIZE
+ 16,
2131 getrows
->cbreserved
);
2134 case CPMGETQUERYSTATUS
:
2136 struct wsp_cpmgetquerystatusin
*querystatus
=
2137 &request
->message
.cpmgetquerystatus
;
2138 err
= ndr_push_wsp_cpmgetquerystatusin(
2144 case CPMGETQUERYSTATUSEX
:
2146 struct wsp_cpmgetquerystatusexin
*statusexin
=
2147 &request
->message
.cpmgetquerystatusex
;
2148 err
= ndr_push_wsp_cpmgetquerystatusexin(
2156 struct wsp_cpmfreecursorin
*freecursor
=
2157 &request
->message
.cpmfreecursor
;
2158 err
= ndr_push_wsp_cpmfreecursorin(
2166 struct wsp_cpmfetchvaluein
*fetchvalue
=
2167 &request
->message
.cpmfetchvalue
;
2168 err
= ndr_push_wsp_cpmfetchvaluein(
2175 case CPMGETAPPROXIMATEPOSITION
:
2177 struct wsp_cpmgetapproximatepositionin
*position
=
2178 &request
->message
.getapproximateposition
;
2179 err
= ndr_push_wsp_cpmgetapproximatepositionin(
2186 status
= NT_STATUS_MESSAGE_NOT_FOUND
;
2191 DBG_ERR("failed to serialise message! (%d)\n", err
);
2192 status
= NT_STATUS_UNSUCCESSFUL
;
2195 if (!req_blob
.data
) {
2196 req_blob
= ndr_push_blob(push_ndr
);
2198 err
= insert_header_and_checksum(ctx
, &req_blob
, &request
->header
);
2200 DBG_NOTICE("\nsending raw message from client len %d\n", (int)req_blob
.length
);
2201 DBG_NOTICE("\nsending raw message from client\n");
2202 DBG_NOTICE( "===============================\n");
2204 dump_data(5, req_blob
.data
, req_blob
.length
);
2206 status
= write_something(ctx
, wsp_ctx
->h
, &req_blob
, &resp_blob
);
2208 if (!NT_STATUS_IS_OK(status
)) {
2209 DBG_ERR("Failed to write message\n");
2212 DBG_NOTICE("\nraw response from server\n");
2213 DBG_NOTICE( "========================\n");
2214 dump_data(5, resp_blob
.data
, resp_blob
.length
);
2216 err
= parse_blob(ctx
,
2222 DBG_ERR("Failed to parse response error %d\n", err
);
2223 status
= NT_STATUS_UNSUCCESSFUL
;
2226 DBG_NOTICE("response status is 0x%x\n", response
->header
.status
);
2227 /* propagate error status to return status */
2228 if (response
->header
.status
& 0x80000000) {
2229 status
= NT_STATUS_UNSUCCESSFUL
;
2235 struct dcerpc_binding_handle
* get_wsp_pipe(struct wsp_client_ctx
*ctx
)