/* * 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 "lib/util/debug.h" #include "lib/cmdline/cmdline.h" #include "lib/cmdline_contexts.h" #include "param.h" #include "client.h" #include "libsmb/proto.h" #include "librpc/rpc/rpc_common.h" #include "librpc/wsp/wsp_util.h" #include "rpc_client/cli_pipe.h" #include "rpc_client/wsp_cli.h" #include "libcli/wsp/wsp_aqs.h" #include "librpc/gen_ndr/ndr_wsp.h" #include "librpc/gen_ndr/ndr_wsp_data.h" #include "dcerpc.h" #define WIN_VERSION_64 0x10000 /* send connectin message */ static NTSTATUS wsp_connect(TALLOC_CTX *ctx, struct wsp_client_ctx *wsp_ctx, const char* clientmachine, const char* clientuser, const char* server, bool *is_64bit) { struct wsp_request *request = NULL; struct wsp_response *response = NULL; uint32_t client_ver; uint32_t server_ver; DATA_BLOB unread = data_blob_null; NTSTATUS status; TALLOC_CTX *local_ctx = talloc_new(ctx); if (local_ctx == NULL) { status = NT_STATUS_NO_MEMORY; goto out; } response = talloc_zero(local_ctx, struct wsp_response); if (!response) { status = NT_STATUS_NO_MEMORY; goto out; } request = talloc_zero(local_ctx, struct wsp_request); if (!request) { status = NT_STATUS_NO_MEMORY; goto out; } if (!init_connectin_request(local_ctx, request, clientmachine, clientuser, server)) { DBG_ERR("Failed in initialise connection message\n"); status = NT_STATUS_INVALID_PARAMETER; goto out; } status = wsp_request_response(local_ctx, wsp_ctx, request, response, &unread); if (NT_STATUS_IS_OK(status)) { client_ver = request->message.cpmconnect.iclientversion; server_ver = response->message.cpmconnect.server_version; *is_64bit = (server_ver & WIN_VERSION_64) && (client_ver & WIN_VERSION_64); } out: data_blob_free(&unread); TALLOC_FREE(local_ctx); return status; } static NTSTATUS create_query(TALLOC_CTX *ctx, struct wsp_client_ctx *wsp_ctx, uint32_t limit, t_select_stmt *select, uint32_t *single_cursor) { struct wsp_request *request = NULL; struct wsp_response *response = NULL; NTSTATUS status; DATA_BLOB unread = data_blob_null; TALLOC_CTX *local_ctx = talloc_new(ctx); if (local_ctx == NULL) { status = NT_STATUS_NO_MEMORY; goto out; } request = talloc_zero(local_ctx, struct wsp_request); if (!request) { status = NT_STATUS_NO_MEMORY; goto out; } response = talloc_zero(local_ctx, struct wsp_response); if (!response) { status = NT_STATUS_NO_MEMORY; goto out;; } if (!create_querysearch_request(ctx, request, select)) { DBG_ERR("error setting up query request message\n"); status = NT_STATUS_INVALID_PARAMETER; goto out; } request->message.cpmcreatequery.rowsetproperties.cmaxresults = limit; status = wsp_request_response(local_ctx, wsp_ctx, request, response, &unread); if (NT_STATUS_IS_OK(status)) { if (unread.length == 4) { *single_cursor = IVAL(unread.data, 0); } } out: data_blob_free(&unread); TALLOC_FREE(local_ctx); return status; } static NTSTATUS create_bindings(TALLOC_CTX *ctx, struct wsp_client_ctx *wsp_ctx, t_select_stmt *select, uint32_t cursor, struct wsp_cpmsetbindingsin *bindings_out, bool is_64bit) { struct wsp_request *request = NULL; struct wsp_response *response = NULL; NTSTATUS status; DATA_BLOB unread = data_blob_null; request = talloc_zero(ctx, struct wsp_request); if (!request) { status = NT_STATUS_NO_MEMORY; goto out; } response = talloc_zero(ctx, struct wsp_response); if (!response) { status = NT_STATUS_NO_MEMORY; goto out; } if (!create_setbindings_request(ctx, request, select, cursor, is_64bit)) { DBG_ERR("Failed to create setbindings message\n"); status = NT_STATUS_INVALID_PARAMETER; goto out; } status = wsp_request_response(ctx, wsp_ctx, request, response, &unread); if (NT_STATUS_IS_OK(status)) { *bindings_out = request->message.cpmsetbindings; } out: data_blob_free(&unread); return status; } static NTSTATUS create_querystatusex(TALLOC_CTX *ctx, struct wsp_client_ctx *wsp_ctx, uint32_t cursor, uint32_t *nrows) { struct wsp_request *request = NULL; struct wsp_response *response = NULL; struct wsp_cpmgetquerystatusexin *statusexin = NULL; NTSTATUS status; DATA_BLOB unread = data_blob_null; TALLOC_CTX *local_ctx = talloc_new(ctx); if (local_ctx == NULL) { status = NT_STATUS_NO_MEMORY; goto out; } request = talloc_zero(local_ctx, struct wsp_request); if (!request) { status = NT_STATUS_NO_MEMORY; goto out; } response = talloc_zero(local_ctx, struct wsp_response); if (!response) { status = NT_STATUS_NO_MEMORY; goto out; } statusexin = &request->message.cpmgetquerystatusex; request->header.msg = CPMGETQUERYSTATUSEX; statusexin->hcursor = cursor; statusexin->bmk = 0xfffffffc; status = wsp_request_response(local_ctx, wsp_ctx, request, response, &unread); if (NT_STATUS_IS_OK(status)) { *nrows = response->message.cpmgetquerystatusex.resultsfound; } out: data_blob_free(&unread); TALLOC_FREE(local_ctx); return status; } static NTSTATUS print_rowsreturned( TALLOC_CTX *ctx, DATA_BLOB *buffer, bool is_64bit, bool disp_all_cols, struct wsp_cpmsetbindingsin *bindings, uint32_t cbreserved, uint64_t address, uint32_t rowsreturned, uint32_t *rows_processed) { NTSTATUS status; uint32_t row = 0; TALLOC_CTX *local_ctx = NULL; struct wsp_cbasestoragevariant **rowsarray = NULL; enum ndr_err_code err; local_ctx = talloc_init("results"); if (local_ctx == NULL) { status = NT_STATUS_NO_MEMORY; goto out; } rowsarray = talloc_zero_array(local_ctx, struct wsp_cbasestoragevariant*, rowsreturned); if (rowsarray == NULL) { status = NT_STATUS_NO_MEMORY; goto out; } err = extract_rowsarray(rowsarray, buffer, is_64bit, bindings, cbreserved, address, rowsreturned, rowsarray); if (err) { DBG_ERR("failed to extract rows from getrows response\n"); status = NT_STATUS_UNSUCCESSFUL; goto out; } for(row = 0; row < rowsreturned; row++) { TALLOC_CTX *row_ctx = NULL; const char *col_str = NULL; row_ctx = talloc_init("row"); if (row_ctx == NULL) { status = NT_STATUS_NO_MEMORY; goto out; } if (disp_all_cols) { int i; for (i = 0; i < bindings->ccolumns; i++){ col_str = variant_as_string( row_ctx, &rowsarray[row][i], true); if (col_str) { printf("%s%s", i ? ", " : "", col_str); } else { printf("%sN/A", i ? ", " : ""); } } } else { col_str = variant_as_string( row_ctx, &rowsarray[row][0], true); printf("%s", col_str); } printf("\n"); TALLOC_FREE(row_ctx); } status = NT_STATUS_OK; out: TALLOC_FREE(local_ctx); *rows_processed = row; return status; } static NTSTATUS create_getrows(TALLOC_CTX *ctx, struct wsp_client_ctx *wsp_ctx, struct wsp_cpmsetbindingsin *bindings, uint32_t cursor, uint32_t nrows, bool disp_all_cols, bool is_64bit) { struct wsp_request *request = NULL; struct wsp_response *response = NULL; NTSTATUS status; DATA_BLOB unread = data_blob_null; uint32_t bmk = 0xfffffffc; uint32_t skip = 0; uint32_t total_rows = 0; uint32_t INITIAL_ROWS = 32; uint32_t requested_rows = INITIAL_ROWS; uint32_t rows_printed; uint64_t baseaddress; uint32_t offset_lowbits = 0xdeabd860; uint32_t offset_hibits = 0xfeeddeaf; TALLOC_CTX *row_ctx; bool loop_again; do { row_ctx = talloc_new(NULL); if (!row_ctx) { status = NT_STATUS_UNSUCCESSFUL; goto out; } request = talloc_zero(row_ctx, struct wsp_request); if (!request) { status = NT_STATUS_NO_MEMORY; goto out; } response = talloc_zero(row_ctx, struct wsp_response); if (!response) { status = NT_STATUS_NO_MEMORY; goto out; } create_seekat_getrows_request(request, request, cursor, bmk, skip, requested_rows, 40, offset_lowbits, bindings->brow, 0); if (is_64bit) { /* * MS-WSP 2.2.2 * ulreservered holds the high 32-bits part of * a 64-bit offset if 64-bit offsets are being used. */ request->header.ulreserved2 = offset_hibits; baseaddress = request->header.ulreserved2; baseaddress <<= 32; baseaddress += offset_lowbits; } else { baseaddress = offset_lowbits; } status = wsp_request_response(request, wsp_ctx, request, response, &unread); if (!NT_STATUS_IS_OK(status)) { goto out; } total_rows += response->message.cpmgetrows.rowsreturned; if (response->message.cpmgetrows.rowsreturned != requested_rows) { uint32_t rowsreturned = response->message.cpmgetrows.rowsreturned; if (response->message.cpmgetrows.etype == EROWSEEKAT) { struct wsp_cpmgetrowsout *resp; struct wsp_crowseekat *seekat; resp = &response->message.cpmgetrows; seekat = &resp->seekdescription.crowseekat; bmk = seekat->bmkoffset; skip = seekat->cskip; } else { bmk = 0xfffffffc; skip = total_rows; } requested_rows = requested_rows - rowsreturned; } else { requested_rows = INITIAL_ROWS; bmk = 0xfffffffc; skip = total_rows; } if (response->message.cpmgetrows.rowsreturned) { status = print_rowsreturned(row_ctx, &unread, is_64bit, disp_all_cols, bindings, 40, baseaddress, response->message.cpmgetrows.rowsreturned, &rows_printed); if (!NT_STATUS_IS_OK(status)) { goto out; } data_blob_free(&unread); } /* * response is a talloc child of row_ctx so we need to * assign loop_again before we delete row_ctx */ loop_again = response->message.cpmgetrows.rowsreturned; TALLOC_FREE(row_ctx); if (nrows && total_rows > nrows) { DBG_ERR("Something is wrong, results returned %d " "exceed expected number of results %d\n", total_rows, nrows); status = NT_STATUS_UNSUCCESSFUL; goto out; } } while (loop_again); out: data_blob_free(&unread); TALLOC_FREE(row_ctx); return status; } const char *default_column = "System.ItemUrl"; static bool is_valid_kind(const char *kind) { const char* kinds[] = {"calendar", "communication", "contact", "document", "email", "feed", "folder", "game", "instantMessage", "journal", "link", "movie", "music", "note", "picture", "program", "recordedtv", "searchfolder", "task", "video", "webhistory"}; char* search_kind = NULL; int i; bool found = false; search_kind = strlower_talloc(NULL, kind); if (search_kind == NULL) { DBG_ERR("couldn't convert %s to lower case\n", kind); return NULL; } for (i=0; icols == NULL) { select_stmt->cols = talloc_zero(select_stmt, t_col_list); if (select_stmt->cols == NULL) { DBG_ERR("out of memory\n"); result = -1; goto out; } select_stmt->cols->num_cols = 1; select_stmt->cols->cols = talloc_zero_array(select_stmt->cols, char*, 1); if (select_stmt->cols->cols == NULL) { DBG_ERR("out of memory\n"); result = -1; goto out; } select_stmt->cols->cols[0] = talloc_strdup(select_stmt->cols, default_column); } status = cli_full_connection_creds(&c, lp_netbios_name(), server, NULL, 0, "IPC$", "IPC", samba_cmdline_get_creds(), flags); if (!NT_STATUS_IS_OK(status)) { DBG_ERR("failed to connect to IPC$: %s\n", nt_errstr(status)); result = -1; goto out; } status = wsp_server_connect(talloc_tos(), server, ev_ctx, samba_cmdline_get_lp_ctx(), samba_cmdline_get_creds(), c, &wsp_ctx); if (!NT_STATUS_IS_OK(status)) { DBG_ERR("failed to connect to wsp: %s\n", nt_errstr(status)); result = -1; goto out; } h = get_wsp_pipe(wsp_ctx); if (h == NULL) { DBG_ERR("Failed to communicate with server, no pipe\n"); result = -1; goto out; } dcerpc_binding_handle_set_timeout(h, DCERPC_REQUEST_TIMEOUT * 1000); /* connect */ DBG_INFO("sending connect\n"); status = wsp_connect(talloc_tos(), wsp_ctx, lpcfg_netbios_name(samba_cmdline_get_lp_ctx()), cli_credentials_get_username( samba_cmdline_get_creds()), server, &is_64bit); if (!NT_STATUS_IS_OK(status)) { DBG_ERR("failed to connect to wsp: %s\n", nt_errstr(status)); result = -1; goto out; } DBG_INFO("sending query\n"); status = create_query(talloc_tos(), wsp_ctx, limit, select_stmt, &cursor); if (!NT_STATUS_IS_OK(status)) { DBG_ERR("failed to send query: %s)\n", nt_errstr(status)); result = -1; goto out; } DBG_INFO("sending createbindings\n"); /* set bindings */ status = create_bindings(talloc_tos(), wsp_ctx, select_stmt, cursor, &bindings_used, is_64bit); if (!NT_STATUS_IS_OK(status)) { DBG_ERR("failed to setbindings: %s)\n", nt_errstr(status)); result = -1; goto out; } status = create_querystatusex(talloc_tos(), wsp_ctx, bindings_used.hcursor, &nrows); if (!nrows) { result = 0; DBG_ERR("no results found\n"); goto out; } printf("found %d results, returning %d \n", nrows, limit ? MIN(nrows, limit) : nrows); status = create_getrows(talloc_tos(), wsp_ctx, &bindings_used, bindings_used.hcursor, limit ? MIN(nrows, limit) : nrows, custom_query, is_64bit); if (!NT_STATUS_IS_OK(status)) { DBG_ERR("Failed to retrieve rows, error: %s\n", nt_errstr(status)); result = -1; goto out; } result = 0; out: TALLOC_FREE(frame); return result; }