/* Copyright (C) 2007-2022 Open Information Security Foundation * * You can copy, redistribute or modify this Program under the terms of * the GNU General Public License version 2 as published by the Free * Software Foundation. * * 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 * version 2 along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. */ /** * \file * * \author Victor Julien * \author Anoop Saldanha */ #include "suricata-common.h" #include "decode.h" #include "threads.h" #include "threadvars.h" #include "tm-threads.h" #include "detect.h" #include "detect-engine-port.h" #include "detect-engine-build.h" #include "detect-parse.h" #include "detect-engine.h" #include "detect-content.h" #include "detect-engine-mpm.h" #include "detect-engine-state.h" #include "util-print.h" #include "util-pool.h" #include "util-unittest.h" #include "util-unittest-helper.h" #include "util-validate.h" #include "flow.h" #include "flow-util.h" #include "flow-private.h" #include "stream-tcp-private.h" #include "stream-tcp-reassemble.h" #include "stream-tcp.h" #include "stream.h" #include "app-layer.h" #include "app-layer-protos.h" #include "app-layer-parser.h" #include "app-layer-detect-proto.h" #include "app-layer-expectation.h" #include "conf.h" #include "util-memcmp.h" #include "util-spm.h" #include "util-debug.h" #include "runmodes.h" typedef struct AppLayerProtoDetectProbingParserElement_ { AppProto alproto; /* \todo don't really need it. See if you can get rid of it */ uint16_t port; /* \todo calculate at runtime and get rid of this var */ uint32_t alproto_mask; /* the min length of data that has to be supplied to invoke the parser */ uint16_t min_depth; /* the max length of data after which this parser won't be invoked */ uint16_t max_depth; /* the to_server probing parser function */ ProbingParserFPtr ProbingParserTs; /* the to_client probing parser function */ ProbingParserFPtr ProbingParserTc; struct AppLayerProtoDetectProbingParserElement_ *next; } AppLayerProtoDetectProbingParserElement; typedef struct AppLayerProtoDetectProbingParserPort_ { /* the port no for which probing parser(s) are invoked */ uint16_t port; uint32_t alproto_mask; /* the max depth for all the probing parsers registered for this port */ uint16_t dp_max_depth; uint16_t sp_max_depth; AppLayerProtoDetectProbingParserElement *dp; AppLayerProtoDetectProbingParserElement *sp; struct AppLayerProtoDetectProbingParserPort_ *next; } AppLayerProtoDetectProbingParserPort; typedef struct AppLayerProtoDetectProbingParser_ { uint8_t ipproto; AppLayerProtoDetectProbingParserPort *port; struct AppLayerProtoDetectProbingParser_ *next; } AppLayerProtoDetectProbingParser; typedef struct AppLayerProtoDetectPMSignature_ { AppProto alproto; uint8_t direction; /**< direction for midstream */ SigIntId id; /* \todo Change this into a non-pointer */ DetectContentData *cd; uint16_t pp_min_depth; uint16_t pp_max_depth; ProbingParserFPtr PPFunc; struct AppLayerProtoDetectPMSignature_ *next; } AppLayerProtoDetectPMSignature; typedef struct AppLayerProtoDetectPMCtx_ { uint16_t pp_max_len; uint16_t min_len; MpmCtx mpm_ctx; /** Mapping between pattern id and signature. As each signature has a * unique pattern with a unique id, we can lookup the signature by * the pattern id. */ AppLayerProtoDetectPMSignature **map; AppLayerProtoDetectPMSignature *head; /* \todo we don't need this except at setup time. Get rid of it. */ PatIntId max_pat_id; SigIntId max_sig_id; } AppLayerProtoDetectPMCtx; typedef struct AppLayerProtoDetectCtxIpproto_ { /* 0 - toserver, 1 - toclient */ AppLayerProtoDetectPMCtx ctx_pm[2]; } AppLayerProtoDetectCtxIpproto; /** * \brief The app layer protocol detection context. */ typedef struct AppLayerProtoDetectCtx_ { /* Context per ip_proto. * \todo Modify ctx_ipp to hold for only tcp and udp. The rest can be * implemented if needed. Waste of space otherwise. */ AppLayerProtoDetectCtxIpproto ctx_ipp[FLOW_PROTO_DEFAULT]; /* Global SPM thread context prototype. */ SpmGlobalThreadCtx *spm_global_thread_ctx; AppLayerProtoDetectProbingParser *ctx_pp; /* Indicates the protocols that have registered themselves * for protocol detection. This table is independent of the * ipproto. */ const char *alproto_names[ALPROTO_MAX]; } AppLayerProtoDetectCtx; typedef struct AppLayerProtoDetectAliases_ { const char *proto_name; const char *proto_alias; struct AppLayerProtoDetectAliases_ *next; } AppLayerProtoDetectAliases; /** * \brief The app layer protocol detection thread context. */ struct AppLayerProtoDetectThreadCtx_ { PrefilterRuleStore pmq; /* The value 2 is for direction(0 - toserver, 1 - toclient). */ MpmThreadCtx mpm_tctx[FLOW_PROTO_DEFAULT][2]; SpmThreadCtx *spm_thread_ctx; }; /* The global app layer proto detection context. */ static AppLayerProtoDetectCtx alpd_ctx; static AppLayerProtoDetectAliases *alpda_ctx = NULL; static void AppLayerProtoDetectPEGetIpprotos(AppProto alproto, uint8_t *ipprotos); /***** Static Internal Calls: Protocol Retrieval *****/ /** \internal * \brief Handle SPM search for Signature * \param buflen full size of the input buffer * \param searchlen pattern matching portion of buffer */ static AppProto AppLayerProtoDetectPMMatchSignature(const AppLayerProtoDetectPMSignature *s, AppLayerProtoDetectThreadCtx *tctx, Flow *f, uint8_t flags, const uint8_t *buf, uint32_t buflen, uint16_t searchlen, bool *rflow) { SCEnter(); if (s->cd->offset > searchlen) { SCLogDebug("s->co->offset (%"PRIu16") > searchlen (%"PRIu16")", s->cd->offset, searchlen); SCReturnUInt(ALPROTO_UNKNOWN); } if (s->cd->depth > searchlen) { SCLogDebug("s->co->depth (%"PRIu16") > searchlen (%"PRIu16")", s->cd->depth, searchlen); SCReturnUInt(ALPROTO_UNKNOWN); } const uint8_t *sbuf = buf + s->cd->offset; uint16_t ssearchlen = s->cd->depth - s->cd->offset; SCLogDebug("s->co->offset (%"PRIu16") s->cd->depth (%"PRIu16")", s->cd->offset, s->cd->depth); uint8_t *found = SpmScan(s->cd->spm_ctx, tctx->spm_thread_ctx, sbuf, ssearchlen); if (found == NULL) { SCReturnUInt(ALPROTO_UNKNOWN); } uint8_t direction = (flags & (STREAM_TOSERVER | STREAM_TOCLIENT)); SCLogDebug("matching, s->direction %s, our dir %s", (s->direction & STREAM_TOSERVER) ? "toserver" : "toclient", (flags & STREAM_TOSERVER) ? "toserver" : "toclient"); if (s->PPFunc == NULL) { if (direction == s->direction) { SCLogDebug("direction is correct"); } else { SCLogDebug("direction is wrong, rflow = true"); *rflow = true; } /* validate using Probing Parser */ } else { if (s->pp_min_depth > buflen) { SCLogDebug("PP can't be run yet as pp_min_depth %u > buflen %u", s->pp_min_depth, buflen); SCReturnInt(ALPROTO_UNKNOWN); } uint8_t rdir = 0; AppProto r = s->PPFunc(f, flags, buf, buflen, &rdir); if (r == s->alproto) { SCLogDebug("found %s/%u, rdir %02x reverse_flow? %s", AppProtoToString(r), r, rdir, (rdir && direction != rdir) ? "true" : "false"); *rflow = (rdir && direction != rdir); SCReturnUInt(s->alproto); } else if (r == ALPROTO_FAILED) { SCReturnUInt(ALPROTO_FAILED); } else { /* unknown: lets see if we will try again later */ if (s->pp_max_depth < buflen) { SCLogDebug("depth reached and answer inconclusive: fail"); SCReturnUInt(ALPROTO_FAILED); } SCReturnUInt(ALPROTO_UNKNOWN); } } SCReturnUInt(s->alproto); } /** * \retval 0 no matches * \retval -1 no matches, mpm depth reached */ static inline int PMGetProtoInspect(AppLayerProtoDetectThreadCtx *tctx, AppLayerProtoDetectPMCtx *pm_ctx, MpmThreadCtx *mpm_tctx, Flow *f, const uint8_t *buf, uint32_t buflen, uint8_t flags, AppProto *pm_results, bool *rflow) { int pm_matches = 0; // maxdepth is u16, so minimum is u16 uint16_t searchlen = (uint16_t)MIN(buflen, pm_ctx->mpm_ctx.maxdepth); SCLogDebug("searchlen %u buflen %u", searchlen, buflen); /* do the mpm search */ uint32_t search_cnt = mpm_table[pm_ctx->mpm_ctx.mpm_type].Search( &pm_ctx->mpm_ctx, mpm_tctx, &tctx->pmq, buf, searchlen); if (search_cnt == 0) { if (buflen >= pm_ctx->mpm_ctx.maxdepth) return -1; return 0; } /* alproto bit field */ uint8_t pm_results_bf[(ALPROTO_MAX / 8) + 1]; memset(pm_results_bf, 0, sizeof(pm_results_bf)); /* loop through unique pattern id's. Can't use search_cnt here, * as that contains all matches, tctx->pmq.pattern_id_array_cnt * contains only *unique* matches. */ for (uint32_t cnt = 0; cnt < tctx->pmq.rule_id_array_cnt; cnt++) { const AppLayerProtoDetectPMSignature *s = pm_ctx->map[tctx->pmq.rule_id_array[cnt]]; while (s != NULL) { AppProto proto = AppLayerProtoDetectPMMatchSignature( s, tctx, f, flags, buf, buflen, searchlen, rflow); /* store each unique proto once */ if (AppProtoIsValid(proto) && !(pm_results_bf[proto / 8] & (1 << (proto % 8))) ) { pm_results[pm_matches++] = proto; pm_results_bf[proto / 8] |= 1 << (proto % 8); } s = s->next; } } if (pm_matches == 0 && buflen >= pm_ctx->pp_max_len) { pm_matches = -2; } PmqReset(&tctx->pmq); return pm_matches; } /** \internal * \brief Run Pattern Sigs against buffer * \param direction direction for the patterns * \param pm_results[out] AppProto array of size ALPROTO_MAX */ static AppProto AppLayerProtoDetectPMGetProto(AppLayerProtoDetectThreadCtx *tctx, Flow *f, const uint8_t *buf, uint32_t buflen, uint8_t flags, AppProto *pm_results, bool *rflow) { SCEnter(); pm_results[0] = ALPROTO_UNKNOWN; AppLayerProtoDetectPMCtx *pm_ctx; MpmThreadCtx *mpm_tctx; int m = -1; if (f->protomap >= FLOW_PROTO_DEFAULT) { pm_results[0] = ALPROTO_FAILED; SCReturnUInt(1); } if (flags & STREAM_TOSERVER) { pm_ctx = &alpd_ctx.ctx_ipp[f->protomap].ctx_pm[0]; mpm_tctx = &tctx->mpm_tctx[f->protomap][0]; } else { pm_ctx = &alpd_ctx.ctx_ipp[f->protomap].ctx_pm[1]; mpm_tctx = &tctx->mpm_tctx[f->protomap][1]; } if (likely(pm_ctx->mpm_ctx.pattern_cnt > 0)) { m = PMGetProtoInspect(tctx, pm_ctx, mpm_tctx, f, buf, buflen, flags, pm_results, rflow); } /* pattern found, yay */ if (m > 0) { FLOW_SET_PM_DONE(f, flags); SCReturnUInt((uint16_t)m); /* handle non-found in non-midstream case */ } else if (!stream_config.midstream) { /* we can give up if mpm gave no results and its search depth * was reached. */ if (m < 0) { FLOW_SET_PM_DONE(f, flags); SCReturnUInt(0); } else if (m == 0) { SCReturnUInt(0); } SCReturnUInt((uint16_t)m); /* handle non-found in midstream case */ } else if (m <= 0) { if (flags & STREAM_TOSERVER) { pm_ctx = &alpd_ctx.ctx_ipp[f->protomap].ctx_pm[1]; mpm_tctx = &tctx->mpm_tctx[f->protomap][1]; } else { pm_ctx = &alpd_ctx.ctx_ipp[f->protomap].ctx_pm[0]; mpm_tctx = &tctx->mpm_tctx[f->protomap][0]; } SCLogDebug("no matches and in midstream mode, lets try the " "*patterns for the other side"); int om = -1; if (likely(pm_ctx->mpm_ctx.pattern_cnt > 0)) { om = PMGetProtoInspect( tctx, pm_ctx, mpm_tctx, f, buf, buflen, flags, pm_results, rflow); } /* found! */ if (om > 0) { FLOW_SET_PM_DONE(f, flags); SCReturnUInt((uint16_t)om); /* both sides failed */ } else if (om < 0 && m && m < 0) { FLOW_SET_PM_DONE(f, flags); SCReturnUInt(0); /* one side still uncertain */ } else if (om == 0 || m == 0) { SCReturnUInt(0); } } SCReturnUInt(0); } static AppLayerProtoDetectProbingParserElement *AppLayerProtoDetectGetProbingParser( AppLayerProtoDetectProbingParser *pp, uint8_t ipproto, AppProto alproto) { AppLayerProtoDetectProbingParserElement *pp_elem = NULL; AppLayerProtoDetectProbingParserPort *pp_port = NULL; while (pp != NULL) { if (pp->ipproto == ipproto) break; pp = pp->next; } if (pp == NULL) return NULL; pp_port = pp->port; while (pp_port != NULL) { if (pp_port->dp != NULL && pp_port->dp->alproto == alproto) { pp_elem = pp_port->dp; break; } if (pp_port->sp != NULL && pp_port->sp->alproto == alproto) { pp_elem = pp_port->sp; break; } pp_port = pp_port->next; } SCReturnPtr(pp_elem, "AppLayerProtoDetectProbingParserElement *"); } static AppLayerProtoDetectProbingParserPort *AppLayerProtoDetectGetProbingParsers(AppLayerProtoDetectProbingParser *pp, uint8_t ipproto, uint16_t port) { AppLayerProtoDetectProbingParserPort *pp_port = NULL; while (pp != NULL) { if (pp->ipproto == ipproto) break; pp = pp->next; } if (pp == NULL) goto end; pp_port = pp->port; while (pp_port != NULL) { if (pp_port->port == port || pp_port->port == 0) { break; } pp_port = pp_port->next; } end: SCReturnPtr(pp_port, "AppLayerProtoDetectProbingParserPort *"); } /** * \brief Call the probing expectation to see if there is some for this flow. * */ static AppProto AppLayerProtoDetectPEGetProto(Flow *f, uint8_t ipproto, uint8_t flags) { AppProto alproto = ALPROTO_UNKNOWN; SCLogDebug("expectation check for %p (dir %d)", f, flags); FLOW_SET_PE_DONE(f, flags); alproto = AppLayerExpectationHandle(f, flags); return alproto; } static inline AppProto PPGetProto(const AppLayerProtoDetectProbingParserElement *pe, Flow *f, uint8_t flags, const uint8_t *buf, uint32_t buflen, uint32_t *alproto_masks, uint8_t *rdir) { while (pe != NULL) { if ((buflen < pe->min_depth) || (alproto_masks[0] & pe->alproto_mask)) { pe = pe->next; continue; } AppProto alproto = ALPROTO_UNKNOWN; if (flags & STREAM_TOSERVER && pe->ProbingParserTs != NULL) { alproto = pe->ProbingParserTs(f, flags, buf, buflen, rdir); } else if (flags & STREAM_TOCLIENT && pe->ProbingParserTc != NULL) { alproto = pe->ProbingParserTc(f, flags, buf, buflen, rdir); } if (AppProtoIsValid(alproto)) { SCReturnUInt(alproto); } if (alproto == ALPROTO_FAILED || (pe->max_depth != 0 && buflen > pe->max_depth)) { alproto_masks[0] |= pe->alproto_mask; } pe = pe->next; } SCReturnUInt(ALPROTO_UNKNOWN); } /** * \brief Call the probing parser if it exists for this flow. * * First we check the flow's dp as it's most likely to match. If that didn't * lead to a PP, we try the sp. * */ static AppProto AppLayerProtoDetectPPGetProto(Flow *f, const uint8_t *buf, uint32_t buflen, uint8_t ipproto, const uint8_t flags, bool *reverse_flow) { const AppLayerProtoDetectProbingParserPort *pp_port_dp = NULL; const AppLayerProtoDetectProbingParserPort *pp_port_sp = NULL; const AppLayerProtoDetectProbingParserElement *pe0 = NULL; const AppLayerProtoDetectProbingParserElement *pe1 = NULL; const AppLayerProtoDetectProbingParserElement *pe2 = NULL; AppProto alproto = ALPROTO_UNKNOWN; uint32_t *alproto_masks = NULL; uint32_t mask = 0; uint8_t idir = (flags & (STREAM_TOSERVER | STREAM_TOCLIENT)); uint8_t dir = idir; uint16_t dp = f->protodetect_dp ? f->protodetect_dp : FLOW_GET_DP(f); uint16_t sp = FLOW_GET_SP(f); bool probe_is_found = false; again_midstream: if (idir != dir) { SWAP_VARS(uint16_t, dp, sp); /* look up parsers in rev dir */ } SCLogDebug("%u->%u %s", sp, dp, (dir == STREAM_TOSERVER) ? "toserver" : "toclient"); if (dir == STREAM_TOSERVER) { /* first try the destination port */ pp_port_dp = AppLayerProtoDetectGetProbingParsers(alpd_ctx.ctx_pp, ipproto, dp); alproto_masks = &f->probing_parser_toserver_alproto_masks; if (pp_port_dp != NULL) { SCLogDebug("toserver - Probing parser found for destination port %"PRIu16, dp); /* found based on destination port, so use dp registration */ pe1 = pp_port_dp->dp; } else { SCLogDebug("toserver - No probing parser registered for dest port %"PRIu16, dp); } pp_port_sp = AppLayerProtoDetectGetProbingParsers(alpd_ctx.ctx_pp, ipproto, sp); if (pp_port_sp != NULL) { SCLogDebug("toserver - Probing parser found for source port %"PRIu16, sp); /* found based on source port, so use sp registration */ pe2 = pp_port_sp->sp; } else { SCLogDebug("toserver - No probing parser registered for source port %"PRIu16, sp); } } else { /* first try the destination port */ pp_port_dp = AppLayerProtoDetectGetProbingParsers(alpd_ctx.ctx_pp, ipproto, dp); if (dir == idir) { // do not update alproto_masks to let a chance to second packet // for instance when sending a junk packet to a DNS server alproto_masks = &f->probing_parser_toclient_alproto_masks; } if (pp_port_dp != NULL) { SCLogDebug("toclient - Probing parser found for destination port %"PRIu16, dp); /* found based on destination port, so use dp registration */ pe1 = pp_port_dp->dp; } else { SCLogDebug("toclient - No probing parser registered for dest port %"PRIu16, dp); } pp_port_sp = AppLayerProtoDetectGetProbingParsers(alpd_ctx.ctx_pp, ipproto, sp); if (pp_port_sp != NULL) { SCLogDebug("toclient - Probing parser found for source port %"PRIu16, sp); pe2 = pp_port_sp->sp; } else { SCLogDebug("toclient - No probing parser registered for source port %"PRIu16, sp); } } if (dir == STREAM_TOSERVER && f->alproto_tc != ALPROTO_UNKNOWN) { pe0 = AppLayerProtoDetectGetProbingParser(alpd_ctx.ctx_pp, ipproto, f->alproto_tc); } else if (dir == STREAM_TOCLIENT && f->alproto_ts != ALPROTO_UNKNOWN) { pe0 = AppLayerProtoDetectGetProbingParser(alpd_ctx.ctx_pp, ipproto, f->alproto_ts); } if (pe1 == NULL && pe2 == NULL && pe0 == NULL) { SCLogDebug("%s - No probing parsers found for either port", (dir == STREAM_TOSERVER) ? "toserver":"toclient"); goto noparsers; } else { probe_is_found = true; } /* run the parser(s): always call with original direction */ uint8_t rdir = 0; alproto = PPGetProto(pe0, f, flags, buf, buflen, alproto_masks, &rdir); if (AppProtoIsValid(alproto)) goto end; alproto = PPGetProto(pe1, f, flags, buf, buflen, alproto_masks, &rdir); if (AppProtoIsValid(alproto)) goto end; alproto = PPGetProto(pe2, f, flags, buf, buflen, alproto_masks, &rdir); if (AppProtoIsValid(alproto)) goto end; /* get the mask we need for this direction */ if (dir == idir) { if (pp_port_dp && pp_port_sp) mask = pp_port_dp->alproto_mask|pp_port_sp->alproto_mask; else if (pp_port_dp) mask = pp_port_dp->alproto_mask; else if (pp_port_sp) mask = pp_port_sp->alproto_mask; if (alproto_masks[0] == mask) { FLOW_SET_PP_DONE(f, dir); SCLogDebug("%s, mask is now %08x, needed %08x, so done", (dir == STREAM_TOSERVER) ? "toserver":"toclient", alproto_masks[0], mask); } else { SCLogDebug("%s, mask is now %08x, need %08x", (dir == STREAM_TOSERVER) ? "toserver":"toclient", alproto_masks[0], mask); } } noparsers: if (stream_config.midstream && idir == dir) { if (idir == STREAM_TOSERVER) { dir = STREAM_TOCLIENT; } else { dir = STREAM_TOSERVER; } SCLogDebug("no match + midstream, retry the other direction %s", (dir == STREAM_TOSERVER) ? "toserver" : "toclient"); goto again_midstream; } else if (!probe_is_found) { FLOW_SET_PP_DONE(f, idir); } end: if (AppProtoIsValid(alproto) && rdir != 0 && rdir != idir) { SCLogDebug("PP found %u, is reverse flow", alproto); *reverse_flow = true; } SCLogDebug("%s, mask is now %08x", (idir == STREAM_TOSERVER) ? "toserver":"toclient", alproto_masks[0]); SCReturnUInt(alproto); } /***** Static Internal Calls: PP registration *****/ static void AppLayerProtoDetectPPGetIpprotos(AppProto alproto, uint8_t *ipprotos) { SCEnter(); const AppLayerProtoDetectProbingParser *pp; const AppLayerProtoDetectProbingParserPort *pp_port; const AppLayerProtoDetectProbingParserElement *pp_pe; for (pp = alpd_ctx.ctx_pp; pp != NULL; pp = pp->next) { for (pp_port = pp->port; pp_port != NULL; pp_port = pp_port->next) { for (pp_pe = pp_port->dp; pp_pe != NULL; pp_pe = pp_pe->next) { if (alproto == pp_pe->alproto) ipprotos[pp->ipproto / 8] |= 1 << (pp->ipproto % 8); } for (pp_pe = pp_port->sp; pp_pe != NULL; pp_pe = pp_pe->next) { if (alproto == pp_pe->alproto) ipprotos[pp->ipproto / 8] |= 1 << (pp->ipproto % 8); } } } SCReturn; } static uint32_t AppLayerProtoDetectProbingParserGetMask(AppProto alproto) { SCEnter(); if (!(alproto > ALPROTO_UNKNOWN && alproto < ALPROTO_FAILED)) { FatalError("Unknown protocol detected - %u", alproto); } SCReturnUInt(1UL << (uint32_t)alproto); } static AppLayerProtoDetectProbingParserElement *AppLayerProtoDetectProbingParserElementAlloc(void) { SCEnter(); AppLayerProtoDetectProbingParserElement *p = SCMalloc(sizeof(AppLayerProtoDetectProbingParserElement)); if (unlikely(p == NULL)) { exit(EXIT_FAILURE); } memset(p, 0, sizeof(AppLayerProtoDetectProbingParserElement)); SCReturnPtr(p, "AppLayerProtoDetectProbingParserElement"); } static void AppLayerProtoDetectProbingParserElementFree(AppLayerProtoDetectProbingParserElement *p) { SCEnter(); SCFree(p); SCReturn; } static AppLayerProtoDetectProbingParserPort *AppLayerProtoDetectProbingParserPortAlloc(void) { SCEnter(); AppLayerProtoDetectProbingParserPort *p = SCMalloc(sizeof(AppLayerProtoDetectProbingParserPort)); if (unlikely(p == NULL)) { exit(EXIT_FAILURE); } memset(p, 0, sizeof(AppLayerProtoDetectProbingParserPort)); SCReturnPtr(p, "AppLayerProtoDetectProbingParserPort"); } static void AppLayerProtoDetectProbingParserPortFree(AppLayerProtoDetectProbingParserPort *p) { SCEnter(); AppLayerProtoDetectProbingParserElement *e; e = p->dp; while (e != NULL) { AppLayerProtoDetectProbingParserElement *e_next = e->next; AppLayerProtoDetectProbingParserElementFree(e); e = e_next; } e = p->sp; while (e != NULL) { AppLayerProtoDetectProbingParserElement *e_next = e->next; AppLayerProtoDetectProbingParserElementFree(e); e = e_next; } SCFree(p); SCReturn; } static AppLayerProtoDetectProbingParser *AppLayerProtoDetectProbingParserAlloc(void) { SCEnter(); AppLayerProtoDetectProbingParser *p = SCMalloc(sizeof(AppLayerProtoDetectProbingParser)); if (unlikely(p == NULL)) { exit(EXIT_FAILURE); } memset(p, 0, sizeof(AppLayerProtoDetectProbingParser)); SCReturnPtr(p, "AppLayerProtoDetectProbingParser"); } static void AppLayerProtoDetectProbingParserFree(AppLayerProtoDetectProbingParser *p) { SCEnter(); AppLayerProtoDetectProbingParserPort *pt = p->port; while (pt != NULL) { AppLayerProtoDetectProbingParserPort *pt_next = pt->next; AppLayerProtoDetectProbingParserPortFree(pt); pt = pt_next; } SCFree(p); SCReturn; } static AppLayerProtoDetectProbingParserElement * AppLayerProtoDetectProbingParserElementCreate(AppProto alproto, uint16_t port, uint16_t min_depth, uint16_t max_depth) { AppLayerProtoDetectProbingParserElement *pe = AppLayerProtoDetectProbingParserElementAlloc(); pe->alproto = alproto; pe->port = port; pe->alproto_mask = AppLayerProtoDetectProbingParserGetMask(alproto); pe->min_depth = min_depth; pe->max_depth = max_depth; pe->next = NULL; if (max_depth != 0 && min_depth >= max_depth) { SCLogError("Invalid arguments sent to " "register the probing parser. min_depth >= max_depth"); goto error; } if (alproto <= ALPROTO_UNKNOWN || alproto >= ALPROTO_MAX) { SCLogError("Invalid arguments sent to register " "the probing parser. Invalid alproto - %d", alproto); goto error; } SCReturnPtr(pe, "AppLayerProtoDetectProbingParserElement"); error: AppLayerProtoDetectProbingParserElementFree(pe); SCReturnPtr(NULL, "AppLayerProtoDetectProbingParserElement"); } static AppLayerProtoDetectProbingParserElement * AppLayerProtoDetectProbingParserElementDuplicate(AppLayerProtoDetectProbingParserElement *pe) { SCEnter(); AppLayerProtoDetectProbingParserElement *new_pe = AppLayerProtoDetectProbingParserElementAlloc(); new_pe->alproto = pe->alproto; new_pe->port = pe->port; new_pe->alproto_mask = pe->alproto_mask; new_pe->min_depth = pe->min_depth; new_pe->max_depth = pe->max_depth; new_pe->ProbingParserTs = pe->ProbingParserTs; new_pe->ProbingParserTc = pe->ProbingParserTc; new_pe->next = NULL; SCReturnPtr(new_pe, "AppLayerProtoDetectProbingParserElement"); } #ifdef DEBUG static void AppLayerProtoDetectPrintProbingParsers(AppLayerProtoDetectProbingParser *pp) { SCEnter(); AppLayerProtoDetectProbingParserPort *pp_port = NULL; AppLayerProtoDetectProbingParserElement *pp_pe = NULL; printf("\nProtocol Detection Configuration\n"); for ( ; pp != NULL; pp = pp->next) { /* print ip protocol */ if (pp->ipproto == IPPROTO_TCP) printf("IPProto: TCP\n"); else if (pp->ipproto == IPPROTO_UDP) printf("IPProto: UDP\n"); else printf("IPProto: %"PRIu8"\n", pp->ipproto); pp_port = pp->port; for ( ; pp_port != NULL; pp_port = pp_port->next) { if (pp_port->dp != NULL) { printf(" Port: %"PRIu16 "\n", pp_port->port); printf(" Destination port: (max-depth: %"PRIu16 ", " "mask - %"PRIu32")\n", pp_port->dp_max_depth, pp_port->alproto_mask); pp_pe = pp_port->dp; for ( ; pp_pe != NULL; pp_pe = pp_pe->next) { printf(" alproto: %s\n", AppProtoToString(pp_pe->alproto)); printf(" port: %"PRIu16 "\n", pp_pe->port); printf(" mask: %"PRIu32 "\n", pp_pe->alproto_mask); printf(" min_depth: %"PRIu32 "\n", pp_pe->min_depth); printf(" max_depth: %"PRIu32 "\n", pp_pe->max_depth); printf("\n"); } } if (pp_port->sp == NULL) { continue; } printf(" Source port: (max-depth: %"PRIu16 ", " "mask - %"PRIu32")\n", pp_port->sp_max_depth, pp_port->alproto_mask); pp_pe = pp_port->sp; for ( ; pp_pe != NULL; pp_pe = pp_pe->next) { printf(" alproto: %s\n", AppProtoToString(pp_pe->alproto)); printf(" port: %"PRIu16 "\n", pp_pe->port); printf(" mask: %"PRIu32 "\n", pp_pe->alproto_mask); printf(" min_depth: %"PRIu32 "\n", pp_pe->min_depth); printf(" max_depth: %"PRIu32 "\n", pp_pe->max_depth); printf("\n"); } } } SCReturn; } #endif static void AppLayerProtoDetectProbingParserElementAppend(AppLayerProtoDetectProbingParserElement **head_pe, AppLayerProtoDetectProbingParserElement *new_pe) { SCEnter(); if (*head_pe == NULL) { *head_pe = new_pe; goto end; } if ((*head_pe)->port == 0) { if (new_pe->port != 0) { new_pe->next = *head_pe; *head_pe = new_pe; } else { AppLayerProtoDetectProbingParserElement *temp_pe = *head_pe; while (temp_pe->next != NULL) temp_pe = temp_pe->next; temp_pe->next = new_pe; } } else { AppLayerProtoDetectProbingParserElement *temp_pe = *head_pe; if (new_pe->port == 0) { while (temp_pe->next != NULL) temp_pe = temp_pe->next; temp_pe->next = new_pe; } else { while (temp_pe->next != NULL && temp_pe->next->port != 0) temp_pe = temp_pe->next; new_pe->next = temp_pe->next; temp_pe->next = new_pe; } } end: SCReturn; } static void AppLayerProtoDetectProbingParserAppend(AppLayerProtoDetectProbingParser **head_pp, AppLayerProtoDetectProbingParser *new_pp) { SCEnter(); if (*head_pp == NULL) { *head_pp = new_pp; goto end; } AppLayerProtoDetectProbingParser *temp_pp = *head_pp; while (temp_pp->next != NULL) temp_pp = temp_pp->next; temp_pp->next = new_pp; end: SCReturn; } static void AppLayerProtoDetectProbingParserPortAppend(AppLayerProtoDetectProbingParserPort **head_port, AppLayerProtoDetectProbingParserPort *new_port) { SCEnter(); if (*head_port == NULL) { *head_port = new_port; goto end; } if ((*head_port)->port == 0) { new_port->next = *head_port; *head_port = new_port; } else { AppLayerProtoDetectProbingParserPort *temp_port = *head_port; while (temp_port->next != NULL && temp_port->next->port != 0) { temp_port = temp_port->next; } new_port->next = temp_port->next; temp_port->next = new_port; } end: SCReturn; } static void AppLayerProtoDetectInsertNewProbingParser(AppLayerProtoDetectProbingParser **pp, uint8_t ipproto, uint16_t port, AppProto alproto, uint16_t min_depth, uint16_t max_depth, uint8_t direction, ProbingParserFPtr ProbingParser1, ProbingParserFPtr ProbingParser2) { SCEnter(); /* get the top level ipproto pp */ AppLayerProtoDetectProbingParser *curr_pp = *pp; while (curr_pp != NULL) { if (curr_pp->ipproto == ipproto) break; curr_pp = curr_pp->next; } if (curr_pp == NULL) { AppLayerProtoDetectProbingParser *new_pp = AppLayerProtoDetectProbingParserAlloc(); new_pp->ipproto = ipproto; AppLayerProtoDetectProbingParserAppend(pp, new_pp); curr_pp = new_pp; } /* get the top level port pp */ AppLayerProtoDetectProbingParserPort *curr_port = curr_pp->port; while (curr_port != NULL) { if (curr_port->port == port) break; curr_port = curr_port->next; } if (curr_port == NULL) { AppLayerProtoDetectProbingParserPort *new_port = AppLayerProtoDetectProbingParserPortAlloc(); new_port->port = port; AppLayerProtoDetectProbingParserPortAppend(&curr_pp->port, new_port); curr_port = new_port; if (direction & STREAM_TOSERVER) { curr_port->dp_max_depth = max_depth; } else { curr_port->sp_max_depth = max_depth; } AppLayerProtoDetectProbingParserPort *zero_port; zero_port = curr_pp->port; while (zero_port != NULL && zero_port->port != 0) { zero_port = zero_port->next; } if (zero_port != NULL) { AppLayerProtoDetectProbingParserElement *zero_pe; zero_pe = zero_port->dp; for ( ; zero_pe != NULL; zero_pe = zero_pe->next) { if (curr_port->dp == NULL) curr_port->dp_max_depth = zero_pe->max_depth; if (zero_pe->max_depth == 0) curr_port->dp_max_depth = zero_pe->max_depth; if (curr_port->dp_max_depth != 0 && curr_port->dp_max_depth < zero_pe->max_depth) { curr_port->dp_max_depth = zero_pe->max_depth; } AppLayerProtoDetectProbingParserElement *dup_pe = AppLayerProtoDetectProbingParserElementDuplicate(zero_pe); AppLayerProtoDetectProbingParserElementAppend(&curr_port->dp, dup_pe); curr_port->alproto_mask |= dup_pe->alproto_mask; } zero_pe = zero_port->sp; for ( ; zero_pe != NULL; zero_pe = zero_pe->next) { if (curr_port->sp == NULL) curr_port->sp_max_depth = zero_pe->max_depth; if (zero_pe->max_depth == 0) curr_port->sp_max_depth = zero_pe->max_depth; if (curr_port->sp_max_depth != 0 && curr_port->sp_max_depth < zero_pe->max_depth) { curr_port->sp_max_depth = zero_pe->max_depth; } AppLayerProtoDetectProbingParserElement *dup_pe = AppLayerProtoDetectProbingParserElementDuplicate(zero_pe); AppLayerProtoDetectProbingParserElementAppend(&curr_port->sp, dup_pe); curr_port->alproto_mask |= dup_pe->alproto_mask; } } /* if (zero_port != NULL) */ } /* if (curr_port == NULL) */ /* insert the pe_pp */ AppLayerProtoDetectProbingParserElement *curr_pe; if (direction & STREAM_TOSERVER) curr_pe = curr_port->dp; else curr_pe = curr_port->sp; while (curr_pe != NULL) { if (curr_pe->alproto == alproto) { SCLogError("Duplicate pp registered - " "ipproto - %" PRIu8 " Port - %" PRIu16 " " "App Protocol - NULL, App Protocol(ID) - " "%" PRIu16 " min_depth - %" PRIu16 " " "max_dept - %" PRIu16 ".", ipproto, port, alproto, min_depth, max_depth); goto error; } curr_pe = curr_pe->next; } /* Get a new parser element */ AppLayerProtoDetectProbingParserElement *new_pe = AppLayerProtoDetectProbingParserElementCreate(alproto, curr_port->port, min_depth, max_depth); if (new_pe == NULL) goto error; curr_pe = new_pe; AppLayerProtoDetectProbingParserElement **head_pe; if (direction & STREAM_TOSERVER) { curr_pe->ProbingParserTs = ProbingParser1; curr_pe->ProbingParserTc = ProbingParser2; if (curr_port->dp == NULL) curr_port->dp_max_depth = new_pe->max_depth; if (new_pe->max_depth == 0) curr_port->dp_max_depth = new_pe->max_depth; if (curr_port->dp_max_depth != 0 && curr_port->dp_max_depth < new_pe->max_depth) { curr_port->dp_max_depth = new_pe->max_depth; } curr_port->alproto_mask |= new_pe->alproto_mask; head_pe = &curr_port->dp; } else { curr_pe->ProbingParserTs = ProbingParser2; curr_pe->ProbingParserTc = ProbingParser1; if (curr_port->sp == NULL) curr_port->sp_max_depth = new_pe->max_depth; if (new_pe->max_depth == 0) curr_port->sp_max_depth = new_pe->max_depth; if (curr_port->sp_max_depth != 0 && curr_port->sp_max_depth < new_pe->max_depth) { curr_port->sp_max_depth = new_pe->max_depth; } curr_port->alproto_mask |= new_pe->alproto_mask; head_pe = &curr_port->sp; } AppLayerProtoDetectProbingParserElementAppend(head_pe, new_pe); if (curr_port->port == 0) { AppLayerProtoDetectProbingParserPort *temp_port = curr_pp->port; while (temp_port != NULL && temp_port->port != 0) { if (direction & STREAM_TOSERVER) { if (temp_port->dp == NULL) temp_port->dp_max_depth = curr_pe->max_depth; if (curr_pe->max_depth == 0) temp_port->dp_max_depth = curr_pe->max_depth; if (temp_port->dp_max_depth != 0 && temp_port->dp_max_depth < curr_pe->max_depth) { temp_port->dp_max_depth = curr_pe->max_depth; } AppLayerProtoDetectProbingParserElementAppend(&temp_port->dp, AppLayerProtoDetectProbingParserElementDuplicate(curr_pe)); temp_port->alproto_mask |= curr_pe->alproto_mask; } else { if (temp_port->sp == NULL) temp_port->sp_max_depth = curr_pe->max_depth; if (curr_pe->max_depth == 0) temp_port->sp_max_depth = curr_pe->max_depth; if (temp_port->sp_max_depth != 0 && temp_port->sp_max_depth < curr_pe->max_depth) { temp_port->sp_max_depth = curr_pe->max_depth; } AppLayerProtoDetectProbingParserElementAppend(&temp_port->sp, AppLayerProtoDetectProbingParserElementDuplicate(curr_pe)); temp_port->alproto_mask |= curr_pe->alproto_mask; } temp_port = temp_port->next; } /* while */ } /* if */ error: SCReturn; } /***** Static Internal Calls: PM registration *****/ static void AppLayerProtoDetectPMGetIpprotos(AppProto alproto, uint8_t *ipprotos) { SCEnter(); for (uint8_t i = 0; i < FLOW_PROTO_DEFAULT; i++) { uint8_t ipproto = FlowGetReverseProtoMapping(i); for (int j = 0; j < 2; j++) { AppLayerProtoDetectPMCtx *pm_ctx = &alpd_ctx.ctx_ipp[i].ctx_pm[j]; for (SigIntId x = 0; x < pm_ctx->max_sig_id; x++) { const AppLayerProtoDetectPMSignature *s = pm_ctx->map[x]; if (s->alproto == alproto) ipprotos[ipproto / 8] |= 1 << (ipproto % 8); } } } SCReturn; } static int AppLayerProtoDetectPMSetContentIDs(AppLayerProtoDetectPMCtx *ctx) { SCEnter(); typedef struct TempContainer_ { PatIntId id; uint16_t content_len; uint8_t *content; } TempContainer; AppLayerProtoDetectPMSignature *s = NULL; uint32_t struct_total_size = 0; uint32_t content_total_size = 0; /* array hash buffer */ uint8_t *ahb = NULL; uint8_t *content = NULL; uint16_t content_len = 0; PatIntId max_id = 0; TempContainer *struct_offset = NULL; uint8_t *content_offset = NULL; int ret = 0; if (ctx->head == NULL) goto end; for (s = ctx->head; s != NULL; s = s->next) { struct_total_size += sizeof(TempContainer); content_total_size += s->cd->content_len; ctx->max_sig_id++; } ahb = SCMalloc(sizeof(uint8_t) * (struct_total_size + content_total_size)); if (unlikely(ahb == NULL)) goto error; struct_offset = (TempContainer *)ahb; content_offset = ahb + struct_total_size; for (s = ctx->head; s != NULL; s = s->next) { TempContainer *tcdup = (TempContainer *)ahb; content = s->cd->content; content_len = s->cd->content_len; for (; tcdup != struct_offset; tcdup++) { if (tcdup->content_len != content_len || SCMemcmp(tcdup->content, content, tcdup->content_len) != 0) { continue; } break; } if (tcdup != struct_offset) { s->cd->id = tcdup->id; continue; } struct_offset->content_len = content_len; struct_offset->content = content_offset; content_offset += content_len; memcpy(struct_offset->content, content, content_len); struct_offset->id = max_id++; s->cd->id = struct_offset->id; struct_offset++; } ctx->max_pat_id = max_id; goto end; error: ret = -1; end: if (ahb != NULL) SCFree(ahb); SCReturnInt(ret); } static int AppLayerProtoDetectPMMapSignatures(AppLayerProtoDetectPMCtx *ctx) { SCEnter(); int ret = 0; AppLayerProtoDetectPMSignature *s, *next_s; int mpm_ret; SigIntId id = 0; ctx->map = SCMalloc(ctx->max_sig_id * sizeof(AppLayerProtoDetectPMSignature *)); if (ctx->map == NULL) goto error; memset(ctx->map, 0, ctx->max_sig_id * sizeof(AppLayerProtoDetectPMSignature *)); /* add an array indexed by rule id to look up the sig */ for (s = ctx->head; s != NULL; ) { next_s = s->next; s->id = id++; SCLogDebug("s->id %u offset %u depth %u", s->id, s->cd->offset, s->cd->depth); if (s->cd->flags & DETECT_CONTENT_NOCASE) { mpm_ret = MpmAddPatternCI(&ctx->mpm_ctx, s->cd->content, s->cd->content_len, s->cd->offset, s->cd->depth, s->cd->id, s->id, 0); if (mpm_ret < 0) goto error; } else { mpm_ret = MpmAddPatternCS(&ctx->mpm_ctx, s->cd->content, s->cd->content_len, s->cd->offset, s->cd->depth, s->cd->id, s->id, 0); if (mpm_ret < 0) goto error; } ctx->map[s->id] = s; s->next = NULL; s = next_s; } ctx->head = NULL; goto end; error: ret = -1; end: SCReturnInt(ret); } static int AppLayerProtoDetectPMPrepareMpm(AppLayerProtoDetectPMCtx *ctx) { SCEnter(); int ret = 0; MpmCtx *mpm_ctx = &ctx->mpm_ctx; if (mpm_table[mpm_ctx->mpm_type].Prepare(mpm_ctx) < 0) goto error; goto end; error: ret = -1; end: SCReturnInt(ret); } static void AppLayerProtoDetectPMFreeSignature(AppLayerProtoDetectPMSignature *sig) { SCEnter(); if (sig == NULL) SCReturn; if (sig->cd) DetectContentFree(NULL, sig->cd); SCFree(sig); SCReturn; } static int AppLayerProtoDetectPMAddSignature(AppLayerProtoDetectPMCtx *ctx, DetectContentData *cd, AppProto alproto, uint8_t direction, ProbingParserFPtr PPFunc, uint16_t pp_min_depth, uint16_t pp_max_depth) { SCEnter(); AppLayerProtoDetectPMSignature *s = SCCalloc(1, sizeof(*s)); if (unlikely(s == NULL)) SCReturnInt(-1); s->alproto = alproto; s->direction = direction; s->cd = cd; s->PPFunc = PPFunc; s->pp_min_depth = pp_min_depth; s->pp_max_depth = pp_max_depth; /* prepend to the list */ s->next = ctx->head; ctx->head = s; SCReturnInt(0); } static int AppLayerProtoDetectPMRegisterPattern(uint8_t ipproto, AppProto alproto, const char *pattern, uint16_t depth, uint16_t offset, uint8_t direction, uint8_t is_cs, ProbingParserFPtr PPFunc, uint16_t pp_min_depth, uint16_t pp_max_depth) { SCEnter(); AppLayerProtoDetectCtxIpproto *ctx_ipp = &alpd_ctx.ctx_ipp[FlowGetProtoMapping(ipproto)]; AppLayerProtoDetectPMCtx *ctx_pm = NULL; int ret = 0; DetectContentData *cd = DetectContentParseEncloseQuotes( alpd_ctx.spm_global_thread_ctx, pattern); if (cd == NULL) goto error; cd->depth = depth; cd->offset = offset; if (!is_cs) { /* Rebuild as nocase */ SpmDestroyCtx(cd->spm_ctx); cd->spm_ctx = SpmInitCtx(cd->content, cd->content_len, 1, alpd_ctx.spm_global_thread_ctx); if (cd->spm_ctx == NULL) { goto error; } cd->flags |= DETECT_CONTENT_NOCASE; } if (depth < cd->content_len) goto error; if (direction & STREAM_TOSERVER) ctx_pm = (AppLayerProtoDetectPMCtx *)&ctx_ipp->ctx_pm[0]; else ctx_pm = (AppLayerProtoDetectPMCtx *)&ctx_ipp->ctx_pm[1]; if (pp_max_depth > ctx_pm->pp_max_len) ctx_pm->pp_max_len = pp_max_depth; if (depth < ctx_pm->min_len) ctx_pm->min_len = depth; /* Finally turn it into a signature and add to the ctx. */ AppLayerProtoDetectPMAddSignature(ctx_pm, cd, alproto, direction, PPFunc, pp_min_depth, pp_max_depth); goto end; error: DetectContentFree(NULL, cd); ret = -1; end: SCReturnInt(ret); } /***** Protocol Retrieval *****/ AppProto AppLayerProtoDetectGetProto(AppLayerProtoDetectThreadCtx *tctx, Flow *f, const uint8_t *buf, uint32_t buflen, uint8_t ipproto, uint8_t flags, bool *reverse_flow) { SCEnter(); SCLogDebug("buflen %u for %s direction", buflen, (flags & STREAM_TOSERVER) ? "toserver" : "toclient"); AppProto alproto = ALPROTO_UNKNOWN; AppProto pm_alproto = ALPROTO_UNKNOWN; if (!FLOW_IS_PM_DONE(f, flags)) { AppProto pm_results[ALPROTO_MAX]; uint16_t pm_matches = AppLayerProtoDetectPMGetProto( tctx, f, buf, buflen, flags, pm_results, reverse_flow); if (pm_matches > 0) { DEBUG_VALIDATE_BUG_ON(pm_matches > 1); alproto = pm_results[0]; // rerun probing parser for other direction if it is unknown uint8_t reverse_dir = (flags & STREAM_TOSERVER) ? STREAM_TOCLIENT : STREAM_TOSERVER; if (FLOW_IS_PP_DONE(f, reverse_dir)) { AppProto rev_alproto = (flags & STREAM_TOSERVER) ? f->alproto_tc : f->alproto_ts; if (rev_alproto == ALPROTO_UNKNOWN) { FLOW_RESET_PP_DONE(f, reverse_dir); } } /* HACK: if detected protocol is dcerpc/udp, we run PP as well * to avoid misdetecting DNS as DCERPC. */ if (!(ipproto == IPPROTO_UDP && alproto == ALPROTO_DCERPC)) goto end; pm_alproto = alproto; /* fall through */ } } if (!FLOW_IS_PP_DONE(f, flags)) { bool rflow = false; alproto = AppLayerProtoDetectPPGetProto(f, buf, buflen, ipproto, flags, &rflow); if (AppProtoIsValid(alproto)) { if (rflow) { *reverse_flow = true; } goto end; } } /* Look if flow can be found in expectation list */ if (!FLOW_IS_PE_DONE(f, flags)) { alproto = AppLayerProtoDetectPEGetProto(f, ipproto, flags); } end: if (!AppProtoIsValid(alproto)) alproto = pm_alproto; SCReturnUInt(alproto); } static void AppLayerProtoDetectFreeProbingParsers(AppLayerProtoDetectProbingParser *pp) { SCEnter(); AppLayerProtoDetectProbingParser *tmp_pp = NULL; if (pp == NULL) goto end; while (pp != NULL) { tmp_pp = pp->next; AppLayerProtoDetectProbingParserFree(pp); pp = tmp_pp; } end: SCReturn; } static void AppLayerProtoDetectFreeAliases(void) { SCEnter(); AppLayerProtoDetectAliases *cur_alias = alpda_ctx; if (cur_alias == NULL) goto end; AppLayerProtoDetectAliases *next_alias = NULL; while (cur_alias != NULL) { next_alias = cur_alias->next; SCFree(cur_alias); cur_alias = next_alias; } alpda_ctx = NULL; end: SCReturn; } /***** State Preparation *****/ int AppLayerProtoDetectPrepareState(void) { SCEnter(); AppLayerProtoDetectPMCtx *ctx_pm; int i, j; int ret = 0; for (i = 0; i < FLOW_PROTO_DEFAULT; i++) { for (j = 0; j < 2; j++) { ctx_pm = &alpd_ctx.ctx_ipp[i].ctx_pm[j]; if (AppLayerProtoDetectPMSetContentIDs(ctx_pm) < 0) goto error; if (ctx_pm->max_sig_id == 0) continue; if (AppLayerProtoDetectPMMapSignatures(ctx_pm) < 0) goto error; if (AppLayerProtoDetectPMPrepareMpm(ctx_pm) < 0) goto error; } } #ifdef DEBUG if (SCLogDebugEnabled()) { AppLayerProtoDetectPrintProbingParsers(alpd_ctx.ctx_pp); } #endif goto end; error: ret = -1; end: SCReturnInt(ret); } /***** PP registration *****/ /** \brief register parser at a port * * \param direction STREAM_TOSERVER or STREAM_TOCLIENT for dp or sp */ void AppLayerProtoDetectPPRegister(uint8_t ipproto, const char *portstr, AppProto alproto, uint16_t min_depth, uint16_t max_depth, uint8_t direction, ProbingParserFPtr ProbingParser1, ProbingParserFPtr ProbingParser2) { SCEnter(); DetectPort *head = NULL; DetectPortParse(NULL,&head, portstr); DetectPort *temp_dp = head; while (temp_dp != NULL) { uint16_t port = temp_dp->port; if (port == 0 && temp_dp->port2 != 0) port++; for (;;) { AppLayerProtoDetectInsertNewProbingParser(&alpd_ctx.ctx_pp, ipproto, port, alproto, min_depth, max_depth, direction, ProbingParser1, ProbingParser2); if (port == temp_dp->port2) { break; } else { port++; } } temp_dp = temp_dp->next; } DetectPortCleanupList(NULL,head); SCReturn; } int AppLayerProtoDetectPPParseConfPorts(const char *ipproto_name, uint8_t ipproto, const char *alproto_name, AppProto alproto, uint16_t min_depth, uint16_t max_depth, ProbingParserFPtr ProbingParserTs, ProbingParserFPtr ProbingParserTc) { SCEnter(); char param[100]; int r; ConfNode *node; ConfNode *port_node = NULL; int config = 0; r = snprintf(param, sizeof(param), "%s%s%s", "app-layer.protocols.", alproto_name, ".detection-ports"); if (r < 0) { FatalError("snprintf failure."); } else if (r > (int)sizeof(param)) { FatalError("buffer not big enough to write param."); } node = ConfGetNode(param); if (node == NULL) { SCLogDebug("Entry for %s not found.", param); r = snprintf(param, sizeof(param), "%s%s%s%s%s", "app-layer.protocols.", alproto_name, ".", ipproto_name, ".detection-ports"); if (r < 0) { FatalError("snprintf failure."); } else if (r > (int)sizeof(param)) { FatalError("buffer not big enough to write param."); } node = ConfGetNode(param); if (node == NULL) goto end; } /* detect by destination port of the flow (e.g. port 53 for DNS) */ port_node = ConfNodeLookupChild(node, "dp"); if (port_node == NULL) port_node = ConfNodeLookupChild(node, "toserver"); if (port_node != NULL && port_node->val != NULL) { AppLayerProtoDetectPPRegister(ipproto, port_node->val, alproto, min_depth, max_depth, STREAM_TOSERVER, /* to indicate dp */ ProbingParserTs, ProbingParserTc); } /* detect by source port of flow */ port_node = ConfNodeLookupChild(node, "sp"); if (port_node == NULL) port_node = ConfNodeLookupChild(node, "toclient"); if (port_node != NULL && port_node->val != NULL) { AppLayerProtoDetectPPRegister(ipproto, port_node->val, alproto, min_depth, max_depth, STREAM_TOCLIENT, /* to indicate sp */ ProbingParserTc, ProbingParserTs); } config = 1; end: SCReturnInt(config); } /***** PM registration *****/ int AppLayerProtoDetectPMRegisterPatternCS(uint8_t ipproto, AppProto alproto, const char *pattern, uint16_t depth, uint16_t offset, uint8_t direction) { SCEnter(); int r = AppLayerProtoDetectPMRegisterPattern(ipproto, alproto, pattern, depth, offset, direction, 1 /* case-sensitive */, NULL, 0, 0); SCReturnInt(r); } int AppLayerProtoDetectPMRegisterPatternCSwPP(uint8_t ipproto, AppProto alproto, const char *pattern, uint16_t depth, uint16_t offset, uint8_t direction, ProbingParserFPtr PPFunc, uint16_t pp_min_depth, uint16_t pp_max_depth) { SCEnter(); int r = AppLayerProtoDetectPMRegisterPattern(ipproto, alproto, pattern, depth, offset, direction, 1 /* case-sensitive */, PPFunc, pp_min_depth, pp_max_depth); SCReturnInt(r); } int AppLayerProtoDetectPMRegisterPatternCI(uint8_t ipproto, AppProto alproto, const char *pattern, uint16_t depth, uint16_t offset, uint8_t direction) { SCEnter(); int r = AppLayerProtoDetectPMRegisterPattern(ipproto, alproto, pattern, depth, offset, direction, 0 /* !case-sensitive */, NULL, 0, 0); SCReturnInt(r); } /***** Setup/General Registration *****/ int AppLayerProtoDetectSetup(void) { SCEnter(); int i, j; memset(&alpd_ctx, 0, sizeof(alpd_ctx)); uint8_t spm_matcher = SinglePatternMatchDefaultMatcher(); uint8_t mpm_matcher = PatternMatchDefaultMatcher(); alpd_ctx.spm_global_thread_ctx = SpmInitGlobalThreadCtx(spm_matcher); if (alpd_ctx.spm_global_thread_ctx == NULL) { FatalError("Unable to alloc SpmGlobalThreadCtx."); } for (i = 0; i < FLOW_PROTO_DEFAULT; i++) { for (j = 0; j < 2; j++) { MpmInitCtx(&alpd_ctx.ctx_ipp[i].ctx_pm[j].mpm_ctx, mpm_matcher); } } AppLayerExpectationSetup(); SCReturnInt(0); } /** * \todo incomplete. Need more work. */ int AppLayerProtoDetectDeSetup(void) { SCEnter(); int ipproto_map = 0; int dir = 0; PatIntId id = 0; AppLayerProtoDetectPMCtx *pm_ctx = NULL; AppLayerProtoDetectPMSignature *sig = NULL; for (ipproto_map = 0; ipproto_map < FLOW_PROTO_DEFAULT; ipproto_map++) { for (dir = 0; dir < 2; dir++) { pm_ctx = &alpd_ctx.ctx_ipp[ipproto_map].ctx_pm[dir]; mpm_table[pm_ctx->mpm_ctx.mpm_type].DestroyCtx(&pm_ctx->mpm_ctx); for (id = 0; id < pm_ctx->max_sig_id; id++) { sig = pm_ctx->map[id]; AppLayerProtoDetectPMFreeSignature(sig); } SCFree(pm_ctx->map); pm_ctx->map = NULL; } } SpmDestroyGlobalThreadCtx(alpd_ctx.spm_global_thread_ctx); AppLayerProtoDetectFreeAliases(); AppLayerProtoDetectFreeProbingParsers(alpd_ctx.ctx_pp); SCReturnInt(0); } void AppLayerProtoDetectRegisterProtocol(AppProto alproto, const char *alproto_name) { SCEnter(); if (alpd_ctx.alproto_names[alproto] == NULL) alpd_ctx.alproto_names[alproto] = alproto_name; SCReturn; } void AppLayerProtoDetectRegisterAlias(const char *proto_name, const char *proto_alias) { SCEnter(); AppLayerProtoDetectAliases *new_alias = SCMalloc(sizeof(AppLayerProtoDetectAliases)); if (unlikely(new_alias == NULL)) { exit(EXIT_FAILURE); } new_alias->proto_name = proto_name; new_alias->proto_alias = proto_alias; new_alias->next = NULL; if (alpda_ctx == NULL) { alpda_ctx = new_alias; } else { AppLayerProtoDetectAliases *cur_alias = alpda_ctx; while (cur_alias->next != NULL) { cur_alias = cur_alias->next; } cur_alias->next = new_alias; } SCReturn; } /** \brief request applayer to wrap up this protocol and rerun protocol * detection. * * When this is called, the old session is reset unconditionally. A * 'detect/log' flush packet is generated for both direction before * the reset, so allow for final detection and logging. * * \param f flow to act on * \param dp destination port to use in protocol detection. Set to 443 * for start tls, set to the HTTP uri port for CONNECT and * set to 0 to not use it. * \param expect_proto expected protocol. AppLayer event will be set if * detected protocol differs from this. */ bool AppLayerRequestProtocolChange(Flow *f, uint16_t dp, AppProto expect_proto) { if (FlowChangeProto(f)) { // If we are already changing protocols, from SMTP to TLS for instance, // and that we do not get TLS but HTTP1, which is requesting change to HTTP2, // we do not proceed the new protocol change return false; } FlowSetChangeProtoFlag(f); f->protodetect_dp = dp; f->alproto_expect = expect_proto; DEBUG_VALIDATE_BUG_ON(f->alproto == ALPROTO_UNKNOWN); f->alproto_orig = f->alproto; // If one side is unknown yet, set it to the other known side if (f->alproto_ts == ALPROTO_UNKNOWN) { f->alproto_ts = f->alproto; } if (f->alproto_tc == ALPROTO_UNKNOWN) { f->alproto_tc = f->alproto; } return true; } /** \brief request applayer to wrap up this protocol and rerun protocol * detection with expectation of TLS. Used by STARTTLS. * * Sets detection port to 443 to make port based TLS detection work for * SMTP, FTP etc as well. * * \param f flow to act on */ bool AppLayerRequestProtocolTLSUpgrade(Flow *f) { return AppLayerRequestProtocolChange(f, 443, ALPROTO_TLS); } void AppLayerProtoDetectReset(Flow *f) { FLOW_RESET_PM_DONE(f, STREAM_TOSERVER); FLOW_RESET_PM_DONE(f, STREAM_TOCLIENT); FLOW_RESET_PP_DONE(f, STREAM_TOSERVER); FLOW_RESET_PP_DONE(f, STREAM_TOCLIENT); FLOW_RESET_PE_DONE(f, STREAM_TOSERVER); FLOW_RESET_PE_DONE(f, STREAM_TOCLIENT); f->probing_parser_toserver_alproto_masks = 0; f->probing_parser_toclient_alproto_masks = 0; // Does not free the structures for the parser // keeps f->alstate for new state creation f->alparser = NULL; f->alproto = ALPROTO_UNKNOWN; f->alproto_ts = ALPROTO_UNKNOWN; f->alproto_tc = ALPROTO_UNKNOWN; } int AppLayerProtoDetectConfProtoDetectionEnabledDefault( const char *ipproto, const char *alproto, bool default_enabled) { SCEnter(); BUG_ON(ipproto == NULL || alproto == NULL); int enabled = 1; char param[100]; ConfNode *node; int r; if (RunmodeIsUnittests()) goto enabled; #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION // so that fuzzig takes place for DNP3 and such default_enabled = true; #endif r = snprintf(param, sizeof(param), "%s%s%s", "app-layer.protocols.", alproto, ".enabled"); if (r < 0) { FatalError("snprintf failure."); } else if (r > (int)sizeof(param)) { FatalError("buffer not big enough to write param."); } node = ConfGetNode(param); if (node == NULL) { SCLogDebug("Entry for %s not found.", param); r = snprintf(param, sizeof(param), "%s%s%s%s%s", "app-layer.protocols.", alproto, ".", ipproto, ".enabled"); if (r < 0) { FatalError("snprintf failure."); } else if (r > (int)sizeof(param)) { FatalError("buffer not big enough to write param."); } node = ConfGetNode(param); if (node == NULL) { SCLogDebug("Entry for %s not found.", param); if (default_enabled) { goto enabled; } else { goto disabled; } } } if (node->val) { if (ConfValIsTrue(node->val)) { goto enabled; } else if (ConfValIsFalse(node->val)) { goto disabled; } else if (strcasecmp(node->val, "detection-only") == 0) { goto enabled; } } /* Invalid or null value. */ SCLogError("Invalid value found for %s.", param); exit(EXIT_FAILURE); disabled: enabled = 0; enabled: SCReturnInt(enabled); } int AppLayerProtoDetectConfProtoDetectionEnabled(const char *ipproto, const char *alproto) { return AppLayerProtoDetectConfProtoDetectionEnabledDefault(ipproto, alproto, true); } AppLayerProtoDetectThreadCtx *AppLayerProtoDetectGetCtxThread(void) { SCEnter(); AppLayerProtoDetectThreadCtx *alpd_tctx = NULL; MpmCtx *mpm_ctx; MpmThreadCtx *mpm_tctx; int i, j; PatIntId max_pat_id = 0; for (i = 0; i < FLOW_PROTO_DEFAULT; i++) { for (j = 0; j < 2; j++) { if (max_pat_id == 0) { max_pat_id = alpd_ctx.ctx_ipp[i].ctx_pm[j].max_pat_id; } else if (alpd_ctx.ctx_ipp[i].ctx_pm[j].max_pat_id && max_pat_id < alpd_ctx.ctx_ipp[i].ctx_pm[j].max_pat_id) { max_pat_id = alpd_ctx.ctx_ipp[i].ctx_pm[j].max_pat_id; } } } alpd_tctx = SCMalloc(sizeof(*alpd_tctx)); if (alpd_tctx == NULL) goto error; memset(alpd_tctx, 0, sizeof(*alpd_tctx)); /* Get the max pat id for all the mpm ctxs. */ if (PmqSetup(&alpd_tctx->pmq) < 0) goto error; for (i = 0; i < FLOW_PROTO_DEFAULT; i++) { for (j = 0; j < 2; j++) { mpm_ctx = &alpd_ctx.ctx_ipp[i].ctx_pm[j].mpm_ctx; mpm_tctx = &alpd_tctx->mpm_tctx[i][j]; mpm_table[mpm_ctx->mpm_type].InitThreadCtx(mpm_ctx, mpm_tctx); } } alpd_tctx->spm_thread_ctx = SpmMakeThreadCtx(alpd_ctx.spm_global_thread_ctx); if (alpd_tctx->spm_thread_ctx == NULL) { goto error; } goto end; error: if (alpd_tctx != NULL) AppLayerProtoDetectDestroyCtxThread(alpd_tctx); alpd_tctx = NULL; end: SCReturnPtr(alpd_tctx, "AppLayerProtoDetectThreadCtx"); } void AppLayerProtoDetectDestroyCtxThread(AppLayerProtoDetectThreadCtx *alpd_tctx) { SCEnter(); MpmCtx *mpm_ctx; MpmThreadCtx *mpm_tctx; int ipproto_map, dir; for (ipproto_map = 0; ipproto_map < FLOW_PROTO_DEFAULT; ipproto_map++) { for (dir = 0; dir < 2; dir++) { mpm_ctx = &alpd_ctx.ctx_ipp[ipproto_map].ctx_pm[dir].mpm_ctx; mpm_tctx = &alpd_tctx->mpm_tctx[ipproto_map][dir]; mpm_table[mpm_ctx->mpm_type].DestroyThreadCtx(mpm_ctx, mpm_tctx); } } PmqFree(&alpd_tctx->pmq); if (alpd_tctx->spm_thread_ctx != NULL) { SpmDestroyThreadCtx(alpd_tctx->spm_thread_ctx); } SCFree(alpd_tctx); SCReturn; } /***** Utility *****/ void AppLayerProtoDetectSupportedIpprotos(AppProto alproto, uint8_t *ipprotos) { SCEnter(); // Custom case for only signature-only protocol so far if (alproto == ALPROTO_HTTP) { AppLayerProtoDetectSupportedIpprotos(ALPROTO_HTTP1, ipprotos); AppLayerProtoDetectSupportedIpprotos(ALPROTO_HTTP2, ipprotos); } else { AppLayerProtoDetectPMGetIpprotos(alproto, ipprotos); AppLayerProtoDetectPPGetIpprotos(alproto, ipprotos); AppLayerProtoDetectPEGetIpprotos(alproto, ipprotos); } SCReturn; } AppProto AppLayerProtoDetectGetProtoByName(const char *alproto_name) { SCEnter(); AppLayerProtoDetectAliases *cur_alias = alpda_ctx; while (cur_alias != NULL) { if (strcasecmp(alproto_name, cur_alias->proto_alias) == 0) { alproto_name = cur_alias->proto_name; } cur_alias = cur_alias->next; } AppProto a; AppProto b = StringToAppProto(alproto_name); for (a = 0; a < ALPROTO_MAX; a++) { if (alpd_ctx.alproto_names[a] != NULL && AppProtoEquals(b, a)) { // That means return HTTP_ANY if HTTP1 or HTTP2 is enabled SCReturnCT(b, "AppProto"); } } SCReturnCT(ALPROTO_UNKNOWN, "AppProto"); } const char *AppLayerProtoDetectGetProtoName(AppProto alproto) { // Special case for http (any version) : // returns "http" if both versions are enabled // and returns "http1" or "http2" if only one version is enabled if (alproto == ALPROTO_HTTP) { if (alpd_ctx.alproto_names[ALPROTO_HTTP1]) { if (alpd_ctx.alproto_names[ALPROTO_HTTP2]) { return "http"; } // else return alpd_ctx.alproto_names[ALPROTO_HTTP1]; } // else return alpd_ctx.alproto_names[ALPROTO_HTTP2]; } return alpd_ctx.alproto_names[alproto]; } void AppLayerProtoDetectSupportedAppProtocols(AppProto *alprotos) { SCEnter(); memset(alprotos, 0, ALPROTO_MAX * sizeof(AppProto)); int alproto; for (alproto = 0; alproto != ALPROTO_MAX; alproto++) { if (alpd_ctx.alproto_names[alproto] != NULL) alprotos[alproto] = 1; } SCReturn; } uint8_t expectation_proto[ALPROTO_MAX]; static void AppLayerProtoDetectPEGetIpprotos(AppProto alproto, uint8_t *ipprotos) { if (expectation_proto[alproto] == IPPROTO_TCP) { ipprotos[IPPROTO_TCP / 8] |= 1 << (IPPROTO_TCP % 8); } if (expectation_proto[alproto] == IPPROTO_UDP) { ipprotos[IPPROTO_UDP / 8] |= 1 << (IPPROTO_UDP % 8); } } void AppLayerRegisterExpectationProto(uint8_t proto, AppProto alproto) { if (expectation_proto[alproto]) { if (proto != expectation_proto[alproto]) { SCLogError("Expectation on 2 IP protocols are not supported"); } } expectation_proto[alproto] = proto; } /***** Unittests *****/ #ifdef UNITTESTS #include "app-layer-htp.h" #include "detect-engine-alert.h" static AppLayerProtoDetectCtx alpd_ctx_ut; void AppLayerProtoDetectUnittestCtxBackup(void) { SCEnter(); alpd_ctx_ut = alpd_ctx; memset(&alpd_ctx, 0, sizeof(alpd_ctx)); SCReturn; } void AppLayerProtoDetectUnittestCtxRestore(void) { SCEnter(); alpd_ctx = alpd_ctx_ut; memset(&alpd_ctx_ut, 0, sizeof(alpd_ctx_ut)); SCReturn; } static int AppLayerProtoDetectTest01(void) { AppLayerProtoDetectUnittestCtxBackup(); AppLayerProtoDetectSetup(); const char *buf = "HTTP"; AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_HTTP1, buf, 4, 0, STREAM_TOCLIENT); buf = "GET"; AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_HTTP1, buf, 4, 0, STREAM_TOSERVER); AppLayerProtoDetectPrepareState(); FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].max_pat_id != 1); FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].max_pat_id != 1); AppLayerProtoDetectDeSetup(); AppLayerProtoDetectUnittestCtxRestore(); PASS; } static int AppLayerProtoDetectTest02(void) { AppLayerProtoDetectUnittestCtxBackup(); AppLayerProtoDetectSetup(); const char *buf = "HTTP"; AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_HTTP1, buf, 4, 0, STREAM_TOCLIENT); buf = "ftp"; AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_FTP, buf, 4, 0, STREAM_TOCLIENT); AppLayerProtoDetectPrepareState(); FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].max_pat_id != 0); FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].max_pat_id != 2); FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].map != NULL); FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map == NULL); FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map[0]->alproto != ALPROTO_FTP); FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map[1]->alproto != ALPROTO_HTTP1); AppLayerProtoDetectDeSetup(); AppLayerProtoDetectUnittestCtxRestore(); PASS; } static int AppLayerProtoDetectTest03(void) { AppLayerProtoDetectUnittestCtxBackup(); AppLayerProtoDetectSetup(); uint8_t l7data[] = "HTTP/1.1 200 OK\r\nServer: Apache/1.0\r\n\r\n"; AppProto pm_results[ALPROTO_MAX]; memset(pm_results, 0, sizeof(pm_results)); Flow f; memset(&f, 0x00, sizeof(f)); f.protomap = FlowGetProtoMapping(IPPROTO_TCP); const char *buf = "HTTP"; AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_HTTP1, buf, 4, 0, STREAM_TOCLIENT); buf = "220 "; AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_FTP, buf, 4, 0, STREAM_TOCLIENT); AppLayerProtoDetectPrepareState(); /* AppLayerProtoDetectGetCtxThread() should be called post AppLayerProtoDetectPrepareState(), since * it sets internal structures which depends on the above function. */ AppLayerProtoDetectThreadCtx *alpd_tctx = AppLayerProtoDetectGetCtxThread(); FAIL_IF_NULL(alpd_tctx); FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].max_pat_id != 0); FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].max_pat_id != 2); FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].map != NULL); FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map == NULL); FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map[0]->alproto != ALPROTO_FTP); FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map[1]->alproto != ALPROTO_HTTP1); bool rflow = false; uint32_t cnt = AppLayerProtoDetectPMGetProto(alpd_tctx, &f, l7data, sizeof(l7data), STREAM_TOCLIENT, pm_results, &rflow); FAIL_IF(cnt != 1); FAIL_IF(pm_results[0] != ALPROTO_HTTP1); AppLayerProtoDetectDestroyCtxThread(alpd_tctx); AppLayerProtoDetectDeSetup(); AppLayerProtoDetectUnittestCtxRestore(); PASS; } static int AppLayerProtoDetectTest04(void) { AppLayerProtoDetectUnittestCtxBackup(); AppLayerProtoDetectSetup(); uint8_t l7data[] = "HTTP/1.1 200 OK\r\nServer: Apache/1.0\r\n\r\n"; Flow f; memset(&f, 0x00, sizeof(f)); AppProto pm_results[ALPROTO_MAX]; memset(pm_results, 0, sizeof(pm_results)); f.protomap = FlowGetProtoMapping(IPPROTO_TCP); const char *buf = "200 "; AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_HTTP1, buf, 13, 0, STREAM_TOCLIENT); AppLayerProtoDetectPrepareState(); /* AppLayerProtoDetectGetCtxThread() should be called post AppLayerProtoDetectPrepareState(), since * it sets internal structures which depends on the above function. */ AppLayerProtoDetectThreadCtx *alpd_tctx = AppLayerProtoDetectGetCtxThread(); FAIL_IF_NULL(alpd_tctx); FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].max_pat_id != 0); FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].max_pat_id != 1); FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].map != NULL); FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map == NULL); FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map[0]->alproto != ALPROTO_HTTP1); bool rdir = false; uint32_t cnt = AppLayerProtoDetectPMGetProto(alpd_tctx, &f, l7data, sizeof(l7data), STREAM_TOCLIENT, pm_results, &rdir); FAIL_IF(cnt != 1); FAIL_IF(pm_results[0] != ALPROTO_HTTP1); AppLayerProtoDetectDestroyCtxThread(alpd_tctx); AppLayerProtoDetectDeSetup(); AppLayerProtoDetectUnittestCtxRestore(); PASS; } static int AppLayerProtoDetectTest05(void) { AppLayerProtoDetectUnittestCtxBackup(); AppLayerProtoDetectSetup(); uint8_t l7data[] = "HTTP/1.1 200 OK\r\nServer: Apache/1.0\r\n\r\nBlahblah"; AppProto pm_results[ALPROTO_MAX]; memset(pm_results, 0, sizeof(pm_results)); Flow f; memset(&f, 0x00, sizeof(f)); f.protomap = FlowGetProtoMapping(IPPROTO_TCP); const char *buf = "HTTP"; AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_HTTP1, buf, 4, 0, STREAM_TOCLIENT); buf = "220 "; AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_FTP, buf, 4, 0, STREAM_TOCLIENT); AppLayerProtoDetectPrepareState(); /* AppLayerProtoDetectGetCtxThread() should be called post AppLayerProtoDetectPrepareState(), since * it sets internal structures which depends on the above function. */ AppLayerProtoDetectThreadCtx *alpd_tctx = AppLayerProtoDetectGetCtxThread(); FAIL_IF_NULL(alpd_tctx); FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].max_pat_id != 0); FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].max_pat_id != 2); FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].map != NULL); FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map == NULL); FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map[0]->alproto != ALPROTO_FTP); FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map[1]->alproto != ALPROTO_HTTP1); bool rdir = false; uint32_t cnt = AppLayerProtoDetectPMGetProto(alpd_tctx, &f, l7data, sizeof(l7data), STREAM_TOCLIENT, pm_results, &rdir); FAIL_IF(cnt != 1); FAIL_IF(pm_results[0] != ALPROTO_HTTP1); AppLayerProtoDetectDestroyCtxThread(alpd_tctx); AppLayerProtoDetectDeSetup(); AppLayerProtoDetectUnittestCtxRestore(); PASS; } static int AppLayerProtoDetectTest06(void) { AppLayerProtoDetectUnittestCtxBackup(); AppLayerProtoDetectSetup(); uint8_t l7data[] = "220 Welcome to the OISF FTP server\r\n"; AppProto pm_results[ALPROTO_MAX]; memset(pm_results, 0, sizeof(pm_results)); Flow f; memset(&f, 0x00, sizeof(f)); f.protomap = FlowGetProtoMapping(IPPROTO_TCP); const char *buf = "HTTP"; AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_HTTP1, buf, 4, 0, STREAM_TOCLIENT); buf = "220 "; AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_FTP, buf, 4, 0, STREAM_TOCLIENT); AppLayerProtoDetectPrepareState(); /* AppLayerProtoDetectGetCtxThread() should be called post AppLayerProtoDetectPrepareState(), since * it sets internal structures which depends on the above function. */ AppLayerProtoDetectThreadCtx *alpd_tctx = AppLayerProtoDetectGetCtxThread(); FAIL_IF_NULL(alpd_tctx); FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].max_pat_id != 0); FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].max_pat_id != 2); FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].map != NULL); FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map == NULL); FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map[0]->alproto != ALPROTO_FTP); FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map[1]->alproto != ALPROTO_HTTP1); bool rdir = false; uint32_t cnt = AppLayerProtoDetectPMGetProto(alpd_tctx, &f, l7data, sizeof(l7data), STREAM_TOCLIENT, pm_results, &rdir); FAIL_IF(cnt != 1); FAIL_IF(pm_results[0] != ALPROTO_FTP); AppLayerProtoDetectDestroyCtxThread(alpd_tctx); AppLayerProtoDetectDeSetup(); AppLayerProtoDetectUnittestCtxRestore(); PASS; } static int AppLayerProtoDetectTest07(void) { AppLayerProtoDetectUnittestCtxBackup(); AppLayerProtoDetectSetup(); uint8_t l7data[] = "220 Welcome to the OISF HTTP/FTP server\r\n"; Flow f; memset(&f, 0x00, sizeof(f)); f.protomap = FlowGetProtoMapping(IPPROTO_TCP); AppProto pm_results[ALPROTO_MAX]; memset(pm_results, 0, sizeof(pm_results)); const char *buf = "HTTP"; AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_HTTP1, buf, 4, 0, STREAM_TOCLIENT); AppLayerProtoDetectPrepareState(); /* AppLayerProtoDetectGetCtxThread() should be called post AppLayerProtoDetectPrepareState(), since * it sets internal structures which depends on the above function. */ AppLayerProtoDetectThreadCtx *alpd_tctx = AppLayerProtoDetectGetCtxThread(); FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].max_pat_id != 0); FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].max_pat_id != 1); FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].map != NULL); FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map == NULL); FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map[0]->alproto != ALPROTO_HTTP1); bool rdir = false; uint32_t cnt = AppLayerProtoDetectPMGetProto(alpd_tctx, &f, l7data, sizeof(l7data), STREAM_TOCLIENT, pm_results, &rdir); FAIL_IF(cnt != 0); AppLayerProtoDetectDestroyCtxThread(alpd_tctx); AppLayerProtoDetectDeSetup(); AppLayerProtoDetectUnittestCtxRestore(); PASS; } static int AppLayerProtoDetectTest08(void) { AppLayerProtoDetectUnittestCtxBackup(); AppLayerProtoDetectSetup(); uint8_t l7data[] = { 0x00, 0x00, 0x00, 0x85, 0xff, 0x53, 0x4d, 0x42, 0x72, 0x00, 0x00, 0x00, 0x00, 0x18, 0x53, 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x00, 0x02, 0x50, 0x43, 0x20, 0x4e, 0x45, 0x54, 0x57, 0x4f, 0x52, 0x4b, 0x20, 0x50, 0x52, 0x4f, 0x47, 0x52, 0x41, 0x4d, 0x20, 0x31, 0x2e, 0x30, 0x00, 0x02, 0x4c, 0x41, 0x4e, 0x4d, 0x41, 0x4e, 0x31, 0x2e, 0x30, 0x00, 0x02, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x73, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x57, 0x6f, 0x72, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x20, 0x33, 0x2e, 0x31, 0x61, 0x00, 0x02, 0x4c, 0x4d, 0x31, 0x2e, 0x32, 0x58, 0x30, 0x30, 0x32, 0x00, 0x02, 0x4c, 0x41, 0x4e, 0x4d, 0x41, 0x4e, 0x32, 0x2e, 0x31, 0x00, 0x02, 0x4e, 0x54, 0x20, 0x4c, 0x4d, 0x20, 0x30, 0x2e, 0x31, 0x32, 0x00 }; AppProto pm_results[ALPROTO_MAX]; memset(pm_results, 0, sizeof(pm_results)); Flow f; memset(&f, 0x00, sizeof(f)); f.protomap = FlowGetProtoMapping(IPPROTO_TCP); const char *buf = "|ff|SMB"; AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_SMB, buf, 8, 4, STREAM_TOCLIENT); AppLayerProtoDetectPrepareState(); /* AppLayerProtoDetectGetCtxThread() should be called post AppLayerProtoDetectPrepareState(), since * it sets internal structures which depends on the above function. */ AppLayerProtoDetectThreadCtx *alpd_tctx = AppLayerProtoDetectGetCtxThread(); FAIL_IF_NULL(alpd_tctx); FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].max_pat_id != 0); FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].max_pat_id != 1); FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].map != NULL); FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map == NULL); FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map[0]->alproto != ALPROTO_SMB); bool rdir = false; uint32_t cnt = AppLayerProtoDetectPMGetProto(alpd_tctx, &f, l7data, sizeof(l7data), STREAM_TOCLIENT, pm_results, &rdir); FAIL_IF(cnt != 1); FAIL_IF(pm_results[0] != ALPROTO_SMB); AppLayerProtoDetectDestroyCtxThread(alpd_tctx); AppLayerProtoDetectDeSetup(); AppLayerProtoDetectUnittestCtxRestore(); PASS; } static int AppLayerProtoDetectTest09(void) { AppLayerProtoDetectUnittestCtxBackup(); AppLayerProtoDetectSetup(); uint8_t l7data[] = { 0x00, 0x00, 0x00, 0x66, 0xfe, 0x53, 0x4d, 0x42, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02 }; AppProto pm_results[ALPROTO_MAX]; memset(pm_results, 0, sizeof(pm_results)); Flow f; memset(&f, 0x00, sizeof(f)); f.protomap = FlowGetProtoMapping(IPPROTO_TCP); const char *buf = "|fe|SMB"; AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_SMB, buf, 8, 4, STREAM_TOCLIENT); AppLayerProtoDetectPrepareState(); /* AppLayerProtoDetectGetCtxThread() should be called post AppLayerProtoDetectPrepareState(), since * it sets internal structures which depends on the above function. */ AppLayerProtoDetectThreadCtx *alpd_tctx = AppLayerProtoDetectGetCtxThread(); FAIL_IF_NULL(alpd_tctx); FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].max_pat_id != 0); FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].max_pat_id != 1); FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].map != NULL); FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map == NULL); FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map[0]->alproto != ALPROTO_SMB); bool rdir = false; uint32_t cnt = AppLayerProtoDetectPMGetProto(alpd_tctx, &f, l7data, sizeof(l7data), STREAM_TOCLIENT, pm_results, &rdir); FAIL_IF(cnt != 1); FAIL_IF(pm_results[0] != ALPROTO_SMB); AppLayerProtoDetectDestroyCtxThread(alpd_tctx); AppLayerProtoDetectDeSetup(); AppLayerProtoDetectUnittestCtxRestore(); PASS; } static int AppLayerProtoDetectTest10(void) { AppLayerProtoDetectUnittestCtxBackup(); AppLayerProtoDetectSetup(); uint8_t l7data[] = { 0x05, 0x00, 0x0b, 0x03, 0x10, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd0, 0x16, 0xd0, 0x16, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0xb8, 0x4a, 0x9f, 0x4d, 0x1c, 0x7d, 0xcf, 0x11, 0x86, 0x1e, 0x00, 0x20, 0xaf, 0x6e, 0x7c, 0x57, 0x00, 0x00, 0x00, 0x00, 0x04, 0x5d, 0x88, 0x8a, 0xeb, 0x1c, 0xc9, 0x11, 0x9f, 0xe8, 0x08, 0x00, 0x2b, 0x10, 0x48, 0x60, 0x02, 0x00, 0x00, 0x00 }; AppProto pm_results[ALPROTO_MAX]; memset(pm_results, 0, sizeof(pm_results)); Flow f; memset(&f, 0x00, sizeof(f)); f.protomap = FlowGetProtoMapping(IPPROTO_TCP); const char *buf = "|05 00|"; AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_DCERPC, buf, 4, 0, STREAM_TOCLIENT); AppLayerProtoDetectPrepareState(); /* AppLayerProtoDetectGetCtxThread() should be called post AppLayerProtoDetectPrepareState(), since * it sets internal structures which depends on the above function. */ AppLayerProtoDetectThreadCtx *alpd_tctx = AppLayerProtoDetectGetCtxThread(); FAIL_IF_NULL(alpd_tctx); FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].max_pat_id != 0); FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].max_pat_id != 1); FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].map != NULL); FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map == NULL); FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map[0]->alproto != ALPROTO_DCERPC); bool rdir = false; uint32_t cnt = AppLayerProtoDetectPMGetProto(alpd_tctx, &f, l7data, sizeof(l7data), STREAM_TOCLIENT, pm_results, &rdir); FAIL_IF(cnt != 1); FAIL_IF(pm_results[0] != ALPROTO_DCERPC); AppLayerProtoDetectDestroyCtxThread(alpd_tctx); AppLayerProtoDetectDeSetup(); AppLayerProtoDetectUnittestCtxRestore(); PASS; } /** * \test Why we still get http for connect... obviously because * we also match on the reply, duh */ static int AppLayerProtoDetectTest11(void) { AppLayerProtoDetectUnittestCtxBackup(); AppLayerProtoDetectSetup(); uint8_t l7data[] = "CONNECT www.ssllabs.com:443 HTTP/1.0\r\n"; uint8_t l7data_resp[] = "HTTP/1.1 405 Method Not Allowed\r\n"; AppProto pm_results[ALPROTO_MAX]; memset(pm_results, 0, sizeof(pm_results)); Flow f; memset(&f, 0x00, sizeof(f)); f.protomap = FlowGetProtoMapping(IPPROTO_TCP); AppLayerProtoDetectPMRegisterPatternCS( IPPROTO_TCP, ALPROTO_HTTP1, "HTTP", 4, 0, STREAM_TOSERVER); AppLayerProtoDetectPMRegisterPatternCS( IPPROTO_TCP, ALPROTO_HTTP1, "GET", 3, 0, STREAM_TOSERVER); AppLayerProtoDetectPMRegisterPatternCS( IPPROTO_TCP, ALPROTO_HTTP1, "PUT", 3, 0, STREAM_TOSERVER); AppLayerProtoDetectPMRegisterPatternCS( IPPROTO_TCP, ALPROTO_HTTP1, "POST", 4, 0, STREAM_TOSERVER); AppLayerProtoDetectPMRegisterPatternCS( IPPROTO_TCP, ALPROTO_HTTP1, "TRACE", 5, 0, STREAM_TOSERVER); AppLayerProtoDetectPMRegisterPatternCS( IPPROTO_TCP, ALPROTO_HTTP1, "OPTIONS", 7, 0, STREAM_TOSERVER); AppLayerProtoDetectPMRegisterPatternCS( IPPROTO_TCP, ALPROTO_HTTP1, "CONNECT", 7, 0, STREAM_TOSERVER); AppLayerProtoDetectPMRegisterPatternCS( IPPROTO_TCP, ALPROTO_HTTP1, "HTTP", 4, 0, STREAM_TOCLIENT); AppLayerProtoDetectPrepareState(); /* AppLayerProtoDetectGetCtxThread() should be called post AppLayerProtoDetectPrepareState(), since * it sets internal structures which depends on the above function. */ AppLayerProtoDetectThreadCtx *alpd_tctx = AppLayerProtoDetectGetCtxThread(); FAIL_IF_NULL(alpd_tctx); FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].max_pat_id != 7); FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].max_pat_id != 1); FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].map == NULL); FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map == NULL); FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].map[0]->alproto != ALPROTO_HTTP1); FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].map[1]->alproto != ALPROTO_HTTP1); FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].map[2]->alproto != ALPROTO_HTTP1); FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].map[3]->alproto != ALPROTO_HTTP1); FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].map[4]->alproto != ALPROTO_HTTP1); FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].map[5]->alproto != ALPROTO_HTTP1); FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].map[6]->alproto != ALPROTO_HTTP1); FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map[0]->alproto != ALPROTO_HTTP1); bool rdir = false; uint32_t cnt = AppLayerProtoDetectPMGetProto(alpd_tctx, &f, l7data, sizeof(l7data), STREAM_TOSERVER, pm_results, &rdir); FAIL_IF(cnt != 1); FAIL_IF(pm_results[0] != ALPROTO_HTTP1); memset(pm_results, 0, sizeof(pm_results)); cnt = AppLayerProtoDetectPMGetProto(alpd_tctx, &f, l7data_resp, sizeof(l7data_resp), STREAM_TOCLIENT, pm_results, &rdir); FAIL_IF(cnt != 1); FAIL_IF(pm_results[0] != ALPROTO_HTTP1); AppLayerProtoDetectDestroyCtxThread(alpd_tctx); AppLayerProtoDetectDeSetup(); AppLayerProtoDetectUnittestCtxRestore(); PASS; } /** * \test AlpProtoSignature test */ static int AppLayerProtoDetectTest12(void) { AppLayerProtoDetectUnittestCtxBackup(); AppLayerProtoDetectSetup(); int r = 0; AppLayerProtoDetectPMRegisterPatternCS( IPPROTO_TCP, ALPROTO_HTTP1, "HTTP", 4, 0, STREAM_TOSERVER); if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].head == NULL || alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].map != NULL) { printf("failure 1\n"); goto end; } AppLayerProtoDetectPrepareState(); if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].max_pat_id != 1) { printf("failure 2\n"); goto end; } if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].head != NULL || alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].map == NULL) { printf("failure 3\n"); goto end; } if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].map[0]->alproto != ALPROTO_HTTP1) { printf("failure 4\n"); goto end; } if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].map[0]->cd->id != 0) { printf("failure 5\n"); goto end; } if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].map[0]->next != NULL) { printf("failure 6\n"); goto end; } r = 1; end: AppLayerProtoDetectDeSetup(); AppLayerProtoDetectUnittestCtxRestore(); return r; } /** * \test What about if we add some sigs only for udp but call for tcp? * It should not detect any proto */ static int AppLayerProtoDetectTest13(void) { AppLayerProtoDetectUnittestCtxBackup(); AppLayerProtoDetectSetup(); uint8_t l7data[] = "CONNECT www.ssllabs.com:443 HTTP/1.0\r\n"; uint8_t l7data_resp[] = "HTTP/1.1 405 Method Not Allowed\r\n"; AppProto pm_results[ALPROTO_MAX]; Flow f; memset(&f, 0x00, sizeof(f)); f.protomap = FlowGetProtoMapping(IPPROTO_TCP); AppLayerProtoDetectPMRegisterPatternCS( IPPROTO_UDP, ALPROTO_HTTP1, "HTTP", 4, 0, STREAM_TOSERVER); AppLayerProtoDetectPMRegisterPatternCS( IPPROTO_UDP, ALPROTO_HTTP1, "GET", 3, 0, STREAM_TOSERVER); AppLayerProtoDetectPMRegisterPatternCS( IPPROTO_UDP, ALPROTO_HTTP1, "PUT", 3, 0, STREAM_TOSERVER); AppLayerProtoDetectPMRegisterPatternCS( IPPROTO_UDP, ALPROTO_HTTP1, "POST", 4, 0, STREAM_TOSERVER); AppLayerProtoDetectPMRegisterPatternCS( IPPROTO_UDP, ALPROTO_HTTP1, "TRACE", 5, 0, STREAM_TOSERVER); AppLayerProtoDetectPMRegisterPatternCS( IPPROTO_UDP, ALPROTO_HTTP1, "OPTIONS", 7, 0, STREAM_TOSERVER); AppLayerProtoDetectPMRegisterPatternCS( IPPROTO_UDP, ALPROTO_HTTP1, "CONNECT", 7, 0, STREAM_TOSERVER); AppLayerProtoDetectPMRegisterPatternCS( IPPROTO_UDP, ALPROTO_HTTP1, "HTTP", 4, 0, STREAM_TOCLIENT); AppLayerProtoDetectPrepareState(); /* AppLayerProtoDetectGetCtxThread() should be called post AppLayerProtoDetectPrepareState(), since * it sets internal structures which depends on the above function. */ AppLayerProtoDetectThreadCtx *alpd_tctx = AppLayerProtoDetectGetCtxThread(); FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_UDP].ctx_pm[0].max_pat_id != 7); FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_UDP].ctx_pm[1].max_pat_id != 1); FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_UDP].ctx_pm[0].map[0]->alproto != ALPROTO_HTTP1); FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_UDP].ctx_pm[0].map[1]->alproto != ALPROTO_HTTP1); FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_UDP].ctx_pm[0].map[2]->alproto != ALPROTO_HTTP1); FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_UDP].ctx_pm[0].map[3]->alproto != ALPROTO_HTTP1); FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_UDP].ctx_pm[0].map[4]->alproto != ALPROTO_HTTP1); FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_UDP].ctx_pm[0].map[5]->alproto != ALPROTO_HTTP1); FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_UDP].ctx_pm[0].map[6]->alproto != ALPROTO_HTTP1); FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_UDP].ctx_pm[1].map[0]->alproto != ALPROTO_HTTP1); memset(pm_results, 0, sizeof(pm_results)); bool rdir = false; uint32_t cnt = AppLayerProtoDetectPMGetProto(alpd_tctx, &f, l7data, sizeof(l7data), STREAM_TOSERVER, pm_results, &rdir); FAIL_IF(cnt != 0); memset(pm_results, 0, sizeof(pm_results)); cnt = AppLayerProtoDetectPMGetProto(alpd_tctx, &f, l7data_resp, sizeof(l7data_resp), STREAM_TOCLIENT, pm_results, &rdir); FAIL_IF(cnt != 0); AppLayerProtoDetectDestroyCtxThread(alpd_tctx); AppLayerProtoDetectDeSetup(); AppLayerProtoDetectUnittestCtxRestore(); PASS; } /** * \test What about if we add some sigs only for udp calling it for UDP? * It should detect ALPROTO_HTTP1 (over udp). This is just a check * to ensure that TCP/UDP differences work correctly. */ static int AppLayerProtoDetectTest14(void) { AppLayerProtoDetectUnittestCtxBackup(); AppLayerProtoDetectSetup(); uint8_t l7data[] = "CONNECT www.ssllabs.com:443 HTTP/1.0\r\n"; uint8_t l7data_resp[] = "HTTP/1.1 405 Method Not Allowed\r\n"; AppProto pm_results[ALPROTO_MAX]; uint32_t cnt; Flow f; memset(&f, 0x00, sizeof(f)); f.protomap = FlowGetProtoMapping(IPPROTO_UDP); AppLayerProtoDetectPMRegisterPatternCS( IPPROTO_UDP, ALPROTO_HTTP1, "HTTP", 4, 0, STREAM_TOSERVER); AppLayerProtoDetectPMRegisterPatternCS( IPPROTO_UDP, ALPROTO_HTTP1, "GET", 3, 0, STREAM_TOSERVER); AppLayerProtoDetectPMRegisterPatternCS( IPPROTO_UDP, ALPROTO_HTTP1, "PUT", 3, 0, STREAM_TOSERVER); AppLayerProtoDetectPMRegisterPatternCS( IPPROTO_UDP, ALPROTO_HTTP1, "POST", 4, 0, STREAM_TOSERVER); AppLayerProtoDetectPMRegisterPatternCS( IPPROTO_UDP, ALPROTO_HTTP1, "TRACE", 5, 0, STREAM_TOSERVER); AppLayerProtoDetectPMRegisterPatternCS( IPPROTO_UDP, ALPROTO_HTTP1, "OPTIONS", 7, 0, STREAM_TOSERVER); AppLayerProtoDetectPMRegisterPatternCS( IPPROTO_UDP, ALPROTO_HTTP1, "CONNECT", 7, 0, STREAM_TOSERVER); AppLayerProtoDetectPMRegisterPatternCS( IPPROTO_UDP, ALPROTO_HTTP1, "HTTP", 4, 0, STREAM_TOCLIENT); AppLayerProtoDetectPrepareState(); /* AppLayerProtoDetectGetCtxThread() should be called post AppLayerProtoDetectPrepareState(), since * it sets internal structures which depends on the above function. */ AppLayerProtoDetectThreadCtx *alpd_tctx = AppLayerProtoDetectGetCtxThread(); FAIL_IF_NULL(alpd_tctx); FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_UDP].ctx_pm[0].max_pat_id != 7); FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_UDP].ctx_pm[1].max_pat_id != 1); FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_UDP].ctx_pm[0].map[0]->alproto != ALPROTO_HTTP1); FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_UDP].ctx_pm[0].map[1]->alproto != ALPROTO_HTTP1); FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_UDP].ctx_pm[0].map[2]->alproto != ALPROTO_HTTP1); FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_UDP].ctx_pm[0].map[3]->alproto != ALPROTO_HTTP1); FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_UDP].ctx_pm[0].map[4]->alproto != ALPROTO_HTTP1); FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_UDP].ctx_pm[0].map[5]->alproto != ALPROTO_HTTP1); FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_UDP].ctx_pm[0].map[6]->alproto != ALPROTO_HTTP1); FAIL_IF(alpd_ctx.ctx_ipp[FLOW_PROTO_UDP].ctx_pm[1].map[0]->alproto != ALPROTO_HTTP1); memset(pm_results, 0, sizeof(pm_results)); bool rdir = false; cnt = AppLayerProtoDetectPMGetProto(alpd_tctx, &f, l7data, sizeof(l7data), STREAM_TOSERVER, pm_results, &rdir); FAIL_IF(cnt != 1); FAIL_IF(pm_results[0] != ALPROTO_HTTP1); memset(pm_results, 0, sizeof(pm_results)); cnt = AppLayerProtoDetectPMGetProto(alpd_tctx, &f, l7data_resp, sizeof(l7data_resp), STREAM_TOCLIENT, pm_results, &rdir); FAIL_IF(cnt != 1); FAIL_IF(pm_results[0] != ALPROTO_HTTP1); AppLayerProtoDetectDestroyCtxThread(alpd_tctx); AppLayerProtoDetectDeSetup(); AppLayerProtoDetectUnittestCtxRestore(); PASS; } typedef struct AppLayerProtoDetectPPTestDataElement_ { const char *alproto_name; AppProto alproto; uint16_t port; uint32_t alproto_mask; uint32_t min_depth; uint32_t max_depth; } AppLayerProtoDetectPPTestDataElement; typedef struct AppLayerProtoDetectPPTestDataPort_ { uint16_t port; uint32_t alproto_mask; uint16_t dp_max_depth; uint16_t sp_max_depth; AppLayerProtoDetectPPTestDataElement *toserver_element; AppLayerProtoDetectPPTestDataElement *toclient_element; int ts_no_of_element; int tc_no_of_element; } AppLayerProtoDetectPPTestDataPort; typedef struct AppLayerProtoDetectPPTestDataIPProto_ { uint8_t ipproto; AppLayerProtoDetectPPTestDataPort *port; int no_of_port; } AppLayerProtoDetectPPTestDataIPProto; static int AppLayerProtoDetectPPTestData(AppLayerProtoDetectProbingParser *pp, AppLayerProtoDetectPPTestDataIPProto *ip_proto, int no_of_ip_proto) { int result = 0; int i = -1, j = -1 , k = -1; #ifdef DEBUG int dir = 0; #endif for (i = 0; i < no_of_ip_proto; i++, pp = pp->next) { if (pp->ipproto != ip_proto[i].ipproto) goto end; AppLayerProtoDetectProbingParserPort *pp_port = pp->port; for (k = 0; k < ip_proto[i].no_of_port; k++, pp_port = pp_port->next) { if (pp_port->port != ip_proto[i].port[k].port) goto end; if (pp_port->alproto_mask != ip_proto[i].port[k].alproto_mask) goto end; if (pp_port->alproto_mask != ip_proto[i].port[k].alproto_mask) goto end; if (pp_port->dp_max_depth != ip_proto[i].port[k].dp_max_depth) goto end; if (pp_port->sp_max_depth != ip_proto[i].port[k].sp_max_depth) goto end; AppLayerProtoDetectProbingParserElement *pp_element = pp_port->dp; #ifdef DEBUG dir = 0; #endif for (j = 0 ; j < ip_proto[i].port[k].ts_no_of_element; j++, pp_element = pp_element->next) { if (pp_element->alproto != ip_proto[i].port[k].toserver_element[j].alproto) { goto end; } if (pp_element->port != ip_proto[i].port[k].toserver_element[j].port) { goto end; } if (pp_element->alproto_mask != ip_proto[i].port[k].toserver_element[j].alproto_mask) { goto end; } if (pp_element->min_depth != ip_proto[i].port[k].toserver_element[j].min_depth) { goto end; } if (pp_element->max_depth != ip_proto[i].port[k].toserver_element[j].max_depth) { goto end; } } /* for */ if (pp_element != NULL) goto end; pp_element = pp_port->sp; #ifdef DEBUG dir = 1; #endif for (j = 0 ; j < ip_proto[i].port[k].tc_no_of_element; j++, pp_element = pp_element->next) { if (pp_element->alproto != ip_proto[i].port[k].toclient_element[j].alproto) { goto end; } if (pp_element->port != ip_proto[i].port[k].toclient_element[j].port) { goto end; } if (pp_element->alproto_mask != ip_proto[i].port[k].toclient_element[j].alproto_mask) { goto end; } if (pp_element->min_depth != ip_proto[i].port[k].toclient_element[j].min_depth) { goto end; } if (pp_element->max_depth != ip_proto[i].port[k].toclient_element[j].max_depth) { goto end; } } /* for */ if (pp_element != NULL) goto end; } if (pp_port != NULL) goto end; } if (pp != NULL) goto end; result = 1; end: #ifdef DEBUG printf("i = %d, k = %d, j = %d(%s)\n", i, k, j, (dir == 0) ? "ts" : "tc"); #endif return result; } static uint16_t ProbingParserDummyForTesting(Flow *f, uint8_t direction, const uint8_t *input, uint32_t input_len, uint8_t *rdir) { return 0; } static int AppLayerProtoDetectTest15(void) { AppLayerProtoDetectUnittestCtxBackup(); AppLayerProtoDetectSetup(); int result = 0; AppLayerProtoDetectPPRegister(IPPROTO_TCP, "80", ALPROTO_HTTP1, 5, 8, STREAM_TOSERVER, ProbingParserDummyForTesting, NULL); AppLayerProtoDetectPPRegister(IPPROTO_TCP, "80", ALPROTO_SMB, 5, 6, STREAM_TOSERVER, ProbingParserDummyForTesting, NULL); AppLayerProtoDetectPPRegister(IPPROTO_TCP, "80", ALPROTO_FTP, 7, 10, STREAM_TOSERVER, ProbingParserDummyForTesting, NULL); AppLayerProtoDetectPPRegister(IPPROTO_TCP, "81", ALPROTO_DCERPC, 9, 10, STREAM_TOSERVER, ProbingParserDummyForTesting, NULL); AppLayerProtoDetectPPRegister(IPPROTO_TCP, "81", ALPROTO_FTP, 7, 15, STREAM_TOSERVER, ProbingParserDummyForTesting, NULL); AppLayerProtoDetectPPRegister(IPPROTO_TCP, "0", ALPROTO_SMTP, 12, 0, STREAM_TOSERVER, ProbingParserDummyForTesting, NULL); AppLayerProtoDetectPPRegister(IPPROTO_TCP, "0", ALPROTO_TLS, 12, 18, STREAM_TOSERVER, ProbingParserDummyForTesting, NULL); AppLayerProtoDetectPPRegister(IPPROTO_TCP, "85", ALPROTO_DCERPC, 9, 10, STREAM_TOSERVER, ProbingParserDummyForTesting, NULL); AppLayerProtoDetectPPRegister(IPPROTO_TCP, "85", ALPROTO_FTP, 7, 15, STREAM_TOSERVER, ProbingParserDummyForTesting, NULL); result = 1; AppLayerProtoDetectPPRegister(IPPROTO_UDP, "85", ALPROTO_IMAP, 12, 23, STREAM_TOSERVER, ProbingParserDummyForTesting, NULL); /* toclient */ AppLayerProtoDetectPPRegister(IPPROTO_TCP, "0", ALPROTO_JABBER, 12, 23, STREAM_TOCLIENT, ProbingParserDummyForTesting, NULL); AppLayerProtoDetectPPRegister(IPPROTO_TCP, "0", ALPROTO_IRC, 12, 14, STREAM_TOCLIENT, ProbingParserDummyForTesting, NULL); AppLayerProtoDetectPPRegister(IPPROTO_TCP, "85", ALPROTO_DCERPC, 9, 10, STREAM_TOCLIENT, ProbingParserDummyForTesting, NULL); AppLayerProtoDetectPPRegister(IPPROTO_TCP, "81", ALPROTO_FTP, 7, 15, STREAM_TOCLIENT, ProbingParserDummyForTesting, NULL); AppLayerProtoDetectPPRegister(IPPROTO_TCP, "0", ALPROTO_TLS, 12, 18, STREAM_TOCLIENT, ProbingParserDummyForTesting, NULL); AppLayerProtoDetectPPRegister(IPPROTO_TCP, "80", ALPROTO_HTTP1, 5, 8, STREAM_TOCLIENT, ProbingParserDummyForTesting, NULL); AppLayerProtoDetectPPRegister(IPPROTO_TCP, "81", ALPROTO_DCERPC, 9, 10, STREAM_TOCLIENT, ProbingParserDummyForTesting, NULL); AppLayerProtoDetectPPRegister(IPPROTO_TCP, "90", ALPROTO_FTP, 7, 15, STREAM_TOCLIENT, ProbingParserDummyForTesting, NULL); AppLayerProtoDetectPPRegister(IPPROTO_TCP, "80", ALPROTO_SMB, 5, 6, STREAM_TOCLIENT, ProbingParserDummyForTesting, NULL); AppLayerProtoDetectPPRegister(IPPROTO_UDP, "85", ALPROTO_IMAP, 12, 23, STREAM_TOCLIENT, ProbingParserDummyForTesting, NULL); AppLayerProtoDetectPPRegister(IPPROTO_TCP, "0", ALPROTO_SMTP, 12, 17, STREAM_TOCLIENT, ProbingParserDummyForTesting, NULL); AppLayerProtoDetectPPRegister(IPPROTO_TCP, "80", ALPROTO_FTP, 7, 10, STREAM_TOCLIENT, ProbingParserDummyForTesting, NULL); AppLayerProtoDetectPPTestDataElement element_ts_80[] = { { "http", ALPROTO_HTTP1, 80, 1 << ALPROTO_HTTP1, 5, 8 }, { "smb", ALPROTO_SMB, 80, 1 << ALPROTO_SMB, 5, 6 }, { "ftp", ALPROTO_FTP, 80, 1 << ALPROTO_FTP, 7, 10 }, { "smtp", ALPROTO_SMTP, 0, 1 << ALPROTO_SMTP, 12, 0 }, { "tls", ALPROTO_TLS, 0, 1 << ALPROTO_TLS, 12, 18 }, { "irc", ALPROTO_IRC, 0, 1 << ALPROTO_IRC, 12, 25 }, { "jabber", ALPROTO_JABBER, 0, 1 << ALPROTO_JABBER, 12, 23 }, }; AppLayerProtoDetectPPTestDataElement element_tc_80[] = { { "http", ALPROTO_HTTP1, 80, 1 << ALPROTO_HTTP1, 5, 8 }, { "smb", ALPROTO_SMB, 80, 1 << ALPROTO_SMB, 5, 6 }, { "ftp", ALPROTO_FTP, 80, 1 << ALPROTO_FTP, 7, 10 }, { "jabber", ALPROTO_JABBER, 0, 1 << ALPROTO_JABBER, 12, 23 }, { "irc", ALPROTO_IRC, 0, 1 << ALPROTO_IRC, 12, 14 }, { "tls", ALPROTO_TLS, 0, 1 << ALPROTO_TLS, 12, 18 }, { "smtp", ALPROTO_SMTP, 0, 1 << ALPROTO_SMTP, 12, 17 } }; AppLayerProtoDetectPPTestDataElement element_ts_81[] = { { "dcerpc", ALPROTO_DCERPC, 81, 1 << ALPROTO_DCERPC, 9, 10 }, { "ftp", ALPROTO_FTP, 81, 1 << ALPROTO_FTP, 7, 15 }, { "smtp", ALPROTO_SMTP, 0, 1 << ALPROTO_SMTP, 12, 0 }, { "tls", ALPROTO_TLS, 0, 1 << ALPROTO_TLS, 12, 18 }, { "irc", ALPROTO_IRC, 0, 1 << ALPROTO_IRC, 12, 25 }, { "jabber", ALPROTO_JABBER, 0, 1 << ALPROTO_JABBER, 12, 23 }, }; AppLayerProtoDetectPPTestDataElement element_tc_81[] = { { "ftp", ALPROTO_FTP, 81, 1 << ALPROTO_FTP, 7, 15 }, { "dcerpc", ALPROTO_DCERPC, 81, 1 << ALPROTO_DCERPC, 9, 10 }, { "jabber", ALPROTO_JABBER, 0, 1 << ALPROTO_JABBER, 12, 23 }, { "irc", ALPROTO_IRC, 0, 1 << ALPROTO_IRC, 12, 14 }, { "tls", ALPROTO_TLS, 0, 1 << ALPROTO_TLS, 12, 18 }, { "smtp", ALPROTO_SMTP, 0, 1 << ALPROTO_SMTP, 12, 17 } }; AppLayerProtoDetectPPTestDataElement element_ts_85[] = { { "dcerpc", ALPROTO_DCERPC, 85, 1 << ALPROTO_DCERPC, 9, 10 }, { "ftp", ALPROTO_FTP, 85, 1 << ALPROTO_FTP, 7, 15 }, { "smtp", ALPROTO_SMTP, 0, 1 << ALPROTO_SMTP, 12, 0 }, { "tls", ALPROTO_TLS, 0, 1 << ALPROTO_TLS, 12, 18 }, { "irc", ALPROTO_IRC, 0, 1 << ALPROTO_IRC, 12, 25 }, { "jabber", ALPROTO_JABBER, 0, 1 << ALPROTO_JABBER, 12, 23 }, }; AppLayerProtoDetectPPTestDataElement element_tc_85[] = { { "dcerpc", ALPROTO_DCERPC, 85, 1 << ALPROTO_DCERPC, 9, 10 }, { "jabber", ALPROTO_JABBER, 0, 1 << ALPROTO_JABBER, 12, 23 }, { "irc", ALPROTO_IRC, 0, 1 << ALPROTO_IRC, 12, 14 }, { "tls", ALPROTO_TLS, 0, 1 << ALPROTO_TLS, 12, 18 }, { "smtp", ALPROTO_SMTP, 0, 1 << ALPROTO_SMTP, 12, 17 } }; AppLayerProtoDetectPPTestDataElement element_ts_90[] = { { "smtp", ALPROTO_SMTP, 0, 1 << ALPROTO_SMTP, 12, 0 }, { "tls", ALPROTO_TLS, 0, 1 << ALPROTO_TLS, 12, 18 }, { "irc", ALPROTO_IRC, 0, 1 << ALPROTO_IRC, 12, 25 }, { "jabber", ALPROTO_JABBER, 0, 1 << ALPROTO_JABBER, 12, 23 }, }; AppLayerProtoDetectPPTestDataElement element_tc_90[] = { { "ftp", ALPROTO_FTP, 90, 1 << ALPROTO_FTP, 7, 15 }, { "jabber", ALPROTO_JABBER, 0, 1 << ALPROTO_JABBER, 12, 23 }, { "irc", ALPROTO_IRC, 0, 1 << ALPROTO_IRC, 12, 14 }, { "tls", ALPROTO_TLS, 0, 1 << ALPROTO_TLS, 12, 18 }, { "smtp", ALPROTO_SMTP, 0, 1 << ALPROTO_SMTP, 12, 17 } }; AppLayerProtoDetectPPTestDataElement element_ts_0[] = { { "smtp", ALPROTO_SMTP, 0, 1 << ALPROTO_SMTP, 12, 0 }, { "tls", ALPROTO_TLS, 0, 1 << ALPROTO_TLS, 12, 18 }, { "irc", ALPROTO_IRC, 0, 1 << ALPROTO_IRC, 12, 25 }, { "jabber", ALPROTO_JABBER, 0, 1 << ALPROTO_JABBER, 12, 23 }, }; AppLayerProtoDetectPPTestDataElement element_tc_0[] = { { "jabber", ALPROTO_JABBER, 0, 1 << ALPROTO_JABBER, 12, 23 }, { "irc", ALPROTO_IRC, 0, 1 << ALPROTO_IRC, 12, 14 }, { "tls", ALPROTO_TLS, 0, 1 << ALPROTO_TLS, 12, 18 }, { "smtp", ALPROTO_SMTP, 0, 1 << ALPROTO_SMTP, 12, 17 } }; AppLayerProtoDetectPPTestDataElement element_ts_85_udp[] = { { "imap", ALPROTO_IMAP, 85, 1 << ALPROTO_IMAP, 12, 23 }, }; AppLayerProtoDetectPPTestDataElement element_tc_85_udp[] = { { "imap", ALPROTO_IMAP, 85, 1 << ALPROTO_IMAP, 12, 23 }, }; AppLayerProtoDetectPPTestDataPort ports_tcp[] = { { 80, ((1 << ALPROTO_HTTP1) | (1 << ALPROTO_SMB) | (1 << ALPROTO_FTP) | (1 << ALPROTO_SMTP) | (1 << ALPROTO_TLS) | (1 << ALPROTO_IRC) | (1 << ALPROTO_JABBER)), ((1 << ALPROTO_HTTP1) | (1 << ALPROTO_SMB) | (1 << ALPROTO_FTP) | (1 << ALPROTO_JABBER) | (1 << ALPROTO_IRC) | (1 << ALPROTO_TLS) | (1 << ALPROTO_SMTP)), 23, element_ts_80, element_tc_80, sizeof(element_ts_80) / sizeof(AppLayerProtoDetectPPTestDataElement), sizeof(element_tc_80) / sizeof(AppLayerProtoDetectPPTestDataElement), }, { 81, ((1 << ALPROTO_DCERPC) | (1 << ALPROTO_FTP) | (1 << ALPROTO_SMTP) | (1 << ALPROTO_TLS) | (1 << ALPROTO_IRC) | (1 << ALPROTO_JABBER)), ((1 << ALPROTO_FTP) | (1 << ALPROTO_DCERPC) | (1 << ALPROTO_JABBER) | (1 << ALPROTO_IRC) | (1 << ALPROTO_TLS) | (1 << ALPROTO_SMTP)), 23, element_ts_81, element_tc_81, sizeof(element_ts_81) / sizeof(AppLayerProtoDetectPPTestDataElement), sizeof(element_tc_81) / sizeof(AppLayerProtoDetectPPTestDataElement), }, { 85, ((1 << ALPROTO_DCERPC) | (1 << ALPROTO_FTP) | (1 << ALPROTO_SMTP) | (1 << ALPROTO_TLS) | (1 << ALPROTO_IRC) | (1 << ALPROTO_JABBER)), ((1 << ALPROTO_DCERPC) | (1 << ALPROTO_JABBER) | (1 << ALPROTO_IRC) | (1 << ALPROTO_TLS) | (1 << ALPROTO_SMTP)), 23, element_ts_85, element_tc_85, sizeof(element_ts_85) / sizeof(AppLayerProtoDetectPPTestDataElement), sizeof(element_tc_85) / sizeof(AppLayerProtoDetectPPTestDataElement) }, { 90, ((1 << ALPROTO_SMTP) | (1 << ALPROTO_TLS) | (1 << ALPROTO_IRC) | (1 << ALPROTO_JABBER)), ((1 << ALPROTO_FTP) | (1 << ALPROTO_JABBER) | (1 << ALPROTO_IRC) | (1 << ALPROTO_TLS) | (1 << ALPROTO_SMTP)), 23, element_ts_90, element_tc_90, sizeof(element_ts_90) / sizeof(AppLayerProtoDetectPPTestDataElement), sizeof(element_tc_90) / sizeof(AppLayerProtoDetectPPTestDataElement) }, { 0, ((1 << ALPROTO_SMTP) | (1 << ALPROTO_TLS) | (1 << ALPROTO_IRC) | (1 << ALPROTO_JABBER)), ((1 << ALPROTO_JABBER) | (1 << ALPROTO_IRC) | (1 << ALPROTO_TLS) | (1 << ALPROTO_SMTP)), 23, element_ts_0, element_tc_0, sizeof(element_ts_0) / sizeof(AppLayerProtoDetectPPTestDataElement), sizeof(element_tc_0) / sizeof(AppLayerProtoDetectPPTestDataElement) } }; AppLayerProtoDetectPPTestDataPort ports_udp[] = { { 85, (1 << ALPROTO_IMAP), (1 << ALPROTO_IMAP), 23, element_ts_85_udp, element_tc_85_udp, sizeof(element_ts_85_udp) / sizeof(AppLayerProtoDetectPPTestDataElement), sizeof(element_tc_85_udp) / sizeof(AppLayerProtoDetectPPTestDataElement), }, }; AppLayerProtoDetectPPTestDataIPProto ip_proto[] = { { IPPROTO_TCP, ports_tcp, sizeof(ports_tcp) / sizeof(AppLayerProtoDetectPPTestDataPort), }, { IPPROTO_UDP, ports_udp, sizeof(ports_udp) / sizeof(AppLayerProtoDetectPPTestDataPort), }, }; if (AppLayerProtoDetectPPTestData(alpd_ctx.ctx_pp, ip_proto, sizeof(ip_proto) / sizeof(AppLayerProtoDetectPPTestDataIPProto)) == 0) { goto end; } result = 1; end: AppLayerProtoDetectDeSetup(); AppLayerProtoDetectUnittestCtxRestore(); return result; } /** \test test if the engine detect the proto and match with it */ static int AppLayerProtoDetectTest16(void) { int result = 0; Flow *f = NULL; HtpState *http_state = NULL; uint8_t http_buf1[] = "POST /one HTTP/1.0\r\n" "User-Agent: Mozilla/1.0\r\n" "Cookie: hellocatch\r\n\r\n"; uint32_t http_buf1_len = sizeof(http_buf1) - 1; TcpSession ssn; Packet *p = NULL; Signature *s = NULL; ThreadVars tv; DetectEngineThreadCtx *det_ctx = NULL; DetectEngineCtx *de_ctx = NULL; AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); memset(&tv, 0, sizeof(ThreadVars)); memset(&ssn, 0, sizeof(TcpSession)); p = UTHBuildPacket(NULL, 0, IPPROTO_TCP); if (p == NULL) { printf("packet setup failed: "); goto end; } f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1024, 80); if (f == NULL) { printf("flow setup failed: "); goto end; } f->protoctx = &ssn; f->proto = IPPROTO_TCP; p->flow = f; p->flowflags |= FLOW_PKT_TOSERVER; p->flowflags |= FLOW_PKT_ESTABLISHED; p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST; f->alproto = ALPROTO_HTTP1; StreamTcpInitConfig(true); de_ctx = DetectEngineCtxInit(); if (de_ctx == NULL) { goto end; } de_ctx->flags |= DE_QUIET; s = de_ctx->sig_list = SigInit(de_ctx, "alert http any any -> any any " "(msg:\"Test content option\"; " "sid:1;)"); if (s == NULL) { goto end; } SigGroupBuild(de_ctx); DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx); int r = AppLayerParserParse( NULL, alp_tctx, f, ALPROTO_HTTP1, STREAM_TOSERVER, http_buf1, http_buf1_len); if (r != 0) { printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); goto end; } http_state = f->alstate; if (http_state == NULL) { printf("no http state: "); goto end; } /* do detect */ SigMatchSignatures(&tv, de_ctx, det_ctx, p); if (!PacketAlertCheck(p, 1)) { printf("sig 1 didn't alert, but it should: "); goto end; } result = 1; end: if (alp_tctx != NULL) AppLayerParserThreadCtxFree(alp_tctx); if (det_ctx != NULL) DetectEngineThreadCtxDeinit(&tv, det_ctx); if (de_ctx != NULL) SigGroupCleanup(de_ctx); if (de_ctx != NULL) DetectEngineCtxFree(de_ctx); StreamTcpFreeConfig(true); UTHFreePackets(&p, 1); UTHFreeFlow(f); return result; } /** \test test if the engine detect the proto on a non standar port * and match with it */ static int AppLayerProtoDetectTest17(void) { int result = 0; Flow *f = NULL; HtpState *http_state = NULL; uint8_t http_buf1[] = "POST /one HTTP/1.0\r\n" "User-Agent: Mozilla/1.0\r\n" "Cookie: hellocatch\r\n\r\n"; uint32_t http_buf1_len = sizeof(http_buf1) - 1; TcpSession ssn; Packet *p = NULL; Signature *s = NULL; ThreadVars tv; DetectEngineThreadCtx *det_ctx = NULL; DetectEngineCtx *de_ctx = NULL; AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); memset(&tv, 0, sizeof(ThreadVars)); memset(&ssn, 0, sizeof(TcpSession)); p = UTHBuildPacketSrcDstPorts(http_buf1, http_buf1_len, IPPROTO_TCP, 12345, 88); f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1024, 80); if (f == NULL) goto end; f->protoctx = &ssn; f->proto = IPPROTO_TCP; p->flow = f; p->flowflags |= FLOW_PKT_TOSERVER; p->flowflags |= FLOW_PKT_ESTABLISHED; p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST; f->alproto = ALPROTO_HTTP1; StreamTcpInitConfig(true); de_ctx = DetectEngineCtxInit(); if (de_ctx == NULL) { goto end; } de_ctx->flags |= DE_QUIET; s = de_ctx->sig_list = SigInit(de_ctx, "alert http any !80 -> any any " "(msg:\"http over non standar port\"; " "sid:1;)"); if (s == NULL) { goto end; } SigGroupBuild(de_ctx); DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx); int r = AppLayerParserParse( NULL, alp_tctx, f, ALPROTO_HTTP1, STREAM_TOSERVER, http_buf1, http_buf1_len); if (r != 0) { printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); goto end; } http_state = f->alstate; if (http_state == NULL) { printf("no http state: "); goto end; } /* do detect */ SigMatchSignatures(&tv, de_ctx, det_ctx, p); if (!PacketAlertCheck(p, 1)) { printf("sig 1 didn't alert, but it should: "); goto end; } result = 1; end: if (alp_tctx != NULL) AppLayerParserThreadCtxFree(alp_tctx); if (det_ctx != NULL) DetectEngineThreadCtxDeinit(&tv, det_ctx); if (de_ctx != NULL) SigGroupCleanup(de_ctx); if (de_ctx != NULL) DetectEngineCtxFree(de_ctx); StreamTcpFreeConfig(true); UTHFreePackets(&p, 1); UTHFreeFlow(f); return result; } /** \test test if the engine detect the proto and doesn't match * because the sig expects another proto (ex ftp)*/ static int AppLayerProtoDetectTest18(void) { int result = 0; Flow *f = NULL; HtpState *http_state = NULL; uint8_t http_buf1[] = "POST /one HTTP/1.0\r\n" "User-Agent: Mozilla/1.0\r\n" "Cookie: hellocatch\r\n\r\n"; uint32_t http_buf1_len = sizeof(http_buf1) - 1; TcpSession ssn; Packet *p = NULL; Signature *s = NULL; ThreadVars tv; DetectEngineThreadCtx *det_ctx = NULL; DetectEngineCtx *de_ctx = NULL; AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); memset(&tv, 0, sizeof(ThreadVars)); memset(&ssn, 0, sizeof(TcpSession)); p = UTHBuildPacket(http_buf1, http_buf1_len, IPPROTO_TCP); f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1024, 80); if (f == NULL) goto end; f->protoctx = &ssn; f->proto = IPPROTO_TCP; p->flow = f; p->flowflags |= FLOW_PKT_TOSERVER; p->flowflags |= FLOW_PKT_ESTABLISHED; p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST; f->alproto = ALPROTO_HTTP1; StreamTcpInitConfig(true); de_ctx = DetectEngineCtxInit(); if (de_ctx == NULL) { goto end; } de_ctx->flags |= DE_QUIET; s = de_ctx->sig_list = SigInit(de_ctx, "alert ftp any any -> any any " "(msg:\"Test content option\"; " "sid:1;)"); if (s == NULL) { goto end; } SigGroupBuild(de_ctx); DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx); int r = AppLayerParserParse( NULL, alp_tctx, f, ALPROTO_HTTP1, STREAM_TOSERVER, http_buf1, http_buf1_len); if (r != 0) { printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); goto end; } http_state = f->alstate; if (http_state == NULL) { printf("no http state: "); goto end; } /* do detect */ SigMatchSignatures(&tv, de_ctx, det_ctx, p); if (PacketAlertCheck(p, 1)) { printf("sig 1 alerted, but it should not (it's not ftp): "); goto end; } result = 1; end: if (alp_tctx != NULL) AppLayerParserThreadCtxFree(alp_tctx); if (det_ctx != NULL) DetectEngineThreadCtxDeinit(&tv, det_ctx); if (de_ctx != NULL) SigGroupCleanup(de_ctx); if (de_ctx != NULL) DetectEngineCtxFree(de_ctx); StreamTcpFreeConfig(true); UTHFreePackets(&p, 1); UTHFreeFlow(f); return result; } /** \test test if the engine detect the proto and doesn't match * because the packet has another proto (ex ftp) */ static int AppLayerProtoDetectTest19(void) { int result = 0; Flow *f = NULL; uint8_t http_buf1[] = "MPUT one\r\n"; uint32_t http_buf1_len = sizeof(http_buf1) - 1; TcpSession ssn; Packet *p = NULL; Signature *s = NULL; ThreadVars tv; DetectEngineThreadCtx *det_ctx = NULL; DetectEngineCtx *de_ctx = NULL; AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); memset(&tv, 0, sizeof(ThreadVars)); memset(&ssn, 0, sizeof(TcpSession)); p = UTHBuildPacketSrcDstPorts(http_buf1, http_buf1_len, IPPROTO_TCP, 12345, 88); f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1024, 80); if (f == NULL) goto end; f->protoctx = &ssn; f->proto = IPPROTO_TCP; p->flow = f; p->flowflags |= FLOW_PKT_TOSERVER; p->flowflags |= FLOW_PKT_ESTABLISHED; p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST; f->alproto = ALPROTO_FTP; StreamTcpInitConfig(true); de_ctx = DetectEngineCtxInit(); if (de_ctx == NULL) { goto end; } de_ctx->flags |= DE_QUIET; s = de_ctx->sig_list = SigInit(de_ctx, "alert http any !80 -> any any " "(msg:\"http over non standar port\"; " "sid:1;)"); if (s == NULL) { goto end; } SigGroupBuild(de_ctx); DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx); int r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_FTP, STREAM_TOSERVER, http_buf1, http_buf1_len); if (r != 0) { printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); goto end; } /* do detect */ SigMatchSignatures(&tv, de_ctx, det_ctx, p); if (PacketAlertCheck(p, 1)) { printf("sig 1 alerted, but it should not (it's ftp): "); goto end; } result = 1; end: if (alp_tctx != NULL) AppLayerParserThreadCtxFree(alp_tctx); if (det_ctx != NULL) DetectEngineThreadCtxDeinit(&tv, det_ctx); if (de_ctx != NULL) SigGroupCleanup(de_ctx); if (de_ctx != NULL) DetectEngineCtxFree(de_ctx); StreamTcpFreeConfig(true); UTHFreePackets(&p, 1); UTHFreeFlow(f); return result; } void AppLayerProtoDetectUnittestsRegister(void) { SCEnter(); UtRegisterTest("AppLayerProtoDetectTest01", AppLayerProtoDetectTest01); UtRegisterTest("AppLayerProtoDetectTest02", AppLayerProtoDetectTest02); UtRegisterTest("AppLayerProtoDetectTest03", AppLayerProtoDetectTest03); UtRegisterTest("AppLayerProtoDetectTest04", AppLayerProtoDetectTest04); UtRegisterTest("AppLayerProtoDetectTest05", AppLayerProtoDetectTest05); UtRegisterTest("AppLayerProtoDetectTest06", AppLayerProtoDetectTest06); UtRegisterTest("AppLayerProtoDetectTest07", AppLayerProtoDetectTest07); UtRegisterTest("AppLayerProtoDetectTest08", AppLayerProtoDetectTest08); UtRegisterTest("AppLayerProtoDetectTest09", AppLayerProtoDetectTest09); UtRegisterTest("AppLayerProtoDetectTest10", AppLayerProtoDetectTest10); UtRegisterTest("AppLayerProtoDetectTest11", AppLayerProtoDetectTest11); UtRegisterTest("AppLayerProtoDetectTest12", AppLayerProtoDetectTest12); UtRegisterTest("AppLayerProtoDetectTest13", AppLayerProtoDetectTest13); UtRegisterTest("AppLayerProtoDetectTest14", AppLayerProtoDetectTest14); UtRegisterTest("AppLayerProtoDetectTest15", AppLayerProtoDetectTest15); UtRegisterTest("AppLayerProtoDetectTest16", AppLayerProtoDetectTest16); UtRegisterTest("AppLayerProtoDetectTest17", AppLayerProtoDetectTest17); UtRegisterTest("AppLayerProtoDetectTest18", AppLayerProtoDetectTest18); UtRegisterTest("AppLayerProtoDetectTest19", AppLayerProtoDetectTest19); SCReturn; } #endif /* UNITTESTS */