2013-03-30 13:21:40 +01:00
|
|
|
/*--------------------------------------------------------------------------
|
2016-03-03 20:11:46 +01:00
|
|
|
* LuaSec 0.6
|
2014-01-29 21:43:33 +01:00
|
|
|
*
|
2016-03-03 20:11:46 +01:00
|
|
|
* Copyright (C) 2014-2016 Kim Alvefur, Paul Aurich, Tobias Markmann
|
2015-08-21 16:21:16 +02:00
|
|
|
* Matthew Wild, Bruno Silvestre.
|
2013-03-30 13:21:40 +01:00
|
|
|
*
|
|
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
|
2015-03-20 16:36:05 +01:00
|
|
|
#include <stdio.h>
|
2013-03-30 13:21:40 +01:00
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
#if defined(WIN32)
|
2015-03-12 21:05:53 +01:00
|
|
|
#include <ws2tcpip.h>
|
2013-03-30 13:21:40 +01:00
|
|
|
#include <windows.h>
|
2015-03-12 21:05:53 +01:00
|
|
|
#else
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <netinet/in.h>
|
|
|
|
#include <arpa/inet.h>
|
2013-03-30 13:21:40 +01:00
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <openssl/ssl.h>
|
|
|
|
#include <openssl/x509v3.h>
|
|
|
|
#include <openssl/evp.h>
|
|
|
|
#include <openssl/err.h>
|
|
|
|
#include <openssl/asn1.h>
|
|
|
|
#include <openssl/bio.h>
|
|
|
|
#include <openssl/bn.h>
|
|
|
|
|
|
|
|
#include <lua.h>
|
|
|
|
#include <lauxlib.h>
|
|
|
|
|
|
|
|
#include "x509.h"
|
|
|
|
|
2016-09-13 18:09:18 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* ASN1_STRING_data is deprecated in OpenSSL 1.1.0
|
|
|
|
*/
|
|
|
|
#if OPENSSL_VERSION_NUMBER>=0x1010000fL && !defined(LIBRESSL_VERSION_NUMBER)
|
|
|
|
#define LSEC_ASN1_STRING_data(x) ASN1_STRING_get0_data(x)
|
|
|
|
#else
|
|
|
|
#define LSEC_ASN1_STRING_data(x) ASN1_STRING_data(x)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
2013-03-30 13:21:40 +01:00
|
|
|
static const char* hex_tab = "0123456789abcdef";
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Push the certificate on the stack.
|
|
|
|
*/
|
|
|
|
void lsec_pushx509(lua_State* L, X509 *cert)
|
|
|
|
{
|
|
|
|
p_x509 cert_obj = (p_x509)lua_newuserdata(L, sizeof(t_x509));
|
|
|
|
cert_obj->cert = cert;
|
2013-10-23 17:42:34 +02:00
|
|
|
cert_obj->encode = LSEC_AI5_STRING;
|
2013-03-30 13:21:40 +01:00
|
|
|
luaL_getmetatable(L, "SSL:Certificate");
|
|
|
|
lua_setmetatable(L, -2);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return the OpenSSL certificate X509.
|
|
|
|
*/
|
|
|
|
X509* lsec_checkx509(lua_State* L, int idx)
|
|
|
|
{
|
|
|
|
return ((p_x509)luaL_checkudata(L, idx, "SSL:Certificate"))->cert;
|
|
|
|
}
|
|
|
|
|
2013-10-23 17:42:34 +02:00
|
|
|
/**
|
|
|
|
* Return LuaSec certificate X509 representation.
|
|
|
|
*/
|
|
|
|
p_x509 lsec_checkp_x509(lua_State* L, int idx)
|
|
|
|
{
|
|
|
|
return (p_x509)luaL_checkudata(L, idx, "SSL:Certificate");
|
|
|
|
}
|
|
|
|
|
2013-03-30 13:21:40 +01:00
|
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
|
2015-08-01 06:07:04 +02:00
|
|
|
#if defined(LUASEC_INET_NTOP)
|
|
|
|
/*
|
|
|
|
* For WinXP (SP3), set the following preprocessor macros:
|
|
|
|
* LUASEC_INET_NTOP
|
|
|
|
* WINVER=0x0501
|
|
|
|
* _WIN32_WINNT=0x0501
|
|
|
|
* NTDDI_VERSION=0x05010300
|
|
|
|
*
|
|
|
|
* For IPv6 addresses, you need to add IPv6 Protocol to your interface.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
static const char *inet_ntop(int af, const char *src, char *dst, socklen_t size)
|
|
|
|
{
|
|
|
|
int addrsize;
|
|
|
|
struct sockaddr *addr;
|
|
|
|
struct sockaddr_in addr4;
|
|
|
|
struct sockaddr_in6 addr6;
|
|
|
|
|
|
|
|
switch (af) {
|
|
|
|
case AF_INET:
|
2015-08-01 06:14:16 +02:00
|
|
|
memset((void*)&addr4, 0, sizeof(addr4));
|
|
|
|
addr4.sin_family = AF_INET;
|
|
|
|
memcpy((void*)&addr4.sin_addr, src, sizeof(struct in_addr));
|
|
|
|
addr = (struct sockaddr*)&addr4;
|
|
|
|
addrsize = sizeof(struct sockaddr_in);
|
|
|
|
break;
|
2015-08-01 06:07:04 +02:00
|
|
|
case AF_INET6:
|
2015-08-01 06:14:16 +02:00
|
|
|
memset((void*)&addr6, 0, sizeof(addr6));
|
|
|
|
addr6.sin6_family = AF_INET6;
|
|
|
|
memcpy((void*)&addr6.sin6_addr, src, sizeof(struct in6_addr));
|
2015-08-01 06:07:04 +02:00
|
|
|
addr = (struct sockaddr*)&addr6;
|
2015-08-01 06:14:16 +02:00
|
|
|
addrsize = sizeof(struct sockaddr_in6);
|
|
|
|
break;
|
2015-08-01 06:07:04 +02:00
|
|
|
default:
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(getnameinfo(addr, addrsize, dst, size, NULL, 0, NI_NUMERICHOST) != 0)
|
|
|
|
return NULL;
|
|
|
|
return dst;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
|
2013-03-30 13:21:40 +01:00
|
|
|
/**
|
|
|
|
* Convert the buffer 'in' to hexadecimal.
|
|
|
|
*/
|
|
|
|
static void to_hex(const char* in, int length, char* out)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < length; i++) {
|
|
|
|
out[i*2] = hex_tab[(in[i] >> 4) & 0xF];
|
|
|
|
out[i*2+1] = hex_tab[(in[i]) & 0xF];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Converts the ASN1_OBJECT into a textual representation and put it
|
|
|
|
* on the Lua stack.
|
|
|
|
*/
|
|
|
|
static void push_asn1_objname(lua_State* L, ASN1_OBJECT *object, int no_name)
|
|
|
|
{
|
|
|
|
char buffer[256];
|
|
|
|
int len = OBJ_obj2txt(buffer, sizeof(buffer), object, no_name);
|
|
|
|
len = (len < sizeof(buffer)) ? len : sizeof(buffer);
|
|
|
|
lua_pushlstring(L, buffer, len);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Push the ASN1 string on the stack.
|
|
|
|
*/
|
2013-10-23 17:42:34 +02:00
|
|
|
static void push_asn1_string(lua_State* L, ASN1_STRING *string, int encode)
|
2013-03-30 13:21:40 +01:00
|
|
|
{
|
2014-04-19 03:50:40 +02:00
|
|
|
int len;
|
2013-10-23 17:42:34 +02:00
|
|
|
unsigned char *data;
|
2014-06-08 12:41:20 +02:00
|
|
|
if (!string) {
|
2013-10-23 17:42:34 +02:00
|
|
|
lua_pushnil(L);
|
2014-06-08 12:41:20 +02:00
|
|
|
return;
|
|
|
|
}
|
2013-10-23 17:42:34 +02:00
|
|
|
switch (encode) {
|
|
|
|
case LSEC_AI5_STRING:
|
2016-09-13 18:09:18 +02:00
|
|
|
lua_pushlstring(L, (char*)LSEC_ASN1_STRING_data(string),
|
2013-03-30 13:21:40 +01:00
|
|
|
ASN1_STRING_length(string));
|
2013-10-23 17:42:34 +02:00
|
|
|
break;
|
|
|
|
case LSEC_UTF8_STRING:
|
|
|
|
len = ASN1_STRING_to_UTF8(&data, string);
|
|
|
|
if (len >= 0) {
|
|
|
|
lua_pushlstring(L, (char*)data, len);
|
|
|
|
OPENSSL_free(data);
|
|
|
|
}
|
2014-06-08 12:38:52 +02:00
|
|
|
else
|
|
|
|
lua_pushnil(L);
|
2013-10-23 17:42:34 +02:00
|
|
|
}
|
2013-03-30 13:21:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return a human readable time.
|
|
|
|
*/
|
|
|
|
static int push_asn1_time(lua_State *L, ASN1_UTCTIME *tm)
|
|
|
|
{
|
|
|
|
char *tmp;
|
|
|
|
long size;
|
|
|
|
BIO *out = BIO_new(BIO_s_mem());
|
|
|
|
ASN1_TIME_print(out, tm);
|
|
|
|
size = BIO_get_mem_data(out, &tmp);
|
|
|
|
lua_pushlstring(L, tmp, size);
|
|
|
|
BIO_free(out);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2014-06-08 13:20:47 +02:00
|
|
|
/**
|
|
|
|
* Return a human readable IP address.
|
|
|
|
*/
|
|
|
|
static void push_asn1_ip(lua_State *L, ASN1_STRING *string)
|
|
|
|
{
|
2015-08-01 06:07:04 +02:00
|
|
|
int af;
|
2014-06-08 13:20:47 +02:00
|
|
|
char dst[INET6_ADDRSTRLEN];
|
2016-09-13 18:09:18 +02:00
|
|
|
unsigned char *ip = (unsigned char*)LSEC_ASN1_STRING_data(string);
|
2014-06-08 13:20:47 +02:00
|
|
|
switch(ASN1_STRING_length(string)) {
|
|
|
|
case 4:
|
2015-08-01 06:07:04 +02:00
|
|
|
af = AF_INET;
|
2014-06-08 13:20:47 +02:00
|
|
|
break;
|
|
|
|
case 16:
|
2015-08-01 06:07:04 +02:00
|
|
|
af = AF_INET6;
|
2014-06-08 13:20:47 +02:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
lua_pushnil(L);
|
|
|
|
return;
|
|
|
|
}
|
2015-08-01 06:07:04 +02:00
|
|
|
if(inet_ntop(af, ip, dst, INET6_ADDRSTRLEN))
|
2014-06-08 13:20:47 +02:00
|
|
|
lua_pushstring(L, dst);
|
|
|
|
else
|
|
|
|
lua_pushnil(L);
|
|
|
|
}
|
|
|
|
|
2013-03-30 13:21:40 +01:00
|
|
|
/**
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
static int push_subtable(lua_State* L, int idx)
|
|
|
|
{
|
|
|
|
lua_pushvalue(L, -1);
|
|
|
|
lua_gettable(L, idx-1);
|
|
|
|
if (lua_isnil(L, -1)) {
|
|
|
|
lua_pop(L, 1);
|
|
|
|
lua_newtable(L);
|
|
|
|
lua_pushvalue(L, -2);
|
|
|
|
lua_pushvalue(L, -2);
|
|
|
|
lua_settable(L, idx-3);
|
|
|
|
lua_replace(L, -2); /* Replace key with table */
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
lua_replace(L, -2); /* Replace key with table */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrive the general names from the object.
|
|
|
|
*/
|
2013-10-23 17:42:34 +02:00
|
|
|
static int push_x509_name(lua_State* L, X509_NAME *name, int encode)
|
2013-03-30 13:21:40 +01:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
int n_entries;
|
|
|
|
ASN1_OBJECT *object;
|
|
|
|
X509_NAME_ENTRY *entry;
|
|
|
|
lua_newtable(L);
|
|
|
|
n_entries = X509_NAME_entry_count(name);
|
|
|
|
for (i = 0; i < n_entries; i++) {
|
|
|
|
entry = X509_NAME_get_entry(name, i);
|
|
|
|
object = X509_NAME_ENTRY_get_object(entry);
|
|
|
|
lua_newtable(L);
|
|
|
|
push_asn1_objname(L, object, 1);
|
|
|
|
lua_setfield(L, -2, "oid");
|
|
|
|
push_asn1_objname(L, object, 0);
|
|
|
|
lua_setfield(L, -2, "name");
|
2013-10-23 17:42:34 +02:00
|
|
|
push_asn1_string(L, X509_NAME_ENTRY_get_data(entry), encode);
|
2013-03-30 13:21:40 +01:00
|
|
|
lua_setfield(L, -2, "value");
|
|
|
|
lua_rawseti(L, -2, i+1);
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrive the Subject from the certificate.
|
|
|
|
*/
|
|
|
|
static int meth_subject(lua_State* L)
|
|
|
|
{
|
2013-10-23 17:42:34 +02:00
|
|
|
p_x509 px = lsec_checkp_x509(L, 1);
|
|
|
|
return push_x509_name(L, X509_get_subject_name(px->cert), px->encode);
|
2013-03-30 13:21:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrive the Issuer from the certificate.
|
|
|
|
*/
|
|
|
|
static int meth_issuer(lua_State* L)
|
|
|
|
{
|
2013-10-23 17:42:34 +02:00
|
|
|
p_x509 px = lsec_checkp_x509(L, 1);
|
|
|
|
return push_x509_name(L, X509_get_issuer_name(px->cert), px->encode);
|
2013-03-30 13:21:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieve the extensions from the certificate.
|
|
|
|
*/
|
|
|
|
int meth_extensions(lua_State* L)
|
|
|
|
{
|
|
|
|
int j;
|
|
|
|
int i = -1;
|
|
|
|
int n_general_names;
|
|
|
|
OTHERNAME *otherName;
|
|
|
|
X509_EXTENSION *extension;
|
|
|
|
GENERAL_NAME *general_name;
|
|
|
|
STACK_OF(GENERAL_NAME) *values;
|
2013-10-23 17:42:34 +02:00
|
|
|
p_x509 px = lsec_checkp_x509(L, 1);
|
|
|
|
X509 *peer = px->cert;
|
2013-03-30 13:21:40 +01:00
|
|
|
|
|
|
|
/* Return (ret) */
|
|
|
|
lua_newtable(L);
|
|
|
|
|
|
|
|
while ((i = X509_get_ext_by_NID(peer, NID_subject_alt_name, i)) != -1) {
|
|
|
|
extension = X509_get_ext(peer, i);
|
|
|
|
if (extension == NULL)
|
|
|
|
break;
|
|
|
|
values = X509V3_EXT_d2i(extension);
|
|
|
|
if (values == NULL)
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* Push ret[oid] */
|
2016-09-13 18:22:25 +02:00
|
|
|
push_asn1_objname(L, X509_EXTENSION_get_object(extension), 1);
|
2013-03-30 13:21:40 +01:00
|
|
|
push_subtable(L, -2);
|
|
|
|
|
|
|
|
/* Set ret[oid].name = name */
|
2016-09-13 18:22:25 +02:00
|
|
|
push_asn1_objname(L, X509_EXTENSION_get_object(extension), 0);
|
2013-03-30 13:21:40 +01:00
|
|
|
lua_setfield(L, -2, "name");
|
|
|
|
|
|
|
|
n_general_names = sk_GENERAL_NAME_num(values);
|
|
|
|
for (j = 0; j < n_general_names; j++) {
|
|
|
|
general_name = sk_GENERAL_NAME_value(values, j);
|
|
|
|
switch (general_name->type) {
|
|
|
|
case GEN_OTHERNAME:
|
|
|
|
otherName = general_name->d.otherName;
|
|
|
|
push_asn1_objname(L, otherName->type_id, 1);
|
|
|
|
if (push_subtable(L, -2)) {
|
|
|
|
push_asn1_objname(L, otherName->type_id, 0);
|
|
|
|
lua_setfield(L, -2, "name");
|
|
|
|
}
|
2013-10-23 17:42:34 +02:00
|
|
|
push_asn1_string(L, otherName->value->value.asn1_string, px->encode);
|
2013-03-30 13:21:40 +01:00
|
|
|
lua_rawseti(L, -2, lua_rawlen(L, -2) + 1);
|
|
|
|
lua_pop(L, 1);
|
|
|
|
break;
|
|
|
|
case GEN_DNS:
|
|
|
|
lua_pushstring(L, "dNSName");
|
2015-08-01 06:14:16 +02:00
|
|
|
push_subtable(L, -2);
|
2013-10-23 17:42:34 +02:00
|
|
|
push_asn1_string(L, general_name->d.dNSName, px->encode);
|
2013-03-30 13:21:40 +01:00
|
|
|
lua_rawseti(L, -2, lua_rawlen(L, -2) + 1);
|
|
|
|
lua_pop(L, 1);
|
|
|
|
break;
|
|
|
|
case GEN_EMAIL:
|
|
|
|
lua_pushstring(L, "rfc822Name");
|
|
|
|
push_subtable(L, -2);
|
2013-10-23 17:42:34 +02:00
|
|
|
push_asn1_string(L, general_name->d.rfc822Name, px->encode);
|
2013-03-30 13:21:40 +01:00
|
|
|
lua_rawseti(L, -2, lua_rawlen(L, -2) + 1);
|
|
|
|
lua_pop(L, 1);
|
|
|
|
break;
|
|
|
|
case GEN_URI:
|
|
|
|
lua_pushstring(L, "uniformResourceIdentifier");
|
|
|
|
push_subtable(L, -2);
|
2013-10-23 17:42:34 +02:00
|
|
|
push_asn1_string(L, general_name->d.uniformResourceIdentifier, px->encode);
|
2013-03-30 13:21:40 +01:00
|
|
|
lua_rawseti(L, -2, lua_rawlen(L, -2)+1);
|
|
|
|
lua_pop(L, 1);
|
|
|
|
break;
|
|
|
|
case GEN_IPADD:
|
|
|
|
lua_pushstring(L, "iPAddress");
|
|
|
|
push_subtable(L, -2);
|
2014-06-08 13:20:47 +02:00
|
|
|
push_asn1_ip(L, general_name->d.iPAddress);
|
2013-03-30 13:21:40 +01:00
|
|
|
lua_rawseti(L, -2, lua_rawlen(L, -2)+1);
|
|
|
|
lua_pop(L, 1);
|
|
|
|
break;
|
|
|
|
case GEN_X400:
|
|
|
|
/* x400Address */
|
|
|
|
/* not supported */
|
|
|
|
break;
|
|
|
|
case GEN_DIRNAME:
|
|
|
|
/* directoryName */
|
|
|
|
/* not supported */
|
|
|
|
break;
|
|
|
|
case GEN_EDIPARTY:
|
|
|
|
/* ediPartyName */
|
|
|
|
/* not supported */
|
|
|
|
break;
|
|
|
|
case GEN_RID:
|
|
|
|
/* registeredID */
|
|
|
|
/* not supported */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
lua_pop(L, 1); /* ret[oid] */
|
|
|
|
i++; /* Next extension */
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Convert the certificate to PEM format.
|
|
|
|
*/
|
|
|
|
static int meth_pem(lua_State* L)
|
|
|
|
{
|
|
|
|
char* data;
|
|
|
|
long bytes;
|
|
|
|
X509* cert = lsec_checkx509(L, 1);
|
|
|
|
BIO *bio = BIO_new(BIO_s_mem());
|
|
|
|
if (!PEM_write_bio_X509(bio, cert)) {
|
|
|
|
lua_pushnil(L);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
bytes = BIO_get_mem_data(bio, &data);
|
|
|
|
if (bytes > 0)
|
|
|
|
lua_pushlstring(L, data, bytes);
|
|
|
|
else
|
|
|
|
lua_pushnil(L);
|
|
|
|
BIO_free(bio);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2014-02-05 01:39:30 +01:00
|
|
|
/**
|
|
|
|
* Extract public key in PEM format.
|
|
|
|
*/
|
|
|
|
static int meth_pubkey(lua_State* L)
|
|
|
|
{
|
|
|
|
char* data;
|
|
|
|
long bytes;
|
|
|
|
int ret = 1;
|
|
|
|
X509* cert = lsec_checkx509(L, 1);
|
|
|
|
BIO *bio = BIO_new(BIO_s_mem());
|
|
|
|
EVP_PKEY *pkey = X509_get_pubkey(cert);
|
|
|
|
if(PEM_write_bio_PUBKEY(bio, pkey)) {
|
|
|
|
bytes = BIO_get_mem_data(bio, &data);
|
|
|
|
if (bytes > 0) {
|
|
|
|
lua_pushlstring(L, data, bytes);
|
|
|
|
switch(EVP_PKEY_type(pkey->type)) {
|
|
|
|
case EVP_PKEY_RSA:
|
|
|
|
lua_pushstring(L, "RSA");
|
|
|
|
break;
|
|
|
|
case EVP_PKEY_DSA:
|
|
|
|
lua_pushstring(L, "DSA");
|
|
|
|
break;
|
|
|
|
case EVP_PKEY_DH:
|
|
|
|
lua_pushstring(L, "DH");
|
|
|
|
break;
|
|
|
|
case EVP_PKEY_EC:
|
|
|
|
lua_pushstring(L, "EC");
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
lua_pushstring(L, "Unknown");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
lua_pushinteger(L, EVP_PKEY_bits(pkey));
|
|
|
|
ret = 3;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
lua_pushnil(L);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
lua_pushnil(L);
|
|
|
|
/* Cleanup */
|
|
|
|
BIO_free(bio);
|
|
|
|
EVP_PKEY_free(pkey);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2013-03-30 13:21:40 +01:00
|
|
|
/**
|
|
|
|
* Compute the fingerprint.
|
|
|
|
*/
|
|
|
|
static int meth_digest(lua_State* L)
|
|
|
|
{
|
|
|
|
unsigned int bytes;
|
|
|
|
const EVP_MD *digest = NULL;
|
|
|
|
unsigned char buffer[EVP_MAX_MD_SIZE];
|
|
|
|
char hex_buffer[EVP_MAX_MD_SIZE*2];
|
|
|
|
X509 *cert = lsec_checkx509(L, 1);
|
|
|
|
const char *str = luaL_optstring(L, 2, NULL);
|
|
|
|
if (!str)
|
|
|
|
digest = EVP_sha1();
|
|
|
|
else {
|
|
|
|
if (!strcmp(str, "sha1"))
|
|
|
|
digest = EVP_sha1();
|
|
|
|
else if (!strcmp(str, "sha256"))
|
|
|
|
digest = EVP_sha256();
|
|
|
|
else if (!strcmp(str, "sha512"))
|
|
|
|
digest = EVP_sha512();
|
|
|
|
}
|
|
|
|
if (!digest) {
|
|
|
|
lua_pushnil(L);
|
2013-09-08 00:56:55 +02:00
|
|
|
lua_pushfstring(L, "digest algorithm not supported (%s)", str);
|
2013-03-30 13:21:40 +01:00
|
|
|
return 2;
|
|
|
|
}
|
|
|
|
if (!X509_digest(cert, digest, buffer, &bytes)) {
|
|
|
|
lua_pushnil(L);
|
2013-09-08 00:56:55 +02:00
|
|
|
lua_pushfstring(L, "error processing the certificate (%s)",
|
|
|
|
ERR_reason_error_string(ERR_get_error()));
|
2013-03-30 13:21:40 +01:00
|
|
|
return 2;
|
|
|
|
}
|
|
|
|
to_hex((char*)buffer, bytes, hex_buffer);
|
|
|
|
lua_pushlstring(L, hex_buffer, bytes*2);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check if the certificate is valid in a given time.
|
|
|
|
*/
|
|
|
|
static int meth_valid_at(lua_State* L)
|
|
|
|
{
|
|
|
|
X509* cert = lsec_checkx509(L, 1);
|
|
|
|
time_t time = luaL_checkinteger(L, 2);
|
|
|
|
lua_pushboolean(L, (X509_cmp_time(X509_get_notAfter(cert), &time) >= 0
|
|
|
|
&& X509_cmp_time(X509_get_notBefore(cert), &time) <= 0));
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return the serial number.
|
|
|
|
*/
|
|
|
|
static int meth_serial(lua_State *L)
|
|
|
|
{
|
|
|
|
char *tmp;
|
|
|
|
BIGNUM *bn;
|
|
|
|
ASN1_INTEGER *serial;
|
|
|
|
X509* cert = lsec_checkx509(L, 1);
|
|
|
|
serial = X509_get_serialNumber(cert);
|
|
|
|
bn = ASN1_INTEGER_to_BN(serial, NULL);
|
|
|
|
tmp = BN_bn2hex(bn);
|
|
|
|
lua_pushstring(L, tmp);
|
|
|
|
BN_free(bn);
|
|
|
|
OPENSSL_free(tmp);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return not before date.
|
|
|
|
*/
|
|
|
|
static int meth_notbefore(lua_State *L)
|
|
|
|
{
|
|
|
|
X509* cert = lsec_checkx509(L, 1);
|
|
|
|
return push_asn1_time(L, X509_get_notBefore(cert));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return not after date.
|
|
|
|
*/
|
|
|
|
static int meth_notafter(lua_State *L)
|
|
|
|
{
|
|
|
|
X509* cert = lsec_checkx509(L, 1);
|
|
|
|
return push_asn1_time(L, X509_get_notAfter(cert));
|
|
|
|
}
|
|
|
|
|
2014-04-19 22:58:28 +02:00
|
|
|
/**
|
|
|
|
* Check if this certificate issued some other certificate
|
|
|
|
*/
|
2015-03-20 16:36:05 +01:00
|
|
|
static int meth_issued(lua_State* L)
|
2014-04-19 22:58:28 +02:00
|
|
|
{
|
2015-03-20 16:36:05 +01:00
|
|
|
int ret, i, len;
|
|
|
|
|
|
|
|
X509_STORE_CTX* ctx = NULL;
|
|
|
|
X509_STORE* root = NULL;
|
2015-03-31 17:48:44 +02:00
|
|
|
STACK_OF(X509)* chain = NULL;
|
2015-03-20 16:36:05 +01:00
|
|
|
|
2014-04-19 22:58:28 +02:00
|
|
|
X509* issuer = lsec_checkx509(L, 1);
|
2015-03-31 17:48:44 +02:00
|
|
|
X509* subject = lsec_checkx509(L, 2);
|
|
|
|
X509* cert = NULL;
|
2015-03-20 16:36:05 +01:00
|
|
|
|
2015-03-31 17:48:44 +02:00
|
|
|
len = lua_gettop(L);
|
|
|
|
|
|
|
|
/* Check that all arguments are certificates */
|
|
|
|
|
|
|
|
for (i = 3; i <= len; i++) {
|
|
|
|
lsec_checkx509(L, i);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Before allocating things that require freeing afterwards */
|
|
|
|
|
|
|
|
chain = sk_X509_new_null();
|
2015-03-20 16:36:05 +01:00
|
|
|
ctx = X509_STORE_CTX_new();
|
|
|
|
root = X509_STORE_new();
|
|
|
|
|
|
|
|
if (ctx == NULL || root == NULL) {
|
|
|
|
lua_pushnil(L);
|
|
|
|
lua_pushstring(L, "X509_STORE_new() or X509_STORE_CTX_new() error");
|
|
|
|
ret = 2;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = X509_STORE_add_cert(root, issuer);
|
|
|
|
|
|
|
|
if(!ret) {
|
|
|
|
lua_pushnil(L);
|
|
|
|
lua_pushstring(L, "X509_STORE_add_cert() error");
|
|
|
|
ret = 2;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2015-03-31 17:48:44 +02:00
|
|
|
for (i = 3; i <= len && lua_isuserdata(L, i); i++) {
|
|
|
|
cert = lsec_checkx509(L, i);
|
|
|
|
sk_X509_push(chain, cert);
|
2015-03-20 16:36:05 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
ret = X509_STORE_CTX_init(ctx, root, subject, chain);
|
|
|
|
|
|
|
|
if(!ret) {
|
|
|
|
lua_pushnil(L);
|
|
|
|
lua_pushstring(L, "X509_STORE_CTX_init() error");
|
|
|
|
ret = 2;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Actual verification */
|
|
|
|
if (X509_verify_cert(ctx) <= 0) {
|
|
|
|
ret = X509_STORE_CTX_get_error(ctx);
|
|
|
|
lua_pushnil(L);
|
2014-04-22 01:17:34 +02:00
|
|
|
lua_pushstring(L, X509_verify_cert_error_string(ret));
|
2015-03-20 16:36:05 +01:00
|
|
|
ret = 2;
|
|
|
|
} else {
|
|
|
|
lua_pushboolean(L, 1);
|
|
|
|
ret = 1;
|
2014-04-22 01:17:34 +02:00
|
|
|
}
|
2015-03-20 16:36:05 +01:00
|
|
|
|
|
|
|
cleanup:
|
|
|
|
|
|
|
|
if (ctx != NULL) {
|
|
|
|
X509_STORE_CTX_free(ctx);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (chain != NULL) {
|
|
|
|
X509_STORE_free(root);
|
|
|
|
}
|
|
|
|
|
2015-03-31 17:48:44 +02:00
|
|
|
sk_X509_free(chain);
|
2015-03-20 16:36:05 +01:00
|
|
|
|
|
|
|
return ret;
|
2014-04-19 22:58:28 +02:00
|
|
|
}
|
|
|
|
|
2013-03-30 13:21:40 +01:00
|
|
|
/**
|
|
|
|
* Collect X509 objects.
|
|
|
|
*/
|
|
|
|
static int meth_destroy(lua_State* L)
|
|
|
|
{
|
|
|
|
X509_free(lsec_checkx509(L, 1));
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int meth_tostring(lua_State *L)
|
|
|
|
{
|
|
|
|
X509* cert = lsec_checkx509(L, 1);
|
|
|
|
lua_pushfstring(L, "X509 certificate: %p", cert);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2013-10-23 17:42:34 +02:00
|
|
|
/**
|
|
|
|
* Set the encode for ASN.1 string.
|
|
|
|
*/
|
|
|
|
static int meth_set_encode(lua_State* L)
|
|
|
|
{
|
|
|
|
int succ = 0;
|
|
|
|
p_x509 px = lsec_checkp_x509(L, 1);
|
|
|
|
const char *enc = luaL_checkstring(L, 2);
|
|
|
|
if (strncmp(enc, "ai5", 3) == 0) {
|
|
|
|
succ = 1;
|
|
|
|
px->encode = LSEC_AI5_STRING;
|
|
|
|
} else if (strncmp(enc, "utf8", 4) == 0) {
|
|
|
|
succ = 1;
|
|
|
|
px->encode = LSEC_UTF8_STRING;
|
|
|
|
}
|
|
|
|
lua_pushboolean(L, succ);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2013-03-30 13:21:40 +01:00
|
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
static int load_cert(lua_State* L)
|
|
|
|
{
|
|
|
|
X509 *cert;
|
|
|
|
size_t bytes;
|
|
|
|
const char* data;
|
|
|
|
BIO *bio = BIO_new(BIO_s_mem());
|
|
|
|
data = luaL_checklstring(L, 1, &bytes);
|
|
|
|
BIO_write(bio, data, bytes);
|
|
|
|
cert = PEM_read_bio_X509(bio, NULL, NULL, NULL);
|
|
|
|
if (cert)
|
|
|
|
lsec_pushx509(L, cert);
|
|
|
|
else
|
|
|
|
lua_pushnil(L);
|
|
|
|
BIO_free(bio);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Certificate methods.
|
|
|
|
*/
|
|
|
|
static luaL_Reg methods[] = {
|
|
|
|
{"digest", meth_digest},
|
2013-10-23 17:42:34 +02:00
|
|
|
{"setencode", meth_set_encode},
|
2013-03-30 13:21:40 +01:00
|
|
|
{"extensions", meth_extensions},
|
|
|
|
{"issuer", meth_issuer},
|
|
|
|
{"notbefore", meth_notbefore},
|
|
|
|
{"notafter", meth_notafter},
|
2014-04-19 22:58:28 +02:00
|
|
|
{"issued", meth_issued},
|
2013-03-30 13:21:40 +01:00
|
|
|
{"pem", meth_pem},
|
2014-04-19 23:11:32 +02:00
|
|
|
{"pubkey", meth_pubkey},
|
2013-03-30 13:21:40 +01:00
|
|
|
{"serial", meth_serial},
|
|
|
|
{"subject", meth_subject},
|
|
|
|
{"validat", meth_valid_at},
|
|
|
|
{NULL, NULL}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* X509 metamethods.
|
|
|
|
*/
|
|
|
|
static luaL_Reg meta[] = {
|
|
|
|
{"__gc", meth_destroy},
|
|
|
|
{"__tostring", meth_tostring},
|
|
|
|
{NULL, NULL}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* X509 functions.
|
|
|
|
*/
|
|
|
|
static luaL_Reg funcs[] = {
|
|
|
|
{"load", load_cert},
|
|
|
|
{NULL, NULL}
|
|
|
|
};
|
|
|
|
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
LSEC_API int luaopen_ssl_x509(lua_State *L)
|
|
|
|
{
|
|
|
|
/* Register the functions and tables */
|
|
|
|
luaL_newmetatable(L, "SSL:Certificate");
|
2015-02-12 19:32:54 +01:00
|
|
|
setfuncs(L, meta);
|
2013-03-30 13:21:40 +01:00
|
|
|
|
2015-02-06 20:44:08 +01:00
|
|
|
luaL_newlib(L, methods);
|
2013-03-30 13:21:40 +01:00
|
|
|
lua_setfield(L, -2, "__index");
|
|
|
|
|
2015-02-06 20:44:08 +01:00
|
|
|
luaL_newlib(L, funcs);
|
2013-03-30 13:21:40 +01:00
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|