luasec/src/context.c

435 lines
10 KiB
C
Raw Normal View History

2012-09-02 16:15:49 +02:00
/*--------------------------------------------------------------------------
2012-09-02 16:30:04 +02:00
* LuaSec 0.3.2
2012-09-02 16:27:04 +02:00
* Copyright (C) 2006-2009 Bruno Silvestre
2012-09-02 16:15:49 +02:00
*
*--------------------------------------------------------------------------*/
#include <string.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <lua.h>
#include <lauxlib.h>
#include "context.h"
struct ssl_option_s {
const char *name;
unsigned long code;
};
typedef struct ssl_option_s ssl_option_t;
static ssl_option_t ssl_options[] = {
/* OpenSSL 0.9.7 and 0.9.8 */
{"all", SSL_OP_ALL},
{"cipher_server_preference", SSL_OP_CIPHER_SERVER_PREFERENCE},
{"dont_insert_empty_fragments", SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS},
{"ephemeral_rsa", SSL_OP_EPHEMERAL_RSA},
{"netscape_ca_dn_bug", SSL_OP_NETSCAPE_CA_DN_BUG},
{"netscape_challenge_bug", SSL_OP_NETSCAPE_CHALLENGE_BUG},
{"microsoft_big_sslv3_buffer", SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER},
{"microsoft_sess_id_bug", SSL_OP_MICROSOFT_SESS_ID_BUG},
{"msie_sslv2_rsa_padding", SSL_OP_MSIE_SSLV2_RSA_PADDING},
{"netscape_demo_cipher_change_bug", SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG},
{"netscape_reuse_cipher_change_bug", SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG},
{"no_session_resumption_on_renegotiation",
SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION},
{"no_sslv2", SSL_OP_NO_SSLv2},
{"no_sslv3", SSL_OP_NO_SSLv3},
{"no_tlsv1", SSL_OP_NO_TLSv1},
{"pkcs1_check_1", SSL_OP_PKCS1_CHECK_1},
{"pkcs1_check_2", SSL_OP_PKCS1_CHECK_2},
{"single_dh_use", SSL_OP_SINGLE_DH_USE},
{"ssleay_080_client_dh_bug", SSL_OP_SSLEAY_080_CLIENT_DH_BUG},
{"sslref2_reuse_cert_type_bug", SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG},
{"tls_block_padding_bug", SSL_OP_TLS_BLOCK_PADDING_BUG},
{"tls_d5_bug", SSL_OP_TLS_D5_BUG},
{"tls_rollback_bug", SSL_OP_TLS_ROLLBACK_BUG},
/* OpenSSL 0.9.8 only */
#if OPENSSL_VERSION_NUMBER > 0x00908000L
{"cookie_exchange", SSL_OP_COOKIE_EXCHANGE},
{"no_query_mtu", SSL_OP_NO_QUERY_MTU},
{"single_ecdh_use", SSL_OP_SINGLE_ECDH_USE},
#endif
{NULL, 0L}
};
/*--------------------------- Auxiliary Functions ----------------------------*/
/**
* Return the context.
*/
static p_context checkctx(lua_State *L, int idx)
{
return (p_context)luaL_checkudata(L, idx, "SSL:Context");
}
/**
* Prepare the SSL options flag.
*/
static int set_option_flag(const char *opt, unsigned long *flag)
{
ssl_option_t *p;
for (p = ssl_options; p->name; p++) {
if (!strcmp(opt, p->name)) {
*flag |= p->code;
return 1;
}
}
return 0;
}
/**
* Find the protocol.
*/
static SSL_METHOD* str2method(const char *method)
{
if (!strcmp(method, "sslv3")) return SSLv3_method();
if (!strcmp(method, "tlsv1")) return TLSv1_method();
if (!strcmp(method, "sslv23")) return SSLv23_method();
return NULL;
}
/**
* Prepare the SSL handshake verify flag.
*/
static int set_verify_flag(const char *str, int *flag)
{
if (!strcmp(str, "none")) {
*flag |= SSL_VERIFY_NONE;
return 1;
}
if (!strcmp(str, "peer")) {
*flag |= SSL_VERIFY_PEER;
return 1;
}
if (!strcmp(str, "client_once")) {
*flag |= SSL_VERIFY_CLIENT_ONCE;
return 1;
}
if (!strcmp(str, "fail_if_no_peer_cert")) {
*flag |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
return 1;
}
return 0;
}
2012-09-02 16:22:22 +02:00
/**
* Password callback for reading the private key.
*/
static int passwd_cb(char *buf, int size, int flag, void *udata)
{
lua_State *L = (lua_State*)udata;
switch (lua_type(L, 3)) {
case LUA_TFUNCTION:
lua_pushvalue(L, 3);
lua_call(L, 0, 1);
if (lua_type(L, -1) != LUA_TSTRING)
return 0;
/* fallback */
case LUA_TSTRING:
strncpy(buf, lua_tostring(L, -1), size);
buf[size-1] = '\0';
return (int)strlen(buf);
}
return 0;
}
2012-09-02 16:15:49 +02:00
/*------------------------------ Lua Functions -------------------------------*/
/**
* Create a SSL context.
*/
static int create(lua_State *L)
{
p_context ctx;
SSL_METHOD *method;
method = str2method(luaL_checkstring(L, 1));
if (!method) {
lua_pushnil(L);
lua_pushstring(L, "invalid protocol");
return 2;
}
ctx = (p_context) lua_newuserdata(L, sizeof(t_context));
if (!ctx) {
lua_pushnil(L);
lua_pushstring(L, "error creating context");
return 2;
}
ctx->context = SSL_CTX_new(method);
if (!ctx->context) {
lua_pushnil(L);
lua_pushstring(L, "error creating context");
return 2;
}
ctx->mode = MD_CTX_INVALID;
/* No session support */
SSL_CTX_set_session_cache_mode(ctx->context, SSL_SESS_CACHE_OFF);
luaL_getmetatable(L, "SSL:Context");
lua_setmetatable(L, -2);
return 1;
}
/**
* Load the trusting certificates.
*/
static int load_locations(lua_State *L)
{
SSL_CTX *ctx = ctx_getcontext(L, 1);
const char *cafile = luaL_optstring(L, 2, NULL);
const char *capath = luaL_optstring(L, 3, NULL);
if (SSL_CTX_load_verify_locations(ctx, cafile, capath) != 1) {
lua_pushboolean(L, 0);
lua_pushfstring(L, "error loading CA locations (%s)",
ERR_reason_error_string(ERR_get_error()));
return 2;
}
lua_pushboolean(L, 1);
return 1;
}
/**
* Load the certificate file.
*/
static int load_cert(lua_State *L)
{
SSL_CTX *ctx = ctx_getcontext(L, 1);
const char *filename = luaL_checkstring(L, 2);
if (SSL_CTX_use_certificate_chain_file(ctx, filename) != 1) {
lua_pushboolean(L, 0);
lua_pushfstring(L, "error loading certificate (%s)",
ERR_reason_error_string(ERR_get_error()));
return 2;
}
lua_pushboolean(L, 1);
return 1;
}
/**
* Load the key file -- only in PEM format.
*/
static int load_key(lua_State *L)
{
2012-09-02 16:22:22 +02:00
int ret = 1;
2012-09-02 16:15:49 +02:00
SSL_CTX *ctx = ctx_getcontext(L, 1);
const char *filename = luaL_checkstring(L, 2);
2012-09-02 16:22:22 +02:00
switch (lua_type(L, 3)) {
case LUA_TSTRING:
case LUA_TFUNCTION:
SSL_CTX_set_default_passwd_cb(ctx, passwd_cb);
SSL_CTX_set_default_passwd_cb_userdata(ctx, L);
/* fallback */
case LUA_TNIL:
if (SSL_CTX_use_PrivateKey_file(ctx, filename, SSL_FILETYPE_PEM) == 1)
lua_pushboolean(L, 1);
else {
ret = 2;
lua_pushboolean(L, 0);
lua_pushfstring(L, "error loading private key (%s)",
ERR_reason_error_string(ERR_get_error()));
}
SSL_CTX_set_default_passwd_cb(ctx, NULL);
SSL_CTX_set_default_passwd_cb_userdata(ctx, NULL);
break;
default:
lua_pushstring(L, "invalid callback value");
lua_error(L);
2012-09-02 16:15:49 +02:00
}
2012-09-02 16:22:22 +02:00
return ret;
2012-09-02 16:15:49 +02:00
}
/**
* Set the cipher list.
*/
static int set_cipher(lua_State *L)
{
SSL_CTX *ctx = ctx_getcontext(L, 1);
const char *list = luaL_checkstring(L, 2);
if (SSL_CTX_set_cipher_list(ctx, list) != 1) {
lua_pushboolean(L, 0);
lua_pushfstring(L, "error setting cipher list (%s)",
ERR_reason_error_string(ERR_get_error()));
return 2;
}
lua_pushboolean(L, 1);
return 1;
}
/**
* Set the depth for certificate checking.
*/
static int set_depth(lua_State *L)
{
SSL_CTX *ctx = ctx_getcontext(L, 1);
SSL_CTX_set_verify_depth(ctx, luaL_checkint(L, 2));
lua_pushboolean(L, 1);
return 1;
}
/**
* Set the handshake verify options.
*/
static int set_verify(lua_State *L)
{
int i;
int flag = 0;
SSL_CTX *ctx = ctx_getcontext(L, 1);
int max = lua_gettop(L);
/* any flag? */
if (max > 1) {
for (i = 2; i <= max; i++) {
if (!set_verify_flag(luaL_checkstring(L, i), &flag)) {
lua_pushboolean(L, 0);
lua_pushstring(L, "invalid verify option");
return 2;
}
}
SSL_CTX_set_verify(ctx, flag, NULL);
}
lua_pushboolean(L, 1);
return 1;
}
/**
* Set the protocol options.
*/
static int set_options(lua_State *L)
{
int i;
unsigned long flag = 0L;
SSL_CTX *ctx = ctx_getcontext(L, 1);
int max = lua_gettop(L);
/* any option? */
if (max > 1) {
for (i = 2; i <= max; i++) {
if (!set_option_flag(luaL_checkstring(L, i), &flag)) {
lua_pushboolean(L, 0);
lua_pushstring(L, "invalid option");
return 2;
}
}
SSL_CTX_set_options(ctx, flag);
}
lua_pushboolean(L, 1);
return 1;
}
/**
* Set the context mode.
*/
static int set_mode(lua_State *L)
{
p_context ctx = checkctx(L, 1);
const char *str = luaL_checkstring(L, 2);
if (!strcmp("server", str)) {
ctx->mode = MD_CTX_SERVER;
lua_pushboolean(L, 1);
return 1;
}
if(!strcmp("client", str)) {
ctx->mode = MD_CTX_CLIENT;
lua_pushboolean(L, 1);
return 1;
}
lua_pushboolean(L, 0);
lua_pushstring(L, "invalid mode");
return 1;
}
2012-09-02 16:22:22 +02:00
/**
* Return a pointer to SSL_CTX structure.
*/
static int raw_ctx(lua_State *L)
{
p_context ctx = checkctx(L, 1);
lua_pushlightuserdata(L, (void*)ctx->context);
return 1;
}
2012-09-02 16:15:49 +02:00
/**
* Package functions
*/
static luaL_Reg funcs[] = {
{"create", create},
{"locations", load_locations},
{"loadcert", load_cert},
{"loadkey", load_key},
{"setcipher", set_cipher},
{"setdepth", set_depth},
{"setverify", set_verify},
{"setoptions", set_options},
{"setmode", set_mode},
2012-09-02 16:22:22 +02:00
{"rawcontext", raw_ctx},
2012-09-02 16:15:49 +02:00
{NULL, NULL}
};
/*-------------------------------- Metamethods -------------------------------*/
/**
* Collect SSL context -- GC metamethod.
*/
static int meth_destroy(lua_State *L)
{
p_context ctx = checkctx(L, 1);
if (ctx->context) {
SSL_CTX_free(ctx->context);
ctx->context = NULL;
}
return 0;
}
/**
* Object information -- tostring metamethod.
*/
static int meth_tostring(lua_State *L)
{
p_context ctx = checkctx(L, 1);
lua_pushfstring(L, "SSL context: %p", ctx);
return 1;
}
/**
* Context metamethods.
*/
static luaL_Reg meta[] = {
{"__gc", meth_destroy},
{"__tostring", meth_tostring},
{NULL, NULL}
};
/*----------------------------- Public Functions ---------------------------*/
/**
* Retrieve the SSL context from the Lua stack.
*/
SSL_CTX* ctx_getcontext(lua_State *L, int idx)
{
p_context ctx = checkctx(L, idx);
return ctx->context;
}
/**
* Retrieve the mode from the context in the Lua stack.
*/
char ctx_getmode(lua_State *L, int idx)
{
p_context ctx = checkctx(L, idx);
return ctx->mode;
}
/*------------------------------ Initialization ------------------------------*/
/**
* Registre the module.
*/
int luaopen_ssl_context(lua_State *L)
{
luaL_newmetatable(L, "SSL:Context");
luaL_register(L, NULL, meta);
luaL_register(L, "ssl.context", funcs);
return 1;
}