00001 //--------------------------------------------------------------------------
00002 // Copyright (C) 2015-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 // hyperscan.cc author Russ Combs <rucombs@cisco.com>
00020
00021 #ifdef HAVE_CONFIG_H
00022 #include "config.h"
00023 #endif
00024
00025 #include "hyperscan.h"
00026
00027 #include <hs_compile.h>
00028 #include <hs_runtime.h>
00029
00030 #include <cassert>
00031 #include <cstring>
00032
00033 #include "framework/mpse.h"
00034 #include "log/messages.h"
00035 #include "main/snort_config.h"
00036 #include "utils/stats.h"
00037
00038 struct Pattern
00039 {
00040 std::string pat;
00041 unsigned len;
00042 unsigned flags;
00043 bool no_case;
00044 bool negate;
00045
00046 void* user;
00047 void* user_tree;
00048 void* user_list;
00049
00050 Pattern(const uint8_t*, unsigned, const Mpse::PatternDescriptor&, void*);
00051 void escape(const uint8_t*, unsigned, bool);
00052 };
00053
00054 Pattern::Pattern(
00055 const uint8_t* s, unsigned n, const Mpse::PatternDescriptor& d, void* u)
00056 {
00057 escape(s, n, d.literal);
00058
00059 len = n;
00060 no_case = d.no_case;
00061 negate = d.negated;
00062 flags = d.flags;
00063 user = u;
00064 user_tree = user_list = nullptr;
00065
00066 if ( no_case )
00067 flags |= HS_FLAG_CASELESS;
00068
00069 flags |= HS_FLAG_SINGLEMATCH;
00070 }
00071
00072 void Pattern::escape(const uint8_t* s, unsigned n, bool literal)
00073 {
00074 for ( unsigned i = 0; i < n; ++i )
00075 {
00076 if ( !isprint(s[i]) )
00077 {
00078 char hex[5];
00079 snprintf(hex, sizeof(hex), "\\x%02X", s[i]);
00080 pat += hex;
00081 }
00082 else
00083 {
00084 const char* special = ".^$*+?()[]{}\\|";
00085
00086 if ( literal and strchr(special, s[i]) )
00087 pat += '\\';
00088
00089 pat += s[i];
00090 }
00091 }
00092 }
00093
00094 typedef std::vector<Pattern> PatternVector;
00095
00096 // we need to update scratch in the main thread as each pattern is processed
00097 // and then clone to thread specific after all rules are loaded. s_scratch is
00098 // a prototype that is large enough for all uses.
00099
00100 static hs_scratch_t* s_scratch = nullptr;
00101
00102 //-------------------------------------------------------------------------
00103 // mpse
00104 //-------------------------------------------------------------------------
00105
00106 class HyperscanMpse : public Mpse
00107 {
00108 public:
00109 HyperscanMpse(SnortConfig*, const MpseAgent* a)
00110 : Mpse("hyperscan")
00111 {
00112 agent = a;
00113 ++instances;
00114 }
00115
00116 ~HyperscanMpse() override
00117 {
00118 if ( hs_db )
00119 hs_free_database(hs_db);
00120
00121 if ( agent )
00122 user_dtor();
00123 }
00124
00125 int add_pattern(
00126 SnortConfig*, const uint8_t* pat, unsigned len,
00127 const PatternDescriptor& desc, void* user) override
00128 {
00129 Pattern p(pat, len, desc, user);
00130 pvector.push_back(p);
00131 ++patterns;
00132 return 0;
00133 }
00134
00135 int prep_patterns(SnortConfig*) override;
00136
00137 int _search(const uint8_t*, int, MpseMatch, void*, int*) override;
00138
00139 int get_pattern_count() override
00140 { return pvector.size(); }
00141
00142 int match(unsigned id, unsigned long long to);
00143
00144 static int match(
00145 unsigned id, unsigned long long from, unsigned long long to,
00146 unsigned flags, void*);
00147
00148 private:
00149 void user_ctor(SnortConfig*);
00150 void user_dtor();
00151
00152 const MpseAgent* agent;
00153 PatternVector pvector;
00154
00155 hs_database_t* hs_db = nullptr;
00156
00157 static THREAD_LOCAL MpseMatch match_cb;
00158 static THREAD_LOCAL void* match_ctx;
00159 static THREAD_LOCAL int nfound;
00160
00161 public:
00162 static uint64_t instances;
00163 static uint64_t patterns;
00164 };
00165
00166 THREAD_LOCAL MpseMatch HyperscanMpse::match_cb = nullptr;
00167 THREAD_LOCAL void* HyperscanMpse::match_ctx = nullptr;
00168 THREAD_LOCAL int HyperscanMpse::nfound = 0;
00169
00170 uint64_t HyperscanMpse::instances = 0;
00171 uint64_t HyperscanMpse::patterns = 0;
00172
00173 // other mpse have direct access to their fsm match states and populate
00174 // user list and tree with each pattern that leads to the same match state.
00175 // with hyperscan we don't have internal fsm knowledge and each fast
00176 // pattern is considered to be in a distinct match state. the resulting
00177 // detection option trees for hyperscan are thus just single option chains.
00178
00179 void HyperscanMpse::user_ctor(SnortConfig* sc)
00180 {
00181 for ( auto& p : pvector )
00182 {
00183 if ( p.user )
00184 {
00185 if ( p.negate )
00186 agent->negate_list(p.user, &p.user_list);
00187 else
00188 agent->build_tree(sc, p.user, &p.user_tree);
00189 }
00190 agent->build_tree(sc, nullptr, &p.user_tree);
00191 }
00192 }
00193
00194 void HyperscanMpse::user_dtor()
00195 {
00196 for ( auto& p : pvector )
00197 {
00198 if ( p.user )
00199 agent->user_free(p.user);
00200
00201 if ( p.user_list )
00202 agent->list_free(&p.user_list);
00203
00204 if ( p.user_tree )
00205 agent->tree_free(&p.user_tree);
00206 }
00207 }
00208
00209 int HyperscanMpse::prep_patterns(SnortConfig* sc)
00210 {
00211 if ( pvector.empty() )
00212 return -1;
00213
00214 if ( hs_valid_platform() != HS_SUCCESS )
00215 {
00216 ParseError("This host does not support Hyperscan.");
00217 return -1;
00218 }
00219
00220 hs_compile_error_t* errptr = nullptr;
00221 std::vector<const char*> pats;
00222 std::vector<unsigned> flags;
00223 std::vector<unsigned> ids;
00224
00225 unsigned id = 0;
00226
00227 for ( auto& p : pvector )
00228 {
00229 pats.push_back(p.pat.c_str());
00230 flags.push_back(p.flags);
00231 ids.push_back(id++);
00232 }
00233
00234 if ( hs_compile_multi(&pats[0], &flags[0], &ids[0], pvector.size(), HS_MODE_BLOCK,
00235 nullptr, &hs_db, &errptr) or !hs_db )
00236 {
00237 ParseError("can't compile hyperscan pattern database: %s (%d) - '%s'",
00238 errptr->message, errptr->expression,
00239 errptr->expression >= 0 ? pats[errptr->expression] : "");
00240 hs_free_compile_error(errptr);
00241 return -2;
00242 }
00243
00244 if ( hs_error_t err = hs_alloc_scratch(hs_db, &s_scratch) )
00245 {
00246 ParseError("can't allocate search scratch space (%d)", err);
00247 return -3;
00248 }
00249
00250 if ( agent )
00251 user_ctor(sc);
00252
00253 return 0;
00254 }
00255
00256 int HyperscanMpse::match(unsigned id, unsigned long long to)
00257 {
00258 assert(id < pvector.size());
00259 Pattern& p = pvector[id];
00260 nfound++;
00261 return match_cb(p.user, p.user_tree, (int)to, match_ctx, p.user_list);
00262 }
00263
00264 int HyperscanMpse::match(
00265 unsigned id, unsigned long long /*from*/, unsigned long long to,
00266 unsigned /*flags*/, void* pv)
00267 {
00268 HyperscanMpse* h = (HyperscanMpse*)pv;
00269 return h->match(id, to);
00270 }
00271
00272 int HyperscanMpse::_search(
00273 const uint8_t* buf, int n, MpseMatch mf, void* pv, int* current_state)
00274 {
00275 *current_state = 0;
00276 nfound = 0;
00277
00278 match_cb = mf;
00279 match_ctx = pv;
00280
00281 SnortState* ss = snort_conf->state + get_instance_id();
00282
00283 // scratch is null for the degenerate case w/o patterns
00284 assert(!hs_db or ss->hyperscan_scratch);
00285
00286 hs_scan(hs_db, (const char*)buf, n, 0, (hs_scratch_t*)ss->hyperscan_scratch,
00287 HyperscanMpse::match, this);
00288
00289 return nfound;
00290 }
00291
00292 //-------------------------------------------------------------------------
00293 // public methods
00294 //-------------------------------------------------------------------------
00295
00296 void hyperscan_setup(SnortConfig* sc)
00297 {
00298 for ( unsigned i = 0; i < sc->num_slots; ++i )
00299 {
00300 SnortState* ss = sc->state + i;
00301
00302 if ( s_scratch )
00303 hs_clone_scratch(s_scratch, (hs_scratch_t**)&ss->hyperscan_scratch);
00304 else
00305 ss->hyperscan_scratch = nullptr;
00306 }
00307 if ( s_scratch )
00308 {
00309 hs_free_scratch(s_scratch);
00310 s_scratch = nullptr;
00311 }
00312 }
00313
00314 void hyperscan_cleanup(SnortConfig* sc)
00315 {
00316 for ( unsigned i = 0; i < sc->num_slots; ++i )
00317 {
00318 SnortState* ss = sc->state + i;
00319
00320 if ( ss->hyperscan_scratch )
00321 {
00322 hs_free_scratch((hs_scratch_t*)ss->hyperscan_scratch);
00323 ss->hyperscan_scratch = nullptr;
00324 }
00325 }
00326 }
00327
00328 //-------------------------------------------------------------------------
00329 // api
00330 //-------------------------------------------------------------------------
00331
00332 static Mpse* hs_ctor(
00333 SnortConfig* sc, class Module*, const MpseAgent* a)
00334 {
00335 return new HyperscanMpse(sc, a);
00336 }
00337
00338 static void hs_dtor(Mpse* p)
00339 {
00340 delete p;
00341 }
00342
00343 static void hs_init()
00344 {
00345 HyperscanMpse::instances = 0;
00346 HyperscanMpse::patterns = 0;
00347 }
00348
00349 static void hs_print()
00350 {
00351 LogCount("instances", HyperscanMpse::instances);
00352 LogCount("patterns", HyperscanMpse::patterns);
00353 }
00354
00355 static const MpseApi hs_api =
00356 {
00357 {
00358 PT_SEARCH_ENGINE,
00359 sizeof(MpseApi),
00360 SEAPI_VERSION,
00361 0,
00362 API_RESERVED,
00363 API_OPTIONS,
00364 "hyperscan",
00365 "intel hyperscan-based mpse with regex support",
00366 nullptr,
00367 nullptr
00368 },
00369 MPSE_REGEX,
00370 nullptr, // activate
00371 nullptr, // setup
00372 nullptr, // start
00373 nullptr, // stop
00374 hs_ctor,
00375 hs_dtor,
00376 hs_init,
00377 hs_print,
00378 };
00379
00380 //#ifdef BUILDING_SO
00381 //SO_PUBLIC const BaseApi* snort_plugins[] =
00382 //#else
00383 const BaseApi* se_hyperscan[] =
00384 //#endif
00385 {
00386 &hs_api.base,
00387 nullptr
00388 };
00389
END OF CODE