/* * 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 "librpc/wsp/wsp_util.h" #include "librpc/gen_ndr/wsp.h" #include "librpc/gen_ndr/ndr_wsp.h" #include "lib/util/strv_util.h" #include "lib/util/strv.h" #include "lib/util/util_str_hex.h" #include "source3/param/param_proto.h" #include "lib/util/dlinklist.h" #define BUFFER_SIZE 1024000 struct guidtopropmap_holder { struct guidtopropmap *guidtopropmaploc; }; struct full_propset_info_list { struct full_propset_info_list *prev, *next; struct full_propset_info info; }; struct guidtopropmap { struct guidtopropmap *prev, *next; struct GUID guid; struct full_propset_info_list *propset; }; static struct guidtopropmap *find_guid_props( struct guidtopropmap_holder *holder, const struct GUID *guid) { struct guidtopropmap *mapitem; for (mapitem = holder->guidtopropmaploc; mapitem; mapitem = mapitem->next) { if (GUID_equal(guid, &mapitem->guid)) { return mapitem; } } return NULL; } static bool getbool(char *str) { char *cpy = talloc_strdup(NULL, str); bool result; trim_string(cpy, " ", " "); if (strequal("TRUE", cpy)) { result = true; } else { result = false; } TALLOC_FREE(cpy); return result; } struct { const char* typename; uint16_t type; } vtype_map[] = { {"GUID", VT_CLSID}, {"String", VT_LPWSTR}, {"BString", VT_BSTR}, {"Double", VT_R8}, {"Buffer", VT_BLOB_OBJECT}, {"Byte", VT_UI1}, {"UInt64", VT_UI8}, {"Int64", VT_I8}, {"UInt32", VT_UI4}, {"Int32", VT_I4}, {"UInt16", VT_UI2}, {"Int16", VT_I2}, {"DateTime", VT_FILETIME}, {"Boolean", VT_BOOL} }; static uint16_t getvtype(char *str, bool isvec) { uint16_t result = UINT16_MAX; int i; for (i = 0; i < ARRAY_SIZE(vtype_map); i++) { if (strequal(vtype_map[i].typename, str)) { result = vtype_map[i].type; if (isvec) { result |= VT_VECTOR; } break; } } return result; } static bool parse_csv_line(TALLOC_CTX *ctx, char **csvs, size_t num_values, struct guidtopropmap_holder *propmap_holder) { struct guidtopropmap *mapitem = NULL; struct full_propset_info_list *item = NULL; char *guid_str = NULL; struct GUID guid; bool ok; item = talloc_zero(ctx, struct full_propset_info_list); if (!item) { return false; } item->info.in_inverted_index = false; item->info.is_column = true; item->info.can_col_be_indexed = true; if (strlen(csvs[1])) { guid_str = talloc_strdup(ctx, csvs[1]); } if (!guid_str) { DBG_ERR("out of memory\n"); return false; } if (!trim_string(guid_str, "{", "}")) { return false; } if (strlen(csvs[0])) { char *tmp = talloc_strdup(item, csvs[0]); trim_string(tmp, " ", " "); item->info.name = tmp; } if (strlen(csvs[2])) { item->info.id = atoi(csvs[2]); } if (strlen(csvs[3])) { item->info.in_inverted_index = getbool(csvs[3]); } if (strlen(csvs[4])) { item->info.is_column = getbool(csvs[4]); } if (strlen(csvs[5])) { item->info.can_col_be_indexed = getbool(csvs[5]); } if (strlen(csvs[6])) { bool isvec = false; uint16_t type; if (strlen(csvs[0])) { isvec = getbool(csvs[8]); } type = getvtype(csvs[6], isvec); if (type == UINT16_MAX) { DBG_ERR("failed to parse type\n"); return false; } item->info.vtype = type; } ok = parse_guid_string(guid_str, &guid); if (!ok) { return false; } mapitem = find_guid_props(propmap_holder, &guid); if (!mapitem) { mapitem = talloc_zero(propmap_holder, struct guidtopropmap); if (!mapitem) { return false; } mapitem->guid = guid; DLIST_ADD_END(propmap_holder->guidtopropmaploc, mapitem); } talloc_steal(mapitem, item); DLIST_ADD_END(mapitem->propset, item); return true; } static bool parse_properties_line(TALLOC_CTX *ctx, const char* line, struct guidtopropmap_holder *propmap_holder) { int ret; int pos; char* strv = NULL; char** csv_line = NULL; char* t = NULL; size_t len; ret = strv_split(ctx, &strv, line, ","); if (ret != 0) { DBG_ERR("failed to split line\n"); return false; } len = strv_count(strv); if (len < 9) { DBG_WARNING("skipping line as it doesn't have " "enough fields\n"); return true; } csv_line = talloc_zero_array(ctx, char *, len); if (!csv_line) { DBG_ERR("out of memory\n"); return false; } for (pos = 0; pos < talloc_array_length(csv_line); pos++) { t = strv_next(strv, t); /* the scraped property file can have a non ascii char */ if (strlen(t) == 1 && *t == 0xa0) { csv_line[pos] = talloc_strdup(csv_line, ""); } else { csv_line[pos] = talloc_strdup(csv_line, t); } trim_string(csv_line[pos], " ", " "); } if (!parse_csv_line(csv_line, csv_line, len, propmap_holder)) { DBG_ERR("failed to parse line\n"); TALLOC_FREE(csv_line); return false; } TALLOC_FREE(csv_line); return true; } static bool parse_properties_csvfile(TALLOC_CTX *ctx, struct guidtopropmap_holder *propmap_holder, const char* filename) { char **lines = NULL; int numlines; int i; if (filename == NULL || strlen(filename) == 0) { return false; } lines = file_lines_load(filename, &numlines, BUFFER_SIZE, ctx); if (!lines) { DBG_ERR("Failed to load %s\n", filename); return false; } DBG_ERR("parsed %d lines\n", numlines); for (i = 0; i < numlines; i++) { TALLOC_CTX *line_ctx = talloc_init("line context"); if (!line_ctx) { DBG_ERR("out of memory\n"); return false; } trim_string(lines[i], " ", " "); if (lines[i][0] == '#') { DBG_WARNING("skipping comment at line %d.\n)", i); TALLOC_FREE(line_ctx); continue; } if (!parse_properties_line(line_ctx, lines[i], propmap_holder)) { DBG_ERR("Failed to parse line %d\n", i); } TALLOC_FREE(line_ctx); } return true; } static bool populate_map(struct guidtopropmap_holder *propmap_holder) { const char * path = NULL; path = lp_wsp_property_file(); /* first populate the map from property file */ if (path) { parse_properties_csvfile(propmap_holder, propmap_holder, path); } return true; } static struct guidtopropmap_holder *propmap(void) { static struct guidtopropmap_holder *holder = NULL; if (!holder) { holder = talloc_zero(NULL, struct guidtopropmap_holder); if (holder) { populate_map(holder); } } return holder; } const struct full_propset_info *get_propset_info_with_guid( const char *prop_name, struct GUID *propset_guid) { const struct full_propset_info *result = NULL; struct guidtopropmap_holder *holder = NULL; struct guidtopropmap *mapitem = NULL; size_t i; const struct full_guid_propset *guid_propset = NULL; /* search builtin props first */ for (i = 0; full_propertyset[i].prop_info != NULL; i++) { const struct full_propset_info *item = NULL; guid_propset = &full_propertyset[i]; item = guid_propset->prop_info; while (item->id) { if (strequal(prop_name, item->name)) { *propset_guid = guid_propset->guid; result = item; break; } item++; } if (result) { break; } } if (result) { return result; } /* if we didn't find a match in builtin props try the extra props */ holder = propmap(); for (mapitem = holder->guidtopropmaploc; mapitem; mapitem = mapitem->next) { struct full_propset_info_list *propitem; for (propitem = mapitem->propset; propitem; propitem = propitem->next) { if (strequal(prop_name, propitem->info.name)) { *propset_guid = mapitem->guid; result = &propitem->info; break; } } } return result; } const struct full_propset_info *get_prop_info(const char *prop_name) { const struct full_propset_info *result = NULL; struct GUID guid; result = get_propset_info_with_guid(prop_name, &guid); return result; } char *prop_from_fullprop(TALLOC_CTX *ctx, struct wsp_cfullpropspec *fullprop) { size_t i; char *result = NULL; const struct full_propset_info *item = NULL; const struct full_propset_info_list *prop_item = NULL; bool search_by_id = (fullprop->ulkind == PRSPEC_PROPID); struct guidtopropmap_holder *holder = NULL; struct guidtopropmap *mapitem = NULL; /* check builtin properties */ for (i = 0; full_propertyset[i].prop_info != NULL; i++) { /* find propset */ if (GUID_equal(&fullprop->guidpropset, &full_propertyset[i].guid)) { item = full_propertyset[i].prop_info; break; } } if (item) { while (item->id) { if (search_by_id) { if( fullprop->name_or_id.prspec == item->id) { result = talloc_strdup(ctx, item->name); break; } } else if (strcmp(item->name, fullprop->name_or_id.propname.vstring) == 0) { result = talloc_strdup(ctx, item->name); break; } item++; } } /* not found, search the extra props */ if (!result) { holder = propmap(); for (mapitem = holder->guidtopropmaploc; mapitem; mapitem = mapitem->next) { if (GUID_equal(&fullprop->guidpropset, &mapitem->guid)) { prop_item = mapitem->propset; break; } } for (;prop_item; prop_item = prop_item->next) { if (search_by_id) { if(fullprop->name_or_id.prspec == prop_item->info.id) { result = talloc_strdup(ctx, prop_item->info.name); break; } } else if (strcmp(prop_item->info.name, fullprop->name_or_id.propname.vstring) == 0) { result = talloc_strdup(ctx, prop_item->info.name); break; } } } if (!result) { result = GUID_string(ctx, &fullprop->guidpropset); if (search_by_id) { result = talloc_asprintf(result, "%s/%d", result, fullprop->name_or_id.prspec); } else { result = talloc_asprintf(result, "%s/%s", result, fullprop->name_or_id.propname.vstring); } } return result; } const char *genmeth_to_string(uint32_t genmethod) { const char *result = NULL; switch (genmethod) { case 0: result = "equals"; break; case 1: result = "starts with"; break; case 2: result = "matches inflection"; break; default: result = NULL; break; } return result; } bool is_operator(struct wsp_crestriction *restriction) { bool result; switch(restriction->ultype) { case RTAND: case RTOR: case RTNOT: result = true; break; default: result = false; break; } return result; } const char *op_as_string(struct wsp_crestriction *restriction) { const char *op = NULL; if (is_operator(restriction)) { switch(restriction->ultype) { case RTAND: op = " && "; break; case RTOR: op = " || "; break; case RTNOT: op = "!"; break; } } else if (restriction->ultype == RTPROPERTY) { struct wsp_cpropertyrestriction *prop_restr = &restriction->restriction.cpropertyrestriction; switch (prop_restr->relop & 0XF) { case PREQ: op = "="; break; case PRNE: op = "!="; break; case PRGE: op = ">="; break; case PRLE: op = "<="; break; case PRLT: op = "<"; break; case PRGT: op = ">"; break; default: break; } } else if (restriction->ultype == RTCONTENT) { struct wsp_ccontentrestriction *content = NULL; content = &restriction->restriction.ccontentrestriction; op = genmeth_to_string(content->ulgeneratemethod); } else if (restriction->ultype == RTNATLANGUAGE) { op = "="; } return op; } struct wsp_cfullpropspec *get_full_prop(struct wsp_crestriction *restriction) { struct wsp_cfullpropspec *result; switch (restriction->ultype) { case RTPROPERTY: result = &restriction->restriction.cpropertyrestriction.property; break; case RTCONTENT: result = &restriction->restriction.ccontentrestriction.property; break; case RTNATLANGUAGE: result = &restriction->restriction.cnatlanguagerestriction.property; break; default: result = NULL; break; } return result; } const char *variant_as_string(TALLOC_CTX *ctx, struct wsp_cbasestoragevariant *value, bool quote) { const char* result = NULL; switch(value->vtype) { case VT_UI1: result = talloc_asprintf(ctx, "%u", value->vvalue.vt_ui1); break; case VT_INT: case VT_I4: result = talloc_asprintf(ctx, "%d", value->vvalue.vt_i4); break; case VT_ERROR: case VT_UINT: case VT_UI4: result = talloc_asprintf(ctx, "%u", value->vvalue.vt_ui4); break; case VT_UI2: case VT_I2: result = talloc_asprintf(ctx, "%u", value->vvalue.vt_ui2); break; case VT_BOOL: result = talloc_asprintf(ctx, "%s", value->vvalue.vt_ui2 == 0xFFFF ? "true" : "false"); break; case VT_DATE: case VT_FILETIME: { NTTIME filetime = value->vvalue.vt_ui8; time_t unixtime; struct tm *tm = NULL; char datestring[256]; unixtime = nt_time_to_unix(filetime); tm = gmtime(&unixtime); strftime(datestring, sizeof(datestring), "%FT%TZ", tm); result = talloc_strdup(ctx, datestring); break; } case VT_R4: { float f; if (sizeof(f) != sizeof(value->vvalue.vt_ui4)) { DBG_ERR("can't convert float\n"); break; } memcpy((void*)&f, (void*)&value->vvalue.vt_ui4, sizeof(value->vvalue.vt_ui4)); result = talloc_asprintf(ctx, "%f", f); break; } case VT_R8: { /* should this really be unsigned ? */ double dval; if (sizeof(dval) != sizeof(value->vvalue.vt_i8)) { DBG_ERR("can't convert double\n"); break; } memcpy((void*)&dval, (void*)&value->vvalue.vt_i8, sizeof(dval)); result = talloc_asprintf(ctx, "%f", dval); break; } case VT_I8: { result = talloc_asprintf(ctx, "%" PRIi64, value->vvalue.vt_i8); break; } case VT_UI8: { result = talloc_asprintf(ctx, "%" PRIu64, value->vvalue.vt_ui8); break; } case VT_LPWSTR: result = talloc_asprintf(ctx, "%s%s%s", quote ? "\'" : "", value->vvalue.vt_lpwstr.value, quote ? "\'" : ""); break; case VT_LPWSTR | VT_VECTOR: { int num_elems = value->vvalue.vt_lpwstr_v.vvector_elements; int i; for(i = 0; i < num_elems; i++) { struct vt_lpwstr_vec *vec; const char *val; vec = &value->vvalue.vt_lpwstr_v; val = vec->vvector_data[i].value; result = talloc_asprintf(ctx, "%s%s%s%s%s", result ? result : "", i ? "," : "", quote ? "\'" : "", val, quote ? "\'" : ""); } break; } default: DBG_INFO("can't represent unsupported vtype 0x%x as string\n", value->vtype); break; } return result; } static const struct { uint32_t id; const char *name; } typename_map[] = { {VT_EMPTY, "Empty"}, {VT_NULL, "Null"}, {VT_I2, "VT_I2"}, {VT_I4, "VT_I4"}, {VT_I4, "VT_I4"}, {VT_R4, "VT_R4"}, {VT_R8, "VT_R8"}, {VT_CY, "VT_CY"}, {VT_DATE, "VT_DATE"}, {VT_BSTR, "VT_BSTR"}, {VT_I1, "VT_I1"}, {VT_UI1, "VT_UI1"}, {VT_UI2, "VT_UI2"}, {VT_UI4, "VT_UI4"}, {VT_I8, "VT_I8"}, {VT_UI8, "VT_UI8"}, {VT_INT, "VT_INT"}, {VT_UINT, "VT_UINT"}, {VT_ERROR, "VT_ERROR"}, {VT_BOOL, "VT_BOOL"}, {VT_VARIANT, "VT_VARIANT"}, {VT_DECIMAL, "VT_DECIMAL"}, {VT_FILETIME, "VT_FILETIME"}, {VT_BLOB, "VT_BLOB"}, {VT_BLOB_OBJECT, "VT_BLOB_OBJECT"}, {VT_CLSID, "VT_CLSID"}, {VT_LPSTR, "VT_LPSTR"}, {VT_LPWSTR, "VT_LPWSTR"}, {VT_COMPRESSED_LPWSTR, "VT_COMPRESSED_LPWSTR"}, }; const char *get_vtype_name(uint32_t type) { const char *type_name = NULL; static char result_buf[255]; int i; uint32_t temp = type & ~(VT_VECTOR | VT_ARRAY); for (i = 0; i < ARRAY_SIZE(typename_map); i++) { if (temp == typename_map[i].id) { type_name = typename_map[i].name; break; } } if (type & VT_VECTOR) { snprintf(result_buf, sizeof(result_buf), "Vector | %s", type_name); } else if (type & VT_ARRAY) { snprintf(result_buf, sizeof(result_buf), "Array | %s", type_name); } else { snprintf(result_buf, sizeof(result_buf), "%s", type_name); } return result_buf; } bool is_variable_size(uint16_t vtype) { bool result; switch(vtype) { case VT_LPWSTR: case VT_COMPRESSED_LPWSTR: case VT_BSTR: case VT_BLOB: case VT_BLOB_OBJECT: case VT_VARIANT: result = true; break; default: result = false; break; } return result; } const char *get_store_status(uint8_t status_byte) { const char *result; switch(status_byte) { case 0: result = "StoreStatusOk"; break; case 1: result = "StoreStatusDeferred"; break; case 2: result = "StoreStatusNull"; break; default: result = "Unknown Status"; break; } return result; } void set_variant_lpwstr(TALLOC_CTX *ctx, struct wsp_cbasestoragevariant *vvalue, const char *string_val) { vvalue->vtype = VT_LPWSTR; vvalue->vvalue.vt_lpwstr.value = talloc_strdup(ctx, string_val); } void set_variant_i4(TALLOC_CTX *ctx, struct wsp_cbasestoragevariant *vvalue, uint32_t val) { vvalue->vtype = VT_I4; vvalue->vvalue.vt_i4 = val; } void set_variant_vt_bool(TALLOC_CTX *ctx, struct wsp_cbasestoragevariant *variant, bool bval) { variant->vtype = VT_BOOL; variant->vvalue.vt_bool = bval; } static void fill_int32_vec(TALLOC_CTX* ctx, int32_t **pdest, int32_t* ivector, uint32_t elems) { int i; int32_t *dest = talloc_zero_array(ctx, int32_t, elems); for ( i = 0; i < elems; i++ ) { dest[ i ] = ivector[ i ]; } *pdest = dest; } void set_variant_i4_vector(TALLOC_CTX *ctx, struct wsp_cbasestoragevariant *variant, int32_t* ivector, uint32_t elems) { variant->vtype = VT_VECTOR | VT_I4; variant->vvalue.vt_i4_vec.vvector_elements = elems; fill_int32_vec(ctx, &variant->vvalue.vt_i4_vec.vvector_data, ivector, elems); } static void fill_string_vec(TALLOC_CTX* ctx, struct wsp_cbasestoragevariant *variant, const char **strings, uint16_t elems) { int i; variant->vvalue.vt_lpwstr_v.vvector_elements = elems; variant->vvalue.vt_lpwstr_v.vvector_data = talloc_zero_array(ctx, struct vt_lpwstr, elems); for( i = 0; i < elems; i++ ) { variant->vvalue.vt_lpwstr_v.vvector_data[ i ].value = talloc_strdup(ctx, strings[ i ]); } } static void fill_bstr_vec(TALLOC_CTX *ctx, struct vt_bstr **pvector, const char **strings, uint16_t elems) { int i; struct vt_bstr *vdata = talloc_zero_array(ctx, struct vt_bstr, elems); for( i = 0; i < elems; i++ ) { vdata [ i ].value = talloc_strdup(ctx, strings[ i ]); } *pvector = vdata; } void set_variant_bstr(TALLOC_CTX *ctx, struct wsp_cbasestoragevariant *variant, const char *string_val) { variant->vtype = VT_BSTR; variant->vvalue.vt_bstr.value = talloc_strdup(ctx, string_val); } void set_variant_lpwstr_vector(TALLOC_CTX *ctx, struct wsp_cbasestoragevariant *variant, const char **string_vals, uint32_t elems) { variant->vtype = VT_LPWSTR | VT_VECTOR; fill_string_vec(ctx, variant, string_vals, elems); } void set_variant_array_bstr(TALLOC_CTX *ctx, struct wsp_cbasestoragevariant *variant, const char **string_vals, uint16_t elems) { variant->vtype = VT_BSTR | VT_ARRAY; variant->vvalue.vt_bstr_array.cdims = 1; variant->vvalue.vt_bstr_array.ffeatures = 0; variant->vvalue.vt_bstr_array.rgsabound = talloc_zero_array(ctx, struct safearraybound, 1); variant->vvalue.vt_bstr_array.rgsabound[0].celements = elems; variant->vvalue.vt_bstr_array.rgsabound[0].ilbound = 0; variant->vvalue.vt_bstr_array.cbelements = 0; fill_bstr_vec(ctx, &variant->vvalue.vt_bstr_array.vdata, string_vals, elems); /* * if cbelements is the num bytes per elem it kindof means each * string in the array must be the same size ? */ if (elems >0) { variant->vvalue.vt_bstr_array.cbelements = strlen_m_term(variant->vvalue.vt_bstr_array.vdata[0].value)*2; } } /* create single dim array of vt_i4 */ void set_variant_array_i4(TALLOC_CTX *ctx, struct wsp_cbasestoragevariant *variant, int32_t *vals, uint16_t elems) { /* #TODO see if we can combine with other set_variant_array methods */ variant->vtype = VT_I4 | VT_ARRAY; variant->vvalue.vt_i4_array.cdims = 1; variant->vvalue.vt_i4_array.ffeatures = 0; variant->vvalue.vt_i4_array.rgsabound = talloc_zero_array(ctx, struct safearraybound, 1); variant->vvalue.vt_i4_array.rgsabound[0].celements = elems; variant->vvalue.vt_i4_array.rgsabound[0].ilbound = 0; variant->vvalue.vt_i4_array.cbelements = sizeof(uint32_t); fill_int32_vec(ctx, &variant->vvalue.vt_i4_array.vdata, vals, elems); }