lua调用c语言so动态库--以waf中证书检查为例

2021-09-06 11:06:55 浏览数 (4)

需求

在基于nginx做waf开发时,nginx lua c动态库是常见的开发模式,在lua生态无法满足需求时,就需要我们在lua代码中调用动态库的方式,进行扩展,下面以lua调用c语言 openssl动态库的方式,进行判断证书的创建时间和证书的过期时间为例,进行说明

由于lua没有openss sdk做证书检查校验工作,那么就需要我们基于c语言和openssl库些一个so动态库,以供lua调用去判断证书有效时间、合法性、证书签发者信息等。

c函数从lua获取参数

与lua交互的c函数,所有函数入参参数固定为lua_State *L。

在lua调用c函数时,通过lua_State *L向其传入参数,如果只有一个参数且类型为string,那么在c函数中通过lua_tostring(L,1),获取传来的一个string类型的参数。

若lua向c函数传参时,有两个参数,第一个参数为string类型,第二个参数为number类型。那么在c函数中,需要通过lua_tostring(L,1)和lua_tonumber(L,2),分别获取lua传来的两个参数(即:string类型和number类型)。

代码说明

例如在如下代码中,expire_cert_time函数为获取证书过期时间,create_cert_time函数为获取证书创建时间,函数的参数为证书的路径(即:string类型),那么就可以通过path = lua_tostring(L,1),获取lua传来的证书路径,随后在c语言的函数中调用openssl函数进行一些列的操作。

向lua返回结果

在c函数中处理函数的结尾,通过lua_pushstring(L, buf)向lua返回处理结果,在该例中函数的返回值分别为,证书的创建时间、证书的过期时间,均是字符串类型,所以通过lua_pushstring把返回值push到lua_State L中,如果返回的是数字可以通过lua_pushnumber来实现。

c文件名:libcert.c

代码语言:javascript复制
#include <stdio.h>
#include <lua.h>
#include <lauxlib.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <sys/types.h>
#include<sys/time.h>

#include <openssl/x509.h>
#include <openssl/x509v3.h>
#include <openssl/bio.h>
#include <openssl/pem.h>
#include <openssl/err.h>
#include <openssl/rsa.h>

#include "tools.h"

static int expire_cert_time(lua_State *L)
{
    const char *path = NULL;
    path = lua_tostring(L,1);

    int reason_code;
    BIO *in;
    int ret = 0;
    X509 *x = NULL;

    in = BIO_new(BIO_s_file());
    if (in == NULL) {

    }

    if (BIO_read_filename(in, path) <= 0) {

    }
    x = PEM_read_bio_X509(in, NULL, NULL, NULL);

    long version;
    X509_NAME *issuer = NULL;
    int entriesNum;
    int i;
    ASN1_TIME *time;

    //获取证书版本  
    version = X509_get_version(x);
    //printf("X509 Version: %ldn", version);

    //获取证书颁发者信息,X509_NAME结构体保存了多项信息,包括国家、组织、部门、通用名、mail等。  
    issuer = X509_get_issuer_name(x);

    //获取X509_NAME条目个数  
    entriesNum = sk_X509_NAME_ENTRY_num(issuer->entries);
    //printf("entriesNum : %dn", version);

    time_t  tmp;    //保存证书时间 
    struct tm *p;
    char buf[100];
    char *wday[]={"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};

    //获取证书过期日期  
    time = X509_get_notAfter(x);
    tmp = asn1_time_transform(time);
    p = gmtime(&tmp);

    sprintf(buf, "%ddd d:d:d",(1900 p->tm_year), (1 p->tm_mon),p->tm_mday, p->tm_hour, p->tm_min, p->tm_sec);

    lua_pushstring(L, buf);
    return 1;

}

static int create_cert_time(lua_State *L)
{
    const char *path = NULL;
    path = lua_tostring(L,1);

    int reason_code;
    BIO *in;
    int ret = 0;
    X509 *x = NULL;

    in = BIO_new(BIO_s_file());
    if (in == NULL) {

    }

    if (BIO_read_filename(in, path) <= 0) {

    }
    x = PEM_read_bio_X509(in, NULL, NULL, NULL);

    long version;
    X509_NAME *issuer = NULL;
    int entriesNum;
    unsigned char msginfo[1024];
    long Nid;
    int i;
    X509_NAME_ENTRY *name_entry;
    ASN1_TIME *time;   

  
    X509_NAME *subject = NULL;          //X509_NAME结构体,保存证书拥有者信息  
    ASN1_INTEGER *Serial = NULL;        //保存证书序列号  
    EVP_PKEY *pubKey;                   //保存证书公钥  
    unsigned char derpubkey[1024];
    int derpubkeyLen;
    int msginfoLen;
    unsigned short *pUtf8 = NULL;
    int nUtf8;
    int rv;

    //获取证书版本  
    version = X509_get_version(x);
    //printf("X509 Version: %ldn", version);

    //获取证书颁发者信息,X509_NAME结构体保存了多项信息,包括国家、组织、部门、通用名、mail等。  
    issuer = X509_get_issuer_name(x);


    //获取X509_NAME条目个数  
    entriesNum = sk_X509_NAME_ENTRY_num(issuer->entries);
    //printf("entriesNum : %dn", version);

    time_t  tmptmp;
    //获取证书生效日期  
    time = X509_get_notBefore(x);
    tmptmp = asn1_time_transform(time);
    struct tm *p;

    char *wday[]={"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
    p = gmtime(&tmptmp); 

    char buf[100], buf2[100];
    sprintf(buf, "%ddd d:d:d",(1900 p->tm_year), (1 p->tm_mon),p->tm_mday, p->tm_hour, p->tm_min, p->tm_sec);

    lua_pushstring(L,buf);
    return 1;
}


static const struct luaL_Reg lib[] =
{
    {"create_cert_time", create_cert_time},
    {"expire_cert_time", expire_cert_time},
    {NULL,NULL}
};

int luaopen_libcert(lua_State *L)
{
    luaL_register(L,"libcert",lib);
    return 1;
}

c函数注册至lua

luaL_Reg列表,记录了要注册的函数信息,本例中只有两个函数create_cert_time和expire_cert_time,所以luaL_Reg如下

代码语言:javascript复制
static const struct luaL_Reg lib[] =
{
    {"create_cert_time", create_cert_time},
    {"expire_cert_time", expire_cert_time},
    {NULL,NULL}
};

c库入口函数

通过luaopen_xxx实现,xxx标识c函数封装so动态库的名称,该例中动态库名称为libcert.so,固函数名为luaopen_libcert,luaL_register参数为lua_State、动态库名称libcert、上面luaL_Reg lib。

代码语言:javascript复制
int luaopen_libcert(lua_State *L)
{
    luaL_register(L,"libcert",lib);
    return 1;
}

lua代码调用c动态库

代码和说明如下

代码语言:javascript复制
require("libcert")

--参数为证书路径
c = libcert.create_cert_time("./abc.cert")
e = libcert.expire_cert_time("./abc.cert")

--证书创建时间
print("create time: ",c);
--证书过期时间
print("expire time: ",e);

注意:

本文使用lua5.1版本,在 lua5.2里没有luaL_register函数了,据说是lua不鼓励将模块设置到全局域,可以使用luaL_newlib(L, c)来实现。

0 人点赞