Lua for decection error loading module

Hi
I want to use lua to verify jwt token from http.uri. but I can’t found any lua function to implement it. so I wirte a c library and call c function in lua script. it works when I run a test. but I meet an error when I use this script and library in suricata.

suricata.rule:
alert http any any -> any any (msg:"nacos"; flow: established, to_server; http.uri; content:"/nacos/v1/cs/configs"; lua:./lua/nacos_default_jwt.lua; flowbits: set, nacos.1000001; flowbits: noalert; classtype:bad-unknown; sid: 1000001; rev: 1;)
nacos_default_jwt.lua:
local jwt_lib = require("jwt_library")

local nacos_default_secret="SecretKey012345678901234567890123456789012345678901234567890123456789"


local function extract_access_token(url)
    local token = url:match("accessToken=([^&]+)")
    return token
end

function init (args)
    local needs = {}
    needs["http.uri"] = tostring(true)
    return needs
end

function match(args)
    local http_uri = tostring(args["http.uri"])
    if not http_uri then
        return 0
    end
    local token = extract_access_token(http_uri)
    if not token then
        return 0
    end
    SCLogInfo("extract jwt token success: " .. token)
    local result = jwt_lib.validate_jwt(token, nacos_default_secret)
    SCLogInfo("result: " ..result)
    return 0
end

return 0
jwt_library.c
// jwt_library.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <openssl/bio.h>
#include <openssl/evp.h>
#include <jwt.h>
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>

unsigned char *base64_decode(const char *base64, size_t *out_len) {
    BIO *bio, *b64;
    size_t len = strlen(base64);
    unsigned char *decoded_data = malloc(len);

    if (!decoded_data) {
        perror("malloc");
        exit(EXIT_FAILURE);
    }

    b64 = BIO_new(BIO_f_base64());
    bio = BIO_new_mem_buf(base64, -1);
    bio = BIO_push(b64, bio);
    *out_len = BIO_read(bio, decoded_data, len);

    BIO_free_all(bio);
    return decoded_data;
}

int validate_jwt(const char *token, const char *base64_key) {
    size_t key_len;
    unsigned char *key = base64_decode(base64_key, &key_len);

    jwt_t *jwt = NULL;
    int ret = jwt_decode(&jwt, token, key, key_len);

    free(key);

    if (ret) {
        return 0; 
    }

    jwt_free(jwt);
    return 1; 
}

int lua_validate_jwt(lua_State *L) {
    const char *token = luaL_checkstring(L, 1);
    const char *base64_key = luaL_checkstring(L, 2);

    int result = validate_jwt(token, base64_key);
    lua_pushboolean(L, result);
    return 1;
}

int luaopen_jwt_library(lua_State *L) {
    static const struct luaL_Reg jwt_lib[] = {
        {"validate_jwt", lua_validate_jwt},
        {NULL, NULL}
    };

    luaL_register(L, "jwt_library", jwt_lib);
    return 1;
}

suricata.log:

[8734 - Suricata-Main] 2024-07-25 21:00:36 Notice: suricata: This is Suricata version 8.0.0-dev (87c00acbd 2024-07-12) running in SYSTEM mode
[8734 - Suricata-Main] 2024-07-25 21:00:36 Info: cpu: CPUs/cores online: 1
[8734 - Suricata-Main] 2024-07-25 21:00:36 Info: suricata: Setting engine mode to IDS mode by default
[8734 - Suricata-Main] 2024-07-25 21:00:36 Info: exception-policy: master exception-policy set to: auto
[8734 - Suricata-Main] 2024-07-25 21:00:36 Info: ioctl: enp0s8: MTU 1500
[8734 - Suricata-Main] 2024-07-25 21:00:36 Info: suricata: Use pid file /var/lib/suricata/suricata.pid from config file.
[8735 - Suricata-Main] 2024-07-25 21:00:36 Info: suricata: Preparing unexpected signal handling
[8735 - Suricata-Main] 2024-07-25 21:00:36 Info: conf: Running in live mode, activating unix socket
[8735 - Suricata-Main] 2024-07-25 21:00:36 Info: logopenfile: eve-log output device (regular) initialized: alert.json
[8735 - Suricata-Main] 2024-07-25 21:00:36 Info: logopenfile: eve-log output device (regular) initialized: flow.json
[8735 - Suricata-Main] 2024-07-25 21:00:36 Info: logopenfile: eve-log output device (regular) initialized: tls.json
[8735 - Suricata-Main] 2024-07-25 21:00:36 Info: logopenfile: eve-log output device (regular) initialized: eve.json
[8735 - Suricata-Main] 2024-07-25 21:00:36 Info: logopenfile: alert-debug output device (regular) initialized: alert-debug.log
[8735 - Suricata-Main] 2024-07-25 21:00:36 Info: logopenfile: stats output device (regular) initialized: stats.log
[8735 - Suricata-Main] 2024-07-25 21:00:36 Info: classification-config: Added "43" classification types from the classification file
[8735 - Suricata-Main] 2024-07-25 21:00:36 Info: reference-config: Added "19" reference types from the reference.config file
[8735 - Suricata-Main] 2024-07-25 21:00:36 Error: detect-lua: couldn't prime file: error loading module 'jwt_library' from file '/var/lib/jwt_library.so':
        /var/lib/jwt_library.so:1: unexpected symbol near '<\127>'
