Sun, 03 Jul 2011 13:13:36 -0700
context: Clean up some warnings
/*-------------------------------------------------------------------------- * LuaSec 0.4 * Copyright (C) 2006-2009 Bruno Silvestre * *--------------------------------------------------------------------------*/ #include <string.h> #include <openssl/ssl.h> #include <openssl/err.h> #include <openssl/x509.h> #include <openssl/x509v3.h> #include <lua.h> #include <lauxlib.h> #include "context.h" #include "ssl.h" struct ssl_option_s { const char *name; unsigned long code; }; typedef struct ssl_option_s ssl_option_t; /* index into the SSL storage where the context is. * see SSL_CTX_get_ex_data(). */ static int luasec_sslctx_idx = -1; /* The export DH key */ static DH *dh_512 = NULL; /* The larger key (builtin is 2048, caller may specify larger) */ static DH *dh_larger = NULL; /* Generated via "openssl dhparam -2 -noout -C 512 2>/dev/null" */ static unsigned char dh512_p[] = { 0xE4,0x3F,0x75,0x82,0xAD,0x0B,0x28,0xC7,0xEF,0xCE,0xBC,0x3B, 0x14,0xBB,0xA6,0xF4,0xA2,0xE9,0xA6,0x59,0xCF,0x97,0x1C,0x86, 0x43,0x3B,0x92,0x4A,0x6B,0x15,0x4B,0x0C,0xAC,0x8F,0xFA,0x43, 0xE2,0xA8,0xC3,0x3B,0x7B,0x51,0x1B,0x46,0x21,0xBF,0x8C,0x06, 0x6C,0xB1,0x49,0x75,0xC7,0xAC,0x47,0x1D,0x9D,0x64,0xD5,0x99, 0x33,0x86,0xAD,0xEB, }; /* Generated via "openssl dhparam -2 -noout -C 2048 2>/dev/null" */ static unsigned char dh2048_p[] = { 0x9B,0xF4,0xC5,0x57,0x81,0x8F,0xCF,0x31,0x78,0x95,0x04,0xCD, 0xEA,0xCC,0x30,0xEA,0xF7,0xCA,0x76,0xC8,0x8F,0x91,0xEA,0x0E, 0x44,0x8D,0xE2,0x63,0x19,0x3B,0x4D,0x04,0xC8,0x7D,0x0D,0xFF, 0x3D,0x52,0x76,0x02,0xF3,0xCA,0x1C,0x44,0xAF,0x0E,0xA9,0x59, 0x02,0x40,0x75,0xD6,0xED,0x35,0x4D,0x11,0x5B,0x2B,0x73,0x23, 0xE5,0x53,0x0B,0x1F,0xB0,0x47,0xC4,0x7F,0x95,0x5D,0xB0,0xD5, 0xF3,0xD3,0xAB,0x5F,0x28,0x2B,0xEC,0x2C,0x15,0x0B,0x1B,0x0C, 0xD4,0xBE,0x24,0x2F,0xC5,0x07,0x3C,0xE4,0xC5,0xE6,0x16,0x42, 0x4C,0x31,0x04,0xBB,0x80,0x96,0xFF,0x64,0x50,0xA4,0xA5,0xB5, 0xF5,0x3A,0xBA,0x57,0xE4,0xE6,0xC2,0x23,0x0A,0xB6,0x27,0xC4, 0x06,0x01,0x1E,0x98,0x20,0x09,0xC8,0xB7,0x90,0x09,0x86,0x06, 0xAA,0x85,0xE7,0x02,0xC8,0xC6,0xD9,0x1D,0xAB,0x17,0xEE,0x78, 0x73,0x78,0x88,0x7F,0xA7,0xF2,0x34,0xA7,0xDD,0x02,0x16,0x36, 0x0D,0x77,0x16,0x3E,0x95,0xAE,0x02,0xEE,0x36,0x37,0xD5,0x61, 0x5D,0xFE,0xC6,0x0B,0xDF,0xCE,0xB9,0x26,0x31,0x6F,0x34,0x92, 0xBB,0xBB,0x91,0x29,0x77,0x62,0x1D,0x75,0xA0,0x51,0x8D,0x31, 0x4C,0x64,0x4E,0xBF,0xDC,0xE8,0x67,0x17,0x90,0x6A,0x80,0xE9, 0xD7,0xD8,0x56,0x4E,0x85,0x21,0x9C,0xFB,0xE6,0x1B,0xD8,0x05, 0xFD,0x13,0x77,0x00,0x96,0x2D,0x0C,0x2A,0x95,0x1A,0x08,0x82, 0x2E,0xB3,0xE2,0xFC,0xE8,0xA6,0xF1,0x16,0x37,0x57,0x82,0xD6, 0xF5,0xAB,0xA9,0x43,0x8F,0x33,0xB0,0x57,0x38,0x6E,0x61,0xD4, 0xDD,0xE0,0x1C,0xCB, }; 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 /* OpenSSL 0.9.8f and above */ #if defined(SSL_OP_NO_TICKET) {"no_ticket", SSL_OP_NO_TICKET}, #endif #if defined(SSL_OP_NO_COMPRESSION) {"no_compression", SSL_OP_NO_COMPRESSION}, #endif {NULL, 0L} }; /*--------------------------- Auxiliary Functions ----------------------------*/ /** * Return the context. */ 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 const 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; } /** * 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; } static DH *get_dh(const unsigned char *p, int len) { DH *dh = NULL; static unsigned char g[] = { 0x02 }; if ((dh = DH_new()) == NULL) return NULL; dh->p = BN_bin2bn(p, len, NULL); dh->g = BN_bin2bn(g, sizeof(g), NULL); if (dh->p == NULL || dh->g == NULL) { DH_free(dh); return NULL; } return dh; } /** * DH parameter callback */ static DH *dh_param_cb(SSL *ssl, int is_export, int keylength) { /* Logic in postfix and dovecot, but we're using a 2048-bit group... */ if (is_export && keylength == 512) { if (dh_512 == NULL) { dh_512 = get_dh(dh512_p, sizeof(dh512_p)); } return dh_512; } else { if (dh_larger == NULL) { dh_larger = get_dh(dh2048_p, sizeof(dh2048_p)); } return dh_larger; } } /*------------------------------ Lua Functions -------------------------------*/ /** * Create a SSL context. */ static int create(lua_State *L) { p_context ctx; const SSL_METHOD *method; if (luasec_sslctx_idx == -1) { luasec_sslctx_idx = SSL_CTX_get_ex_new_index(0, "luasec sslctx context", NULL, NULL, NULL); if (luasec_sslctx_idx == -1) { lua_pushnil(L); lua_pushstring(L, "error creating luasec SSL index"); return 2; } } 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->L = L; ctx->context = SSL_CTX_new(method); if (!ctx->context) { lua_pushnil(L); lua_pushstring(L, "error creating context"); return 2; } ctx->verify_flags = LUASEC_VERIFY_FLAGS_NONE; ctx->mode = MD_CTX_INVALID; /* No session support */ SSL_CTX_set_session_cache_mode(ctx->context, SSL_SESS_CACHE_OFF); /* * Support ephemeral diffie-hellman key exchange. This is only needed * for server mode, but clearer to put it here rather than set_mode. */ SSL_CTX_set_tmp_dh_callback(ctx->context, dh_param_cb); #if defined(SSL_CTX_set_tmp_ecdh) /* * Support ECDH parameters. This uses the 384 bit prime field from * NIST. */ SSL_CTX_set_tmp_ecdh(ctx->context, EC_KEY_new_by_curve_name(NID_secp384r1)); #endif SSL_CTX_set_ex_data(ctx->context, luasec_sslctx_idx, ctx); 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) { int ret = 1; SSL_CTX *ctx = ctx_getcontext(L, 1); const char *filename = luaL_checkstring(L, 2); 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); } return ret; } /** * Load a DH params files. This is a global LuaSec thing. */ static int load_dhparams(lua_State *L) { const char *filename = luaL_checkstring(L, 1); FILE *paramfile; DH *dh; paramfile = fopen(filename, "r"); if (!paramfile) { lua_pushboolean(L, 0); lua_pushfstring(L, "error reading dh param file %s: %s", filename, strerror(errno)); return 2; } dh = PEM_read_DHparams(paramfile, NULL, NULL, NULL); fclose(paramfile); if (!dh) { lua_pushboolean(L, 0); lua_pushfstring(L, "error loading dh param file %s: %s", filename, ERR_reason_error_string(ERR_get_error())); return 2; } if (dh_larger) DH_free(dh_larger); dh_larger = dh; lua_pushboolean(L, 1); return 1; } /** * 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; } static void luasec_push_cert_error(lua_State *L, int ref, int depth, int err) { int created = 0; lua_rawgeti(L, LUA_REGISTRYINDEX, ref); lua_rawgeti(L, -1, depth + 1); if (!lua_istable(L, -1)) { /* If the table doesn't exist, create it */ created = 1; lua_pop(L, 1); lua_newtable(L); } lua_pushstring(L, X509_verify_cert_error_string(err)); lua_rawseti(L, -2, lua_objlen(L, -2)+1); if (created) { lua_rawseti(L, -2, depth + 1); lua_pop(L, 1); } else lua_pop(L, 2); } int verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) { SSL_CTX *context; SSL *ssl; p_context l_ctx; p_ssl l_ssl; int err, depth; /* Short-circuit optimization */ if (preverify_ok) return 1; ssl = X509_STORE_CTX_get_ex_data(x509_ctx, SSL_get_ex_data_X509_STORE_CTX_idx()); context = ssl->ctx; l_ssl = SSL_get_ex_data(ssl, luasec_ssl_idx); l_ctx = SSL_CTX_get_ex_data(context, luasec_sslctx_idx); err = X509_STORE_CTX_get_error(x509_ctx); depth = X509_STORE_CTX_get_error_depth(x509_ctx); if (err != X509_V_OK) { if (l_ssl->t_cert_errors == LUA_NOREF) { lua_newtable(l_ctx->L); l_ssl->t_cert_errors = luaL_ref(l_ctx->L, LUA_REGISTRYINDEX); } luasec_push_cert_error(l_ctx->L, l_ssl->t_cert_errors, depth, err); } return (l_ctx->verify_flags & LUASEC_VERIFY_FLAGS_ALWAYS_CONTINUE ? 1 : preverify_ok); } static int luasec_verify(X509_STORE_CTX *x509_ctx, void *ptr) { p_context ctx = ptr; if (ctx->verify_flags & LUASEC_VERIFY_FLAGS_IGNORE_PURPOSE) { X509_VERIFY_PARAM *param = X509_STORE_CTX_get0_param(x509_ctx); if (param) { X509_VERIFY_PARAM_set_purpose(param, X509_PURPOSE_SSL_SERVER); X509_VERIFY_PARAM_set_trust(param, X509_TRUST_SSL_SERVER); } } return X509_verify_cert(x509_ctx); } /** * Set the handshake verify options. */ static int set_verify(lua_State *L) { int i; int flag = 0, vflag = 0; p_context ctx = checkctx(L, 1); int max = lua_gettop(L); /* any flag? */ if (max > 1) { ctx->verify_flags = LUASEC_VERIFY_FLAGS_NONE; for (i = 2; i <= max; i++) { const char *s = luaL_checkstring(L, i); if (!strcmp(s, "continue")) { ctx->verify_flags |= LUASEC_VERIFY_FLAGS_ALWAYS_CONTINUE; } else if (!strcmp(s, "ignore_purpose")) { ctx->verify_flags |= LUASEC_VERIFY_FLAGS_IGNORE_PURPOSE; } else if (!strcmp(s, "crl_check")) { vflag |= X509_V_FLAG_CRL_CHECK; } else if (!strcmp(s, "crl_check_chain")) { vflag |= X509_V_FLAG_CRL_CHECK_ALL; } else if (!set_verify_flag(s, &flag)) { lua_pushboolean(L, 0); lua_pushstring(L, "invalid verify option"); return 2; } } SSL_CTX_set_verify(ctx->context, flag, ctx->verify_flags ? verify_cb : NULL); SSL_CTX_set_cert_verify_callback(ctx->context, luasec_verify, ctx); if(vflag) { X509_STORE *store = SSL_CTX_get_cert_store(ctx->context); X509_STORE_set_flags(store, vflag); } } 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; } /** * 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; } /** * 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}, {"rawcontext", raw_ctx}, {"loaddhparams", load_dhparams}, {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; }