int cli_bm_initoff(const struct cli_matcher *root, struct cli_bm_off *data, const struct cli_target_info *info) { int ret; unsigned int i; struct cli_bm_patt *patt; if(!root->bm_patterns) { data->offtab = data->offset = NULL; data->cnt = data->pos = 0; return CL_SUCCESS; } data->cnt = data->pos = 0; data->offtab = (uint32_t *) cli_malloc(root->bm_patterns * sizeof(uint32_t)); if(!data->offtab) { cli_errmsg("cli_bm_initoff: Can't allocate memory for data->offtab\n"); return CL_EMEM; } data->offset = (uint32_t *) cli_malloc(root->bm_patterns * sizeof(uint32_t)); if(!data->offset) { cli_errmsg("cli_bm_initoff: Can't allocate memory for data->offset\n"); free(data->offtab); return CL_EMEM; } for(i = 0; i < root->bm_patterns; i++) { patt = root->bm_pattab[i]; if(patt->offdata[0] == CLI_OFF_ABSOLUTE) { data->offtab[data->cnt] = patt->offset_min + patt->prefix_length; if(data->offtab[data->cnt] >= info->fsize) continue; data->cnt++; } else if((ret = cli_caloff(NULL, info, root->type, patt->offdata, &data->offset[patt->offset_min], NULL))) { cli_errmsg("cli_bm_initoff: Can't calculate relative offset in signature for %s\n", patt->virname); free(data->offtab); free(data->offset); return ret; } else if((data->offset[patt->offset_min] != CLI_OFF_NONE) && (data->offset[patt->offset_min] + patt->length <= info->fsize)) { if(!data->cnt || (data->offset[patt->offset_min] + patt->prefix_length != data->offtab[data->cnt - 1])) { data->offtab[data->cnt] = data->offset[patt->offset_min] + patt->prefix_length; if(data->offtab[data->cnt] >= info->fsize) continue; data->cnt++; } } } cli_qsort(data->offtab, data->cnt, sizeof(uint32_t), NULL); return CL_SUCCESS; }
/* TODO - handle VI and Macro offset types */ int cli_pcre_recaloff(struct cli_matcher *root, struct cli_pcre_off *data, struct cli_target_info *info, cli_ctx *ctx) { /* TANGENT: maintain relative offset data in cli_ac_data? */ int ret; unsigned int i; struct cli_pcre_meta *pm; uint32_t endoff; if (!data) { return CL_ENULLARG; } if (!root || !root->pcre_metatable || !info || (ctx && ctx->dconf && !(ctx->dconf->pcre & PCRE_CONF_SUPPORT))) { data->shift = NULL; data->offset = NULL; return CL_SUCCESS; } /* allocate data structures */ data->shift = (uint32_t *) cli_calloc(root->pcre_metas, sizeof(uint32_t)); if (!data->shift) { cli_errmsg("cli_pcre_initoff: cannot allocate memory for data->shift\n"); return CL_EMEM; } data->offset = (uint32_t *) cli_calloc(root->pcre_metas, sizeof(uint32_t)); if (!data->offset) { cli_errmsg("cli_pcre_initoff: cannot allocate memory for data->offset\n"); free(data->shift); return CL_EMEM; } pm_dbgmsg("CLI_OFF_NONE: %u\n", CLI_OFF_NONE); pm_dbgmsg("CLI_OFF_ANY: %u\n", CLI_OFF_ANY); /* iterate across all pcre metadata and recalc offsets */ for (i = 0; i < root->pcre_metas; ++i) { pm = root->pcre_metatable[i]; /* skip broken pcres, not getting executed anyways */ if (pm->flags & CLI_PCRE_DISABLED) { data->offset[i] = CLI_OFF_NONE; data->shift[i] = 0; continue; } if (pm->offdata[0] == CLI_OFF_ANY) { data->offset[i] = CLI_OFF_ANY; data->shift[i] = 0; } else if (pm->offdata[0] == CLI_OFF_NONE) { data->offset[i] = CLI_OFF_NONE; data->shift[i] = 0; } else if (pm->offdata[0] == CLI_OFF_ABSOLUTE) { data->offset[i] = pm->offdata[1]; data->shift[i] = pm->offdata[2]; } else { ret = cli_caloff(NULL, info, root->type, pm->offdata, &data->offset[i], &endoff); if (ret != CL_SUCCESS) { cli_errmsg("cli_pcre_recaloff: cannot recalculate relative offset for signature\n"); free(data->shift); free(data->offset); return ret; } /* CLI_OFF_NONE gets passed down, CLI_OFF_ANY gets reinterpreted */ /* TODO - CLI_OFF_VERSION is interpreted as CLI_OFF_ANY(?) */ if (data->offset[i] == CLI_OFF_ANY) { data->offset[i] = CLI_OFF_ANY; data->shift[i] = 0; } else { data->shift[i] = endoff-(data->offset[i]); } } pm_dbgmsg("%u: %u %u->%u(+%u)\n", i, pm->offdata[0], data->offset[i], data->offset[i]+data->shift[i], data->shift[i]); } return CL_SUCCESS; }
int cli_pcre_addpatt(struct cli_matcher *root, const char *virname, const char *trigger, const char *pattern, const char *cflags, const char *offset, const uint32_t *lsigid, unsigned int options) { struct cli_pcre_meta **newmetatable = NULL, *pm = NULL; uint32_t pcre_count; const char *opt; int ret = CL_SUCCESS, rssigs; if (!root || !trigger || !pattern || !offset) { cli_errmsg("cli_pcre_addpatt: NULL root or NULL trigger or NULL pattern or NULL offset\n"); return CL_ENULLARG; } /* TODO: trigger and regex checking (backreference limitations?) (control pattern limitations?) */ /* cli_ac_chklsig will fail a empty trigger; empty patterns can cause an infinite loop */ if (*trigger == '\0' || *pattern == '\0') { cli_errmsg("cli_pcre_addpatt: trigger or pattern cannot be an empty string\n"); return CL_EMALFDB; } if (cflags && *cflags == '\0') { cflags = NULL; } if (lsigid) pm_dbgmsg("cli_pcre_addpatt: Adding /%s/%s%s triggered on (%s) as subsig %d for lsigid %d\n", pattern, cflags ? " with flags " : "", cflags ? cflags : "", trigger, lsigid[1], lsigid[0]); else pm_dbgmsg("cli_pcre_addpatt: Adding /%s/%s%s triggered on (%s) [no lsigid]\n", pattern, cflags ? " with flags " : "", cflags ? cflags : "", trigger); #ifdef PCRE_BYPASS /* check for trigger bypass */ if (strcmp(trigger, PCRE_BYPASS)) { #endif /* validate the lsig trigger */ rssigs = cli_ac_chklsig(trigger, trigger + strlen(trigger), NULL, NULL, NULL, 1); if(rssigs == -1) { cli_errmsg("cli_pcre_addpatt: regex subsig /%s/ is missing a valid logical trigger\n", pattern); return CL_EMALFDB; } if (lsigid) { if (rssigs > lsigid[1]) { cli_errmsg("cli_pcre_addpatt: regex subsig %d logical trigger refers to subsequent subsig %d\n", lsigid[1], rssigs); return CL_EMALFDB; } if (rssigs == lsigid[1]) { cli_errmsg("cli_pcre_addpatt: regex subsig %d logical trigger is self-referential\n", lsigid[1]); return CL_EMALFDB; } } else { cli_dbgmsg("cli_pcre_addpatt: regex subsig is missing lsigid data\n"); } #ifdef PCRE_BYPASS } #endif /* allocating entries */ pm = (struct cli_pcre_meta *)mpool_calloc(root->mempool, 1, sizeof(*pm)); if (!pm) { cli_errmsg("cli_pcre_addpatt: Unable to allocate memory for new pcre meta\n"); return CL_EMEM; } pm->trigger = cli_mpool_strdup(root->mempool, trigger); if (!pm->trigger) { cli_errmsg("cli_pcre_addpatt: Unable to allocate memory for trigger string\n"); cli_pcre_freemeta(root, pm); mpool_free(root->mempool, pm); return CL_EMEM; } pm->virname = (char *)cli_mpool_virname(root->mempool, virname, options & CL_DB_OFFICIAL); if(!pm->virname) { cli_errmsg("cli_pcre_addpatt: Unable to allocate memory for virname or NULL virname\n"); cli_pcre_freemeta(root, pm); mpool_free(root->mempool, pm); return CL_EMEM; } if (lsigid) { root->ac_lsigtable[lsigid[0]]->virname = pm->virname; pm->lsigid[0] = 1; pm->lsigid[1] = lsigid[0]; pm->lsigid[2] = lsigid[1]; } else { /* sigtool */ pm->lsigid[0] = 0; } pm->pdata.expression = strdup(pattern); if (!pm->pdata.expression) { cli_errmsg("cli_pcre_addpatt: Unable to allocate memory for expression\n"); cli_pcre_freemeta(root, pm); mpool_free(root->mempool, pm); return CL_EMEM; } /* offset parsing and usage, similar to cli_ac_addsig */ /* relative and type-specific offsets handled during scan */ ret = cli_caloff(offset, NULL, root->type, pm->offdata, &(pm->offset_min), &(pm->offset_max)); if (ret != CL_SUCCESS) { cli_errmsg("cli_pcre_addpatt: cannot calculate offset data: %s for pattern: %s\n", offset, pattern); cli_pcre_freemeta(root, pm); mpool_free(root->mempool, pm); return ret; } if(pm->offdata[0] != CLI_OFF_ANY) { if(pm->offdata[0] == CLI_OFF_ABSOLUTE) root->pcre_absoff_num++; else root->pcre_reloff_num++; } /* parse and add options, also totally not from snort */ if (cflags) { opt = cflags; /* cli_pcre_addoptions handles pcre specific options */ while (cli_pcre_addoptions(&(pm->pdata), &opt, 0) != CL_SUCCESS) { /* handle matcher specific options here */ switch (*opt) { case 'g': pm->flags |= CLI_PCRE_GLOBAL; break; case 'r': pm->flags |= CLI_PCRE_ROLLING; break; case 'e': pm->flags |= CLI_PCRE_ENCOMPASS; break; default: cli_errmsg("cli_pcre_addpatt: unknown/extra pcre option encountered %c\n", *opt); cli_pcre_freemeta(root, pm); mpool_free(root->mempool, pm); return CL_EMALFDB; } opt++; } if (pm->flags) { pm_dbgmsg("Matcher: %s%s%s\n", pm->flags & CLI_PCRE_GLOBAL ? "CLAMAV_GLOBAL " : "", pm->flags & CLI_PCRE_ROLLING ? "CLAMAV_ROLLING " : "", pm->flags & CLI_PCRE_ENCOMPASS ? "CLAMAV_ENCOMPASS " : ""); } else pm_dbgmsg("Matcher: NONE\n"); if (pm->pdata.options) { #if USING_PCRE2 pm_dbgmsg("Compiler: %s%s%s%s%s%s%s\n", pm->pdata.options & PCRE2_CASELESS ? "PCRE2_CASELESS " : "", pm->pdata.options & PCRE2_DOTALL ? "PCRE2_DOTALL " : "", pm->pdata.options & PCRE2_MULTILINE ? "PCRE2_MULTILINE " : "", pm->pdata.options & PCRE2_EXTENDED ? "PCRE2_EXTENDED " : "", pm->pdata.options & PCRE2_ANCHORED ? "PCRE2_ANCHORED " : "", pm->pdata.options & PCRE2_DOLLAR_ENDONLY ? "PCRE2_DOLLAR_ENDONLY " : "", pm->pdata.options & PCRE2_UNGREEDY ? "PCRE2_UNGREEDY " : ""); #else pm_dbgmsg("Compiler: %s%s%s%s%s%s%s\n", pm->pdata.options & PCRE_CASELESS ? "PCRE_CASELESS " : "", pm->pdata.options & PCRE_DOTALL ? "PCRE_DOTALL " : "", pm->pdata.options & PCRE_MULTILINE ? "PCRE_MULTILINE " : "", pm->pdata.options & PCRE_EXTENDED ? "PCRE_EXTENDED " : "", pm->pdata.options & PCRE_ANCHORED ? "PCRE_ANCHORED " : "", pm->pdata.options & PCRE_DOLLAR_ENDONLY ? "PCRE_DOLLAR_ENDONLY " : "", pm->pdata.options & PCRE_UNGREEDY ? "PCRE_UNGREEDY " : ""); #endif } else pm_dbgmsg("Compiler: NONE\n"); } /* add metadata to the performance tracker */ if (options & CL_DB_PCRE_STATS) pcre_perf_events_init(pm, virname); /* add pcre data to root after reallocation */ pcre_count = root->pcre_metas+1; newmetatable = (struct cli_pcre_meta **)mpool_realloc(root->mempool, root->pcre_metatable, pcre_count * sizeof(struct cli_pcre_meta *)); if (!newmetatable) { cli_errmsg("cli_pcre_addpatt: Unable to allocate memory for new pcre meta table\n"); cli_pcre_freemeta(root, pm); mpool_free(root->mempool, pm); return CL_EMEM; } newmetatable[pcre_count-1] = pm; root->pcre_metatable = newmetatable; root->pcre_metas = pcre_count; return CL_SUCCESS; }
int cli_bm_addpatt(struct cli_matcher *root, struct cli_bm_patt *pattern, const char *offset) { uint16_t idx, i; const unsigned char *pt = pattern->pattern; struct cli_bm_patt *prev, *next = NULL; int ret; if(pattern->length < BM_MIN_LENGTH) { cli_errmsg("cli_bm_addpatt: Signature for %s is too short\n", pattern->virname); return CL_EMALFDB; } if((ret = cli_caloff(offset, NULL, root->type, pattern->offdata, &pattern->offset_min, &pattern->offset_max))) { cli_errmsg("cli_bm_addpatt: Can't calculate offset for signature %s\n", pattern->virname); return ret; } if(pattern->offdata[0] != CLI_OFF_ANY) { if(pattern->offdata[0] == CLI_OFF_ABSOLUTE) root->bm_absoff_num++; else root->bm_reloff_num++; } /* bm_offmode doesn't use the prefilter for BM signatures anyway, so * don't add these to the filter. */ if(root->filter && !root->bm_offmode) { /* the bm_suffix load balancing below can shorten the sig, * we want to see the entire signature! */ if (filter_add_static(root->filter, pattern->pattern, pattern->length, pattern->virname) == -1) { cli_warnmsg("cli_bm_addpatt: cannot use filter for trie\n"); mpool_free(root->mempool, root->filter); root->filter = NULL; } /* TODO: should this affect maxpatlen? */ } #if BM_MIN_LENGTH == BM_BLOCK_SIZE /* try to load balance bm_suffix (at the cost of bm_shift) */ for(i = 0; i < pattern->length - BM_BLOCK_SIZE + 1; i++) { idx = HASH(pt[i], pt[i + 1], pt[i + 2]); if(!root->bm_suffix[idx]) { if(i) { pattern->prefix = pattern->pattern; pattern->prefix_length = i; pattern->pattern = &pattern->pattern[i]; pattern->length -= i; pt = pattern->pattern; } break; } } #endif for(i = 0; i <= BM_MIN_LENGTH - BM_BLOCK_SIZE; i++) { idx = HASH(pt[i], pt[i + 1], pt[i + 2]); root->bm_shift[idx] = MIN(root->bm_shift[idx], BM_MIN_LENGTH - BM_BLOCK_SIZE - i); } prev = next = root->bm_suffix[idx]; while(next) { if(pt[0] >= next->pattern0) break; prev = next; next = next->next; } if(next == root->bm_suffix[idx]) { pattern->next = root->bm_suffix[idx]; if(root->bm_suffix[idx]) pattern->cnt = root->bm_suffix[idx]->cnt; root->bm_suffix[idx] = pattern; } else { pattern->next = prev->next; prev->next = pattern; } pattern->pattern0 = pattern->pattern[0]; root->bm_suffix[idx]->cnt++; if(root->bm_offmode) { root->bm_pattab = (struct cli_bm_patt **) mpool_realloc2(root->mempool, root->bm_pattab, (root->bm_patterns + 1) * sizeof(struct cli_bm_patt *)); if(!root->bm_pattab) { cli_errmsg("cli_bm_addpatt: Can't allocate memory for root->bm_pattab\n"); return CL_EMEM; } root->bm_pattab[root->bm_patterns] = pattern; if(pattern->offdata[0] != CLI_OFF_ABSOLUTE) pattern->offset_min = root->bm_patterns; } root->bm_patterns++; return CL_SUCCESS; }
int cli_bm_scanbuff(const unsigned char *buffer, uint32_t length, const char **virname, const struct cli_bm_patt **patt, const struct cli_matcher *root, uint32_t offset, const struct cli_target_info *info, struct cli_bm_off *offdata, uint32_t *viroffset) { uint32_t i, j, off, off_min, off_max; uint8_t found, pchain, shift; uint16_t idx, idxchk; struct cli_bm_patt *p; const unsigned char *bp, *pt; unsigned char prefix; int ret; if(!root || !root->bm_shift) return CL_CLEAN; if(length < BM_MIN_LENGTH) return CL_CLEAN; i = BM_MIN_LENGTH - BM_BLOCK_SIZE; if(offdata) { if(!offdata->cnt) return CL_CLEAN; if(offdata->pos == offdata->cnt) offdata->pos--; for(; offdata->pos && offdata->offtab[offdata->pos] > offset; offdata->pos--); if(offdata->offtab[offdata->pos] < offset) offdata->pos++; if(offdata->pos >= offdata->cnt) return CL_CLEAN; i += offdata->offtab[offdata->pos] - offset; } for(; i < length - BM_BLOCK_SIZE + 1; ) { idx = HASH(buffer[i], buffer[i + 1], buffer[i + 2]); shift = root->bm_shift[idx]; if(shift == 0) { prefix = buffer[i - BM_MIN_LENGTH + BM_BLOCK_SIZE]; p = root->bm_suffix[idx]; if(p && p->cnt == 1 && p->pattern0 != prefix) { if(offdata) { off = offset + i - BM_MIN_LENGTH + BM_BLOCK_SIZE; for(; offdata->pos < offdata->cnt && off >= offdata->offtab[offdata->pos]; offdata->pos++); if(offdata->pos == offdata->cnt || off >= offdata->offtab[offdata->pos]) return CL_CLEAN; i += offdata->offtab[offdata->pos] - off; } else { i++; } continue; } pchain = 0; while(p) { if(p->pattern0 != prefix) { if(pchain) break; p = p->next; continue; } else pchain = 1; off = i - BM_MIN_LENGTH + BM_BLOCK_SIZE; bp = buffer + off; if((off + p->length > length) || (p->prefix_length > off)) { p = p->next; continue; } if(offdata) { if(p->offdata[0] == CLI_OFF_ABSOLUTE) { if(p->offset_min != offset + off - p->prefix_length) { p = p->next; continue; } } else if((offdata->offset[p->offset_min] == CLI_OFF_NONE) || (offdata->offset[p->offset_min] != offset + off - p->prefix_length)) { p = p->next; continue; } } idxchk = MIN(p->length, length - off) - 1; if(idxchk) { if((bp[idxchk] != p->pattern[idxchk]) || (bp[idxchk / 2] != p->pattern[idxchk / 2])) { p = p->next; continue; } } if(p->prefix_length) { off -= p->prefix_length; bp -= p->prefix_length; pt = p->prefix; } else { pt = p->pattern; } found = 1; for(j = 0; j < p->length + p->prefix_length && off < length; j++, off++) { if(bp[j] != pt[j]) { found = 0; break; } } if(found && (p->boundary & BM_BOUNDARY_EOL)) { if(off != length) { p = p->next; continue; } } if(found && p->length + p->prefix_length == j) { if(!offdata && (p->offset_min != CLI_OFF_ANY)) { if(p->offdata[0] != CLI_OFF_ABSOLUTE) { if(!info) { p = p->next; continue; } ret = cli_caloff(NULL, info, root->type, p->offdata, &off_min, &off_max); if(ret != CL_SUCCESS) { cli_errmsg("cli_bm_scanbuff: Can't calculate relative offset in signature for %s\n", p->virname); return ret; } } else { off_min = p->offset_min; off_max = p->offset_max; } off = offset + i - p->prefix_length - BM_MIN_LENGTH + BM_BLOCK_SIZE; if(off_min == CLI_OFF_NONE || off_max < off || off_min > off) { p = p->next; continue; } } if(virname) { *virname = p->virname; if(viroffset) *viroffset = offset + i + j - BM_MIN_LENGTH + BM_BLOCK_SIZE; } if(patt) *patt = p; return CL_VIRUS; } p = p->next; } shift = 1; } if(offdata) { off = offset + i - BM_MIN_LENGTH + BM_BLOCK_SIZE; for(; offdata->pos < offdata->cnt && off >= offdata->offtab[offdata->pos]; offdata->pos++); if(offdata->pos == offdata->cnt || off >= offdata->offtab[offdata->pos]) return CL_CLEAN; i += offdata->offtab[offdata->pos] - off; } else { i += shift; } } return CL_CLEAN; }