[8735 - Suricata-Main] 2024-07-25 21:00:36 Error: detect: error parsing signature "alert http any any -> any any (msg:"nacos"; flow: established, to_server; http.uri; content:"/nacos/v1/cs/configs"; lua:./lua/nacos_default_jwt.lua; flowbits: set, nacos.1000001; flowbits: noalert; classtype:bad-unknown; sid: 1000001; rev: 1;)" from file /home/suricata/rules/suricata.rules at line 31
[8735 - Suricata-Main] 2024-07-25 21:00:36 Warning: detect: 1 rule files specified, but no rules were loaded!
[8735 - Suricata-Main] 2024-07-25 21:00:36 Info: threshold-config: Threshold config parsed: 0 rule(s) found
[8735 - Suricata-Main] 2024-07-25 21:00:36 Info: detect: 0 signatures processed. 0 are IP-only rules, 0 are inspecting packet payload, 0 inspect application layer, 0 are decoder event only
[8735 - Suricata-Main] 2024-07-25 21:00:36 Info: runmodes: enp0s8: creating 1 thread
[8735 - Suricata-Main] 2024-07-25 21:00:36 Info: unix-manager: unix socket '/var/run/suricata/suricata-command.socket'
[8735 - Suricata-Main] 2024-07-25 21:00:36 Notice: threads: Threads created -> W: 1 FM: 1 FR: 1   Engine started.

In 8.0.0, we no longer allow loading modules. We want to expand the functionality exposed to the scripting language to support cases like this.

See Feature #7074: lua: expose base64 functions - Suricata - Open Information Security Foundation

Thanks for your reply. Will…I can’t wait this feature release. I‘m trying to add a function into suricata source code, and use that function in lua script. it looks working.

#include "util-base64.h"
#include "rust.h"
#include <jwt.h>

/**
 * \internal
 * \brief base64 decode from char* to char*
 * \retval base64decoded char*
 */
unsigned char *base64_decode(const char *base64)
{
    uint32_t input_len = strlen(base64);
    uint32_t output_len = strlen(base64);
    
    uint8_t* output_buffer = (uint8_t*)malloc(output_len);
    if (output_buffer == NULL) {
        SCLogError("Failed to allocate thread data");
        return NULL;
    }

    uint32_t consumed_bytes = 0;
    uint32_t decoded_bytes = 0;
    Base64Ecode ret = DecodeBase64(output_buffer, output_len, (const uint8_t*)base64,
                                   input_len, &consumed_bytes, &decoded_bytes, Base64ModeRFC2045);

    if (ret != BASE64_ECODE_OK) {
        SCLogError("Failed to decode base64 input");
        free(output_buffer);
        return NULL;
    }

    char* decoded_str = (char*)malloc(decoded_bytes + 1);
    if (decoded_str == NULL) {
        SCLogError("Failed to allocate memory");
        free(output_buffer);
        return NULL;
    }
    memcpy(decoded_str, output_buffer, decoded_bytes);
    decoded_str[decoded_bytes] = '\0';

    free(output_buffer);

    return decoded_str;
}

/**
 * \internal
 * \brief jwt token verify by libjwt
 * \retval verify result by int
 */
int LuaCallbackJwtVerifyImpl(const char *token, const char *base64_key)
{
    unsigned char *key = base64_decode(base64_key);
    SCLogNotice("base64decoded key: %s", key);
    SCLogNotice("token: %s", token);

    if (token == NULL || key == NULL)
    {
        SCLogNotice("token or key is null");
        return 0;
    }

    jwt_t *jwt = NULL;

    int ret = jwt_decode(&jwt, token, key, strlen(key));
    if (ret != 0 || jwt == NULL)
    {
        SCLogNotice("Failed to decode JWT");
        return 0;
    }

    jwt_free(jwt);
    free(key);
    if (ret == 0)
    {
        // verify success
        return 1;
    }
    else
    {
        // verify failed
        return 0;
    }
}

why not use the built-in base64 decoder in our code? Could keep things a lot simpler.

I am embarrassed. I am not good at C language. This code is generated by GPT. In addition, the DecodeBase64 I called is indeed a function in utils-base64.c. Is there something I misunderstood?

Ah it seems the code above is a mix of using the built-in and and the “jwt” stuff.