/* * 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 "libcli/wsp/wsp_aqs.h" #include "libcli/wsp/wsp_aqs_parser.tab.h" #include "libcli/wsp/wsp_aqs_lexer.h" #include "librpc/wsp/wsp_util.h" #include "librpc/gen_ndr/ndr_wsp.h" #include #include int yyparse(t_select_stmt **select, yyscan_t scanner); static void reverse_cols(t_select_stmt *select) { int num_elems, fwd, rev; char **cols; if (!select->cols) { return; } num_elems = select->cols->num_cols; cols = select->cols->cols; for(fwd = 0, rev = num_elems - 1; fwd <= rev; fwd++, rev--) { char * tmp = cols[rev]; cols[rev] = cols[fwd]; cols[fwd] = tmp; } } t_select_stmt *get_wsp_sql_tree(const char *expr) { t_select_stmt *select = NULL; yyscan_t scanner; YY_BUFFER_STATE state; if (yylex_init(&scanner)) { DBG_ERR("couldn't initialize\n"); return NULL; } state = yy_scan_string(expr, scanner); if (yyparse(&select, scanner)) { DBG_ERR("some parse error\n"); return NULL; } /* * parsed columns are in reverse order to how they are specified * in the AQS like statement, reverse them again to correct this. */ reverse_cols(select); yy_delete_buffer(state, scanner); yylex_destroy(scanner); return select; } t_col_list *create_cols(TALLOC_CTX *ctx, const char *col, t_col_list *append_list) { t_col_list *cols = append_list; if (!get_prop_info(col)) { DBG_ERR("Unknown property %s\n", col); return NULL; } if (cols == NULL) { cols = talloc_zero(ctx, t_col_list); if (cols == NULL) { DBG_ERR("out of memory\n"); return NULL; } cols->num_cols = 0; cols->cols = NULL; DBG_INFO("returning new cols %p with item %s\n", cols, col); } if (col) { int old_index = cols->num_cols; if (old_index == 0) { cols->cols = talloc_array(cols, char*, 1); } else { cols->cols = (char **)talloc_realloc(cols, cols->cols, char*, old_index + 1); } if (!cols->cols) { return NULL; /* can we create a parser error here */ } cols->num_cols++; cols->cols[old_index] = talloc_strdup(cols, col); if (cols->cols[old_index] == NULL) { DBG_ERR("out of memory\n"); return NULL; } } return cols; } t_select_stmt *create_select(TALLOC_CTX *ctx, t_col_list *cols, t_query *where) { t_select_stmt *result = talloc_zero(ctx, t_select_stmt); if (result == NULL) { DBG_ERR("out of memory\n"); return NULL; } result->cols = cols; result->where = where; return result; } t_basic_restr *create_basic_restr(TALLOC_CTX *ctx, uint32_t prop_type, t_optype op, t_value_holder *values) { t_basic_restr *result = talloc_zero(ctx, t_basic_restr); if (result == NULL) { DBG_ERR("out of memory\n"); return NULL; } result->prop_type = prop_type; result->op = op; if (values->type == VALUE_RANGE) { t_restr *left_node; t_restr *right_node; t_basic_restr *left_val; t_basic_restr *right_val; if (op != eEQ) { DBG_ERR("Unsupported operation %d\n", op); TALLOC_FREE(result); goto out; } if (values->value.value_range->lower == NULL) { DBG_ERR("range lower limit doesn't exist\n"); TALLOC_FREE(result); goto out; } /* * detect special case where upper range doesn't exist * and convert to a property value. (this won't happen from * the cmdline directly but only as a result of a range * created 'specially' in code, e.g. special gigantic size * range. */ if (values->value.value_range->upper == NULL) { result->op = eGE; result->values = values->value.value_range->lower; goto out; } result->values = talloc_zero(result, t_value_holder); if (result->values == NULL) { DBG_ERR("out of memory\n"); TALLOC_FREE(result); goto out; } /* * try create a restriction tree (>=lower AND values, prop_type, eGE, values->value.value_range->lower); right_val = create_basic_restr(result->values, prop_type, eLT, values->value.value_range->upper); if (!left_val || !right_val) { DBG_ERR("Failed creating basic_restriction values " "for range\n"); TALLOC_FREE(result); goto out; } left_node = create_restr(result->values, eVALUE, NULL, NULL, left_val); right_node = create_restr(result->values, eVALUE, NULL, NULL, right_val); if (!left_node || !right_node) { DBG_ERR("Failed creating restr nodes for range\n"); TALLOC_FREE(result); goto out; } result->values->type = RESTR; result->values->value.restr_tree = create_restr(result->values, eAND, left_node, right_node, NULL); if (!result->values->value.restr_tree) { DBG_ERR("Failed creating restr tree for range\n"); TALLOC_FREE(result); goto out; } } else { result->values = values; } out: return result; } /* * The parser reads numbers as VT_UI8, booleans as VT_BOOL and strings as * VT_LPWSTR */ typedef bool (*conv_func) (TALLOC_CTX *ctx, t_value_holder *src, struct wsp_cbasestoragevariant *dest, uint16_t dest_type); /* * default converter #TODO probably should cater for detecting over/underrun * depending on the dest_type we are narrowing to */ static bool default_convertor(TALLOC_CTX *ctx, t_value_holder *src, struct wsp_cbasestoragevariant *dest, uint16_t dest_type) { if (src->type != NUMBER) { return false; } dest->vvalue.vt_ui8 = src->value.number; dest->vtype = dest_type; return true; } static bool convert_string_to_lpwstr_v(TALLOC_CTX *ctx, t_value_holder *src, struct wsp_cbasestoragevariant *dest, uint16_t dest_type) { const char *str = src->value.string; set_variant_lpwstr_vector(ctx, dest, &str, 1); return true; } static bool convert_string_to_lpwstr(TALLOC_CTX *ctx, t_value_holder *src, struct wsp_cbasestoragevariant *dest, uint16_t dest_type) { const char *str = src->value.string; set_variant_lpwstr(ctx, dest, str); return true; } static bool convert_bool_to_lpwstr(TALLOC_CTX *ctx, t_value_holder *src, struct wsp_cbasestoragevariant *dest, uint16_t dest_type) { set_variant_lpwstr( ctx, dest, src->value.boolean ? "true": "false"); return true; } static bool convert_string_to_filetime(TALLOC_CTX *ctx, t_value_holder *src, struct wsp_cbasestoragevariant *dest, uint16_t dest_type) { static const char *fmts[] = { "%FT%TZ", "%FT%T", "%F %T", "%F %R", "%F", }; struct tm tm; time_t timeval = 0; int i; ZERO_STRUCT(tm); for (i = 0; i < ARRAY_SIZE(fmts); i++) { if (strptime(src->value.string, fmts[i], &tm)) { timeval = timegm(&tm); break; } } if (timeval) { NTTIME nt; unix_to_nt_time(&nt, timeval); dest->vtype = VT_FILETIME; dest->vvalue.vt_filetime = nt; return true; } return false; } const struct { uint16_t src_vtype; uint16_t dest_vtype; conv_func convert_type; } type_conv_map[] = { {NUMBER, VT_I8, default_convertor}, {NUMBER, VT_UI8, default_convertor}, {NUMBER, VT_INT, default_convertor}, {NUMBER, VT_UINT, default_convertor}, {NUMBER, VT_I4, default_convertor}, {NUMBER, VT_UI4, default_convertor}, {NUMBER, VT_I2, default_convertor}, {NUMBER, VT_UI2, default_convertor}, {NUMBER, VT_BOOL, default_convertor}, {NUMBER, VT_FILETIME, default_convertor}, {NUMBER, VT_BOOL, default_convertor}, {BOOL, VT_LPWSTR, convert_bool_to_lpwstr}, {STRING, VT_LPWSTR, convert_string_to_lpwstr}, {STRING, VT_LPWSTR | VT_VECTOR, convert_string_to_lpwstr_v}, {STRING, VT_FILETIME, convert_string_to_filetime}, }; static bool process_prop_value(TALLOC_CTX *ctx, const struct full_propset_info *prop_info, t_value_holder *node_value, struct wsp_cbasestoragevariant *prop_value) { int i; /* coerce type as required */ for (i = 0; i < ARRAY_SIZE(type_conv_map); i++ ) { if (type_conv_map[i].src_vtype == node_value->type && type_conv_map[i].dest_vtype == prop_info->vtype) { type_conv_map[i].convert_type(ctx, node_value, prop_value, prop_info->vtype); return true; } } return false; } t_basic_query *create_basic_query(TALLOC_CTX *ctx, const char *propname, t_basic_restr *restr) { t_basic_query *result = talloc_zero(ctx, t_basic_query); if (result == NULL) { DBG_ERR("out of memory\n"); goto out; } result->prop = talloc_strdup(result, propname); result->prop_info = get_propset_info_with_guid(propname, &result->guid); if (!result->prop_info) { DBG_ERR("Unknown property %s\n",propname); TALLOC_FREE(result); goto out; } result->basic_restriction = restr; out: return result; } static struct wsp_crestriction *create_restriction(TALLOC_CTX *ctx, t_basic_query *query) { struct wsp_crestriction *crestriction = NULL; struct wsp_cfullpropspec *prop = NULL; t_basic_restr *restr = NULL; t_value_holder *src = NULL; crestriction = talloc_zero(ctx, struct wsp_crestriction); if (crestriction == NULL) { DBG_ERR("out of memory\n"); goto done; } restr = query->basic_restriction; src = restr->values; if (restr->prop_type == RTNONE) { /* shouldn't end up here */ DBG_ERR("Unexpected t_basic_restr type\n"); TALLOC_FREE(crestriction); goto done; } crestriction->weight = 1000; if (restr->prop_type == RTCONTENT) { struct wsp_ccontentrestriction *content = NULL; crestriction->ultype = RTCONTENT; if (src->type != STRING) { DBG_ERR("expected string value for %s\n", query->prop); TALLOC_FREE(crestriction); goto done; } content = &crestriction->restriction.ccontentrestriction; content->pwcsphrase = src->value.string; content->cc = strlen(src->value.string); /* * In the future we might generate the lcid from * environ (or config) */ content->lcid = WSP_DEFAULT_LCID; if (restr->op == eEQUALS) { content->ulgeneratemethod = 0; } else { content->ulgeneratemethod = 1; } prop = &content->property; } else if (restr->prop_type == RTPROPERTY) { struct wsp_cbasestoragevariant *dest = &crestriction->restriction.cpropertyrestriction.prval; crestriction->ultype = RTPROPERTY; if (!process_prop_value(ctx, query->prop_info, src, dest)) { DBG_ERR("Failed to process value for property %s\n", query->prop); TALLOC_FREE(crestriction); goto done; } crestriction->restriction.cpropertyrestriction.relop = restr->op; prop = &crestriction->restriction.cpropertyrestriction.property; } else { TALLOC_FREE(crestriction); goto done; } prop->guidpropset = query->guid; prop->ulkind = PRSPEC_PROPID; prop->name_or_id.prspec = query->prop_info->id; done: return crestriction; } /* expands restr_node into a tree of t_query nodes */ static void build_query(TALLOC_CTX *ctx, t_query *node, t_restr *restr_node, const char* prop) { if (!node) { return; } if (!restr_node) { return; } node->type = restr_node->type; if (restr_node->left) { node->left = talloc_zero(ctx, t_query); SMB_ASSERT(node->left != NULL); build_query(ctx, node->left, restr_node->left, prop); } if (restr_node->right) { node->right = talloc_zero(ctx, t_query); SMB_ASSERT(node->right != NULL); build_query(ctx, node->right, restr_node->right, prop); } if (restr_node->type == eVALUE) { node->restriction = create_restriction(ctx, create_basic_query(ctx, prop, restr_node->basic_restr)); } } t_query *create_query_node(TALLOC_CTX *ctx, t_nodetype op, t_query *left, t_query *right, t_basic_query *value) { t_query *result = talloc_zero(ctx, t_query); if (result == NULL) { return result; } result->type = op; result->left = left; result->right = right; if (op == eVALUE) { t_basic_restr *restr = value->basic_restriction; /* expand restr node */ if (restr->values->type == RESTR) { build_query(ctx, result, restr->values->value.restr_tree, value->prop); } else { result->restriction = create_restriction(ctx, value); if (!result->restriction) { TALLOC_FREE(result); } } } return result; } t_restr *create_restr(TALLOC_CTX *ctx, t_nodetype op, t_restr *left, t_restr *right, t_basic_restr *value) { t_restr *result = talloc_zero(ctx, t_restr); if (result == NULL) { return result; } result->type = op; result->right = right; result->left = left; result->basic_restr = value; return result; } t_value_holder *create_string_val(TALLOC_CTX* ctx, const char *text) { t_value_holder *result = talloc_zero(ctx, t_value_holder); if (result == NULL) { DBG_ERR("out of memory\n"); return NULL; } result->value.string = text; result->type = STRING; return result; } t_value_holder *create_num_val(TALLOC_CTX* ctx, int64_t val) { t_value_holder *result = talloc_zero(ctx, t_value_holder); if (result == NULL) { DBG_ERR("out of memory\n"); return NULL; } result->type = NUMBER; result->value.number = val; return result; } t_value_holder *create_bool_val(TALLOC_CTX* ctx, bool val) { t_value_holder *result = talloc_zero(ctx, t_value_holder); if (result == NULL) { DBG_ERR("out of memory\n"); return NULL; } result->type = BOOL; result->value.boolean = val; return result; } t_value_holder *create_value_range(TALLOC_CTX* ctx, t_value_holder *left, t_value_holder *right) { t_value_holder *result = talloc_zero(ctx, t_value_holder); if (result == NULL) { DBG_ERR("out of memory\n"); return NULL; } result->type = VALUE_RANGE; result->value.value_range = talloc_zero(result, struct value_range); if (!result->value.value_range) { TALLOC_FREE(result); goto out; } result->value.value_range->lower = left; result->value.value_range->upper = right; out: return result; } static void zero_time(struct tm *tm) { tm->tm_hour = 0; tm->tm_min = 0; tm->tm_sec = 0; } typedef bool (*daterange_func) (TALLOC_CTX *ctx, uint64_t *date1, uint64_t *date2); static bool create_date_range(TALLOC_CTX *ctx, uint64_t *date1, uint64_t *date2, int32_t lower_mday_adj, int32_t lower_mon_adj, int32_t upper_mday_adj, int32_t upper_mon_adj) { struct tm tm_now; time_t now; struct tm tm_tmp; time_t lower; time_t upper; time(&now); gmtime_r(&now, &tm_now); tm_tmp = tm_now; zero_time(&tm_tmp); tm_tmp.tm_mday += lower_mday_adj; tm_tmp.tm_mon += lower_mon_adj; lower = mktime(&tm_tmp); tm_tmp = tm_now; zero_time(&tm_tmp); tm_tmp.tm_mday += upper_mday_adj; tm_tmp.tm_mon += upper_mon_adj; upper = mktime(&tm_tmp); unix_to_nt_time(date1, lower); unix_to_nt_time(date2, upper); return true; } static void get_now_tm(struct tm *tm_now) { time_t now; time(&now); gmtime_r(&now, tm_now); } static bool create_thismonth_range(TALLOC_CTX *ctx, uint64_t *date1, uint64_t *date2) { struct tm tm_now; int32_t firstofmonth_adj; get_now_tm(&tm_now); firstofmonth_adj = 1 - tm_now.tm_mday; return create_date_range(ctx, date1, date2, firstofmonth_adj, 0, firstofmonth_adj, 1); } static bool create_lastyear_range(TALLOC_CTX *ctx, uint64_t *date1, uint64_t *date2) { struct tm tm_now; int32_t firstofmonth_adj; int32_t january_adj; get_now_tm(&tm_now); firstofmonth_adj = 1 - tm_now.tm_mday; january_adj = -tm_now.tm_mon; return create_date_range(ctx, date1, date2, firstofmonth_adj, january_adj - 12, firstofmonth_adj, january_adj); } static bool create_thisyear_range(TALLOC_CTX *ctx, uint64_t *date1, uint64_t *date2) { struct tm tm_now; int32_t firstofmonth_adj; int32_t january_adj; get_now_tm(&tm_now); firstofmonth_adj = 1 - tm_now.tm_mday; january_adj = -tm_now.tm_mon; return create_date_range(ctx, date1, date2, firstofmonth_adj, january_adj, firstofmonth_adj, january_adj + 12); } static bool create_lastmonth_range(TALLOC_CTX *ctx, uint64_t *date1, uint64_t *date2) { struct tm tm_now; int32_t firstofmonth_adj; get_now_tm(&tm_now); firstofmonth_adj = 1 - tm_now.tm_mday; return create_date_range(ctx, date1, date2, firstofmonth_adj, -1, firstofmonth_adj, 0); } static bool create_today_range(TALLOC_CTX *ctx, uint64_t *date1, uint64_t *date2) { return create_date_range(ctx, date1, date2, 0, 0, 1, 0); } static bool create_yesterday_range(TALLOC_CTX *ctx, uint64_t *date1, uint64_t *date2) { return create_date_range(ctx, date1, date2, -1, 0, 0, 0); } static bool create_thisweek_range(TALLOC_CTX *ctx, uint64_t *date1, uint64_t *date2) { struct tm tm_now; time_t now; int32_t startofweek_adj; time(&now); gmtime_r(&now, &tm_now); if (tm_now.tm_wday) { startofweek_adj = 1 - tm_now.tm_wday; } else { startofweek_adj = -6; } /* lower will be the start of this week */ return create_date_range(ctx, date1, date2, startofweek_adj, 0, startofweek_adj + 7, 0); } static bool create_lastweek_range(TALLOC_CTX *ctx, uint64_t *date1, uint64_t *date2) { struct tm tm_now; time_t now; int32_t startofweek_adj; time(&now); gmtime_r(&now, &tm_now); if (tm_now.tm_wday) { startofweek_adj = 1 - tm_now.tm_wday; } else { startofweek_adj = -6; } /* upper will be the start of this week */ return create_date_range(ctx, date1, date2, startofweek_adj - 7, 0,startofweek_adj, 0); } t_value_holder *create_date_range_shortcut(TALLOC_CTX *ctx, daterange_type daterange) { int i; static const struct { daterange_type range; daterange_func create_fn; } date_conv_map[] = { {eYESTERDAY, create_yesterday_range}, {eTODAY, create_today_range}, {eTHISMONTH, create_thismonth_range}, {eLASTMONTH, create_lastmonth_range}, {eTHISWEEK, create_thisweek_range}, {eLASTWEEK, create_lastweek_range}, {eTHISYEAR, create_thisyear_range}, {eLASTYEAR, create_lastyear_range}, }; t_value_holder *result = NULL; t_value_holder *lower = NULL; t_value_holder *upper = NULL; lower = talloc_zero(ctx, t_value_holder); if (lower == NULL) { DBG_ERR("out of memory\n"); goto out; } upper = talloc_zero(ctx, t_value_holder); if (upper == NULL) { DBG_ERR("out of memory\n"); goto out; } result = create_value_range(result, lower, upper); if (result == NULL) { TALLOC_FREE(result); goto out; } lower->type = NUMBER; upper->type = NUMBER; result->value.value_range->lower = lower; result->value.value_range->upper = upper; for (i = 0; i < ARRAY_SIZE(date_conv_map); i++) { if (date_conv_map[i].range == daterange) { if (!date_conv_map[i].create_fn(result, &lower->value.number, &upper->value.number)) { TALLOC_FREE(result); break; } break; } } out: return result; } t_value_holder *create_size_range_shortcut(TALLOC_CTX *ctx, sizerange_type sizerange) { static const struct { sizerange_type range; uint32_t lower; uint32_t upper; } sizes[] = { {eEMPTY, 0x0, 0x1}, {eTINY, 0x1, 0x2801}, {eSMALL, 0x2801, 0x19001}, {eMEDIUM, 0x19001, 0x100001}, {eLARGE, 0x100001, 0x10000001}, {eHUGE, 0x10000001, 0x80000001}, {eGIGANTIC, 0x80000001, 0} /* special case not a range */ }; int i; t_value_holder *result = NULL; uint32_t lower_size; uint32_t upper_size; bool rangefound = false; t_value_holder *left = NULL; t_value_holder *right = NULL; for (i = 0; i < ARRAY_SIZE(sizes); i++) { if (sizes[i].range == sizerange) { result = talloc_zero(ctx, t_value_holder); if (result == NULL) { DBG_ERR("out of memory\n"); return NULL; } lower_size = sizes[i].lower; upper_size = sizes[i].upper; rangefound = true; break; } } if (!rangefound) { return NULL; } left = talloc_zero(ctx, t_value_holder); if (left == NULL) { return NULL; } left->type = NUMBER; left->value.number = lower_size; if (upper_size) { right = talloc_zero(ctx, t_value_holder); if (right == NULL) { return NULL; } right->type = NUMBER; right->value.number = upper_size; } result = create_value_range(ctx, left, right); return result; }