• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

OISF / suricata / 23374838686

21 Mar 2026 07:29AM UTC coverage: 59.341% (-20.0%) from 79.315%
23374838686

Pull #15075

github

web-flow
Merge 90b4e834f into 6587e363a
Pull Request #15075: Stack 8001 v16.4

38 of 70 new or added lines in 10 files covered. (54.29%)

34165 existing lines in 563 files now uncovered.

119621 of 201584 relevant lines covered (59.34%)

650666.92 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/util-lua-ja3.c
1
/* Copyright (C) 2017 Open Information Security Foundation
2
 *
3
 * You can copy, redistribute or modify this Program under the terms of
4
 * the GNU General Public License version 2 as published by the Free
5
 * Software Foundation.
6
 *
7
 * This program is distributed in the hope that it will be useful,
8
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10
 * GNU General Public License for more details.
11
 *
12
 * You should have received a copy of the GNU General Public License
13
 * version 2 along with this program; if not, write to the Free Software
14
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15
 * 02110-1301, USA.
16
 */
17

18

19
/**
20
 * \file
21
 *
22
 * \author Mats Klepsland <mats.klepsland@gmail.com>
23
 *
24
 */
25

26
#include "suricata-common.h"
27
#include "util-lua-ja3.h"
28
#include "util-lua.h"
29
#include "util-lua-common.h"
30
#include "app-layer-ssl.h"
31
#include "rust.h"
32

33
static const char ja3_tx[] = "suricata:ja3:tx";
34

35
struct LuaTx {
36
    void *tx; // Quic or TLS Transaction
37
    AppProto alproto;
38
};
39

40
static int LuaJa3GetTx(lua_State *L)
UNCOV
41
{
×
UNCOV
42
    AppProto alproto = ALPROTO_QUIC;
×
UNCOV
43
    if (LuaStateNeedProto(L, ALPROTO_TLS)) {
×
UNCOV
44
        alproto = ALPROTO_TLS;
×
UNCOV
45
    } else if (!(LuaStateNeedProto(L, ALPROTO_QUIC))) {
×
46
        return LuaCallbackError(L, "error: protocol nor tls neither quic");
×
47
    }
×
UNCOV
48
    void *tx = LuaStateGetTX(L);
×
UNCOV
49
    if (tx == NULL) {
×
50
        return LuaCallbackError(L, "error: no tx available");
×
51
    }
×
UNCOV
52
    struct LuaTx *ltx = (struct LuaTx *)lua_newuserdata(L, sizeof(*ltx));
×
UNCOV
53
    if (ltx == NULL) {
×
54
        return LuaCallbackError(L, "error: fail to allocate user data");
×
55
    }
×
UNCOV
56
    ltx->tx = tx;
×
UNCOV
57
    ltx->alproto = alproto;
×
58

UNCOV
59
    luaL_getmetatable(L, ja3_tx);
×
UNCOV
60
    lua_setmetatable(L, -2);
×
61

UNCOV
62
    return 1;
×
UNCOV
63
}
×
64

65
static int LuaJa3TxGetHash(lua_State *L)
UNCOV
66
{
×
UNCOV
67
    struct LuaTx *ltx = luaL_testudata(L, 1, ja3_tx);
×
UNCOV
68
    if (ltx == NULL) {
×
69
        lua_pushnil(L);
×
70
        return 1;
×
71
    }
×
UNCOV
72
    if (ltx->alproto == ALPROTO_TLS) {
×
73
        SSLState *ssl_state = (SSLState *)ltx->tx;
×
74
        if (ssl_state->client_connp.ja3_hash == NULL) {
×
75
            lua_pushnil(L);
×
76
            return 1;
×
77
        }
×
78
        return LuaPushStringBuffer(L, (uint8_t *)ssl_state->client_connp.ja3_hash,
×
79
                strlen(ssl_state->client_connp.ja3_hash));
×
80
    } // else QUIC {
×
UNCOV
81
    const uint8_t *buf = NULL;
×
UNCOV
82
    uint32_t b_len = 0;
×
UNCOV
83
    if (!SCQuicTxGetJa3(ltx->tx, STREAM_TOSERVER, &buf, &b_len)) {
×
84
        lua_pushnil(L);
×
85
        return 1;
×
86
    }
×
UNCOV
87
    uint8_t ja3_hash[SC_MD5_HEX_LEN + 1];
×
88
    // this adds a final zero
UNCOV
89
    SCMd5HashBufferToHex(buf, b_len, (char *)ja3_hash, SC_MD5_HEX_LEN + 1);
×
UNCOV
90
    return LuaPushStringBuffer(L, ja3_hash, SC_MD5_HEX_LEN);
×
UNCOV
91
}
×
92

