/* * anylike * * anylike is an IKEv2 Implementation written in Lua and C. It's main * design goal is to provide anytun and uanytun or any other SATP * implementation with a key exchange mechanism but it should also be * possible to use anylike as key exchange daemon for IPSec security * associations. The use of Lua guarantees that anylike is easily * portable to many platforms including very small ones like wireless * routers. * * * Copyright (C) 2009-2010 Markus Grueneis * Christian Pointner * * This file is part of anylike. * * anylike is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * any later version. * * anylike is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with anylike. If not, see . */ #include #include #include #include #include #include #include #include "datatypes.h" #include "options.h" #include "string_list.h" #include "log.h" #include "l_log.h" #include "l_crypt.h" #include "l_sig_handler.h" #ifndef USE_OPENSSL #include #endif #ifndef WINVER #include "daemon.h" #endif #ifndef USE_OPENSSL #define MIN_GNUTLS_VERSION "2.6.4" void anylike_gnutls_log_func(int level, const char* msg) { log_printf(DEBUG, msg); // TODO: convert level to proper log priority } int init_gnutls() { int ret = gnutls_global_init(); if(ret != GNUTLS_E_SUCCESS) { log_printf(ERROR, "gnutls_global_init() returned with error: %s", gnutls_strerror(ret)); return -1; } if(!gnutls_check_version(MIN_GNUTLS_VERSION)) { log_printf(NOTICE, "invalid Version of gnutls, should be >= %s but is %s", MIN_GNUTLS_VERSION, gnutls_check_version(NULL)); gnutls_global_deinit(); return -1; } gnutls_global_set_log_function(anylike_gnutls_log_func); gnutls_global_set_log_level(11); // TODO: find a better level (this is very talky) log_printf(NOTICE, "gnutls init finished"); return 0; } #endif void cleanup_crypt() { #ifndef USE_OPENSSL gnutls_global_deinit(); #endif } extern const char anylike_lua_bytecode[]; extern const uint32_t anylike_lua_bytecode_len; #define LUA_MAIN_LOOP_FUNC "main_loop" #if LUA_VERSION_NUM > 501 static const luaL_Reg anylike_lualibs[] = { {"_G", luaopen_base}, {LUA_LOADLIBNAME, luaopen_package}, {LUA_TABLIBNAME, luaopen_table}, {LUA_STRLIBNAME, luaopen_string}, {LUA_MATHLIBNAME, luaopen_math}, {LUA_LOGLIBNAME, luaopen_log}, {LUA_SIGNALLIBNAME, luaopen_signal}, {LUA_CRYPTLIBNAME, luaopen_crypt}, {NULL, NULL} }; #else static const luaL_Reg anylike_lualibs[] = { {"", luaopen_base}, {LUA_LOADLIBNAME, luaopen_package}, {LUA_TABLIBNAME, luaopen_table}, {LUA_STRLIBNAME, luaopen_string}, {LUA_MATHLIBNAME, luaopen_math}, {LUA_LOGLIBNAME, luaopen_log}, {LUA_SIGNALLIBNAME, luaopen_signal}, {LUA_CRYPTLIBNAME, luaopen_crypt}, {NULL, NULL} }; #endif int init_main_loop(lua_State *L) { const luaL_Reg *lib = anylike_lualibs; #if LUA_VERSION_NUM > 501 for (; lib->func; lib++) { luaL_requiref(L, lib->name, lib->func, 1); lua_pop(L, 1); } #else for (; lib->func; lib++) { lua_pushcfunction(L, lib->func); lua_pushstring(L, lib->name); lua_call(L, 1, 0); } #endif int ret = luaL_loadbuffer(L, anylike_lua_bytecode, anylike_lua_bytecode_len, "anylike"); if(ret) { const char* err_str = luaL_checkstring(L, -1); switch(ret) { case LUA_ERRSYNTAX: log_printf(ERROR, "luaL_loadbuffer() syntax error: %s", err_str); break; case LUA_ERRMEM: log_printf(ERROR, "luaL_loadbuffer() malloc error: %s", err_str); break; default: log_printf(ERROR, "luaL_loadbuffer() unknown error: %s", err_str); break; } return -1; } ret = lua_pcall(L, 0, 0, 0); if(ret) { const char* err_str = luaL_checkstring(L, -1); switch(ret) { case LUA_ERRRUN: log_printf(ERROR, "lua_pcall() runtime error: %s", err_str); break; case LUA_ERRMEM: log_printf(ERROR, "lua_pcall() malloc error: %s", err_str); break; case LUA_ERRERR: log_printf(ERROR, "lua_pcall() error at error handler function: %s", err_str); break; } return -1; } return 0; } int call_main_loop(lua_State* L, options_t* opt) { lua_getglobal(L, LUA_MAIN_LOOP_FUNC); if(!lua_isfunction(L, -1)) { log_printf(ERROR, "there is no function '%s' inside anylike bytecode", LUA_MAIN_LOOP_FUNC); return -1; }; options_lua_push(opt, L); int ret = lua_pcall(L, 1, LUA_MULTRET, 0); if(ret) { const char* err_str = luaL_checkstring(L, -1); switch(ret) { case LUA_ERRRUN: log_printf(ERROR, "lua_pcall(%s) runtime error: %s", LUA_MAIN_LOOP_FUNC, err_str); break; case LUA_ERRMEM: log_printf(ERROR, "lua_pcall(%s) malloc error: %s", LUA_MAIN_LOOP_FUNC, err_str); break; case LUA_ERRERR: log_printf(ERROR, "lua_pcall(%s) error at error handler function: %s", LUA_MAIN_LOOP_FUNC, err_str); break; } return -1; } int n = lua_gettop(L); log_printf(DEBUG, "%s returned %d values", LUA_MAIN_LOOP_FUNC, n); int i; for (i = 1; i <= n; i++) log_printf(DEBUG, "return value [%d] = '%s'", i, luaL_checkstring(L, i)); ret = lua_tointeger(L, 1); return ret; } int main_loop(options_t* opt) { lua_State *L; L = luaL_newstate(); if(!L) { log_printf(ERROR, "error creating lua state"); return -1; } int ret = init_main_loop(L); if(!ret) ret = call_main_loop(L, opt); lua_close(L); return ret; } int main(int argc, char* argv[]) { log_init(); options_t opt; int ret = options_parse(&opt, argc, argv); if(ret) { if(ret > 0) { fprintf(stderr, "syntax error near: %s\n\n", argv[ret]); } if(ret == -2) { fprintf(stderr, "memory error on options_parse, exitting\n"); } if(ret == -3) { options_print_version(); } if(ret != -2 && ret != -3) options_print_usage(); if(ret == -1 || ret == -3) ret = 0; options_clear(&opt); log_close(); exit(ret); } string_list_element_t* tmp = opt.log_targets_.first_; while(tmp) { ret = log_add_target(tmp->string_); if(ret) { switch(ret) { case -2: fprintf(stderr, "memory error on log_add_target, exitting\n"); break; case -3: fprintf(stderr, "unknown log target: '%s', exitting\n", tmp->string_); break; case -4: fprintf(stderr, "this log target is only allowed once: '%s', exitting\n", tmp->string_); break; default: fprintf(stderr, "syntax error near: '%s', exitting\n", tmp->string_); break; } options_clear(&opt); log_close(); exit(ret); } tmp = tmp->next_; } log_printf(NOTICE, "just started..."); options_parse_post(&opt); #ifndef WINVER priv_info_t priv; if(opt.username_) if(priv_init(&priv, opt.username_, opt.groupname_)) { options_clear(&opt); log_close(); exit(-1); } #endif #ifndef USE_OPENSSL ret = init_gnutls(); if(ret) { log_printf(ERROR, "error on gnutls initialization, exitting"); options_clear(&opt); log_close(); exit(ret); } #endif #ifndef WINVER FILE* pid_file = NULL; if(opt.pid_file_) { pid_file = fopen(opt.pid_file_, "w"); if(!pid_file) { log_printf(WARNING, "unable to open pid file: %s", strerror(errno)); } } if(opt.chroot_dir_) if(do_chroot(opt.chroot_dir_)) { cleanup_crypt(); options_clear(&opt); log_close(); exit(-1); } if(opt.username_) if(priv_drop(&priv)) { cleanup_crypt(); options_clear(&opt); log_close(); exit(-1); } if(opt.daemonize_) { pid_t oldpid = getpid(); daemonize(); log_printf(INFO, "running in background now (old pid: %d)", oldpid); } if(pid_file) { pid_t pid = getpid(); fprintf(pid_file, "%d", pid); fclose(pid_file); } #endif ret = main_loop(&opt); cleanup_crypt(); options_clear(&opt); if(!ret) log_printf(NOTICE, "normal shutdown"); else if(ret < 0) log_printf(NOTICE, "shutdown after error"); else log_printf(NOTICE, "shutdown after signal"); log_close(); return ret; }