#include "StdAfx.h" #include #include #ifdef ACL_UNIX # include # include /* gettimeofday, not POSIX */ #elif defined(WIN32) # include #endif #include #include #include #include /* OpenSSL library. */ #ifdef USE_TLS #include /* For the PRNG */ #endif #include "../attr/attr.h" #ifndef UCHAR_MAX #define UCHAR_MAX 0xff #endif /* TLS library. */ #ifdef USE_TLS #include "tls.h" /* TLS_MGR_SCACHE_ */ #include "tls_private.h" #include "tls_mgr.h" #include "tls_prng.h" #include "tls_scache.h" #include "tls_params.h" /* Application-specific. */ /* * Tunables. */ char *var_tls_rand_source; int var_tls_rand_bytes; int var_tls_reseed_period; int var_tls_prng_exch_period; int var_server_tls_loglevel; char *var_server_tls_scache_db; int var_server_tls_scache_timeout; int var_client_tls_loglevel; char *var_client_tls_scache_db; int var_client_tls_scache_timeout; char *var_tls_rand_exch_name; int var_tlsmgr_stand_alone = 0; /* * Bound the time that we are willing to wait for an I/O operation. This * produces better error messages than waiting until the watchdog timer * kills the process. */ #define TLS_MGR_TIMEOUT 10 /* * State for updating the PRNG exchange file. */ static TLS_PRNG_SRC *rand_exch; /* * State for seeding the internal PRNG from external source. */ static TLS_PRNG_SRC *rand_source_dev; static TLS_PRNG_SRC *rand_source_egd; static TLS_PRNG_SRC *rand_source_file; /* * The external entropy source type is encoded in the source name. The * obvious alternative is to have separate configuration parameters per * source type, so that one process can query multiple external sources. */ #define DEV_PREF "dev:" #define DEV_PREF_LEN (sizeof((DEV_PREF)) - 1) #define DEV_PATH(dev) ((dev) + EGD_PREF_LEN) #define EGD_PREF "egd:" #define EGD_PREF_LEN (sizeof((EGD_PREF)) - 1) #define EGD_PATH(egd) ((egd) + EGD_PREF_LEN) /* * State for TLS session caches. */ typedef struct { char *cache_label; /* cache short-hand name */ TLS_SCACHE *cache_info; /* cache handle */ int cache_active; /* cache status */ char **cache_db; /* main.cf parameter value */ int *cache_loglevel; /* main.cf parameter value */ int *cache_timeout; /* main.cf parameter value */ } TLSMGR_SCACHE; TLSMGR_SCACHE cache_table[] = { { TLS_MGR_SCACHE_SERVER, 0, 0, &var_server_tls_scache_db, &var_server_tls_loglevel, &var_server_tls_scache_timeout }, { TLS_MGR_SCACHE_CLIENT, 0, 0, &var_client_tls_scache_db, &var_client_tls_loglevel, &var_client_tls_scache_timeout }, { 0, 0, 0, 0, 0, 0 }, }; static ACL_EVENT *__eventp; /* * SLMs. */ #define STR(x) acl_vstring_str(x) #define LEN(x) ACL_VSTRING_LEN(x) #define STREQ(x, y) (strcmp((x), (y)) == 0) /* tlsmgr_prng_exch_event - update PRNG exchange file */ static void tlsmgr_prng_exch_event(int event_type acl_unused, ACL_EVENT *event acl_unused, void *dummy) { const char *myname = "tlsmgr_prng_exch_event"; unsigned char randbyte; int next_period; struct acl_stat st; if (acl_msg_verbose) acl_msg_info("%s: update PRNG exchange file", myname); /* * Sanity check. If the PRNG exchange file was removed, there is no point * updating it further. Restart the process and update the new file. */ if (acl_fstat(rand_exch->fd.file, &st) < 0) acl_msg_fatal("%s: cannot fstat() the PRNG exchange file: %s", myname, acl_last_serror()); if (st.st_nlink == 0) { acl_msg_warn("%s: PRNG exchange file was removed -- exiting to reopen", myname); sleep(1); exit(0); } tls_prng_exch_update(rand_exch); /* * Make prediction difficult for outsiders and calculate the time for the * next execution randomly. */ RAND_bytes(&randbyte, 1); next_period = (var_tls_prng_exch_period * randbyte) / UCHAR_MAX; acl_event_request_timer(__eventp, tlsmgr_prng_exch_event, dummy, next_period, 0); } /* tlsmgr_reseed_event - re-seed the internal PRNG pool */ static void tlsmgr_reseed_event(int event_type acl_unused, ACL_EVENT *event acl_unused, void *dummy) { const char *myname = "tlsmgr_reseed_event"; int next_period; unsigned char randbyte; int must_exit = 0; /* * Reseed the internal PRNG from external source. Errors are recoverable. * We simply restart and reconnect without making a fuss. This is OK * because we do require that exchange file updates succeed. The exchange * file is the only entropy source that really matters in the long term. * * If the administrator specifies an external randomness source that we * could not open upon start-up, restart to see if we can open it now * (and log a nagging warning if we can't). */ if (*var_tls_rand_source) { /* * Source is a random device. */ if (rand_source_dev) { if (tls_prng_dev_read(rand_source_dev, var_tls_rand_bytes) <= 0) { acl_msg_info("%s: cannot read from entropy device %s: %s -- " "exiting to reopen", myname, DEV_PATH(var_tls_rand_source), acl_last_serror()); must_exit = 1; } } /* * Source is an EGD compatible socket. */ else if (rand_source_egd) { if (tls_prng_egd_read(rand_source_egd, var_tls_rand_bytes) <= 0) { acl_msg_info("%s: lost connection to EGD server %s -- " "exiting to reconnect", myname, EGD_PATH(var_tls_rand_source)); must_exit = 1; } } /* * Source is a regular file. Read the content once and close the * file. */ else if (rand_source_file) { if (tls_prng_file_read(rand_source_file, var_tls_rand_bytes) <= 0) acl_msg_warn("%s: cannot read from entropy file %s: %s", myname, var_tls_rand_source, acl_last_serror()); tls_prng_file_close(rand_source_file); rand_source_file = 0; var_tls_rand_source[0] = 0; } /* * Could not open the external source upon start-up. See if we can * open it this time. Save PRNG state before we exit. */ else { acl_msg_info("exiting to reopen external entropy source %s", var_tls_rand_source); must_exit = 1; } } /* * Save PRNG state in case we must exit. */ if (must_exit) { if (rand_exch) tls_prng_exch_update(rand_exch); sleep(1); acl_msg_info("exit now"); exit(0); } /* * Make prediction difficult for outsiders and calculate the time for the * next execution randomly. */ RAND_bytes(&randbyte, 1); next_period = (var_tls_reseed_period * randbyte) / UCHAR_MAX; acl_event_request_timer(__eventp, tlsmgr_reseed_event, dummy, next_period, 0); } /* tlsmgr_cache_run_event - start TLS session cache scan */ static void tlsmgr_cache_run_event(int event_type acl_unused, ACL_EVENT *event acl_unused, void *ctx) { const char *myname = "tlsmgr_cache_run_event"; TLSMGR_SCACHE *cache = (TLSMGR_SCACHE *) ctx; /* * This routine runs when it is time for another TLS session cache scan. * Make sure this routine gets called again in the future. * * Don't start a new scan when the timer goes off while cache cleanup is * still in progress. */ if (cache->cache_info->verbose) acl_msg_info("%s: start TLS %s session cache cleanup", myname, cache->cache_label); if (cache->cache_active == 0) cache->cache_active = tls_scache_sequence(cache->cache_info, DICT_SEQ_FUN_FIRST, TLS_SCACHE_SEQUENCE_NOTHING); acl_event_request_timer(__eventp, tlsmgr_cache_run_event, (char *) cache, cache->cache_info->timeout, 0); } /* tlsmgr_loop - TLS manager main loop */ static int tlsmgr_loop(char *unused_name acl_unused, char **unused_argv acl_unused) { struct timeval tv; int active = 0; TLSMGR_SCACHE *ent; /* * Update the PRNG pool with the time of day. We do it here after every * event (including internal timer events and external client request * events), instead of doing it in individual event call-back routines. */ gettimeofday(&tv, 0); RAND_seed(&tv, sizeof(struct timeval)); /* * This routine runs as part of the event handling loop, after the event * manager has delivered a timer or I/O event, or after it has waited for * a specified amount of time. The result value of tlsmgr_loop() * specifies how long the event manager should wait for the next event. * * We use this loop to interleave TLS session cache cleanup with other * activity. Interleaved processing is needed when we use a client-server * protocol for entropy and session state exchange with client(8) and * server(8) processes. */ #define DONT_WAIT 0 #define WAIT_FOR_EVENT (-1) for (ent = cache_table; ent->cache_label; ++ent) { if (ent->cache_info && ent->cache_active) active |= ent->cache_active = tls_scache_sequence(ent->cache_info, DICT_SEQ_FUN_NEXT, TLS_SCACHE_SEQUENCE_NOTHING); } return (active ? DONT_WAIT : WAIT_FOR_EVENT); } /* tlsmgr_request_receive - receive request */ static int tlsmgr_request_receive(ACL_VSTREAM *client_stream, ACL_VSTRING *request) { const char *myname = "tlsmgr_request_receive"; int count; /* * Kluge: choose the protocol depending on the request size. */ if (acl_read_wait(ACL_VSTREAM_SOCK(client_stream), var_ipc_timeout) < 0) { acl_msg_warn("%s: timeout while waiting for data from %s", myname, ACL_VSTREAM_PATH(client_stream)); return (-1); } if ((count = acl_peekfd(ACL_VSTREAM_SOCK(client_stream))) < 0) { acl_msg_warn("%s: cannot examine read buffer of %s: %s", myname, ACL_VSTREAM_PATH(client_stream), acl_last_serror()); return (-1); } /* * Short request: master trigger. Use the string+null protocol. */ if (count <= 2) { if (acl_vstring_gets_null(request, client_stream) == ACL_VSTREAM_EOF) { acl_msg_warn("%s: end-of-input while reading request from %s: %s", myname, ACL_VSTREAM_PATH(client_stream), acl_last_serror()); return (-1); } } /* * Long request: real tlsmgr client. Use the attribute list protocol. */ else { if (attr_scan(client_stream, ATTR_FLAG_MORE | ATTR_FLAG_STRICT, ATTR_TYPE_STR, TLS_MGR_ATTR_REQ, request, ATTR_TYPE_END) != 1) { return (-1); } } return (0); } /* tlsmgr_service - respond to external request */ static void tlsmgr_service(ACL_VSTREAM *client_stream, char *unused_service acl_unused, char **argv acl_unused) { const char *myname = "tlsmgr_service"; static ACL_VSTRING *request = 0; static ACL_VSTRING *cache_type = 0; static ACL_VSTRING *cache_id = 0; static ACL_VSTRING *buffer = 0; int len; static char wakeup[] = { /* master wakeup request */ TRIGGER_REQ_WAKEUP, 0, }; TLSMGR_SCACHE *ent; int status = TLS_MGR_STAT_FAIL; /* * Sanity check. This service takes no command-line arguments. */ if (var_tlsmgr_stand_alone && argv[0]) acl_msg_fatal("unexpected command-line argument: %s", argv[0]); /* * Initialize. We're select threaded, so we can use static buffers. */ if (request == 0) { request = acl_vstring_alloc(10); cache_type = acl_vstring_alloc(10); cache_id = acl_vstring_alloc(10); buffer = acl_vstring_alloc(10); } /* * This routine runs whenever a client connects to the socket dedicated * to the tlsmgr service (including wake up events sent by the master). * All connection-management stuff is handled by the common code in * multi_server.c. */ if (tlsmgr_request_receive(client_stream, request) == 0) { /* * Load session from cache. */ if (STREQ(STR(request), TLS_MGR_REQ_LOOKUP)) { if (attr_scan(client_stream, ATTR_FLAG_STRICT, ATTR_TYPE_STR, TLS_MGR_ATTR_CACHE_TYPE, cache_type, ATTR_TYPE_STR, TLS_MGR_ATTR_CACHE_ID, cache_id, ATTR_TYPE_END) == 2) { for (ent = cache_table; ent->cache_label; ++ent) if (strcmp(ent->cache_label, STR(cache_type)) == 0) break; if (ent->cache_label == 0) { acl_msg_warn("%s: bogus cache type \"%s\" in \"%s\" request", myname, STR(cache_type), TLS_MGR_REQ_LOOKUP); ACL_VSTRING_RESET(buffer); } else if (ent->cache_info == 0) { /* * Cache type valid, but not enabled */ ACL_VSTRING_RESET(buffer); } else { status = tls_scache_lookup(ent->cache_info, STR(cache_id), buffer) ? TLS_MGR_STAT_OK : TLS_MGR_STAT_ERR; } } attr_print(client_stream, ATTR_FLAG_NONE, ATTR_TYPE_INT, TLS_ATTR_STATUS, status, ATTR_TYPE_DATA, TLS_MGR_ATTR_SESSION, LEN(buffer), STR(buffer), ATTR_TYPE_END); } /* * Save session to cache. */ else if (STREQ(STR(request), TLS_MGR_REQ_UPDATE)) { if (attr_scan(client_stream, ATTR_FLAG_STRICT, ATTR_TYPE_STR, TLS_MGR_ATTR_CACHE_TYPE, cache_type, ATTR_TYPE_STR, TLS_MGR_ATTR_CACHE_ID, cache_id, ATTR_TYPE_DATA, TLS_MGR_ATTR_SESSION, buffer, ATTR_TYPE_END) == 3) { for (ent = cache_table; ent->cache_label; ++ent) if (strcmp(ent->cache_label, STR(cache_type)) == 0) break; if (ent->cache_label == 0) { acl_msg_warn("%s: bogus cache type \"%s\" in \"%s\" request", myname, STR(cache_type), TLS_MGR_REQ_UPDATE); } else if (ent->cache_info != 0) { status = 0; tls_scache_update(ent->cache_info, STR(cache_id), STR(buffer), LEN(buffer)) ? TLS_MGR_STAT_OK : TLS_MGR_STAT_ERR; } } attr_print(client_stream, ATTR_FLAG_NONE, ATTR_TYPE_INT, TLS_ATTR_STATUS, status, ATTR_TYPE_END); } /* * Delete session from cache. */ else if (STREQ(STR(request), TLS_MGR_REQ_DELETE)) { if (attr_scan(client_stream, ATTR_FLAG_STRICT, ATTR_TYPE_STR, TLS_MGR_ATTR_CACHE_TYPE, cache_type, ATTR_TYPE_STR, TLS_MGR_ATTR_CACHE_ID, cache_id, ATTR_TYPE_END) == 2) { for (ent = cache_table; ent->cache_label; ++ent) if (strcmp(ent->cache_label, STR(cache_type)) == 0) break; if (ent->cache_label == 0) { acl_msg_warn("%s: bogus cache type \"%s\" in \"%s\" request", myname, STR(cache_type), TLS_MGR_REQ_DELETE); } else if (ent->cache_info != 0) { status = tls_scache_delete(ent->cache_info, STR(cache_id)) ? TLS_MGR_STAT_OK : TLS_MGR_STAT_ERR; } } attr_print(client_stream, ATTR_FLAG_NONE, ATTR_TYPE_INT, TLS_ATTR_STATUS, status, ATTR_TYPE_END); } /* * Entropy request. */ else if (STREQ(STR(request), TLS_MGR_REQ_SEED)) { if (attr_scan(client_stream, ATTR_FLAG_STRICT, ATTR_TYPE_INT, TLS_MGR_ATTR_SIZE, &len, ATTR_TYPE_END) == 1) { ACL_VSTRING_RESET(buffer); if (len <= 0 || len > 255) { acl_msg_warn("%s: bogus seed length \"%d\" in \"%s\" request", myname, len, TLS_MGR_REQ_SEED); } else { ACL_VSTRING_SPACE(buffer, len); RAND_bytes((unsigned char *) STR(buffer), len); ACL_VSTRING_AT_OFFSET(buffer, len); /* XXX not part of the * official interface */ status = TLS_MGR_STAT_OK; } } attr_print(client_stream, ATTR_FLAG_NONE, ATTR_TYPE_INT, TLS_ATTR_STATUS, status, ATTR_TYPE_DATA, TLS_MGR_ATTR_SEED, LEN(buffer), STR(buffer), ATTR_TYPE_END); } /* * Caching policy request. */ else if (STREQ(STR(request), TLS_MGR_REQ_POLICY)) { int cachable = 0; if (attr_scan(client_stream, ATTR_FLAG_STRICT, ATTR_TYPE_STR, TLS_MGR_ATTR_CACHE_TYPE, cache_type, ATTR_TYPE_END) == 1) { for (ent = cache_table; ent->cache_label; ++ent) if (strcmp(ent->cache_label, STR(cache_type)) == 0) break; if (ent->cache_label == 0) { acl_msg_warn("%s: bogus cache type \"%s\" in \"%s\" request", myname, STR(cache_type), TLS_MGR_REQ_POLICY); } else { cachable = (ent->cache_info != 0) ? 1 : 0; status = TLS_MGR_STAT_OK; } } attr_print(client_stream, ATTR_FLAG_NONE, ATTR_TYPE_INT, TLS_ATTR_STATUS, status, ATTR_TYPE_INT, TLS_MGR_ATTR_CACHABLE, cachable, ATTR_TYPE_END); } /* * Master trigger. Normally, these triggers arrive only after some * other process requested the tlsmgr's service. The purpose is to * restart the tlsmgr after it aborted due to a fatal run-time error, * so that it can continue its housekeeping even while nothing is * using TLS. * * XXX Which begs the question, if TLS isn't used often, do we need a * tlsmgr background process? It could terminate when the session * caches are empty. */ else if (var_tlsmgr_stand_alone && STREQ(STR(request), wakeup)) { if (acl_msg_verbose) acl_msg_info("received master trigger"); #ifdef ACL_UNIX acl_multi_server_disconnect(client_stream); #endif return; /* NOT: acl_vstream_fflush */ } /* * protocol error */ else { attr_print(client_stream, ATTR_FLAG_NONE, ATTR_TYPE_INT, TLS_ATTR_STATUS, TLS_MGR_STAT_FAIL, ATTR_TYPE_END); } } /* * Protocol error. */ else { attr_print(client_stream, ATTR_FLAG_NONE, ATTR_TYPE_INT, TLS_ATTR_STATUS, TLS_MGR_STAT_FAIL, ATTR_TYPE_END); } acl_vstream_fflush(client_stream); } /* tlsmgr_pre_init - pre-jail initialization */ static void tlsmgr_pre_init(char *unused_name acl_unused, char **unused_argv acl_unused) { const char *myname = "tlsmgr_pre_init"; char *path; struct timeval tv; TLSMGR_SCACHE *ent; ACL_HTABLE *dup_filter; const char *dup_label; /* * If nothing else works then at least this will get us a few bits of * entropy. * * XXX This is our first call into the OpenSSL library. We should find out * if this can be moved to the post-jail initialization phase, without * breaking compatibility with existing installations. */ #ifdef WIN32 # define getpid _getpid #endif gettimeofday(&tv, 0); tv.tv_sec ^= getpid(); RAND_seed(&tv, sizeof(struct timeval)); /* * Open the external entropy source. We will not be able to open it again * after we are sent to chroot jail, so we keep it open. Errors are not * fatal. The exchange file (see below) is the only entropy source that * really matters in the long run. * * Security note: we open the entropy source while privileged, but we don't * access the source until after we release privileges. This way, none of * the OpenSSL code gets to execute while we are privileged. */ if (*var_tls_rand_source) { /* * Source is a random device. */ if (!strncmp(var_tls_rand_source, DEV_PREF, DEV_PREF_LEN)) { path = DEV_PATH(var_tls_rand_source); rand_source_dev = tls_prng_dev_open(path, TLS_MGR_TIMEOUT); if (rand_source_dev == 0) acl_msg_warn("%s: cannot open entropy device %s: %s", myname, path, acl_last_serror()); } /* * Source is an EGD compatible socket. */ else if (!strncmp(var_tls_rand_source, EGD_PREF, EGD_PREF_LEN)) { path = EGD_PATH(var_tls_rand_source); rand_source_egd = tls_prng_egd_open(path, TLS_MGR_TIMEOUT); if (rand_source_egd == 0) acl_msg_warn("%s: cannot connect to EGD server %s: %s", myname, path, acl_last_serror()); } /* * Source is regular file. We read this only once. */ else { rand_source_file = tls_prng_file_open(var_tls_rand_source, TLS_MGR_TIMEOUT); } } else { acl_msg_warn("%s: no entropy source specified with parameter %s", myname, VAR_TLS_RAND_SOURCE); acl_msg_warn("%s: encryption keys etc. may be predictable", myname); } /* * Open the PRNG exchange file before going to jail, but don't use root * privileges. Start the exchange file read/update pseudo thread after * dropping privileges. */ if (*var_tls_rand_exch_name) { rand_exch = tls_prng_exch_open(var_tls_rand_exch_name); if (rand_exch == 0) acl_msg_fatal("cannot open PRNG exchange file %s: %s", var_tls_rand_exch_name, acl_last_serror()); } /* * Open the session cache files and discard old information before going * to jail, but don't use root privilege. Start the cache maintenance * pseudo threads after dropping privileges. */ if (!var_tlsmgr_stand_alone) tls_scache_init(); dup_filter = acl_htable_create(sizeof(cache_table) / sizeof(cache_table[0]), 0); for (ent = cache_table; ent->cache_label; ++ent) { if (**ent->cache_db) { if ((dup_label = acl_htable_find(dup_filter, *ent->cache_db)) != 0) acl_msg_fatal("do not use the same TLS cache file %s for %s and %s", *ent->cache_db, dup_label, ent->cache_label); acl_htable_enter(dup_filter, *ent->cache_db, ent->cache_label); ent->cache_info = tls_scache_open(*ent->cache_db, ent->cache_label, *ent->cache_loglevel >= 2, *ent->cache_timeout); } } acl_htable_free(dup_filter, NULL); } /* tlsmgr_post_init - post-jail initialization */ static void tlsmgr_post_init(char *unused_name acl_unused, char **unused_argv acl_unused) { const char *myname = "tlsmgr_post_init"; TLSMGR_SCACHE *ent; #define NULL_EVENT (0) #define NULL_CONTEXT ((char *) 0) #ifdef ACL_UNIX if (var_tlsmgr_stand_alone) __eventp = acl_multi_server_event(); else #endif __eventp = acl_event_new_select(1, 0); if (__eventp == NULL) acl_msg_fatal("%s: __eventp null", myname); if (var_tlsmgr_stand_alone) { /* * This routine runs after the skeleton code has entered the chroot jail, * but before any client requests are serviced. Prevent automatic process * suicide after a limited number of client requests or after a limited * amount of idle time. */ #ifdef ACL_UNIX acl_var_multi_use_limit = 0; acl_var_multi_idle_limit = 0; #endif } /* * Start the internal PRNG re-seeding pseudo thread first. */ if (*var_tls_rand_source) { if (var_tls_reseed_period > INT_MAX / UCHAR_MAX) var_tls_reseed_period = INT_MAX / UCHAR_MAX; tlsmgr_reseed_event(NULL_EVENT, NULL, NULL_CONTEXT); } /* * Start the exchange file read/update pseudo thread. */ if (*var_tls_rand_exch_name) { if (var_tls_prng_exch_period > INT_MAX / UCHAR_MAX) var_tls_prng_exch_period = INT_MAX / UCHAR_MAX; tlsmgr_prng_exch_event(NULL_EVENT, NULL, NULL_CONTEXT); } /* * Start the cache maintenance pseudo threads last. Strictly speaking * there is nothing to clean up after we truncate the database to zero * length, but early cleanup makes verbose logging more informative (we * get positive confirmation that the cleanup threads are running). */ for (ent = cache_table; ent->cache_label; ++ent) if (ent->cache_info) tlsmgr_cache_run_event(NULL_EVENT, NULL, (char *) ent); } /* tlsmgr_before_exit - save PRNG state before exit */ static void tlsmgr_before_exit(char *unused_service_name acl_unused, char **unused_argv acl_unused) { /* * Save state before we exit after "postfix reload". */ if (rand_exch) tls_prng_exch_update(rand_exch); } #ifdef ACL_UNIX void tlsmgr_alone_start(int argc, char *argv[]) { static ACL_CONFIG_STR_TABLE str_table[] = { { VAR_TLS_RAND_SOURCE, DEF_TLS_RAND_SOURCE, &var_tls_rand_source }, { VAR_TLS_RAND_EXCH_NAME, DEF_TLS_RAND_EXCH_NAME, &var_tls_rand_exch_name }, { VAR_SERVER_TLS_SCACHE_DB, DEF_SERVER_TLS_SCACHE_DB, &var_server_tls_scache_db }, { VAR_CLIENT_TLS_SCACHE_DB, DEF_CLIENT_TLS_SCACHE_DB, &var_client_tls_scache_db }, { 0, 0, 0 }, }; static ACL_CONFIG_INT_TABLE int_table[] = { { VAR_TLS_RAND_BYTES, DEF_TLS_RAND_BYTES, &var_tls_rand_bytes, 1, 0 }, { VAR_SERVER_TLS_LOGLEVEL, DEF_SERVER_TLS_LOGLEVEL, &var_server_tls_loglevel, 0, 0 }, { VAR_CLIENT_TLS_LOGLEVEL, DEF_CLIENT_TLS_LOGLEVEL, &var_client_tls_loglevel, 0, 0 }, { VAR_TLS_RESEED_PERIOD, DEF_TLS_RESEED_PERIOD, &var_tls_reseed_period, 1, 0 }, { VAR_TLS_PRNG_UPD_PERIOD, DEF_TLS_PRNG_UPD_PERIOD, &var_tls_prng_exch_period, 1, 0 }, { VAR_SERVER_TLS_SCACHTIME, DEF_SERVER_TLS_SCACHTIME, &var_server_tls_scache_timeout, 0, 0 }, { VAR_CLIENT_TLS_SCACHTIME, DEF_CLIENT_TLS_SCACHTIME, &var_client_tls_scache_timeout, 0, 0 }, { 0, 0, 0, 0, 0 }, }; /* * Use the multi service skeleton, and require that no-one else is * monitoring our service port while this process runs. */ acl_multi_server_main(argc, argv, tlsmgr_service, ACL_MASTER_SERVER_INT_TABLE, int_table, ACL_MASTER_SERVER_STR_TABLE, str_table, ACL_MASTER_SERVER_PRE_INIT, tlsmgr_pre_init, ACL_MASTER_SERVER_POST_INIT, tlsmgr_post_init, ACL_MASTER_SERVER_EXIT, tlsmgr_before_exit, ACL_MASTER_SERVER_LOOP, tlsmgr_loop, ACL_MASTER_SERVER_SOLITARY, 0); } #endif /* ACL_UNIX */ static void client_ready_callback(int event_type acl_unused, ACL_EVENT *event, ACL_VSTREAM *stream acl_unused, void *ctx) { ACL_VSTREAM *client_stream = (ACL_VSTREAM*) ctx; tlsmgr_service(client_stream, NULL, NULL); acl_event_enable_read(event, client_stream, 0, client_ready_callback, client_stream); } static void listen_callback(int event_type acl_unused, ACL_EVENT *event, ACL_VSTREAM *stream, void *ctx acl_unused) { const char *myname = "listen_callback"; ACL_VSTREAM *client_stream; client_stream = acl_vstream_accept(stream, NULL, 0); if (client_stream == NULL) { acl_msg_warn("%s(%d): accept error(%s)", myname, __LINE__, acl_last_serror()); return; } acl_event_enable_read(event, client_stream, 0, client_ready_callback, client_stream); } static void *tlsmgr_thread_main(void *arg) { ACL_VSTREAM *server_stream = (ACL_VSTREAM*) arg; int delay_sec; acl_event_enable_listen(__eventp, server_stream, 0, listen_callback, server_stream); while (1) { delay_sec = tlsmgr_loop(NULL, NULL); acl_event_set_delay_sec(__eventp, delay_sec); acl_event_loop(__eventp); } acl_vstream_close(server_stream); tlsmgr_before_exit(NULL, NULL); return (NULL); } void tlsmgr_local_start(const char *addr) { const char *myname = "tlsmgr_local_start"; static ACL_CONFIG_STR_TABLE str_table[] = { { VAR_TLS_RAND_SOURCE, DEF_TLS_RAND_SOURCE, &var_tls_rand_source }, { VAR_TLS_RAND_EXCH_NAME, DEF_TLS_RAND_EXCH_NAME, &var_tls_rand_exch_name }, { VAR_SERVER_TLS_SCACHE_DB, DEF_SERVER_TLS_SCACHE_DB, &var_server_tls_scache_db }, { VAR_CLIENT_TLS_SCACHE_DB, DEF_CLIENT_TLS_SCACHE_DB, &var_client_tls_scache_db }, { 0, 0, 0 }, }; static ACL_CONFIG_INT_TABLE int_table[] = { { VAR_TLS_RAND_BYTES, DEF_TLS_RAND_BYTES, &var_tls_rand_bytes, 1, 0 }, { VAR_SERVER_TLS_LOGLEVEL, DEF_SERVER_TLS_LOGLEVEL, &var_server_tls_loglevel, 0, 0 }, { VAR_CLIENT_TLS_LOGLEVEL, DEF_CLIENT_TLS_LOGLEVEL, &var_client_tls_loglevel, 0, 0 }, { VAR_TLS_RESEED_PERIOD, DEF_TLS_RESEED_PERIOD, &var_tls_reseed_period, 1, 0 }, { VAR_TLS_PRNG_UPD_PERIOD, DEF_TLS_PRNG_UPD_PERIOD, &var_tls_prng_exch_period, 1, 0 }, { VAR_SERVER_TLS_SCACHTIME, DEF_SERVER_TLS_SCACHTIME, &var_server_tls_scache_timeout, 0, 0 }, { VAR_CLIENT_TLS_SCACHTIME, DEF_CLIENT_TLS_SCACHTIME, &var_client_tls_scache_timeout, 0, 0 }, { 0, 0, 0, 0, 0 }, }; acl_pthread_t tid; acl_pthread_attr_t attr; char def_addr[] = "127.0.0.1:0"; ACL_VSTREAM *server_stream; acl_get_app_conf_str_table(str_table); acl_get_app_conf_int_table(int_table); server_stream = acl_vstream_listen((addr == NULL || *addr == 0) ? def_addr : addr, 128); if (server_stream == NULL) { tlsmgr_before_exit(NULL, NULL); acl_msg_error("%s(%d): listen addr(%s) error(%s)", myname, __LINE__, addr, acl_last_serror()); return; } snprintf(var_tlsmgr_service, sizeof(var_tlsmgr_service), "inet:%s", ACL_VSTREAM_LOCAL(server_stream)); tlsmgr_pre_init(NULL, NULL); tlsmgr_post_init(NULL, NULL); acl_pthread_attr_init(&attr); #ifdef ACL_UNIX acl_pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); #else acl_pthread_attr_setdetachstate(&attr, ACL_PTHREAD_CREATE_DETACHED); #endif acl_pthread_create(&tid, &attr, tlsmgr_thread_main, server_stream); } #endif /* USE_TLS */