/*--------------------------------------------------------------------------
* LuaSec 0.4
* Copyright (C) 2006-2009 Bruno Silvestre
*
*--------------------------------------------------------------------------*/
#include <string.h>
#include <openssl/ssl.h>
#include <openssl/x509v3.h>
#include <openssl/err.h>
#include <lua.h>
#include <lauxlib.h>
#include "io.h"
#include "buffer.h"
#include "timeout.h"
#include "socket.h"
#include "ssl.h"
#include "x509.h"
#define min(a, b) (a<b)?a:b
void luasec_push_x509(lua_State* L, X509 *cert)
{
p_x509 cert_obj = (p_x509) lua_newuserdata(L, sizeof(t_x509));
cert_obj->cert = cert;
luaL_getmetatable(L, "SSL:Certificate");
lua_setmetatable(L, -2);
}
X509* luasec_to_x509(lua_State* L, int idx)
{
return ((p_x509)luaL_checkudata(L, idx, "SSL:Certificate"))->cert;
}
void luasec_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);
lua_pushlstring(L, buffer, min(sizeof(buffer),len));
}
void luasec_push_asn1_string(lua_State* L, ASN1_STRING *string)
{
if (string)
lua_pushlstring(L, (char*)ASN1_STRING_data(string), ASN1_STRING_length(string));
else
lua_pushnil(L);
}
int luasec_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;
}
void luasec_push_x509_name(lua_State* L, X509_NAME *name)
{
int i, n_entries;
lua_newtable(L);
n_entries = X509_NAME_entry_count(name);
for (i = 0; i < n_entries; i++)
{
X509_NAME_ENTRY *entry;
ASN1_OBJECT *object;
entry = X509_NAME_get_entry(name, i);
object = X509_NAME_ENTRY_get_object(entry);
lua_newtable(L);
luasec_push_asn1_objname(L, object, 1);
lua_setfield(L, -2, "oid");
luasec_push_asn1_objname(L, object, 0);
lua_setfield(L, -2, "name");
luasec_push_asn1_string(L, X509_NAME_ENTRY_get_data(entry));
lua_setfield(L, -2, "value");
lua_rawseti(L, -2, lua_objlen(L, -2)+1);
}
}
int meth_subject(lua_State* L)
{
luasec_push_x509_name(L, X509_get_subject_name(luasec_to_x509(L, 1)));
return 1;
}
int meth_issuer(lua_State* L)
{
luasec_push_x509_name(L, X509_get_issuer_name(luasec_to_x509(L, 1)));
return 1;
}
int meth_extensions(lua_State* L)
{
X509 *peer;
int i, j;
peer = luasec_to_x509(L, 1);
lua_newtable(L); /* ret */
i = -1;
while ((i = X509_get_ext_by_NID(peer, NID_subject_alt_name, i)) != -1)
{
X509_EXTENSION *extension;
STACK_OF(GENERAL_NAME) *values;
int n_general_names;
extension = X509_get_ext(peer, i);
if (extension == NULL)
break;
values = X509V3_EXT_d2i(extension);
if (values == NULL)
break;
/* Push ret[oid] */
luasec_push_asn1_objname(L, extension->object, 1);
luasec_push_subtable(L, -2);
/* Set ret[oid].name = name */
luasec_push_asn1_objname(L, extension->object, 0);
lua_setfield(L, -2, "name");
n_general_names = sk_GENERAL_NAME_num(values);
for (j = 0; j < n_general_names; j++)
{
GENERAL_NAME *general_name;
general_name = sk_GENERAL_NAME_value(values, j);
switch(general_name->type)
{
case GEN_OTHERNAME:
{
OTHERNAME *otherName = general_name->d.otherName;
luasec_push_asn1_objname(L, otherName->type_id, 1);
if (luasec_push_subtable(L, -2))
{
luasec_push_asn1_objname(L, otherName->type_id, 0);
lua_setfield(L, -2, "name");
}
luasec_push_asn1_string(L, otherName->value->value.asn1_string);
lua_rawseti(L, -2, lua_objlen(L, -2)+1);
lua_pop(L, 1);
break;
}
case GEN_EMAIL:
lua_pushstring(L, "rfc822Name");
luasec_push_subtable(L, -2);
luasec_push_asn1_string(L, general_name->d.rfc822Name);
lua_rawseti(L, -2, lua_objlen(L, -2)+1);
lua_pop(L, 1);
break;
case GEN_DNS:
lua_pushstring(L, "dNSName");
luasec_push_subtable(L, -2);
luasec_push_asn1_string(L, general_name->d.dNSName);
lua_rawseti(L, -2, lua_objlen(L, -2)+1);
lua_pop(L, 1);
break;
case GEN_X400:
/* x400Address */
break;
case GEN_DIRNAME:
/* directoryName */
break;
case GEN_EDIPARTY:
/* ediPartyName */
break;
case GEN_URI:
lua_pushstring(L, "uniformResourceIdentifier");
luasec_push_subtable(L, -2);
luasec_push_asn1_string(L, general_name->d.uniformResourceIdentifier);
lua_rawseti(L, -2, lua_objlen(L, -2)+1);
lua_pop(L, 1);
break;
case GEN_IPADD:
lua_pushstring(L, "iPAddress");
luasec_push_subtable(L, -2);
luasec_push_asn1_string(L, general_name->d.iPAddress);
lua_rawseti(L, -2, lua_objlen(L, -2)+1);
lua_pop(L, 1);
break;
case GEN_RID:
/* registeredID */
break;
default:
break;
}
}
lua_pop(L, 1); /* ret[oid] */
i++; /* Next extension */
}
return 1;
}
int meth_valid_at(lua_State* L)
{
X509* cert = luasec_to_x509(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;
}
int meth_pem(lua_State* L)
{
X509* cert = luasec_to_x509(L, 1);
BIO *bio = BIO_new(BIO_s_mem());
char* data; long bytes;
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;
}
const char* hex_tab = "0123456789abcdef";
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];
}
}
int meth_digest(lua_State* L)
{
X509 *cert;
unsigned int bytes;
unsigned char buffer[EVP_MAX_MD_SIZE];
char hex_buffer[EVP_MAX_MD_SIZE*2];
const EVP_MD *digest;
cert = luasec_to_x509(L, 1);
if (lua_gettop(L) < 2 || strcmp(luaL_checkstring(L, 2), "sha1") == 0)
{
digest = EVP_sha1();
}
else
{
lua_pushnil(L);
lua_pushstring(L, "digest algorithm not supported");
return 2;
}
if (!X509_digest(cert, digest, buffer, &bytes))
{
lua_pushnil(L);
lua_pushstring(L, "out of memory");
return 2;
}
to_hex((char*)buffer, bytes, hex_buffer);
lua_pushlstring(L, hex_buffer, bytes*2);
return 1;
}
int meth_destroy(lua_State* L)
{
X509_free(luasec_to_x509(L, 1));
return 0;
}
int meth_tostring(lua_State *L)
{
X509 *cert = luasec_to_x509(L, 1);
lua_pushfstring(L, "X509 certificate: %p", cert);
return 1;
}
int cert_from_pem(lua_State* L)
{
X509 *cert;
BIO *bio = BIO_new(BIO_s_mem());
const char* data; size_t bytes;
data = luaL_checklstring(L, 1, &bytes);
BIO_write(bio, data, bytes);
cert = PEM_read_bio_X509(bio, NULL, NULL, NULL);
if (cert)
luasec_push_x509(L, cert);
else
lua_pushnil(L);
BIO_free(bio);
return 1;
}
/**
* Certificate metamethods
*/
static luaL_Reg methods[] = {
{"subject", meth_subject},
{"issuer", meth_issuer},
{"extensions", meth_extensions},
{"valid_at", meth_valid_at},
{"pem", meth_pem},
{"digest", meth_digest},
{NULL, NULL}
};
/**
* ssl.x509 functions
*/
static luaL_Reg funcs[] = {
{"cert_from_pem", cert_from_pem},
{NULL, NULL}
};
/**
* Context metamethods.
*/
static luaL_Reg meta[] = {
{"__gc", meth_destroy},
{"__tostring", meth_tostring},
{NULL, NULL}
};
LUASEC_API int luaopen_ssl_x509(lua_State *L)
{
/* Register the functions and tables */
luaL_newmetatable(L, "SSL:Certificate");
luaL_register(L, NULL, meta);
lua_newtable(L);
luaL_register(L, NULL, methods);
lua_setfield(L, -2, "__index");
luaL_register(L, "ssl.x509", funcs);
return 1;
}