93
static int LuaJa3TxGetString(lua_State *L)
UNCOV
94
{
×
UNCOV
95
    struct LuaTx *ltx = luaL_testudata(L, 1, ja3_tx);
×
UNCOV
96
    if (ltx == NULL) {
×
97
        lua_pushnil(L);
×
98
        return 1;
×
99
    }
×
UNCOV
100
    if (ltx->alproto == ALPROTO_TLS) {
×
101
        SSLState *ssl_state = (SSLState *)ltx->tx;
×
102
        if (ssl_state->client_connp.ja3_str == NULL ||
×
103
                ssl_state->client_connp.ja3_str->data == NULL) {
×
104
            lua_pushnil(L);
×
105
            return 1;
×
106
        }
×
107
        return LuaPushStringBuffer(L, (uint8_t *)ssl_state->client_connp.ja3_str->data,
×
108
                ssl_state->client_connp.ja3_str->used);
×
109
    } // else QUIC {
×
UNCOV
110
    const uint8_t *buf = NULL;
×
UNCOV
111
    uint32_t b_len = 0;
×
UNCOV
112
    if (!SCQuicTxGetJa3(ltx->tx, STREAM_TOSERVER, &buf, &b_len)) {
×
113
        lua_pushnil(L);
×
114
        return 1;
×
115
    }
×
UNCOV
116
    return LuaPushStringBuffer(L, buf, b_len);
×
UNCOV
117
}
×
118

119
static int LuaJa3TxGetServerHash(lua_State *L)
UNCOV
120
{
×
UNCOV
121
    struct LuaTx *ltx = luaL_testudata(L, 1, ja3_tx);
×
UNCOV
122
    if (ltx == NULL) {
×
123
        lua_pushnil(L);
×
124
        return 1;
×
125
    }
×
UNCOV
126
    if (ltx->alproto == ALPROTO_TLS) {
×
UNCOV
127
        SSLState *ssl_state = (SSLState *)ltx->tx;
×
UNCOV
128
        if (ssl_state->server_connp.ja3_hash == NULL) {
×
129
            lua_pushnil(L);
×
130
            return 1;
×
131
        }
×
UNCOV
132
        return LuaPushStringBuffer(L, (uint8_t *)ssl_state->server_connp.ja3_hash,
×
UNCOV
133
                strlen(ssl_state->server_connp.ja3_hash));
×
UNCOV
134
    } // else QUIC {
×
135
    const uint8_t *buf = NULL;
×
136
    uint32_t b_len = 0;
×
137
    if (!SCQuicTxGetJa3(ltx->tx, STREAM_TOCLIENT, &buf, &b_len)) {
×
138
        lua_pushnil(L);
×
139
        return 1;
×
140
    }
×
141
    uint8_t ja3_hash[SC_MD5_HEX_LEN + 1];
×
142
    // this adds a final zero
143
    SCMd5HashBufferToHex(buf, b_len, (char *)ja3_hash, SC_MD5_HEX_LEN + 1);
×
144
    return LuaPushStringBuffer(L, ja3_hash, SC_MD5_HEX_LEN);
×
145
}
×
146

147
static int LuaJa3TxGetServerString(lua_State *L)
UNCOV
148
{
×
UNCOV
149
    struct LuaTx *ltx = luaL_testudata(L, 1, ja3_tx);
×
UNCOV
150
    if (ltx == NULL) {
×
151
        lua_pushnil(L);
×
152
        return 1;
×
153
    }
×
UNCOV
154
    if (ltx->alproto == ALPROTO_TLS) {
×
UNCOV
155
        SSLState *ssl_state = (SSLState *)ltx->tx;
×
UNCOV
156
        if (ssl_state->server_connp.ja3_str == NULL ||
×
UNCOV
157
                ssl_state->server_connp.ja3_str->data == NULL) {
×
158
            lua_pushnil(L);
×
159
            return 1;
×
160
        }
×
UNCOV
161
        return LuaPushStringBuffer(L, (uint8_t *)ssl_state->server_connp.ja3_str->data,
×
UNCOV
162
                ssl_state->server_connp.ja3_str->used);
×
UNCOV
163
    } // else QUIC {
×
164
    const uint8_t *buf = NULL;
×
165
    uint32_t b_len = 0;
×
166
    if (!SCQuicTxGetJa3(ltx->tx, STREAM_TOCLIENT, &buf, &b_len)) {
×
167
        lua_pushnil(L);
×
168
        return 1;
×
169
    }
×
170
    return LuaPushStringBuffer(L, buf, b_len);
×
171
}
×
172

173
static const struct luaL_Reg txlib[] = {
174
    // clang-format off
175
    { "ja3_get_hash", LuaJa3TxGetHash },
176
    { "ja3_get_string", LuaJa3TxGetString },
177
    { "ja3s_get_hash", LuaJa3TxGetServerHash },
178
    { "ja3s_get_string", LuaJa3TxGetServerString },
179
    { NULL, NULL, }
180
    // clang-format on
181
};
182

183
static int LuaJa3Enable(lua_State *L)
UNCOV
184
{
×
UNCOV
185
    SSLEnableJA3();
×
UNCOV
186
    return 1;
×
UNCOV
187
}
×
188

189
static const struct luaL_Reg ja3lib[] = {
190
    // clang-format off
191
    { "get_tx", LuaJa3GetTx },
192
    { "enable_ja3", LuaJa3Enable },
193
    { NULL, NULL,},
194
    // clang-format on
195
};
196

197
int SCLuaLoadJa3Lib(lua_State *L)
UNCOV
198
{
×
UNCOV
199
    luaL_newmetatable(L, ja3_tx);
×
UNCOV
200
    lua_pushvalue(L, -1);
×
UNCOV
201
    lua_setfield(L, -2, "__index");
×
UNCOV
202
    luaL_setfuncs(L, txlib, 0);
×
203

UNCOV
204
    luaL_newlib(L, ja3lib);
×
UNCOV
205
    return 1;
×
UNCOV
206
}
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc