diff --git a/src/detect-lua.c b/src/detect-lua.c index 39b8232558..29d10c5d70 100644 --- a/src/detect-lua.c +++ b/src/detect-lua.c @@ -75,6 +75,7 @@ static void DetectLuaRegisterTests(void); static void DetectLuaFree(DetectEngineCtx *, void *); static int g_lua_ja3_list_id = 0; static int g_lua_ja3s_list_id = 0; +static int g_lua_http_uri_list_id = 0; /** * \brief Registration function for keyword: lua @@ -104,6 +105,12 @@ void DetectLuaRegister(void) DetectAppLayerInspectEngineRegister( "ja3s.lua", ALPROTO_QUIC, SIG_FLAG_TOCLIENT, 1, DetectEngineInspectGenericList, NULL); + g_lua_http_uri_list_id = DetectBufferTypeRegister("http.uri.lua"); + DetectAppLayerInspectEngineRegister("http.uri.lua", ALPROTO_HTTP1, SIG_FLAG_TOSERVER, + HTP_REQUEST_PROGRESS_LINE, DetectEngineInspectGenericList, NULL); + DetectAppLayerInspectEngineRegisterSubState("http.uri.lua", ALPROTO_HTTP2, SIG_FLAG_TOSERVER, + HTTP2TxTypeStream, HTTP2ProgHeaders, DetectEngineInspectGenericList, NULL); + SCLogDebug("registering lua rule option"); } @@ -112,6 +119,7 @@ void DetectLuaRegister(void) #define FLAG_DATATYPE_PAYLOAD BIT_U32(1) #define FLAG_LIST_JA3 BIT_U32(3) #define FLAG_LIST_JA3S BIT_U32(4) +#define FLAG_LIST_HTTP_URI BIT_U32(5) #define FLAG_ERROR_LOGGED BIT_U32(23) #define FLAG_BLOCKED_FUNCTION_LOGGED BIT_U32(24) #define FLAG_INSTRUCTION_LIMIT_LOGGED BIT_U32(25) @@ -659,6 +667,9 @@ static int DetectLuaSetupPrime(DetectEngineCtx *de_ctx, DetectLuaData *ld, const ld->flags |= FLAG_LIST_JA3; } else if (strcmp(k, "ja3s") == 0) { ld->flags |= FLAG_LIST_JA3S; + } else if (strcmp(k, "http.uri") == 0) { + // TODO/DRAFT/decision-required: Should this rather be named http.request_line ? + ld->flags |= FLAG_LIST_HTTP_URI; } else if (strcmp(k, "packet") == 0) { ld->flags |= FLAG_DATATYPE_PACKET; } else if (strcmp(k, "payload") == 0) { @@ -770,6 +781,8 @@ static int DetectLuaSetup (DetectEngineCtx *de_ctx, Signature *s, const char *st list = g_lua_ja3_list_id; } else if (lua->flags & FLAG_LIST_JA3S) { list = g_lua_ja3s_list_id; + } else if (lua->flags & FLAG_LIST_HTTP_URI) { + list = g_lua_http_uri_list_id; } } diff --git a/src/util-lua-http.c b/src/util-lua-http.c index f5599a4bb5..ccb3f45347 100644 --- a/src/util-lua-http.c +++ b/src/util-lua-http.c @@ -31,16 +31,13 @@ static const char htp_tx[] = "suricata:http:tx"; struct LuaTx { - htp_tx_t *tx; + void *tx; + AppProto alproto; }; -static int LuaHttpGetTx(lua_State *luastate) +static int LuaHttpGetTxProtoChecked(Flow *flow, lua_State *luastate) { - if (!LuaStateNeedProto(luastate, ALPROTO_HTTP1)) { - return LuaCallbackError(luastate, "error: protocol not http"); - } - - htp_tx_t *tx = LuaStateGetTX(luastate); + void *tx = LuaStateGetTX(luastate); if (tx == NULL) { return LuaCallbackError(luastate, "error: no tx available"); } @@ -50,6 +47,7 @@ static int LuaHttpGetTx(lua_State *luastate) } ltx->tx = tx; + ltx->alproto = flow->alproto; luaL_getmetatable(luastate, htp_tx); lua_setmetatable(luastate, -2); @@ -57,6 +55,46 @@ static int LuaHttpGetTx(lua_State *luastate) return 1; } +static int LuaHttpGetTx(lua_State *luastate) +{ + Flow *flow = LuaStateGetFlow(luastate); + if (flow == NULL) + return LuaCallbackError(luastate, "internal error: no flow"); + + if (flow->alproto != ALPROTO_HTTP1 && flow->alproto != ALPROTO_HTTP2 && + flow->alproto != ALPROTO_DOH2) { + return LuaCallbackError(luastate, "error: protocol not http"); + } + + return LuaHttpGetTxProtoChecked(flow, luastate); +} + +static int LuaHttpGetHttp1Tx(lua_State *luastate) +{ + Flow *flow = LuaStateGetFlow(luastate); + if (flow == NULL) + return LuaCallbackError(luastate, "internal error: no flow"); + + if (flow->alproto != ALPROTO_HTTP1) { + return LuaCallbackError(luastate, "error: protocol not http"); + } + + return LuaHttpGetTxProtoChecked(flow, luastate); +} + +static int LuaHttpGetHttp2Tx(lua_State *luastate) +{ + Flow *flow = LuaStateGetFlow(luastate); + if (flow == NULL) + return LuaCallbackError(luastate, "internal error: no flow"); + + if (flow->alproto != ALPROTO_HTTP2 && flow->alproto != ALPROTO_DOH2) { + return LuaCallbackError(luastate, "error: protocol not http"); + } + + return LuaHttpGetTxProtoChecked(flow, luastate); +} + static int LuaHttpGetRequestHost(lua_State *luastate) { struct LuaTx *tx = luaL_testudata(luastate, 1, htp_tx); @@ -64,6 +102,10 @@ static int LuaHttpGetRequestHost(lua_State *luastate) lua_pushnil(luastate); return 1; } + if (tx->alproto != ALPROTO_HTTP1) { + lua_pushnil(luastate); + return 1; + } const struct bstr *host = htp_tx_request_hostname(tx->tx); if (host == NULL) { lua_pushnil(luastate); @@ -80,6 +122,16 @@ static int LuaHttpGetRequestUriRaw(lua_State *luastate) lua_pushnil(luastate); return 1; } + if (tx->alproto == ALPROTO_HTTP2 || tx->alproto == ALPROTO_DOH2) { + uint32_t b_len = 0; + const uint8_t *b = NULL; + + if (SCHttp2TxGetUri(tx->tx, &b, &b_len) != 1) { + lua_pushnil(luastate); + return 1; + } + return LuaPushStringBuffer(luastate, b, b_len); + } // else ALPROTO_HTTP1 const struct bstr *uri = htp_tx_request_uri(tx->tx); if (uri == NULL) { lua_pushnil(luastate); @@ -96,6 +148,17 @@ static int LuaHttpGetRequestUriNormalized(lua_State *luastate) lua_pushnil(luastate); return 1; } + if (tx->alproto == ALPROTO_HTTP2 || tx->alproto == ALPROTO_DOH2) { + uint32_t b_len = 0; + const uint8_t *b = NULL; + + if (SCHttp2TxGetUri(tx->tx, &b, &b_len) != 1) { + lua_pushnil(luastate); + return 1; + } + return LuaPushStringBuffer(luastate, b, b_len); + } // else ALPROTO_HTTP1 + bstr *request_uri_normalized = (bstr *)htp_tx_normalized_uri(tx->tx); if (request_uri_normalized == NULL || bstr_ptr(request_uri_normalized) == NULL || @@ -113,6 +176,16 @@ static int LuaHttpGetRequestLine(lua_State *luastate) lua_pushnil(luastate); return 1; } + if (tx->alproto == ALPROTO_HTTP2 || tx->alproto == ALPROTO_DOH2) { + uint32_t b_len = 0; + const uint8_t *b = NULL; + + if (SCHttp2TxGetRequestLine(tx->tx, &b, &b_len) != 1) { + lua_pushnil(luastate); + return 1; + } + return LuaPushStringBuffer(luastate, b, b_len); + } // else ALPROTO_HTTP1 const struct bstr *line = htp_tx_request_line(tx->tx); if (line == NULL) { @@ -130,6 +203,10 @@ static int LuaHttpGetResponseLine(lua_State *luastate) lua_pushnil(luastate); return 1; } + if (tx->alproto != ALPROTO_HTTP1) { + lua_pushnil(luastate); + return 1; + } const struct bstr *line = htp_tx_response_line(tx->tx); if (line == NULL) { @@ -147,6 +224,10 @@ static int LuaHttpGetHeader(lua_State *luastate, int dir) lua_pushnil(luastate); return 1; } + if (tx->alproto != ALPROTO_HTTP1) { + lua_pushnil(luastate); + return 1; + } /* since arg was added at last, it must be on top of the stack */ const char *name = LuaGetStringArgument(luastate, lua_gettop(luastate)); @@ -185,6 +266,10 @@ static int LuaHttpGetRawHeaders(lua_State *luastate, int dir) lua_pushnil(luastate); return 1; } + if (tx->alproto != ALPROTO_HTTP1) { + lua_pushnil(luastate); + return 1; + } HtpTxUserData *htud = (HtpTxUserData *)htp_tx_get_user_data(tx->tx); uint8_t *raw = htud->request_headers_raw; @@ -217,6 +302,10 @@ static int LuaHttpGetHeaders(lua_State *luastate, int dir) lua_pushnil(luastate); return 1; } + if (tx->alproto != ALPROTO_HTTP1) { + lua_pushnil(luastate); + return 1; + } const htp_headers_t *table = htp_tx_request_headers(tx->tx); if (dir == 1) @@ -258,6 +347,10 @@ static int LuaHttpGetBody(lua_State *luastate, int dir) lua_pushnil(luastate); return 1; } + if (tx->alproto != ALPROTO_HTTP1) { + lua_pushnil(luastate); + return 1; + } HtpTxUserData *htud = (HtpTxUserData *)htp_tx_get_user_data(tx->tx); @@ -329,6 +422,8 @@ static const struct luaL_Reg txlib[] = { static const struct luaL_Reg htplib[] = { // clang-format off {"get_tx", LuaHttpGetTx }, + {"get_http1_tx", LuaHttpGetHttp1Tx }, + {"get_http2_tx", LuaHttpGetHttp2Tx }, {NULL, NULL,}, // clang-format on };