src/x509.c

Sat, 20 Nov 2010 20:04:11 -0800

author
Paul Aurich <paul@darkrain42.org>
date
Sat, 20 Nov 2010 20:04:11 -0800
changeset 32
c47594a84f04
parent 31
87625285de20
child 33
cc36229b3be1
permissions
-rw-r--r--

imported patch whitespace

/*--------------------------------------------------------------------------
 * 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_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;
      }
      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, 1), "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;
}

mercurial