00001 //--------------------------------------------------------------------------
00002 // Copyright (C) 2016-2017 Cisco and/or its affiliates. All rights reserved.
00003 //
00004 // This program is free software; you can redistribute it and/or modify it
00005 // under the terms of the GNU General Public License Version 2 as published
00006 // by the Free Software Foundation. You may not use, modify or distribute
00007 // this program under any other version of the GNU General Public License.
00008 //
00009 // This program is distributed in the hope that it will be useful, but
00010 // WITHOUT ANY WARRANTY; without even the implied warranty of
00011 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
00012 // General Public License for more details.
00013 //
00014 // You should have received a copy of the GNU General Public License along
00015 // with this program; if not, write to the Free Software Foundation, Inc.,
00016 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00017 //--------------------------------------------------------------------------
00018
00019 #ifdef HAVE_CONFIG_H
00020 #include "config.h"
00021 #endif
00022
00023 #include "fp_utils.h"
00024
00025 #include <cassert>
00026 #include <cstring>
00027
00028 #include "ips_options/ips_flow.h"
00029 #include "log/messages.h"
00030 #include "main/thread_config.h"
00031 #include "ports/port_group.h"
00032 #include "target_based/snort_protocols.h"
00033 #include "utils/util.h"
00034
00035 #ifdef UNIT_TEST
00036 #include "catch/catch.hpp"
00037 #endif
00038
00039 #include "pattern_match_data.h"
00040 #include "treenodes.h"
00041
00042 //--------------------------------------------------------------------------
00043 // private utilities
00044 //--------------------------------------------------------------------------
00045
00046 static void finalize_content(OptFpList* ofl)
00047 {
00048 PatternMatchData* pmd = get_pmd(ofl, 0, RULE_WO_DIR);
00049
00050 if ( !pmd )
00051 return;
00052
00053 if ( pmd->is_negated() )
00054 pmd->last_check = (PmdLastCheck*)snort_calloc(
00055 ThreadConfig::get_instance_max(), sizeof(*pmd->last_check));
00056 }
00057
00058 static void clear_fast_pattern_only(OptFpList* ofl)
00059 {
00060 PatternMatchData* pmd = get_pmd(ofl, 0, RULE_WO_DIR);
00061
00062 if ( pmd && pmd->fp_only > 0 )
00063 pmd->fp_only = 0;
00064 }
00065
00066 static bool pmd_can_be_fp(
00067 PatternMatchData* pmd, CursorActionType cat, bool only_literals)
00068 {
00069 if ( cat <= CAT_SET_OTHER )
00070 return false;
00071
00072 if ( only_literals and !pmd->is_literal() )
00073 return false;
00074
00075 return pmd->can_be_fp();
00076 }
00077
00078 static PmType get_pm_type(CursorActionType cat)
00079 {
00080 switch ( cat )
00081 {
00082 case CAT_SET_RAW:
00083 case CAT_SET_OTHER:
00084 return PM_TYPE_PKT;
00085
00086 case CAT_SET_BODY:
00087 return PM_TYPE_BODY;
00088
00089 case CAT_SET_HEADER:
00090 return PM_TYPE_HEADER;
00091
00092 case CAT_SET_KEY:
00093 return PM_TYPE_KEY;
00094
00095 case CAT_SET_FILE:
00096 return PM_TYPE_FILE;
00097
00098 default:
00099 break;
00100 }
00101 assert(false);
00102 return PM_TYPE_MAX;
00103 }
00104
00105 static RuleDirection get_dir(OptTreeNode* otn)
00106 {
00107 if ( OtnFlowFromServer(otn) )
00108 return RULE_FROM_SERVER;
00109
00110 if ( OtnFlowFromClient(otn) )
00111 return RULE_FROM_CLIENT;
00112
00113 return RULE_WO_DIR;
00114 }
00115
00116 //--------------------------------------------------------------------------
00117 // public utilities
00118 //--------------------------------------------------------------------------
00119
00120 PatternMatchData* get_pmd(OptFpList* ofl, int proto, RuleDirection direction)
00121 {
00122 if ( !ofl->ips_opt )
00123 return nullptr;
00124
00125 return ofl->ips_opt->get_pattern(proto, direction);
00126 }
00127
00128 bool is_fast_pattern_only(OptFpList* ofl)
00129 {
00130 PatternMatchData* pmd = get_pmd(ofl, 0, RULE_WO_DIR);
00131
00132 if ( !pmd )
00133 return false;
00134
00135 return pmd->fp_only > 0;
00136 }
00137
00138 /*
00139 * Trim zero byte prefixes, this increases uniqueness
00140 * will not alter regex since they can't contain bald \0
00141 *
00142 * returns
00143 * length - of trimmed pattern
00144 * buff - ptr to new beginning of trimmed buffer
00145 */
00146 int flp_trim(const char* p, int plen, const char** buff)
00147 {
00148 int i;
00149 int size = 0;
00150
00151 if ( !p )
00152 return 0;
00153
00154 for (i=0; i<plen; i++)
00155 {
00156 if ( p[i] != 0 )
00157 break;
00158 }
00159
00160 if ( i < plen )
00161 size = plen - i;
00162 else
00163 size = 0;
00164
00165 if ( buff && (size==0) )
00166 {
00167 *buff = nullptr;
00168 }
00169 else if ( buff )
00170 {
00171 *buff = &p[i];
00172 }
00173 return size;
00174 }
00175
00176 void validate_fast_pattern(OptTreeNode* otn)
00177 {
00178 OptFpList* fp = nullptr;
00179 bool relative_is_bad_mkay = false;
00180
00181 for (OptFpList* fpl = otn->opt_func; fpl; fpl = fpl->next)
00182 {
00183 // a relative option is following a fast_pattern/only and
00184 if ( relative_is_bad_mkay )
00185 {
00186 if (fpl->isRelative)
00187 {
00188 assert(fp);
00189 clear_fast_pattern_only(fp);
00190 }
00191 }
00192
00193 // reset the check if one of these are present.
00194 if ( fpl->ips_opt and !fpl->ips_opt->get_pattern(0))
00195 {
00196 if ( fpl->ips_opt->get_cursor_type() > CAT_NONE )
00197 relative_is_bad_mkay = false;
00198 }
00199 // set/unset the check on content options.
00200 else
00201 {
00202 if ( is_fast_pattern_only(fpl) )
00203 {
00204 fp = fpl;
00205 relative_is_bad_mkay = true;
00206 }
00207 else
00208 relative_is_bad_mkay = false;
00209 }
00210 finalize_content(fpl);
00211 }
00212 }
00213
00214 //--------------------------------------------------------------------------
00215 // class to help determine which of two candidate patterns is better for
00216 // a rule that does not have a valid fast_pattern flag.
00217 //--------------------------------------------------------------------------
00218
00219 struct FpSelector
00220 {
00221 CursorActionType cat;
00222 PatternMatchData* pmd;
00223 int size;
00224
00225 FpSelector(CursorActionType, PatternMatchData*);
00226
00227 FpSelector()
00228 { cat = CAT_NONE; pmd = nullptr; size = 0; }
00229
00230 bool is_better_than(FpSelector&, bool srvc, RuleDirection, bool only_literals = false);
00231 };
00232
00233 FpSelector::FpSelector(CursorActionType c, PatternMatchData* p)
00234 {
00235 cat = c;
00236 pmd = p;
00237
00238 // FIXIT-H unconditional trim is bad mkay? see fpGetFinalPattern
00239 size = flp_trim(pmd->pattern_buf, pmd->pattern_size, nullptr);
00240 }
00241
00242 bool FpSelector::is_better_than(
00243 FpSelector& rhs, bool srvc, RuleDirection dir, bool only_literals)
00244 {
00245 if ( !pmd_can_be_fp(pmd, cat, only_literals) )
00246 {
00247 if ( pmd->is_fast_pattern() )
00248 {
00249 ParseWarning(WARN_RULES, "content ineligible for fast_pattern matcher - ignored");
00250 pmd->flags &= ~PatternMatchData::FAST_PAT;
00251 }
00252 return false;
00253 }
00254
00255 if ( !rhs.pmd )
00256 return true;
00257
00258 if ( !srvc )
00259 {
00260 if ( cat == CAT_SET_RAW and rhs.cat != CAT_SET_RAW )
00261 return true;
00262
00263 if ( cat != CAT_SET_RAW and rhs.cat == CAT_SET_RAW )
00264 return false;
00265 }
00266 else if ( dir == RULE_FROM_SERVER )
00267 {
00268 if ( cat != CAT_SET_KEY and rhs.cat == CAT_SET_KEY )
00269 return true;
00270
00271 if ( cat == CAT_SET_KEY and rhs.cat != CAT_SET_KEY )
00272 return false;
00273 }
00274 if ( pmd->is_fast_pattern() )
00275 {
00276 if ( rhs.pmd->is_fast_pattern() )
00277 {
00278 ParseWarning(WARN_RULES,
00279 "only one fast_pattern content per rule allowed - using first");
00280 pmd->flags &= ~PatternMatchData::FAST_PAT;
00281 return false;
00282 }
00283 return true;
00284 }
00285 if ( rhs.pmd->is_fast_pattern() )
00286 return false;
00287
00288 if ( !pmd->is_negated() && rhs.pmd->is_negated() )
00289 return true;
00290
00291 if ( size > rhs.size )
00292 return true;
00293
00294 return false;
00295 }
00296
00297 //--------------------------------------------------------------------------
00298 // public methods
00299 //--------------------------------------------------------------------------
00300
00301 PatternMatchVector get_fp_content(
00302 OptTreeNode* otn, OptFpList*& next, bool srvc, bool only_literals)
00303 {
00304 CursorActionType curr_cat = CAT_SET_RAW;
00305 FpSelector best;
00306 bool content = false;
00307 bool fp_only = true;
00308 PatternMatchVector pmds;
00309
00310 for (OptFpList* ofl = otn->opt_func; ofl; ofl = ofl->next)
00311 {
00312 if ( !ofl->ips_opt )
00313 continue;
00314
00315 CursorActionType cat = ofl->ips_opt->get_cursor_type();
00316
00317 if ( cat > CAT_ADJUST )
00318 {
00319 curr_cat = cat;
00320 fp_only = !ofl->ips_opt->fp_research();
00321 }
00322
00323 RuleDirection dir = get_dir(otn);
00324 PatternMatchData* tmp = get_pmd(ofl, otn->proto, dir);
00325
00326 if ( !tmp )
00327 continue;
00328
00329 content = true;
00330
00331 if ( !fp_only )
00332 tmp->fp_only = -1;
00333
00334 tmp->pm_type = get_pm_type(curr_cat);
00335
00336 FpSelector curr(curr_cat, tmp);
00337
00338 if ( curr.is_better_than(best, srvc, dir, only_literals) )
00339 {
00340 best = curr;
00341 next = ofl->next;
00342 pmds.clear();
00343 // Add alternate pattern
00344 PatternMatchData* alt_pmd = ofl->ips_opt->get_alternate_pattern();
00345 if (alt_pmd)
00346 pmds.push_back(alt_pmd);
00347 // Add main pattern last
00348 pmds.push_back(best.pmd);
00349 }
00350 }
00351
00352 if ( best.pmd and otn->proto == SNORT_PROTO_FILE and best.cat != CAT_SET_FILE )
00353 {
00354 ParseWarning(WARN_RULES, "file rule %u:%u does not have file_data fast pattern",
00355 otn->sigInfo.gid, otn->sigInfo.sid);
00356 pmds.clear();
00357 }
00358
00359 if ( content && !best.pmd)
00360 ParseWarning(WARN_RULES, "content based rule %u:%u has no eligible fast pattern",
00361 otn->sigInfo.gid, otn->sigInfo.sid);
00362
00363 return pmds;
00364 }
00365
00366 //--------------------------------------------------------------------------
00367 // unit tests
00368 //--------------------------------------------------------------------------
00369
00370 #ifdef UNIT_TEST
00371 static void set_pmd(PatternMatchData& pmd, unsigned flags, const char* s)
00372 {
00373 memset(&pmd, 0, sizeof(pmd));
00374
00375 if ( flags & 0x01 )
00376 pmd.set_negated();
00377 if ( flags & 0x02 )
00378 pmd.set_no_case();
00379 if ( flags & 0x04 )
00380 pmd.set_relative();
00381 if ( flags & 0x08 )
00382 pmd.set_literal();
00383 if ( flags & 0x10 )
00384 pmd.set_fast_pattern();
00385
00386 pmd.pattern_buf = s;
00387 pmd.pattern_size = strlen(s);
00388 }
00389
00390 TEST_CASE("pmd_no_options", "[PatternMatchData]")
00391 {
00392 PatternMatchData pmd;
00393 set_pmd(pmd, 0x0, "foo");
00394 CHECK(pmd.can_be_fp());
00395 }
00396
00397 TEST_CASE("pmd_negated", "[PatternMatchData]")
00398 {
00399 PatternMatchData pmd;
00400 set_pmd(pmd, 0x1, "foo");
00401 CHECK(!pmd.can_be_fp());
00402 }
00403
00404 TEST_CASE("pmd_no_case", "[PatternMatchData]")
00405 {
00406 PatternMatchData pmd;
00407 set_pmd(pmd, 0x2, "foo");
00408 CHECK(pmd.can_be_fp());
00409 }
00410
00411 TEST_CASE("pmd_relative", "[PatternMatchData]")
00412 {
00413 PatternMatchData pmd;
00414 set_pmd(pmd, 0x4, "foo");
00415 CHECK(pmd.can_be_fp());
00416 }
00417
00418 TEST_CASE("pmd_negated_no_case", "[PatternMatchData]")
00419 {
00420 PatternMatchData pmd;
00421 set_pmd(pmd, 0x3, "foo");
00422 CHECK(pmd.can_be_fp());
00423 }
00424
00425 TEST_CASE("pmd_negated_relative", "[PatternMatchData]")
00426 {
00427 PatternMatchData pmd;
00428 set_pmd(pmd, 0x5, "foo");
00429 CHECK(!pmd.can_be_fp());
00430 }
00431
00432 TEST_CASE("pmd_negated_no_case_offset", "[PatternMatchData]")
00433 {
00434 PatternMatchData pmd;
00435 set_pmd(pmd, 0x1, "foo");
00436 pmd.offset = 3;
00437 CHECK(!pmd.can_be_fp());
00438 }
00439
00440 TEST_CASE("pmd_negated_no_case_depth", "[PatternMatchData]")
00441 {
00442 PatternMatchData pmd;
00443 set_pmd(pmd, 0x3, "foo");
00444 pmd.depth = 1;
00445 CHECK(!pmd.can_be_fp());
00446 }
00447
00448 TEST_CASE("fp_simple", "[FastPatternSelect]")
00449 {
00450 FpSelector test;
00451 PatternMatchData pmd;
00452 set_pmd(pmd, 0x0, "foo");
00453 FpSelector left(CAT_SET_RAW, &pmd);
00454 CHECK(left.is_better_than(test, false, RULE_WO_DIR));
00455
00456 test.size = 1;
00457 CHECK(left.is_better_than(test, false, RULE_WO_DIR));
00458 }
00459
00460 TEST_CASE("fp_negated", "[FastPatternSelect]")
00461 {
00462 PatternMatchData p0;
00463 set_pmd(p0, 0x0, "foo");
00464 FpSelector s0(CAT_SET_RAW, &p0);
00465
00466 PatternMatchData p1;
00467 set_pmd(p1, 0x1, "foo");
00468 FpSelector s1(CAT_SET_RAW, &p1);
00469
00470 CHECK(s0.is_better_than(s1, false, RULE_WO_DIR));
00471 CHECK(!s1.is_better_than(s0, false, RULE_WO_DIR));
00472 }
00473
00474 TEST_CASE("fp_cat1", "[FastPatternSelect]")
00475 {
00476 PatternMatchData p0;
00477 set_pmd(p0, 0x0, "longer");
00478 FpSelector s0(CAT_SET_FILE, &p0);
00479
00480 PatternMatchData p1;
00481 set_pmd(p1, 0x0, "short");
00482 FpSelector s1(CAT_SET_BODY, &p1);
00483
00484 CHECK(s0.is_better_than(s1, true, RULE_WO_DIR));
00485 }
00486
00487 TEST_CASE("fp_cat2", "[FastPatternSelect]")
00488 {
00489 PatternMatchData p0;
00490 set_pmd(p0, 0x0, "foo");
00491 FpSelector s0(CAT_SET_RAW, &p0);
00492
00493 PatternMatchData p1;
00494 set_pmd(p1, 0x0, "foo");
00495 FpSelector s1(CAT_SET_FILE, &p1);
00496
00497 CHECK(s0.is_better_than(s1, false, RULE_WO_DIR));
00498 CHECK(!s1.is_better_than(s0, false, RULE_WO_DIR));
00499 }
00500
00501 TEST_CASE("fp_cat3", "[FastPatternSelect]")
00502 {
00503 PatternMatchData p0;
00504 set_pmd(p0, 0x0, "foo");
00505 FpSelector s0(CAT_SET_RAW, &p0);
00506
00507 PatternMatchData p1;
00508 set_pmd(p1, 0x0, "foo");
00509 FpSelector s1(CAT_SET_FILE, &p1);
00510
00511 CHECK(!s0.is_better_than(s1, true, RULE_WO_DIR));
00512 }
00513
00514 TEST_CASE("fp_size", "[FastPatternSelect]")
00515 {
00516 PatternMatchData p0;
00517 set_pmd(p0, 0x0, "longer");
00518 FpSelector s0(CAT_SET_HEADER, &p0);
00519
00520 PatternMatchData p1;
00521 set_pmd(p1, 0x0, "short");
00522 FpSelector s1(CAT_SET_HEADER, &p1);
00523
00524 CHECK(s0.is_better_than(s1, false, RULE_WO_DIR));
00525 }
00526
00527 TEST_CASE("fp_pkt_key_port", "[FastPatternSelect]")
00528 {
00529 PatternMatchData p0;
00530 set_pmd(p0, 0x0, "short");
00531 FpSelector s0(CAT_SET_RAW, &p0);
00532
00533 PatternMatchData p1;
00534 set_pmd(p1, 0x0, "longer");
00535 FpSelector s1(CAT_SET_KEY, &p1);
00536
00537 CHECK(s0.is_better_than(s1, false, RULE_WO_DIR));
00538 }
00539
00540 TEST_CASE("fp_pkt_key_port_user", "[FastPatternSelect]")
00541 {
00542 PatternMatchData p0;
00543 set_pmd(p0, 0x10, "short");
00544 FpSelector s0(CAT_SET_KEY, &p0);
00545
00546 PatternMatchData p1;
00547 set_pmd(p1, 0x0, "longer");
00548 FpSelector s1(CAT_SET_KEY, &p1);
00549
00550 CHECK(s0.is_better_than(s1, false, RULE_WO_DIR));
00551 }
00552
00553 TEST_CASE("fp_pkt_key_port_user_user", "[FastPatternSelect]")
00554 {
00555 PatternMatchData p0;
00556 set_pmd(p0, 0x10, "longer");
00557 FpSelector s0(CAT_SET_KEY, &p0);
00558
00559 PatternMatchData p1;
00560 set_pmd(p1, 0x10, "short");
00561 FpSelector s1(CAT_SET_KEY, &p1);
00562
00563 CHECK(!s0.is_better_than(s1, false, RULE_WO_DIR));
00564 }
00565
00566 TEST_CASE("fp_pkt_key_port_user_user2", "[FastPatternSelect]")
00567 {
00568 PatternMatchData p0;
00569 set_pmd(p0, 0x0, "longer");
00570 FpSelector s0(CAT_SET_KEY, &p0);
00571
00572 PatternMatchData p1;
00573 set_pmd(p1, 0x10, "short");
00574 FpSelector s1(CAT_SET_KEY, &p1);
00575
00576 CHECK(!s0.is_better_than(s1, false, RULE_WO_DIR));
00577 }
00578
00579 TEST_CASE("fp_pkt_key_srvc_1", "[FastPatternSelect]")
00580 {
00581 PatternMatchData p0;
00582 set_pmd(p0, 0x0, "short");
00583 FpSelector s0(CAT_SET_RAW, &p0);
00584
00585 PatternMatchData p1;
00586 set_pmd(p1, 0x0, "longer");
00587 FpSelector s1(CAT_SET_KEY, &p1);
00588
00589 CHECK(s1.is_better_than(s0, true, RULE_WO_DIR));
00590 }
00591
00592 TEST_CASE("fp_pkt_key_srvc_2", "[FastPatternSelect]")
00593 {
00594 PatternMatchData p0;
00595 set_pmd(p0, 0x0, "longer");
00596 FpSelector s0(CAT_SET_RAW, &p0);
00597
00598 PatternMatchData p1;
00599 set_pmd(p1, 0x0, "short");
00600 FpSelector s1(CAT_SET_KEY, &p1);
00601
00602 CHECK(s0.is_better_than(s1, true, RULE_WO_DIR));
00603 }
00604
00605 TEST_CASE("fp_pkt_key_srvc_rsp", "[FastPatternSelect]")
00606 {
00607 PatternMatchData p0;
00608 set_pmd(p0, 0x0, "short");
00609 FpSelector s0(CAT_SET_RAW, &p0);
00610
00611 PatternMatchData p1;
00612 set_pmd(p1, 0x0, "longer");
00613 FpSelector s1(CAT_SET_KEY, &p1);
00614
00615 CHECK(s0.is_better_than(s1, true, RULE_FROM_SERVER));
00616 CHECK(!s1.is_better_than(s0, true, RULE_FROM_SERVER));
00617 }
00618 #endif
00619
END OF CODE