From f21d51de3d1de5dc94e8ccd0fa7638dd3836cf75 Mon Sep 17 00:00:00 2001 From: zhengchao Date: Mon, 14 Jan 2019 18:23:46 +0600 Subject: 1. ssl增加no_verify_cert开关,可以关闭证书校验; 2. ssl_utils.cc改名为ssl_utils.cpp; 3. 暂时使用tcmalloc接管内存分配; 4. 原work thread选择算法存在bug,暂时改为轮询; 5. FieldStat状态输出暂时改为Field格式,便于观察实时性能,Future的状态输出暂时改为累计值; MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common/src/tfe_future.cpp | 2 +- platform/CMakeLists.txt | 3 +- platform/src/proxy.cpp | 33 +- platform/src/ssl_stream.cpp | 16 +- platform/src/ssl_utils.cc | 2156 ------------------------ platform/src/ssl_utils.cpp | 2158 +++++++++++++++++++++++++ plugin/business/pangu-http/src/pangu_http.cpp | 2 +- vendor/CMakeLists.txt | 19 +- vendor/gperftools-2.7.tar.gz | Bin 0 -> 1523562 bytes 9 files changed, 2208 insertions(+), 2181 deletions(-) delete mode 100644 platform/src/ssl_utils.cc create mode 100644 platform/src/ssl_utils.cpp create mode 100644 vendor/gperftools-2.7.tar.gz diff --git a/common/src/tfe_future.cpp b/common/src/tfe_future.cpp index 042ff06..00488fd 100644 --- a/common/src/tfe_future.cpp +++ b/common/src/tfe_future.cpp @@ -64,7 +64,7 @@ void future_promise_library_init(const char* profile) int value=0; memset(&g_FP_instance,0,sizeof(g_FP_instance)); - g_FP_instance.favorite=FS_CALC_SPEED; + g_FP_instance.favorite=FS_CALC_CURRENT; strcpy(g_FP_instance.histogram_bins, FP_HISTOGRAM_BINS); if(profile!=NULL) { diff --git a/platform/CMakeLists.txt b/platform/CMakeLists.txt index 09ae435..a17c417 100644 --- a/platform/CMakeLists.txt +++ b/platform/CMakeLists.txt @@ -1,6 +1,6 @@ add_executable(tfe src/key_keeper.cpp src/kni_acceptor.cpp src/ssl_stream.cpp src/ssl_sess_cache.cpp src/ssl_trusted_cert_storage.cpp - src/ssl_utils.cc src/tcp_stream.cpp src/main.cpp src/proxy.cpp) + src/ssl_utils.cpp src/tcp_stream.cpp src/main.cpp src/proxy.cpp) target_include_directories(tfe PUBLIC ${CMAKE_CURRENT_LIST_DIR}/include/external) target_include_directories(tfe PRIVATE ${CMAKE_CURRENT_LIST_DIR}/include/internal) @@ -12,6 +12,7 @@ target_link_libraries(tfe pthread dl libevent-static libevent-static-openssl libevent-static-pthreads + gperftools-static MESA_handle_logger MESA_prof_load cjson diff --git a/platform/src/proxy.cpp b/platform/src/proxy.cpp index 2ba022f..910854b 100644 --- a/platform/src/proxy.cpp +++ b/platform/src/proxy.cpp @@ -75,7 +75,9 @@ struct tfe_thread_ctx * tfe_proxy_thread_ctx_acquire(struct tfe_proxy * ctx) { unsigned int min_thread_id = 0; unsigned int min_load = 0; - + static unsigned int counter=0; + counter++; + /* for (unsigned int tid = 0; tid < ctx->nr_work_threads; tid++) { struct tfe_thread_ctx * thread_ctx = ctx->work_threads[tid]; @@ -84,7 +86,8 @@ struct tfe_thread_ctx * tfe_proxy_thread_ctx_acquire(struct tfe_proxy * ctx) min_thread_id = min_load > thread_load ? tid : min_thread_id; min_load = min_load > thread_load ? thread_load : min_load; } - +*/ + min_thread_id=counter%ctx->nr_work_threads; ATOMIC_INC(&ctx->work_threads[min_thread_id]->load); return ctx->work_threads[min_thread_id]; } @@ -276,19 +279,19 @@ int tfe_proxy_config(struct tfe_proxy * proxy, const char * profile) static const char * __str_stat_spec_map[] = { [STAT_SIGPIPE] = "SIGPIPE", - [STAT_FD_OPEN_BY_KNI_ACCEPT] = "FdRcv", - [STAT_FD_CLOSE_BY_KNI_ACCEPT_FAIL] = "FdRcvFail", - [STAT_FD_INSTANT_CLOSE] = "FdClsInstant", - [STAT_FD_DEFER_CLOSE_IN_QUEUE] = "FdClsDefInQ", - [STAT_FD_DEFER_CLOSE_SUCCESS] = "FdClsDefSuc", - [STAT_STREAM_OPEN] = "StrOpen", - [STAT_STREAM_CLS] = "StrCls", - [STAT_STREAM_CLS_DOWN_EOF] = "StrDownEOF", - [STAT_STREAM_CLS_UP_EOF] = "StrUpEOF", - [STAT_STREAM_CLS_DOWN_ERR] = "StrDownErr", - [STAT_STREAM_CLS_UP_ERR] = "StrUpErr", - [STAT_STREAM_CLS_KILL] = "StrKill", - [STAT_STREAM_TCP_PLAIN] = "Plain", + [STAT_FD_OPEN_BY_KNI_ACCEPT] = "fd_rx", + [STAT_FD_CLOSE_BY_KNI_ACCEPT_FAIL] = "fd_rx_err", + [STAT_FD_INSTANT_CLOSE] = "fd_inst_cls", + [STAT_FD_DEFER_CLOSE_IN_QUEUE] = "fd_dfr_cls", + [STAT_FD_DEFER_CLOSE_SUCCESS] = "fd_dfr_clsd", + [STAT_STREAM_OPEN] = "stm_open", + [STAT_STREAM_CLS] = "stm_cls", + [STAT_STREAM_CLS_DOWN_EOF] = "dstm_eof", + [STAT_STREAM_CLS_UP_EOF] = "ustm_eof", + [STAT_STREAM_CLS_DOWN_ERR] = "dstm_err", + [STAT_STREAM_CLS_UP_ERR] = "ustm_err", + [STAT_STREAM_CLS_KILL] = "stm_kill", + [STAT_STREAM_TCP_PLAIN] = "plain", [STAT_STREAM_TCP_SSL] = "SSL", [TFE_STAT_MAX] = NULL }; diff --git a/platform/src/ssl_stream.cpp b/platform/src/ssl_stream.cpp index 62184ec..bb6d57b 100644 --- a/platform/src/ssl_stream.cpp +++ b/platform/src/ssl_stream.cpp @@ -121,7 +121,7 @@ struct ssl_mgr unsigned int no_tls12; unsigned int no_sessticket; unsigned int no_alpn; - + unsigned int no_cert_verify; CONST_SSL_METHOD * (* sslmethod)(void); //Parameter of SSL_CTX_new int ssl_min_version, ssl_max_version; char ssl_session_context[8]; @@ -312,7 +312,7 @@ void ssl_stat_init(struct ssl_mgr * mgr) { if(spec[i]!=NULL) { - mgr->fs_id[i]=FS_register(mgr->fs_handle, FS_STYLE_STATUS, FS_CALC_CURRENT,spec[i]); + mgr->fs_id[i]=FS_register(mgr->fs_handle, FS_STYLE_FIELD, FS_CALC_CURRENT,spec[i]); } } @@ -578,10 +578,9 @@ struct ssl_mgr * ssl_manager_init(const char * ini_profile, const char * section sizeof(mgr->default_ciphers), DFLT_CIPHERS); MESA_load_profile_uint_def(ini_profile, section, "no_session_ticket", &(mgr->no_sessticket), 0); MESA_load_profile_uint_def(ini_profile, section, "no_alpn", &(mgr->no_alpn), 0); + MESA_load_profile_uint_def(ini_profile, section, "no_cert_verify", &(mgr->no_cert_verify), 0); - - MESA_load_profile_uint_def(ini_profile, section, "session_cache_slots", &(mgr->cache_slots), 4 * 1024 * 1024); MESA_load_profile_uint_def(ini_profile, section, "session_cache_expire_seconds", &(mgr->sess_expire_seconds), 30 * 60); @@ -1033,8 +1032,15 @@ static void ssl_server_connected_eventcb(struct bufferevent * bev, short events, if(!SSL_session_reused(s_stream->ssl)) { - s_stream->is_peer_cert_verify_passed = ssl_trusted_cert_storage_verify_conn(s_stream->mgr->trust_CA_store, + if(mgr->no_cert_verify) + { + s_stream->is_peer_cert_verify_passed=1; + } + else + { + s_stream->is_peer_cert_verify_passed = ssl_trusted_cert_storage_verify_conn(s_stream->mgr->trust_CA_store, s_stream->ssl, error_str, sizeof(error_str)); + } if(s_stream->is_peer_cert_verify_passed) { //ONLY verified session is cacheable. diff --git a/platform/src/ssl_utils.cc b/platform/src/ssl_utils.cc deleted file mode 100644 index 3e882cd..0000000 --- a/platform/src/ssl_utils.cc +++ /dev/null @@ -1,2156 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * Print OpenSSL version and build-time configuration to standard error and - * return. - */ -void ssl_openssl_version(void) -{ - fprintf(stderr, "compiled against %s (%lx)\n", OPENSSL_VERSION_TEXT, (long unsigned int) OPENSSL_VERSION_NUMBER); - fprintf(stderr, "rtlinked against %s (%lx)\n", SSLeay_version(SSLEAY_VERSION), SSLeay()); - - if ((OPENSSL_VERSION_NUMBER ^ SSLeay()) & 0xfffff000L) - { - fprintf(stderr, "---------------------------------------" - "---------------------------------------\n"); - fprintf(stderr, "WARNING: OpenSSL version mismatch may " - "lead to crashes or other problems!\n"); - fprintf(stderr, "If there are multiple versions of " - "OpenSSL available, make sure to use\n"); - fprintf(stderr, "the same version of the library at " - "runtime as well as for compiling against.\n"); - fprintf(stderr, "---------------------------------------" - "---------------------------------------\n"); - } - -#ifdef LIBRESSL_VERSION_NUMBER - fprintf(stderr, "LibreSSL detected: %s (%lx)\n", - LIBRESSL_VERSION_TEXT, - (long unsigned int)LIBRESSL_VERSION_NUMBER); -#endif /* LIBRESSL_VERSION_NUMBER */ -#ifdef OPENSSL_IS_BORINGSSL - fprintf(stderr, "BoringSSL detected\n") -#endif /* OPENSSL_IS_BORINGSSL */ - -#ifndef OPENSSL_NO_TLSEXT - fprintf(stderr, "OpenSSL has support for TLS extensions\n" - "TLS Server Name Indication (SNI) supported\n"); -#else /* OPENSSL_NO_TLSEXT */ - fprintf(stderr, "OpenSSL has no support for TLS extensions\n" - "TLS Server Name Indication (SNI) not supported\n"); -#endif /* OPENSSL_NO_TLSEXT */ -#ifdef OPENSSL_THREADS -#ifndef OPENSSL_NO_THREADID - fprintf(stderr, "OpenSSL is thread-safe with THREADID\n"); -#else /* OPENSSL_NO_THREADID */ - fprintf(stderr, "OpenSSL is thread-safe without THREADID\n"); -#endif /* OPENSSL_NO_THREADID */ -#else /* !OPENSSL_THREADS */ - fprintf(stderr, "OpenSSL is not thread-safe\n"); -#endif /* !OPENSSL_THREADS */ -#ifdef SSL_MODE_RELEASE_BUFFERS - fprintf(stderr, "Using SSL_MODE_RELEASE_BUFFERS\n"); -#else /* !SSL_MODE_RELEASE_BUFFERS */ - fprintf(stderr, "Not using SSL_MODE_RELEASE_BUFFERS\n"); -#endif /* !SSL_MODE_RELEASE_BUFFERS */ -#if (OPENSSL_VERSION_NUMBER == 0x0090819fL) || \ - (OPENSSL_VERSION_NUMBER == 0x100000bfL) || \ - (OPENSSL_VERSION_NUMBER == 0x1000105fL) - fprintf(stderr, "Using direct access workaround when loading certs\n"); -#endif /* OpenSSL 0.9.8y, 1.0.0k or 1.0.1e */ - - fprintf(stderr, "SSL/TLS protocol availability: %s\n", - SSL_PROTO_SUPPORT_S); - - fprintf(stderr, "SSL/TLS algorithm availability:"); -#ifndef OPENSSL_NO_SHA0 - fprintf(stderr, " SHA0"); -#else /* !OPENSSL_NO_SHA0 */ - fprintf(stderr, " !SHA0"); -#endif /* !OPENSSL_NO_SHA0 */ -#ifndef OPENSSL_NO_RSA - fprintf(stderr, " RSA"); -#else /* !OPENSSL_NO_RSA */ - fprintf(stderr, " !RSA"); -#endif /* !OPENSSL_NO_RSA */ -#ifndef OPENSSL_NO_DSA - fprintf(stderr, " DSA"); -#else /* !OPENSSL_NO_DSA */ - fprintf(stderr, " !DSA"); -#endif /* !OPENSSL_NO_DSA */ -#ifndef OPENSSL_NO_ECDSA - fprintf(stderr, " ECDSA"); -#else /* !OPENSSL_NO_ECDSA */ - fprintf(stderr, " !ECDSA"); -#endif /* !OPENSSL_NO_ECDSA */ -#ifndef OPENSSL_NO_DH - fprintf(stderr, " DH"); -#else /* !OPENSSL_NO_DH */ - fprintf(stderr, " !DH"); -#endif /* !OPENSSL_NO_DH */ -#ifndef OPENSSL_NO_ECDH - fprintf(stderr, " ECDH"); -#else /* !OPENSSL_NO_ECDH */ - fprintf(stderr, " !ECDH"); -#endif /* !OPENSSL_NO_ECDH */ -#ifndef OPENSSL_NO_EC - fprintf(stderr, " EC"); -#else /* !OPENSSL_NO_EC */ - fprintf(stderr, " !EC"); -#endif /* !OPENSSL_NO_EC */ - fprintf(stderr, "\n"); - - fprintf(stderr, "OpenSSL option availability:"); -#ifdef SSL_OP_NO_COMPRESSION - fprintf(stderr, " SSL_OP_NO_COMPRESSION"); -#else /* !SSL_OP_NO_COMPRESSION */ - fprintf(stderr, " !SSL_OP_NO_COMPRESSION"); -#endif /* SSL_OP_NO_COMPRESSION */ -#ifdef SSL_OP_NO_TICKET - fprintf(stderr, " SSL_OP_NO_TICKET"); -#else /* !SSL_OP_NO_TICKET */ - fprintf(stderr, " !SSL_OP_NO_TICKET"); -#endif /* SSL_OP_NO_TICKET */ -#ifdef SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION - fprintf(stderr, " SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION"); -#else /* !SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION */ - fprintf(stderr, " !SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION"); -#endif /* !SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION */ -#ifdef SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS - fprintf(stderr, " SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS"); -#else /* !SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS */ - fprintf(stderr, " !SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS"); -#endif /* !SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS */ -#ifdef SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION - fprintf(stderr, " SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION"); -#else /* !SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION */ - fprintf(stderr, " !SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION"); -#endif /* !SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION */ -#ifdef SSL_OP_TLS_ROLLBACK_BUG - fprintf(stderr, " SSL_OP_TLS_ROLLBACK_BUG"); -#else /* !SSL_OP_TLS_ROLLBACK_BUG */ - fprintf(stderr, " !SSL_OP_TLS_ROLLBACK_BUG"); -#endif /* !SSL_OP_TLS_ROLLBACK_BUG */ - fprintf(stderr, "\n"); -} - -/* - * 1 if OpenSSL has been initialized, 0 if not. When calling a _load() - * function the first time, OpenSSL will automatically be initialized. - * Not protected by a mutex and thus not thread-safe. - */ -static int ssl_initialized = 0; - -int ssl_init(void) -{ - int fd; - char buf[256]; - - if (ssl_initialized) return 0; - - /* general initialization */ - SSL_library_init(); - -#ifdef PURIFY - CRYPTO_malloc_init(); - CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ON); -#endif /* PURIFY */ - - SSL_load_error_strings(); - OpenSSL_add_all_algorithms(); - - if ((fd = open("/dev/urandom", O_RDONLY)) == -1) - { - TFE_LOG_ERROR(NULL, "Error opening /dev/urandom for reading: %s", strerror(errno)); - goto __errout; - } - - while (!RAND_status()) - { - if (read(fd, buf, sizeof(buf)) == -1) - { - TFE_LOG_ERROR(NULL, "Error reading from /dev/urandom: %s", strerror(errno)); - goto __errout; - } - - RAND_seed(buf, sizeof(buf)); - } - - if (!RAND_poll()) - { - TFE_LOG_ERROR(NULL, "RAND_poll() failed."); - return -1; - } - - ssl_initialized = 1; - close(fd); - - return 0; - -__errout: - if (fd > 0) close(fd); - return -1; -} - -/* - * Re-initialize OpenSSL after forking. Returns 0 on success, -1 on failure. - */ -int ssl_reinit(void) -{ - if (!ssl_initialized) - return 0; - return 0; -} - -/* - * Deinitialize OpenSSL and free as much memory as possible. - * Some 10k-100k will still remain resident no matter what. - */ -void -ssl_fini(void) -{ - if (!ssl_initialized) - return; - - ENGINE_cleanup(); - CONF_modules_finish(); - CONF_modules_unload(1); - CONF_modules_free(); - - EVP_cleanup(); - ERR_free_strings(); - CRYPTO_cleanup_all_ex_data(); -} - -/* - * Format raw SHA1 hash into newly allocated string, with or without colons. - */ -char * ssl_sha1_to_str(unsigned char * rawhash, int colons) -{ - char * str; - int rv; - - rv = asprintf(&str, colons ? - "%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X" - "%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X" : - "%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X" - "%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X", - rawhash[0], rawhash[1], rawhash[2], rawhash[3], - rawhash[4], rawhash[5], rawhash[6], rawhash[7], - rawhash[8], rawhash[9], rawhash[10], rawhash[11], - rawhash[12], rawhash[13], rawhash[14], rawhash[15], - rawhash[16], rawhash[17], rawhash[18], rawhash[19]); - - if (rv == -1) - return NULL; - return str; -} - -/* - * Format SSL state into newly allocated string. - * Returns pointer to string that must be freed by caller, or NULL on error. - */ -char * ssl_ssl_state_to_str(SSL * ssl) -{ - char * str = NULL; - int rv; - - rv = asprintf(&str, "%08x = %s%s%04x = %s (%s) [%s]", - SSL_get_state(ssl), - (SSL_get_state(ssl) & SSL_ST_CONNECT) ? "SSL_ST_CONNECT|" : "", - (SSL_get_state(ssl) & SSL_ST_ACCEPT) ? "SSL_ST_ACCEPT|" : "", - SSL_get_state(ssl) & SSL_ST_MASK, - SSL_state_string(ssl), - SSL_state_string_long(ssl), - SSL_is_server(ssl) ? "accept socket" : "connect socket"); - - return (rv < 0) ? NULL : str; -} - -/* - * Generates a NSS key log format compatible string containing the client - * random and the master key, intended to be used to decrypt externally - * captured network traffic using tools like Wireshark. - * - * Only supports the CLIENT_RANDOM method (SSL 3.0 - TLS 1.2). - * - * https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/Key_Log_Format - */ -char * ssl_ssl_masterkey_to_str(SSL * ssl) -{ - char * str = NULL; - int rv; - unsigned char * k, * r; - - unsigned char kbuf[48], rbuf[32]; - k = &kbuf[0]; - r = &rbuf[0]; - SSL_SESSION* sess=SSL_get0_session(ssl); - SSL_SESSION_get_master_key(sess, k, sizeof(kbuf)); - SSL_get_client_random(ssl, r, sizeof(rbuf)); - - rv = asprintf(&str, - "CLIENT_RANDOM " - "%02X%02X%02X%02X%02X%02X%02X%02X" - "%02X%02X%02X%02X%02X%02X%02X%02X" - "%02X%02X%02X%02X%02X%02X%02X%02X" - "%02X%02X%02X%02X%02X%02X%02X%02X" - " " - "%02X%02X%02X%02X%02X%02X%02X%02X" - "%02X%02X%02X%02X%02X%02X%02X%02X" - "%02X%02X%02X%02X%02X%02X%02X%02X" - "%02X%02X%02X%02X%02X%02X%02X%02X" - "%02X%02X%02X%02X%02X%02X%02X%02X" - "%02X%02X%02X%02X%02X%02X%02X%02X" - "\n", - r[0], r[1], r[2], r[3], r[4], r[5], r[6], r[7], - r[8], r[9], r[10], r[11], r[12], r[13], r[14], r[15], - r[16], r[17], r[18], r[19], r[20], r[21], r[22], r[23], - r[24], r[25], r[26], r[27], r[28], r[29], r[30], r[31], - k[0], k[1], k[2], k[3], k[4], k[5], k[6], k[7], - k[8], k[9], k[10], k[11], k[12], k[13], k[14], k[15], - k[16], k[17], k[18], k[19], k[20], k[21], k[22], k[23], - k[24], k[25], k[26], k[27], k[28], k[29], k[30], k[31], - k[32], k[33], k[34], k[35], k[36], k[37], k[38], k[39], - k[40], k[41], k[42], k[43], k[44], k[45], k[46], k[47]); - - return (rv < 0) ? NULL : str; -} - -#ifndef OPENSSL_NO_DH -static unsigned char dh_g[] = {0x02}; -static unsigned char dh512_p[] = { - 0xAB, 0xC0, 0x34, 0x16, 0x95, 0x8B, 0x57, 0xE5, 0x5C, 0xB3, 0x4E, 0x6E, - 0x16, 0x0B, 0x35, 0xC5, 0x6A, 0xCC, 0x4F, 0xD3, 0xE5, 0x46, 0xE2, 0x23, - 0x6A, 0x5B, 0xBB, 0x5D, 0x3D, 0x52, 0xEA, 0xCE, 0x4F, 0x7D, 0xCA, 0xFF, - 0xB4, 0x8B, 0xC9, 0x78, 0xDC, 0xA0, 0xFC, 0xBE, 0xF3, 0xB5, 0xE6, 0x61, - 0xA6, 0x6D, 0x58, 0xFC, 0xA0, 0x0F, 0xF7, 0x9B, 0x97, 0xE6, 0xC7, 0xE8, - 0x1F, 0xCD, 0x16, 0x73}; -static unsigned char dh1024_p[] = { - 0x99, 0x28, 0x34, 0x48, 0x9E, 0xB7, 0xD1, 0x4F, 0x0D, 0x17, 0x09, 0x97, - 0xB9, 0x9B, 0x20, 0xFE, 0xE5, 0x65, 0xE0, 0xE2, 0x56, 0x37, 0x80, 0xA2, - 0x9F, 0x2C, 0x2D, 0x87, 0x10, 0x58, 0x39, 0xAD, 0xF3, 0xC5, 0xA9, 0x08, - 0x24, 0xC7, 0xAA, 0xA9, 0x29, 0x3A, 0x13, 0xDF, 0x4E, 0x0A, 0x6D, 0x11, - 0x39, 0xB1, 0x1C, 0x3F, 0xFE, 0xFE, 0x0A, 0x5E, 0xAD, 0x2E, 0x5C, 0x10, - 0x97, 0x38, 0xAC, 0xE8, 0xEB, 0xAA, 0x4A, 0xA1, 0xC0, 0x5C, 0x1D, 0x27, - 0x65, 0x9C, 0xC8, 0x53, 0xAC, 0x35, 0xDD, 0x84, 0x1F, 0x47, 0x0E, 0x04, - 0xF1, 0x90, 0x61, 0x62, 0x2E, 0x29, 0x2C, 0xC6, 0x28, 0x91, 0x6D, 0xF0, - 0xE2, 0x5E, 0xCE, 0x60, 0x3E, 0xF7, 0xF8, 0x37, 0x99, 0x4D, 0x9F, 0xFB, - 0x68, 0xEC, 0x7F, 0x9D, 0x32, 0x74, 0xD1, 0xAA, 0xD4, 0x4C, 0xF5, 0xCD, - 0xC2, 0xD7, 0xD7, 0xAC, 0xDA, 0x69, 0xF5, 0x2B}; -static unsigned char dh2048_p[] = { - 0xAB, 0x88, 0x97, 0xCA, 0xF1, 0xE1, 0x60, 0x39, 0xFA, 0xB1, 0xA8, 0x7D, - 0xB3, 0x7A, 0x38, 0x08, 0xF0, 0x7A, 0x3D, 0x21, 0xC4, 0xE6, 0xB8, 0x32, - 0x3D, 0xAB, 0x0F, 0xE7, 0x8C, 0xA1, 0x59, 0x47, 0xB2, 0x0A, 0x7A, 0x3A, - 0x20, 0x2A, 0x1B, 0xD4, 0xBA, 0xFC, 0x4C, 0xC5, 0xEE, 0xA2, 0xB9, 0xB9, - 0x65, 0x47, 0xCC, 0x13, 0x99, 0xD7, 0xA6, 0xCA, 0xFF, 0x23, 0x05, 0x91, - 0xAB, 0x5C, 0x82, 0xB8, 0xB4, 0xFD, 0xB1, 0x2E, 0x5B, 0x0F, 0x8E, 0x03, - 0x3C, 0x23, 0xD6, 0x6A, 0xE2, 0x83, 0x95, 0xD2, 0x8E, 0xEB, 0xDF, 0x3A, - 0xAF, 0x89, 0xF0, 0xA0, 0x14, 0x09, 0x12, 0xF6, 0x54, 0x54, 0x93, 0xF4, - 0xD4, 0x41, 0x56, 0x7A, 0x0E, 0x56, 0x20, 0x1F, 0x1D, 0xBA, 0x3F, 0x07, - 0xD2, 0x89, 0x1B, 0x40, 0xD0, 0x1C, 0x08, 0xDF, 0x00, 0x7F, 0x34, 0xF4, - 0x28, 0x4E, 0xF7, 0x53, 0x8D, 0x4A, 0x00, 0xC3, 0xC0, 0x89, 0x9E, 0x63, - 0x96, 0xE9, 0x52, 0xDF, 0xA5, 0x2C, 0x00, 0x4E, 0xB0, 0x82, 0x6A, 0x10, - 0x28, 0x8D, 0xB9, 0xE7, 0x7A, 0xCB, 0xC3, 0xD6, 0xC1, 0xC0, 0x4D, 0x91, - 0xC4, 0x6F, 0xD3, 0x99, 0xD1, 0x86, 0x71, 0x67, 0x0A, 0xA1, 0xFC, 0xF4, - 0x7D, 0x40, 0x88, 0x8D, 0xAC, 0xCB, 0xBC, 0xEA, 0x17, 0x85, 0x0B, 0xC6, - 0x12, 0x3E, 0x4A, 0xB9, 0x60, 0x74, 0x93, 0x54, 0x14, 0x39, 0x10, 0xBF, - 0x21, 0xB0, 0x8B, 0xB1, 0x55, 0x3F, 0xBB, 0x6A, 0x1F, 0x42, 0x82, 0x0A, - 0x40, 0x3A, 0x15, 0xCD, 0xD3, 0x79, 0xD0, 0x02, 0xA4, 0xF5, 0x79, 0x78, - 0x03, 0xBD, 0x47, 0xCC, 0xD5, 0x08, 0x6A, 0x46, 0xAE, 0x36, 0xE4, 0xCD, - 0xB1, 0x17, 0x48, 0x30, 0xB4, 0x02, 0xBC, 0x50, 0x68, 0xE3, 0xA2, 0x76, - 0xD0, 0x5C, 0xB9, 0xE6, 0xBE, 0x4C, 0xFD, 0x50, 0xEF, 0xD0, 0x3F, 0x39, - 0x4F, 0x53, 0x16, 0x3B}; -static unsigned char dh4096_p[] = { - 0xB1, 0xCC, 0x09, 0x86, 0xEE, 0xF9, 0xB9, 0xC9, 0xB9, 0x87, 0xC4, 0xB9, - 0xD7, 0x31, 0x95, 0x84, 0x94, 0x65, 0xED, 0x82, 0x64, 0x11, 0xA7, 0x0A, - 0xFE, 0xC2, 0x60, 0xAE, 0x7C, 0x74, 0xFB, 0x72, 0x8F, 0x0D, 0xA6, 0xDD, - 0x02, 0x49, 0x5B, 0x69, 0xD6, 0x96, 0x05, 0xBE, 0x5E, 0x9B, 0x09, 0x83, - 0xD8, 0xF3, 0x91, 0x55, 0x30, 0x86, 0x97, 0x6C, 0x48, 0x7B, 0x99, 0x82, - 0xCC, 0x1E, 0x1E, 0x25, 0xE6, 0x25, 0xCC, 0xA3, 0x66, 0xDE, 0x8A, 0x78, - 0xEE, 0x7F, 0x4F, 0x86, 0x95, 0x06, 0xBE, 0x64, 0x86, 0xFD, 0x60, 0x6A, - 0x3F, 0x0D, 0x8F, 0x62, 0x17, 0x89, 0xDB, 0xE1, 0x01, 0xC1, 0x75, 0x3A, - 0x78, 0x42, 0xA8, 0x26, 0xEC, 0x00, 0x78, 0xF3, 0xDA, 0x40, 0x8D, 0x0D, - 0x4D, 0x53, 0x82, 0xD7, 0x21, 0xC8, 0x46, 0xC9, 0xE3, 0x80, 0xB4, 0xCF, - 0xEA, 0x46, 0x85, 0xE9, 0xC4, 0x9D, 0xD0, 0xC0, 0x4D, 0x27, 0x0F, 0xF8, - 0x34, 0x3B, 0x86, 0x8F, 0xFC, 0x40, 0x56, 0x49, 0x64, 0x76, 0x61, 0xBC, - 0x35, 0x6A, 0xB8, 0xC5, 0x32, 0x19, 0x00, 0x5E, 0x21, 0x1C, 0x34, 0xCB, - 0x74, 0x5B, 0x60, 0x85, 0x8C, 0x38, 0x52, 0x50, 0x4D, 0xAA, 0x25, 0xE4, - 0x1A, 0xE6, 0xE4, 0xDF, 0x0A, 0xD2, 0x8F, 0x2B, 0xD1, 0x35, 0xC7, 0x92, - 0x7D, 0x6F, 0x54, 0x61, 0x8E, 0x3F, 0xFB, 0xE2, 0xC8, 0x81, 0xD0, 0xAC, - 0x64, 0xE2, 0xA8, 0x30, 0xEA, 0x8E, 0xAD, 0xFE, 0xC0, 0x9E, 0x0B, 0xBF, - 0x34, 0xAC, 0x79, 0x96, 0x38, 0x31, 0x1E, 0xEA, 0xF2, 0x7E, 0xEE, 0x0A, - 0x10, 0x34, 0x7C, 0x1A, 0x30, 0x5F, 0xAF, 0x96, 0x2F, 0x7F, 0xB5, 0x1D, - 0xA7, 0x3D, 0x35, 0x7A, 0x30, 0x70, 0x40, 0xE7, 0xD6, 0x22, 0x1E, 0xD0, - 0x9A, 0x34, 0xC7, 0x6B, 0xE4, 0xF1, 0x78, 0xED, 0xD9, 0xCD, 0x18, 0xBF, - 0x2A, 0x1A, 0x98, 0xB7, 0x6C, 0x6E, 0x18, 0x40, 0xB5, 0xBE, 0xDF, 0xE4, - 0x78, 0x8E, 0x34, 0xB2, 0x7B, 0xE5, 0x88, 0xE6, 0xFD, 0x24, 0xBD, 0xBB, - 0x2E, 0x30, 0x72, 0x54, 0xC7, 0xF4, 0xA0, 0xF1, 0x25, 0xFF, 0xB1, 0x37, - 0x42, 0x07, 0x8C, 0xF2, 0xB9, 0xA1, 0xA4, 0xA7, 0x76, 0x39, 0xB8, 0x11, - 0x17, 0xF3, 0xA8, 0x2E, 0x78, 0x68, 0xF4, 0xBF, 0x98, 0x25, 0x59, 0x17, - 0x59, 0x9B, 0x0D, 0x0B, 0x9B, 0xE3, 0x0F, 0xFF, 0xDC, 0xC8, 0x47, 0x21, - 0xE1, 0x0B, 0x9A, 0x44, 0x79, 0xC7, 0x5F, 0x8E, 0x83, 0x1E, 0x04, 0xA1, - 0xB2, 0x9F, 0x9B, 0xFC, 0xB3, 0x4E, 0xD9, 0xF9, 0x8F, 0x03, 0xBC, 0x0A, - 0x04, 0x00, 0x5C, 0x59, 0xB7, 0x51, 0xAA, 0x75, 0xF8, 0x7A, 0x03, 0x07, - 0x81, 0x6D, 0x67, 0x3E, 0x28, 0x37, 0xE4, 0x74, 0x5B, 0x8C, 0x2A, 0x4B, - 0x6C, 0x10, 0x92, 0x75, 0xA5, 0x79, 0x4B, 0x6D, 0x30, 0xB7, 0x6E, 0xD6, - 0x9E, 0x16, 0xC2, 0x87, 0x69, 0x34, 0xFE, 0xD7, 0x2A, 0x4F, 0xD6, 0xC0, - 0xF3, 0xCD, 0x9C, 0x46, 0xED, 0xC0, 0xB2, 0x84, 0x8D, 0x7E, 0x93, 0xD2, - 0xE9, 0xBE, 0x59, 0x18, 0x92, 0xC1, 0x2C, 0xD6, 0x6C, 0x71, 0x50, 0xA1, - 0x98, 0xDA, 0xD1, 0xAC, 0xDB, 0x88, 0x40, 0x1F, 0x69, 0xDC, 0xDB, 0xB2, - 0xA0, 0x90, 0x01, 0x8E, 0x12, 0xD6, 0x40, 0x1A, 0x8E, 0xC5, 0x69, 0x9C, - 0x91, 0x67, 0xAC, 0xD8, 0x4C, 0x27, 0xCD, 0x08, 0xB8, 0x32, 0x97, 0xE1, - 0x13, 0x0C, 0xFF, 0xB1, 0x06, 0x65, 0x03, 0x98, 0x6F, 0x9E, 0xF7, 0xB8, - 0xA8, 0x75, 0xBA, 0x59, 0xFD, 0x23, 0x98, 0x94, 0x80, 0x9C, 0xA7, 0x46, - 0x32, 0x98, 0x28, 0x7A, 0x0A, 0x3A, 0xA6, 0x95, 0x16, 0x6A, 0x52, 0x8E, - 0x8F, 0x2C, 0xC9, 0x49, 0xB7, 0x59, 0x99, 0x2A, 0xE6, 0xCA, 0x82, 0x88, - 0x36, 0xD3, 0x2B, 0xA4, 0x73, 0xFA, 0x89, 0xBB, -}; - -/* - * OpenSSL temporary DH callback which loads DH parameters from static memory. - */ -DH * ssl_tmp_dh_callback(SSL * s, int is_export, int keylength) -{ - DH * dh; - int rv = 0; - - if (!(dh = DH_new())) - { - TFE_LOG_ERROR(NULL, "DH_new() failed\n"); - return NULL; - } - - switch (keylength) - { - case 512: - rv = DH_set0_pqg(dh, BN_bin2bn(dh512_p, sizeof(dh512_p), NULL), NULL, - BN_bin2bn(dh_g, sizeof(dh_g), NULL)); - break; - case 1024: - rv = DH_set0_pqg(dh, BN_bin2bn(dh1024_p, sizeof(dh1024_p), NULL), NULL, - BN_bin2bn(dh_g, sizeof(dh_g), NULL)); - break; - case 2048: - rv = DH_set0_pqg(dh, BN_bin2bn(dh2048_p, sizeof(dh2048_p), NULL), NULL, - BN_bin2bn(dh_g, sizeof(dh_g), NULL)); - break; - case 4096: - rv = DH_set0_pqg(dh, BN_bin2bn(dh4096_p, sizeof(dh4096_p), NULL), NULL, - BN_bin2bn(dh_g, sizeof(dh_g), NULL)); - break; - default: - TFE_LOG_ERROR(NULL, "Unhandled DH keylength %i%s", keylength, (is_export ? " (export)" : "")); - DH_free(dh); - return NULL; - } - - if (!rv) - { - TFE_LOG_ERROR(NULL, "Failed to load DH p and g from memory\n"); - DH_free(dh); - return NULL; - } - - return (dh); -} - -/* - * Load DH parameters from a PEM file. - * Not thread-safe. - */ -DH * ssl_dh_load(const char * filename) -{ - DH * dh; - FILE * fh; - - if (ssl_init() == -1) - return NULL; - - if (!(fh = fopen(filename, "r"))) - { - return NULL; - } - dh = PEM_read_DHparams(fh, NULL, NULL, NULL); - fclose(fh); - return dh; -} -#endif /* !OPENSSL_NO_DH */ - -#ifndef OPENSSL_NO_EC -/* - * Load an Elliptic Curve by name. If curvename is NULL, a default curve is - * loaded. - */ -EC_KEY * ssl_ec_by_name(const char * curvename) -{ - int nid; - - if (!curvename) curvename = "prime256v1"; - if ((nid = OBJ_sn2nid(curvename)) == NID_undef) - { - return NULL; - } - - return EC_KEY_new_by_curve_name(nid); -} -#endif /* !OPENSSL_NO_EC */ - -/* - * Add a X509v3 extension to a cert and handle errors. - * Returns -1 on errors, 0 on success. - */ -int ssl_x509_v3ext_add(X509V3_CTX * ctx, X509 * crt, const char * k, const char * v) -{ - X509_EXTENSION * ext; - - if (!(ext = X509V3_EXT_conf(NULL, ctx, k, v))) - { - return -1; - } - if (X509_add_ext(crt, ext, -1) != 1) - { - X509_EXTENSION_free(ext); - return -1; - } - X509_EXTENSION_free(ext); - return 0; -} - -/* - * Copy a X509v3 extension from one cert to another. - * If the extension is not present in the original cert, - * the extension will not be added to the destination cert. - * Returns 1 if ext was copied, 0 if not present in origcrt, -1 on error. - */ -int ssl_x509_v3ext_copy_by_nid(X509 * crt, X509 * origcrt, int nid) -{ - X509_EXTENSION * ext; - int pos; - - pos = X509_get_ext_by_NID(origcrt, nid, -1); - if (pos == -1) - return 0; - ext = X509_get_ext(origcrt, pos); - if (!ext) - return -1; - if (X509_add_ext(crt, ext, -1) != 1) - return -1; - return 1; -} - -/* - * Best effort randomness generator. - * Not for real life cryptography applications. - * Returns 0 on success, -1 on failure. - */ -int ssl_rand(void * p, size_t sz) -{ - int rv; - rv = RAND_bytes((unsigned char *) p, sz); - - if (rv == 1) return 0; - return -1; -} - -/* - * Copy the serial number from src cert to dst cert - * and modify it by a random offset. - * If reading the serial fails for some reason, generate a new - * random serial and store it in the dst cert. - * Using the same serial is not a good idea since some SSL stacks - * check for duplicate cert serials. - * Returns 0 on success, -1 on error. - */ -int ssl_x509_serial_copyrand(X509 * dstcrt, X509 * srccrt) -{ - ASN1_INTEGER * srcptr, * dstptr; - BIGNUM * bnserial; - unsigned int rand; - int rv; - -#ifndef PURIFY - rv = ssl_rand(&rand, sizeof(rand)); -#else /* PURIFY */ - rand = 0xF001; - rv = 0; -#endif /* PURIFY */ - dstptr = X509_get_serialNumber(dstcrt); - srcptr = X509_get_serialNumber(srccrt); - if ((rv == -1) || !dstptr || !srcptr) - return -1; - bnserial = ASN1_INTEGER_to_BN(srcptr, NULL); - if (!bnserial) - { - /* random 32-bit serial */ - ASN1_INTEGER_set(dstptr, rand); - } - else - { - /* original serial plus random 32-bit offset */ - BN_add_word(bnserial, rand); - BN_to_ASN1_INTEGER(bnserial, dstptr); - BN_free(bnserial); - } - return 0; -} - -/* - * Create a fake X509v3 cert, signed by the provided CA, - * based on the original cert retrieved from the real server. - * The returned cert is created using X509_new() and thus must - * be freed by the caller using X509_free(). - * The optional argument extraname is added to subjectAltNames if provided. - */ -X509 * ssl_x509_forge(X509 * cacrt, EVP_PKEY * cakey, X509 * origcrt, EVP_PKEY * key, - const char * extraname, const char * crlurl) -{ - X509_NAME * subject, * issuer; - GENERAL_NAMES * names; - GENERAL_NAME * gn; - X509 * crt; - int rv; - - subject = X509_get_subject_name(origcrt); - issuer = X509_get_subject_name(cacrt); - if (!subject || !issuer) - return NULL; - - crt = X509_new(); - if (!crt) - return NULL; - - if (!X509_set_version(crt, 0x02) || - !X509_set_subject_name(crt, subject) || - !X509_set_issuer_name(crt, issuer) || - ssl_x509_serial_copyrand(crt, origcrt) == -1 || - !X509_gmtime_adj(X509_get_notBefore(crt), (long) -60 * 60 * 24) || - !X509_gmtime_adj(X509_get_notAfter(crt), (long) 60 * 60 * 24 * 364) || - !X509_set_pubkey(crt, key)) - goto errout; - - /* add standard v3 extensions; cf. RFC 2459 */ - - X509V3_CTX ctx; - X509V3_set_ctx(&ctx, cacrt, crt, NULL, NULL, 0); - if (ssl_x509_v3ext_add(&ctx, crt, "subjectKeyIdentifier", "hash") == -1 || - ssl_x509_v3ext_add(&ctx, crt, "authorityKeyIdentifier", "keyid,issuer:always") == -1) - goto errout; - - rv = ssl_x509_v3ext_copy_by_nid(crt, origcrt, - NID_basic_constraints); - if (rv == 0) - rv = ssl_x509_v3ext_add(&ctx, crt, "basicConstraints", - "CA:FALSE"); - if (rv == -1) - goto errout; - - rv = ssl_x509_v3ext_copy_by_nid(crt, origcrt, - NID_key_usage); - if (rv == 0) - rv = ssl_x509_v3ext_add(&ctx, crt, "keyUsage", - "digitalSignature," - "keyEncipherment"); - if (rv == -1) - goto errout; - - rv = ssl_x509_v3ext_copy_by_nid(crt, origcrt, - NID_ext_key_usage); - if (rv == 0) - rv = ssl_x509_v3ext_add(&ctx, crt, "extendedKeyUsage", - "serverAuth"); - if (rv == -1) - goto errout; - - if (crlurl) - { - char * crlurlval; - if (asprintf(&crlurlval, "URI:%s", crlurl) < 0) - goto errout; - if (ssl_x509_v3ext_add(&ctx, crt, "crlDistributionPoints", - crlurlval) == -1) - { - free(crlurlval); - goto errout; - } - free(crlurlval); - } - - if (!extraname) - { - /* no extraname provided: copy original subjectAltName ext */ - if (ssl_x509_v3ext_copy_by_nid(crt, origcrt, - NID_subject_alt_name) == -1) - goto errout; - } - else - { - names = (GENERAL_NAMES *) X509_get_ext_d2i(origcrt, NID_subject_alt_name, 0, 0); - if (!names) - { - /* no subjectAltName present: add new one */ - char * cfval; - if (asprintf(&cfval, "DNS:%s", extraname) < 0) - goto errout; - if (ssl_x509_v3ext_add(&ctx, crt, "subjectAltName", - cfval) == -1) - { - free(cfval); - goto errout; - } - free(cfval); - } - else - { - /* add extraname to original subjectAltName - * and add it to the new cert */ - gn = GENERAL_NAME_new(); - if (!gn) - goto errout2; - gn->type = GEN_DNS; - gn->d.dNSName = ASN1_IA5STRING_new(); - if (!gn->d.dNSName) - goto errout3; - ASN1_STRING_set(gn->d.dNSName, - (unsigned char *) extraname, - strlen(extraname)); - sk_GENERAL_NAME_push(names, gn); - X509_EXTENSION * ext = X509V3_EXT_i2d( - NID_subject_alt_name, 0, names); - if (!X509_add_ext(crt, ext, -1)) - { - if (ext) - { - X509_EXTENSION_free(ext); - } - goto errout3; - } - X509_EXTENSION_free(ext); - sk_GENERAL_NAME_pop_free(names, GENERAL_NAME_free); - } - } -#ifdef DEBUG_CERTIFICATE - ssl_x509_v3ext_add(&ctx, crt, "nsComment", "Generated by " PKGLABEL); -#endif /* DEBUG_CERTIFICATE */ - - const EVP_MD * md; - switch (EVP_PKEY_type(EVP_PKEY_base_id(cakey))) - { -#ifndef OPENSSL_NO_RSA - case EVP_PKEY_RSA: - switch (X509_get_signature_nid(origcrt)) - { - case NID_md5WithRSAEncryption: md = EVP_md5(); - break; - case NID_ripemd160WithRSA: md = EVP_ripemd160(); - break; - case NID_sha1WithRSAEncryption: md = EVP_sha1(); - break; - case NID_sha224WithRSAEncryption: md = EVP_sha224(); - break; - case NID_sha256WithRSAEncryption: md = EVP_sha256(); - break; - case NID_sha384WithRSAEncryption: md = EVP_sha384(); - break; - case NID_sha512WithRSAEncryption: md = EVP_sha512(); - break; -#ifndef OPENSSL_NO_SHA0 - case NID_shaWithRSAEncryption: md = EVP_sha(); - break; -#endif /* !OPENSSL_NO_SHA0 */ - default: md = EVP_sha256(); - break; - } - break; -#endif /* !OPENSSL_NO_RSA */ -#ifndef OPENSSL_NO_DSA - case EVP_PKEY_DSA: - switch (X509_get_signature_nid(origcrt)) - { - case NID_dsaWithSHA1: - case NID_dsaWithSHA1_2: md = EVP_sha1(); - break; - case NID_dsa_with_SHA224: md = EVP_sha224(); - break; - case NID_dsa_with_SHA256: md = EVP_sha256(); - break; -#ifndef OPENSSL_NO_SHA0 - case NID_dsaWithSHA: md = EVP_sha(); - break; -#endif /* !OPENSSL_NO_SHA0 */ - default: md = EVP_sha256(); - break; - } - break; -#endif /* !OPENSSL_NO_DSA */ -#ifndef OPENSSL_NO_ECDSA - case EVP_PKEY_EC: - switch (X509_get_signature_nid(origcrt)) - { - case NID_ecdsa_with_SHA1: md = EVP_sha1(); - break; - case NID_ecdsa_with_SHA224: md = EVP_sha224(); - break; - case NID_ecdsa_with_SHA256: md = EVP_sha256(); - break; - case NID_ecdsa_with_SHA384: md = EVP_sha384(); - break; - case NID_ecdsa_with_SHA512: md = EVP_sha512(); - break; - default: md = EVP_sha256(); - break; - } - break; -#endif /* !OPENSSL_NO_ECDSA */ - default: goto errout; - } - if (!X509_sign(crt, cakey, md)) - goto errout; - - return crt; - -errout3: - GENERAL_NAME_free(gn); -errout2: - sk_GENERAL_NAME_pop_free(names, GENERAL_NAME_free); -errout: - X509_free(crt); - return NULL; -} - -/* - * Load a X509 cert chain from a PEM file. - * Returns the first cert in *crt and all subsequent certificates in - * *chain. If crt is NULL, the first cert is prepended to *chain - * instead of returned separately. If *chain is NULL, a new stack of X509* - * is created in *chain, else the certs are pushed onto an existing stack. - * Returns -1 on error. - * Not thread-safe. - * - * By accessing (SSLCTX*)->extra_certs directly on OpenSSL before 1.0.2, we - * depend on OpenSSL internals in this function. OpenSSL 1.0.2 introduced - * the SSL_get0_chain_certs() API for accessing the cert chain. - */ -int ssl_x509chain_load(X509 ** crt, STACK_OF(X509) ** chain, const char * filename) -{ - X509 * tmpcrt; - SSL_CTX * tmpctx; - SSL * tmpssl; - STACK_OF(X509) * tmpchain; - int rv; - - if (ssl_init() == -1) - return -1; - - tmpctx = SSL_CTX_new(SSLv23_server_method()); - if (!tmpctx) - goto leave1; - - rv = SSL_CTX_use_certificate_chain_file(tmpctx, filename); - if (rv != 1) - goto leave2; - tmpssl = SSL_new(tmpctx); - if (!tmpssl) - goto leave2; - - tmpcrt = SSL_get_certificate(tmpssl); - if (!tmpcrt) - goto leave3; - - if (!*chain) - { - *chain = sk_X509_new_null(); - if (!*chain) - goto leave3; - } - -#if (OPENSSL_VERSION_NUMBER < 0x1000200fL) || defined(LIBRESSL_VERSION_NUMBER) - tmpchain = tmpctx->extra_certs; -#else /* OpenSSL >= 1.0.2 */ - rv = SSL_CTX_get0_chain_certs(tmpctx, &tmpchain); - if (rv != 1) - goto leave3; -#endif /* OpenSSL >= 1.0.2 */ - - if (crt) - { - *crt = tmpcrt; - } - else - { - sk_X509_push(*chain, tmpcrt); - } - ssl_x509_refcount_inc(tmpcrt); - - for (int i = 0; i < sk_X509_num(tmpchain); i++) - { - tmpcrt = sk_X509_value(tmpchain, i); - ssl_x509_refcount_inc(tmpcrt); - sk_X509_push(*chain, tmpcrt); - } - SSL_free(tmpssl); - SSL_CTX_free(tmpctx); - return 0; - -leave3: - SSL_free(tmpssl); -leave2: - SSL_CTX_free(tmpctx); -leave1: - return -1; -} - -/* - * Use a X509 cert chain for an SSL context. - * Copies the cert stack to the SSL_CTX internal data structures - * and increases reference counts accordingly. - */ -void ssl_x509chain_use(SSL_CTX * sslctx, X509 * crt, STACK_OF(X509) * chain) -{ - SSL_CTX_use_certificate(sslctx, crt); - - for (int i = 0; i < sk_X509_num(chain); i++) - { - X509 * tmpcrt; - - tmpcrt = sk_X509_value(chain, i); - ssl_x509_refcount_inc(tmpcrt); - SSL_CTX_add_extra_chain_cert(sslctx, tmpcrt); - } -} - -/* - * Load a X509 cert from a PEM file. - * Returned X509 must be freed using X509_free() by the caller. - * Not thread-safe. - */ -X509 * ssl_x509_load(const char * filename) -{ - X509 * crt = NULL; - SSL_CTX * tmpctx; - SSL * tmpssl; - int rv; - - if (ssl_init() == -1) - return NULL; - - tmpctx = SSL_CTX_new(SSLv23_server_method()); - if (!tmpctx) - goto leave1; - rv = SSL_CTX_use_certificate_file(tmpctx, filename, SSL_FILETYPE_PEM); - if (rv != 1) - goto leave2; - tmpssl = SSL_new(tmpctx); - if (!tmpssl) - goto leave2; - crt = SSL_get_certificate(tmpssl); - if (crt) - ssl_x509_refcount_inc(crt); - SSL_free(tmpssl); -leave2: - SSL_CTX_free(tmpctx); -leave1: - return crt; -} - -/* - * Load a private key from a PEM file. - * Returned EVP_PKEY must be freed using EVP_PKEY_free() by the caller. - * Not thread-safe. - */ -EVP_PKEY * ssl_key_load(const char * filename) -{ - EVP_PKEY * key = NULL; - SSL_CTX * tmpctx; - SSL * tmpssl; - int rv; - - if (ssl_init() == -1) - return NULL; - - tmpctx = SSL_CTX_new(SSLv23_server_method()); - if (!tmpctx) - goto leave1; - rv = SSL_CTX_use_PrivateKey_file(tmpctx, filename, SSL_FILETYPE_PEM); - if (rv != 1) - goto leave2; - tmpssl = SSL_new(tmpctx); - if (!tmpssl) - goto leave2; - key = SSL_get_privatekey(tmpssl); - if (key) - ssl_key_refcount_inc(key); - SSL_free(tmpssl); -leave2: - SSL_CTX_free(tmpctx); -leave1: - return key; -} - -/* - * Generate a new RSA key. - * Returned EVP_PKEY must be freed using EVP_PKEY_free() by the caller. - */ -EVP_PKEY * ssl_key_genrsa(const int keysize) -{ - EVP_PKEY * pkey; - RSA * rsa; - -#if OPENSSL_VERSION_NUMBER >= 0x10100000L - BIGNUM * bn; - int rv; - rsa = RSA_new(); - bn = BN_new(); - BN_dec2bn(&bn, "3"); - rv = RSA_generate_key_ex(rsa, keysize, bn, NULL); - BN_free(bn); - if (rv != 1) - { - RSA_free(rsa); - return NULL; - } -#else /* OPENSSL_VERSION_NUMBER < 0x10100000L */ - rsa = RSA_generate_key(keysize, 3, NULL, NULL); - if (!rsa) - return NULL; -#endif /* OPENSSL_VERSION_NUMBER < 0x10100000L */ - pkey = EVP_PKEY_new(); - EVP_PKEY_assign_RSA(pkey, rsa); /* does not increment refcount */ - return pkey; -} - -/* - * Returns the subjectKeyIdentifier compatible key id of the public key. - * keyid will receive a binary SHA-1 hash of SSL_KEY_IDSZ bytes. - * Returns 0 on success, -1 on failure. - */ -int ssl_key_identifier_sha1(EVP_PKEY * key, unsigned char * keyid) -{ - X509_PUBKEY * pubkey = NULL; - const unsigned char * pk; - int length; - - /* X509_PUBKEY_set() will attempt to free pubkey if != NULL */ - if (X509_PUBKEY_set(&pubkey, key) != 1 || !pubkey) - return -1; - if (!X509_PUBKEY_get0_param(NULL, &pk, &length, NULL, pubkey)) - goto errout; - if (!EVP_Digest(pk, length, keyid, NULL, EVP_sha1(), NULL)) - goto errout; - X509_PUBKEY_free(pubkey); - return 0; - -errout: - X509_PUBKEY_free(pubkey); - return -1; -} - -char* ssl_X509_print_name(X509_NAME* name) -{ - //ref: https://kahdev.wordpress.com/2008/11/23/a-certificates-name-issuer-and-its-keyusage/ - // Set up a BIO to put the name line into. - BIO *name_Bio = BIO_new(BIO_s_mem()); - - // Now, put the name line into the BIO. - X509_NAME_print_ex(name_Bio, name, 0, XN_FLAG_ONELINE); - - // Obtain a reference to the data and copy out - // just the length of the data. - char *data_start = NULL; - char *name_string = NULL; - long nameLength = BIO_get_mem_data(name_Bio, &data_start); - - name_string = ALLOC(char, nameLength + 1); - memset(name_string, 0, nameLength + 1); - memcpy(name_string, data_start, nameLength); - BIO_vfree(name_Bio); - return name_string; -} -/* - * Returns the result of ssl_key_identifier_sha1() as hex characters with or - * without colons in a newly allocated string. - */ -char * ssl_key_identifier(EVP_PKEY * key, int colons) -{ - unsigned char id[SSL_KEY_IDSZ]; - - if (ssl_key_identifier_sha1(key, id) == -1) - return NULL; - - return ssl_sha1_to_str(id, colons); -} - -/* - * Returns the one-line representation of the subject DN in a newly allocated - * string which must be freed by the caller. - */ -char * ssl_x509_subject(const X509 * crt) -{ - return ssl_X509_print_name(X509_get_subject_name(crt)); -} - -/* - * Returns the one-line representation of the issuer in a newly allocated - * string which must be freed by the caller. - */ -char * ssl_x509_issuer(const X509 * crt) -{ - return ssl_X509_print_name(X509_get_issuer_name(crt)); -} - - -/* - * Parse the common name from the subject distinguished name. - * Returns string allocated using malloc(), caller must free(). - * Returns NULL on errors. - */ -char * -ssl_x509_subject_cn(X509 * crt, size_t * psz) -{ - X509_NAME * ptr; - char * cn; - size_t sz; - - ptr = X509_get_subject_name(crt); /* does not inc refcounts */ - if (!ptr) - return NULL; - sz = (size_t) X509_NAME_get_text_by_NID(ptr, NID_commonName, NULL, 0) + 1; - - if ((sz == 0) || !(cn = (char *) malloc(sz))) - return NULL; - if (X509_NAME_get_text_by_NID(ptr, NID_commonName, cn, sz) == -1) - { - free(cn); - return NULL; - } - *psz = sz; - return cn; -} - -/* - * Write the SHA1 fingerprint of cert to fpr as SSL_X509_FPRSZ (20) - * bytes long binary buffer. - * Returns -1 on error, 0 on success. - */ -int -ssl_x509_fingerprint_sha1(X509 * crt, unsigned char * fpr) -{ - unsigned int sz = SSL_X509_FPRSZ; - - return X509_digest(crt, EVP_sha1(), fpr, &sz) ? 0 : -1; -} - -/* - * Returns the result of ssl_x509_fingerprint_sha1() as hex characters with or - * without colons in a newly allocated string. - */ -char * -ssl_x509_fingerprint(X509 * crt, int colons) -{ - unsigned char fpr[SSL_X509_FPRSZ]; - - if (ssl_x509_fingerprint_sha1(crt, fpr) == -1) - return NULL; - - return ssl_sha1_to_str(fpr, colons); -} - -#ifndef OPENSSL_NO_DH -/* - * Increment the reference count of DH parameters in a thread-safe - * manner. - */ -void -ssl_dh_refcount_inc(DH * dh) -{ -#if defined(OPENSSL_THREADS) && OPENSSL_VERSION_NUMBER < 0x10100000L - CRYPTO_add(&dh->references, 1, CRYPTO_LOCK_DH); -#else /* !OPENSSL_THREADS */ - DH_up_ref(dh); -#endif /* !OPENSSL_THREADS */ -} -#endif /* !OPENSSL_NO_DH */ - -/* - * Increment the reference count of an X509 cert in a thread-safe - * manner. - */ -void -ssl_key_refcount_inc(EVP_PKEY * key) -{ -#if defined(OPENSSL_THREADS) && OPENSSL_VERSION_NUMBER < 0x10100000L - CRYPTO_add(&key->references, 1, CRYPTO_LOCK_EVP_PKEY); -#else /* !OPENSSL_THREADS */ - EVP_PKEY_up_ref(key); -#endif /* !OPENSSL_THREADS */ -} - -/* - * Increment the reference count of an X509 cert in a thread-safe - * manner. This differs from X509_dup() in that it does not create new, - * full copy of the cert, but only increases the reference count. - */ -void -ssl_x509_refcount_inc(X509 * crt) -{ -#if defined(OPENSSL_THREADS) && OPENSSL_VERSION_NUMBER < 0x10100000L - CRYPTO_add(&crt->references, 1, CRYPTO_LOCK_X509); -#else /* !OPENSSL_THREADS */ - X509_up_ref(crt); -#endif /* !OPENSSL_THREADS */ -} - -/* - * Match a URL/URI hostname against a single cert DNS name - * using RFC 6125 rules (6.4.3 Checking of Wildcard Certificates): - * - * 1. The client SHOULD NOT attempt to match a presented identifier in - * which the wildcard character comprises a label other than the - * left-most label (e.g., do not match bar.*.example.net). - * - * 2. If the wildcard character is the only character of the left-most - * label in the presented identifier, the client SHOULD NOT compare - * against anything but the left-most label of the reference - * identifier (e.g., *.example.com would match foo.example.com but - * not bar.foo.example.com or example.com). - * - * 3. The client MAY match a presented identifier in which the wildcard - * character is not the only character of the label (e.g., - * baz*.example.net and *baz.example.net and b*z.example.net would - * be taken to match baz1.example.net and foobaz.example.net and - * buzz.example.net, respectively). However, the client SHOULD NOT - * attempt to match a presented identifier where the wildcard - * character is embedded within an A-label or U-label [IDNA-DEFS] of - * an internationalized domain name [IDNA-PROTO]. - * - * The optional partial matching in rule 3 is not implemented. - * Returns 1 on match, 0 on no match. - */ -int -ssl_dnsname_match( - const char * certname, size_t certnamesz, - const char * hostname, size_t hostnamesz) -{ - if (hostnamesz < certnamesz) - return 0; - if ((hostnamesz == certnamesz) && - !memcmp(certname, hostname, certnamesz)) - return 1; - if (!memcmp(certname, "xn--", 4)) - return 0; - if ((certnamesz == 1) && (certname[0] == '*') && - !memchr(hostname, '.', hostnamesz)) - return 1; - if ((certnamesz > 2) && (certname[0] == '*') && (certname[1] == '.') && - !memcmp(&certname[1], - &hostname[hostnamesz - (certnamesz - 1)], - certnamesz - 1) && - (memchr(hostname, '.', hostnamesz) == - &hostname[hostnamesz - (certnamesz - 1)])) - return 1; - return 0; -} - -/* - * Transform a NULL-terminated hostname into a matching wildcard hostname, - * e.g. "test.example.org" -> "*.example.org". - * Returns string which must be free()'d by the caller, or NULL on error. - */ -char * -ssl_wildcardify(const char * hostname) -{ - char * dot, * wildcarded; - size_t dotsz; - - if (!(dot = (char *) strchr(hostname, '.'))) - return strdup("*"); - dotsz = strlen(dot); - if (!(wildcarded = (char *) malloc(dotsz + 2))) - return NULL; - wildcarded[0] = '*'; - for (size_t i = 0; i < dotsz; i++) - { - wildcarded[i + 1] = dot[i]; - } - wildcarded[dotsz + 1] = '\0'; - return wildcarded; -} - -/* - * Match DNS name against cert subject CN and subjectAltNames DNS names. - * Returns 1 if any name matches, 0 if none matches. - */ -int -ssl_x509_names_match(X509 * crt, const char * dnsname) -{ - GENERAL_NAMES * altnames; - char * cn; - size_t cnsz, dnsnamesz; - - dnsnamesz = strlen(dnsname); - - cn = ssl_x509_subject_cn(crt, &cnsz); - if (cn && ssl_dnsname_match(cn, cnsz, dnsname, dnsnamesz)) - { - free(cn); - return 1; - } - if (cn) - { - free(cn); - } - - altnames = (GENERAL_NAMES *) X509_get_ext_d2i(crt, NID_subject_alt_name, 0, 0); - if (!altnames) - return 0; - for (int i = 0; i < sk_GENERAL_NAME_num(altnames); i++) - { - GENERAL_NAME * gn = sk_GENERAL_NAME_value(altnames, i); - if (gn->type == GEN_DNS) - { - unsigned char * altname; - int altnamesz; - ASN1_STRING_to_UTF8(&altname, gn->d.dNSName); - altnamesz = ASN1_STRING_length(gn->d.dNSName); - if (altnamesz < 0) - break; - if (ssl_dnsname_match((char *) altname, - (size_t) altnamesz, - dnsname, dnsnamesz)) - { - OPENSSL_free((char *) altname); - GENERAL_NAMES_free(altnames); - return 1; - } - OPENSSL_free((char *) altname); - } - } - GENERAL_NAMES_free(altnames); - return 0; -} - -/* - * Returns a NULL terminated array of pointers to all common names found - * in the Subject DN CN and subjectAltNames extension (DNSName only). - * Caller must free returned buffer and all pointers within. - * Embedded NULL characters in hostnames are replaced with '!'. - */ -char ** ssl_x509_names(X509 * crt) -{ - GENERAL_NAMES * altnames; - char * cn; - size_t cnsz; - char ** res, ** p; - size_t count; - - cn = ssl_x509_subject_cn(crt, &cnsz); - altnames = (GENERAL_NAMES *) X509_get_ext_d2i(crt, NID_subject_alt_name, NULL, NULL); - - count = (size_t) (altnames ? sk_GENERAL_NAME_num(altnames) : 0) + (cn ? 2 : 1); - res = (char **) malloc(count * sizeof(char *)); - - if (!res) - return NULL; - p = res; - if (cn) - *(p++) = cn; - if (!altnames) - { - *p = NULL; - return res; - } - for (int i = 0; i < sk_GENERAL_NAME_num(altnames); i++) - { - GENERAL_NAME * gn = sk_GENERAL_NAME_value(altnames, i); - if (gn->type == GEN_DNS) - { - unsigned char * altname; - int altnamesz; - - ASN1_STRING_to_UTF8(&altname, gn->d.dNSName); - if (!altname) - break; - altnamesz = ASN1_STRING_length(gn->d.dNSName); - if (altnamesz < 0) - { - OPENSSL_free((char *) altname); - break; - } - *p = (char *) malloc(altnamesz + 1); - if (!*p) - { - OPENSSL_free((char *) altname); - GENERAL_NAMES_free(altnames); - for (p = res; *p; p++) - free(*p); - free(res); - return NULL; - } - for (int j = 0; j < altnamesz; j++) - { - (*p)[j] = altname[j] ? altname[j] : '!'; - } - (*p)[altnamesz] = '\0'; - OPENSSL_free((char *) altname); - p++; - } - } - *p = NULL; - GENERAL_NAMES_free(altnames); - return res; -} - -/* - * Returns a printable representation of a cert's common names found - * in the Subject DN CN and subjectAltNames extension, separated by slashes. - * Caller must free returned buffer. - * Embedded NULL characters in hostnames are replaced with '!'. - * If no CN and no subjectAltNames are found, returns "-". - */ -char * ssl_x509_names_to_str(X509 * crt) -{ - char ** names; - size_t sz; - char * buf = NULL, * next; - - names = ssl_x509_names(crt); - if (!names) - return strdup("-"); - - sz = 0; - for (char ** p = names; *p; p++) - { - sz += strlen(*p) + 1; - } - if (!sz) - { - goto out1; - } - - if (!(buf = (char *) malloc(sz))) - goto out2; - next = buf; - for (char ** p = names; *p; p++) - { - char * src = *p; - while (*src) - { - *next++ = *src++; - } - *next++ = '/'; - } - *--next = '\0'; -out2: - for (char ** p = names; *p; p++) - free(*p); -out1: - free(names); - return buf; -} - -/* - * Returns a zero-terminated buffer containing the ASN1 IA5 string. - * Returned buffer must be free()'d by caller. - */ -static char * ssl_ia5string_strdup(ASN1_IA5STRING * ia5) -{ - char * str; - - if (!ia5 || !ia5->length) - return NULL; - - str = (char *) malloc(ia5->length + 1); - if (!str) - return NULL; - memcpy(str, ia5->data, ia5->length); - str[ia5->length] = 0; - return str; -} - -/* - * Returns a NULL terminated array of pointers to copies of Authority - * Information Access (AIA) URLs of a given type contained in the cert. - * Caller must free returned buffer and all pointers within. - */ -char ** ssl_x509_aias(X509 * crt, const int type) -{ - AUTHORITY_INFO_ACCESS * aias; - char ** res; - int count, i, j; - - aias = (AUTHORITY_INFO_ACCESS *) X509_get_ext_d2i(crt, NID_info_access, NULL, NULL); - if (!aias || !(count = sk_ACCESS_DESCRIPTION_num(aias))) - return NULL; - - res = (char **) malloc((count + 1) * sizeof(char *)); - if (!res) - { - sk_ACCESS_DESCRIPTION_pop_free(aias, ACCESS_DESCRIPTION_free); - return NULL; - } - - for (i = 0, j = 0; i < count; i++) - { - ACCESS_DESCRIPTION * aia; - - aia = sk_ACCESS_DESCRIPTION_value(aias, i); - if (aia && - OBJ_obj2nid(aia->method) == type && - aia->location->type == GEN_URI) - { - res[j] = ssl_ia5string_strdup(aia->location->d.ia5); - if (res[j]) - j++; - } - } - res[j] = NULL; - sk_ACCESS_DESCRIPTION_pop_free(aias, ACCESS_DESCRIPTION_free); - return res; -} - -/* - * Returns a NULL terminated array of pointers to copies of Authority - * Information Access (AIA) URLs of type OCSP contained in the cert. - * Caller must free returned buffer and all pointers within. - */ -char ** ssl_x509_ocsps(X509 * crt) -{ - return ssl_x509_aias(crt, NID_ad_OCSP); -} - -/* - * Check whether the cert is valid based on current time. - * Return 1 if valid, 0 otherwise. - */ -int ssl_x509_is_valid(X509 * crt) -{ - if (X509_cmp_current_time(X509_get_notAfter(crt)) <= 0) - return 0; - if (X509_cmp_current_time(X509_get_notBefore(crt)) > 0) - return 0; - return 1; -} - -/* - * Print X509 cert data to a newly allocated string. - * Caller must free returned string. - * Returns NULL on errors. - */ -char * -ssl_x509_to_str(X509 * crt) -{ - BIO * bio; - char * p, * ret; - size_t sz; - - bio = BIO_new(BIO_s_mem()); - if (!bio) - return NULL; - - X509_print(bio, crt); - sz = (size_t) BIO_get_mem_data(bio, &p); - - if (!(ret = (char *) malloc(sz + 1))) - { - BIO_free(bio); - return NULL; - } - memcpy(ret, p, sz); - ret[sz] = '\0'; - BIO_free(bio); - return ret; -} - -/* - * Convert X509 cert to PEM format in a newly allocated string. - * Caller must free returned string. - * Returns NULL on errors. - */ -char * ssl_x509_to_pem(X509 * crt) -{ - BIO * bio; - char * p, * ret; - size_t sz; - - bio = BIO_new(BIO_s_mem()); - if (!bio) - return NULL; - - PEM_write_bio_X509(bio, crt); - sz = (size_t) BIO_get_mem_data(bio, &p); - - if (!(ret = (char *) malloc(sz + 1))) - { - BIO_free(bio); - return NULL; - } - - memcpy(ret, p, sz); - ret[sz] = '\0'; - BIO_free(bio); - return ret; -} - -/* - * Print SSL_SESSION data to a newly allocated string. - * Caller must free returned string. - * Returns NULL on errors. - */ -char * ssl_session_to_str(SSL_SESSION * sess) -{ - BIO * bio; - char * p, * ret; - size_t sz; - - bio = BIO_new(BIO_s_mem()); - if (!bio) - return NULL; - - SSL_SESSION_print(bio, sess); - sz = (size_t) BIO_get_mem_data(bio, &p); /* sets p to internal buffer */ - - if (!(ret = (char *) malloc(sz + 1))) - { - BIO_free(bio); - return NULL; - } - memcpy(ret, p, sz); - ret[sz] = '\0'; - BIO_free(bio); - return ret; -} - -/* - * Returns non-zero if the session timeout has not expired yet, - * zero if the session has expired or an error occured. - */ -int ssl_session_is_valid(SSL_SESSION * sess) -{ - time_t curtimet; - long curtime, timeout; - - curtimet = time(NULL); - if (curtimet == (time_t) -1) - return 0; - curtime = curtimet; - if ((curtime < 0) || ((time_t) curtime != curtimet)) - return 0; - timeout = SSL_SESSION_get_timeout(sess); - if (curtime < timeout) - return 0; - return (SSL_SESSION_get_time(sess) > curtime - timeout); -} - -/* - * Returns 1 if buf contains a DER encoded OCSP request which can be parsed. - * Returns 0 otherwise. - */ -int ssl_is_ocspreq(const unsigned char * buf, size_t sz) -{ - OCSP_REQUEST * req; - const unsigned char * p; - - p = (const unsigned char *) buf; - req = d2i_OCSP_REQUEST(NULL, &p, sz); /* increments p */ - if (!req) - return 0; - OCSP_REQUEST_free(req); - return 1; -} - - -struct cipher_suite -{ - int value; - const char* name; -}; - -struct cipher_suite cipher_suite_list[] = -{ - {0xC030, "ECDHE-RSA-AES256-GCM-SHA384"}, - {0xC02C, "ECDHE-ECDSA-AES256-GCM-SHA384"}, - {0xC028, "ECDHE-RSA-AES256-SHA384"}, - {0xC024, "ECDHE-ECDSA-AES256-SHA384"}, - {0xC014, "ECDHE-RSA-AES256-SHA"}, - {0xC00A, "ECDHE-ECDSA-AES256-SHA"}, - {0x00A5, "DH-DSS-AES256-GCM-SHA384"}, - {0x00A3, "DHE-DSS-AES256-GCM-SHA384"}, - {0x00A1, "DH-RSA-AES256-GCM-SHA384"}, - {0x009F, "DHE-RSA-AES256-GCM-SHA384"}, - {0x006B, "DHE-RSA-AES256-SHA256"}, - {0x006A, "DHE-DSS-AES256-SHA256"}, - {0x0069, "DH-RSA-AES256-SHA256"}, - {0x0068, "DH-DSS-AES256-SHA256"}, - {0x0039, "DHE-RSA-AES256-SHA"}, - {0x0038, "DHE-DSS-AES256-SHA"}, - {0x0037, "DH-RSA-AES256-SHA"}, - {0x0036, "DH-DSS-AES256-SHA"}, - {0x0088, "DHE-RSA-CAMELLIA256-SHA"}, - {0x0087, "DHE-DSS-CAMELLIA256-SHA"}, - {0x0086, "DH-RSA-CAMELLIA256-SHA"}, - {0x0085, "DH-DSS-CAMELLIA256-SHA"}, - {0xC019, "AECDH-AES256-SHA"}, - {0x00A7, "ADH-AES256-GCM-SHA384"}, - {0x006D, "ADH-AES256-SHA256"}, - {0x003A, "ADH-AES256-SHA"}, - {0x0089, "ADH-CAMELLIA256-SHA"}, - {0xC032, "ECDH-RSA-AES256-GCM-SHA384"}, - {0xC02E, "ECDH-ECDSA-AES256-GCM-SHA384"}, - {0xC02A, "ECDH-RSA-AES256-SHA384"}, - {0xC026, "ECDH-ECDSA-AES256-SHA384"}, - {0xC00F, "ECDH-RSA-AES256-SHA"}, - {0xC005, "ECDH-ECDSA-AES256-SHA"}, - {0x009D, "AES256-GCM-SHA384"}, - {0x003D, "AES256-SHA256"}, - {0x0035, "AES256-SHA"}, - {0x0084, "CAMELLIA256-SHA"}, - {0x008D, "PSK-AES256-CBC-SHA"}, - {0xC02F, "ECDHE-RSA-AES128-GCM-SHA256"}, - {0xC02B, "ECDHE-ECDSA-AES128-GCM-SHA256"}, - {0xC027, "ECDHE-RSA-AES128-SHA256"}, - {0xC023, "ECDHE-ECDSA-AES128-SHA256"}, - {0xC013, "ECDHE-RSA-AES128-SHA"}, - {0xC009, "ECDHE-ECDSA-AES128-SHA"}, - {0x00A4, "DH-DSS-AES128-GCM-SHA256"}, - {0x00A2, "DHE-DSS-AES128-GCM-SHA256"}, - {0x00A0, "DH-RSA-AES128-GCM-SHA256"}, - {0x009E, "DHE-RSA-AES128-GCM-SHA256"}, - {0x0067, "DHE-RSA-AES128-SHA256"}, - {0x0040, "DHE-DSS-AES128-SHA256"}, - {0x003F, "DH-RSA-AES128-SHA256"}, - {0x003E, "DH-DSS-AES128-SHA256"}, - {0x0033, "DHE-RSA-AES128-SHA"}, - {0x0032, "DHE-DSS-AES128-SHA"}, - {0x0031, "DH-RSA-AES128-SHA"}, - {0x0030, "DH-DSS-AES128-SHA"}, - {0x009A, "DHE-RSA-SEED-SHA"}, - {0x0099, "DHE-DSS-SEED-SHA"}, - {0x0098, "DH-RSA-SEED-SHA"}, - {0x0097, "DH-DSS-SEED-SHA"}, - {0x0045, "DHE-RSA-CAMELLIA128-SHA"}, - {0x0044, "DHE-DSS-CAMELLIA128-SHA"}, - {0x0043, "DH-RSA-CAMELLIA128-SHA"}, - {0x0042, "DH-DSS-CAMELLIA128-SHA"}, - {0xC018, "AECDH-AES128-SHA"}, - {0x00A6, "ADH-AES128-GCM-SHA256"}, - {0x006C, "ADH-AES128-SHA256"}, - {0x0034, "ADH-AES128-SHA"}, - {0x009B, "ADH-SEED-SHA"}, - {0x0046, "ADH-CAMELLIA128-SHA"}, - {0xC031, "ECDH-RSA-AES128-GCM-SHA256"}, - {0xC02D, "ECDH-ECDSA-AES128-GCM-SHA256"}, - {0xC029, "ECDH-RSA-AES128-SHA256"}, - {0xC025, "ECDH-ECDSA-AES128-SHA256"}, - {0xC00E, "ECDH-RSA-AES128-SHA"}, - {0xC004, "ECDH-ECDSA-AES128-SHA"}, - {0x009C, "AES128-GCM-SHA256"}, - {0x003C, "AES128-SHA256"}, - {0x002F, "AES128-SHA"}, - {0x0096, "SEED-SHA"}, - {0x0041, "CAMELLIA128-SHA"}, - {0x008C, "PSK-AES128-CBC-SHA"}, - {0xC012, "ECDHE-RSA-DES-CBC3-SHA"}, - {0xC008, "ECDHE-ECDSA-DES-CBC3-SHA"}, - {0x0016, "EDH-RSA-DES-CBC3-SHA"}, - {0x0013, "EDH-DSS-DES-CBC3-SHA"}, - {0x0010, "DH-RSA-DES-CBC3-SHA"}, - {0x000D, "DH-DSS-DES-CBC3-SHA"}, - {0xC017, "AECDH-DES-CBC3-SHA"}, - {0x001B, "ADH-DES-CBC3-SHA"}, - {0xC00D, "ECDH-RSA-DES-CBC3-SHA"}, - {0xC003, "ECDH-ECDSA-DES-CBC3-SHA"}, - {0x000A, "DES-CBC3-SHA"}, - {0x0007, "IDEA-CBC-SHA"}, - {0x008B, "PSK-3DES-EDE-CBC-SHA"}, - {0x0021, "KRB5-IDEA-CBC-SHA"}, - {0x001F, "KRB5-DES-CBC3-SHA"}, - {0x0025, "KRB5-IDEA-CBC-MD5"}, - {0x0023, "KRB5-DES-CBC3-MD5"}, - {0xC011, "ECDHE-RSA-RC4-SHA"}, - {0xC007, "ECDHE-ECDSA-RC4-SHA"}, - {0xC016, "AECDH-RC4-SHA"}, - {0x0018, "ADH-RC4-MD5"}, - {0xC00C, "ECDH-RSA-RC4-SHA"}, - {0xC002, "ECDH-ECDSA-RC4-SHA"}, - {0x0005, "RC4-SHA"}, - {0x0004, "RC4-MD5"}, - {0x008A, "PSK-RC4-SHA"}, - {0x0020, "KRB5-RC4-SHA"}, - {0x0024, "KRB5-RC4-MD5"}, - {0xC010, "ECDHE-RSA-NULL-SHA"}, - {0xC006, "ECDHE-ECDSA-NULL-SHA"}, - {0xC015, "AECDH-NULL-SHA"}, - {0xC00B, "ECDH-RSA-NULL-SHA"}, - {0xC001, "ECDH-ECDSA-NULL-SHA"}, - {0x003B, "NULL-SHA256"}, - {0x0002, "NULL-SHA"}, - {0x0001, "NULL-MD5"} -}; - -struct cipher_suite cipher_suite_list_tls13[] = -{ - {0x1301, "TLS_AES_128_GCM_SHA256"}, - {0x1302, "TLS_AES_256_GCM_SHA384"}, - {0x1303, "TLS_CHACHA20_POLY1305_SHA256"}, - {0x1304, "TLS_AES_128_CCM_SHA256"}, - {0x1305, "TLS_AES_128_CCM_8_SHA256"} -}; - - -void ssl_chello_free(struct ssl_chello* chello) -{ - if(chello==NULL) - { - return; - } - free(chello->sni); - chello->sni = NULL; - free(chello->alpn); - chello->alpn = NULL; - free(chello->cipher_suites); - chello->cipher_suites = NULL; - free(chello->cipher_suites_tls13); - chello->cipher_suites_tls13 = NULL; - free(chello); -} - -static char* parse_alpn_extension(const unsigned char* buff, size_t buff_len, enum chello_parse_result* result) -{ - size_t pos = 0; - size_t len = ((size_t)buff[pos] << 8) + (size_t)buff[pos + 1]; - if(2 + len != buff_len) - { - *result = CHELLO_PARSE_INVALID_FORMAT; - return NULL; - } - char* alpn = ALLOC(char, len + 1); - strncpy((char*)alpn, (const char*)buff + 2, len); - alpn[len] = '\0'; - *result = CHELLO_PARSE_SUCCESS; - return alpn; -} - -static char* parse_server_name_extension(const unsigned char* buff, size_t buff_len, enum chello_parse_result* result) -{ - size_t pos = 2; /* skip server name list length */ - size_t len; - char* sni = NULL; - while (pos + 3 < buff_len) - { - len = ((size_t)buff[pos + 1] << 8) + (size_t)buff[pos + 2]; - if (pos + 3 + len > buff_len) - { - *result = CHELLO_PARSE_INVALID_FORMAT; - return NULL; - } - switch (buff[pos]) - { - case 0x00: /* host_name */ - sni = (char*)malloc(len + 1); - strncpy(sni, (const char*)buff + pos + 3, len); - sni[len] = '\0'; - *result = CHELLO_PARSE_SUCCESS; - } - pos += 3 + len; - } - if (pos != buff_len) - { - *result = CHELLO_PARSE_INVALID_FORMAT; - } - return sni; -} - -static enum chello_parse_result parse_extensions(const unsigned char* buff, size_t buff_len, struct ssl_chello* chello) { - size_t pos = 0; - /* Parse each 4 bytes for the extension header */ - while (pos + 4 <= buff_len) - { - size_t len = ((size_t)buff[pos + 2] << 8) + (size_t)buff[pos + 3]; - /* Check if it's a server name extension */ - if (buff[pos] == 0x00 && buff[pos + 1] == 0x00) - { - if (pos + 4 + len > buff_len) - { - return CHELLO_PARSE_INVALID_FORMAT; - } - enum chello_parse_result result = CHELLO_PARSE_SUCCESS; - chello->sni = parse_server_name_extension(buff + pos + 4, len, &result); - if(result != CHELLO_PARSE_SUCCESS) - { - return result; - } - } - /* Check if it's a alpn extension */ - if (buff[pos] == 0x00 && buff[pos + 1] == 0x10) - { - if (pos + 4 + len > buff_len) - { - return CHELLO_PARSE_INVALID_FORMAT; - } - enum chello_parse_result result = CHELLO_PARSE_SUCCESS; - chello->alpn = parse_alpn_extension(buff + pos + 4, len, &result); - if(result != CHELLO_PARSE_SUCCESS) - { - return result; - } - } - pos += (4 + len); - } - /* Check we ended where we expected to */ - if (pos != buff_len) - { - return CHELLO_PARSE_INVALID_FORMAT; - } - return CHELLO_PARSE_SUCCESS; -} - -static char* parse_cipher_suites(struct cipher_suite* _cipher_suite_list, int n, const unsigned char* buff, size_t buff_len, enum chello_parse_result* result) -{ - char* cipher_suites_str = (char* )malloc(TFE_STRING_MAX); - cipher_suites_str[0] = '\0'; - size_t pos = 0; - int flag = 0; - while(pos < buff_len) - { - int i = 0; - for(i = 0;i < n; i++) - { - int val = (buff[pos] << 8) + buff[pos + 1]; - if(_cipher_suite_list[i].value == val) - { - if(strnlen(_cipher_suite_list[i].name, TFE_STRING_MAX) + strnlen(cipher_suites_str, TFE_STRING_MAX) + 1 > TFE_STRING_MAX) - { - flag = 1; - break; - } - strncat(cipher_suites_str, _cipher_suite_list[i].name, TFE_STRING_MAX); - strncat(cipher_suites_str, ":", TFE_STRING_MAX); - } - } - pos += 2; - if(flag == 1) - { - break; - } - } - int len = strnlen(cipher_suites_str, TFE_STRING_MAX); - if(len > 0) - { - cipher_suites_str[len-1] = '\0'; - } - if(pos != buff_len && flag == 0) - { - *result = CHELLO_PARSE_INVALID_FORMAT; - free(cipher_suites_str); - return NULL; - } - *result = CHELLO_PARSE_SUCCESS; - return cipher_suites_str; -} - -struct ssl_chello* ssl_chello_parse(const unsigned char* buff, size_t buff_len, enum chello_parse_result* result) -{ - if(buff == NULL) - { - *result = CHELLO_PARSE_INVALID_FORMAT; - return NULL; - } - if(buff_len < 1) - { - *result = CHELLO_PARSE_NOT_ENOUGH_BUFF; - return NULL; - } - if(buff[0] != 0x80 && buff[0] != 0x16) - { - *result = CHELLO_PARSE_INVALID_FORMAT; - return NULL; - } - /* SSL 2.0 compatible Client Hello - * High bit of first byte (length) and content type is Client Hello - * See RFC5246 Appendix E.2 - * if it is SSL 2.0, only parse version - */ - if(buff[0] == 0x80) - { - struct ssl_chello* _chello = (struct ssl_chello*)ALLOC(struct ssl_chello, 1); - _chello->min_version.major = 0x02; - if(buff_len < 2) - { - *result = CHELLO_PARSE_NOT_ENOUGH_BUFF; - return _chello; - } - size_t len = (size_t)buff[1]; - if (buff_len < len + 2) - { - *result = CHELLO_PARSE_NOT_ENOUGH_BUFF; - return _chello; - } - buff_len = len + 2; - size_t pos = 2; - /* Handshark Message Type: Client Hello */ - if (pos + 1 > buff_len) - { - *result = CHELLO_PARSE_INVALID_FORMAT; - return _chello; - } - if (buff[pos] != 0x01) - { - *result = CHELLO_PARSE_INVALID_FORMAT; - return _chello; - } - pos += 1; - /* Version */ - if(pos + 2 > buff_len) - { - *result = CHELLO_PARSE_INVALID_FORMAT; - return _chello; - } - _chello->max_version.major = buff[pos]; - _chello->max_version.minor = buff[pos + 1]; - _chello->max_version.ossl_format=(uint16_t)_chello->max_version.major<<8|_chello->max_version.minor; - *result = CHELLO_PARSE_SUCCESS; - return _chello; - } - else - { - if (buff_len < 5) - { - *result = CHELLO_PARSE_NOT_ENOUGH_BUFF; - return NULL; - } - if(buff[1] != 3 || buff[2] > 4 || buff[2] < 0) - { - *result = CHELLO_PARSE_INVALID_FORMAT; - return NULL; - } - struct ssl_chello* _chello = (struct ssl_chello*)ALLOC(struct ssl_chello, 1); - _chello->min_version.major = buff[1]; - _chello->min_version.minor = buff[2]; - _chello->min_version.ossl_format=(uint16_t)_chello->min_version.major<<8|_chello->min_version.minor; - _chello->max_version.major = -1; - _chello->max_version.minor = -1; - _chello->sni = NULL; - _chello->alpn = NULL; - _chello->cipher_suites = NULL; - _chello->cipher_suites_tls13 = NULL; - /* TLS record length */ - size_t len = ((size_t)buff[3] << 8) + (size_t)buff[4] + 5; - if (buff_len < len) - { - *result = CHELLO_PARSE_NOT_ENOUGH_BUFF; - return _chello; - } - buff_len = len; - size_t pos = 5; - if (pos + 1 > buff_len) - { - *result = CHELLO_PARSE_INVALID_FORMAT; - return _chello; - } - if (buff[pos] != 0x01) - { - *result = CHELLO_PARSE_INVALID_FORMAT; - return _chello; - } - pos += 4; - if(pos + 2 > buff_len) - { - *result = CHELLO_PARSE_INVALID_FORMAT; - return _chello; - } - _chello->max_version.major = buff[pos]; - _chello->max_version.minor = buff[pos+1]; - _chello->max_version.ossl_format=(uint16_t)_chello->max_version.major<<8|_chello->max_version.minor; - - pos += 34; - /* Session ID */ - if (pos + 1 > buff_len) - { - *result = CHELLO_PARSE_INVALID_FORMAT; - return _chello; - } - len = (size_t)buff[pos]; - pos += 1 + len; - /* Cipher Suites */ - if (pos + 2 > buff_len) - { - *result = CHELLO_PARSE_INVALID_FORMAT; - return _chello; - } - len = ((size_t)buff[pos] << 8) + (size_t)buff[pos + 1]; - pos += 2; - if(pos + len > buff_len) - { - *result = CHELLO_PARSE_INVALID_FORMAT; - return _chello; - } - int n = sizeof(cipher_suite_list) / sizeof(struct cipher_suite); - _chello->cipher_suites = parse_cipher_suites(cipher_suite_list, n, buff + pos, len, result); - if(*result != CHELLO_PARSE_SUCCESS) - { - return _chello; - } - n = sizeof(cipher_suite_list_tls13) / sizeof(struct cipher_suite); - _chello->cipher_suites_tls13 = parse_cipher_suites(cipher_suite_list_tls13, n, buff + pos, len, result); - if(*result != CHELLO_PARSE_SUCCESS) - { - return _chello; - } - pos += len; - /* Compression Methods */ - if (pos >= buff_len) - { - *result = CHELLO_PARSE_INVALID_FORMAT; - return _chello; - } - len = (size_t)buff[pos]; - pos += 1 + len; - /* ssl 3.0, no extensions */ - if(_chello->min_version.major == 3 && _chello->min_version.minor == 0) - { - if(pos == buff_len) - { - *result = CHELLO_PARSE_SUCCESS; - return _chello; - } - else - { - *result = CHELLO_PARSE_INVALID_FORMAT; - return _chello; - } - } - /* Extensions */ - if (pos + 2 > buff_len) - { - *result = CHELLO_PARSE_INVALID_FORMAT; - return _chello; - } - len = ((size_t)buff[pos] << 8) + (size_t)buff[pos + 1]; - pos += 2; - if (pos + len > buff_len) - { - *result = CHELLO_PARSE_INVALID_FORMAT; - return _chello; - } - enum chello_parse_result rtn = parse_extensions(buff + pos, len, _chello); - *result = rtn; - return _chello; - } -} diff --git a/platform/src/ssl_utils.cpp b/platform/src/ssl_utils.cpp new file mode 100644 index 0000000..d07a57d --- /dev/null +++ b/platform/src/ssl_utils.cpp @@ -0,0 +1,2158 @@ +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Print OpenSSL version and build-time configuration to standard error and + * return. + */ +void ssl_openssl_version(void) +{ + fprintf(stderr, "compiled against %s (%lx)\n", OPENSSL_VERSION_TEXT, (long unsigned int) OPENSSL_VERSION_NUMBER); + fprintf(stderr, "rtlinked against %s (%lx)\n", SSLeay_version(SSLEAY_VERSION), SSLeay()); + + if ((OPENSSL_VERSION_NUMBER ^ SSLeay()) & 0xfffff000L) + { + fprintf(stderr, "---------------------------------------" + "---------------------------------------\n"); + fprintf(stderr, "WARNING: OpenSSL version mismatch may " + "lead to crashes or other problems!\n"); + fprintf(stderr, "If there are multiple versions of " + "OpenSSL available, make sure to use\n"); + fprintf(stderr, "the same version of the library at " + "runtime as well as for compiling against.\n"); + fprintf(stderr, "---------------------------------------" + "---------------------------------------\n"); + } + +#ifdef LIBRESSL_VERSION_NUMBER + fprintf(stderr, "LibreSSL detected: %s (%lx)\n", + LIBRESSL_VERSION_TEXT, + (long unsigned int)LIBRESSL_VERSION_NUMBER); +#endif /* LIBRESSL_VERSION_NUMBER */ +#ifdef OPENSSL_IS_BORINGSSL + fprintf(stderr, "BoringSSL detected\n") +#endif /* OPENSSL_IS_BORINGSSL */ + +#ifndef OPENSSL_NO_TLSEXT + fprintf(stderr, "OpenSSL has support for TLS extensions\n" + "TLS Server Name Indication (SNI) supported\n"); +#else /* OPENSSL_NO_TLSEXT */ + fprintf(stderr, "OpenSSL has no support for TLS extensions\n" + "TLS Server Name Indication (SNI) not supported\n"); +#endif /* OPENSSL_NO_TLSEXT */ +#ifdef OPENSSL_THREADS +#ifndef OPENSSL_NO_THREADID + fprintf(stderr, "OpenSSL is thread-safe with THREADID\n"); +#else /* OPENSSL_NO_THREADID */ + fprintf(stderr, "OpenSSL is thread-safe without THREADID\n"); +#endif /* OPENSSL_NO_THREADID */ +#else /* !OPENSSL_THREADS */ + fprintf(stderr, "OpenSSL is not thread-safe\n"); +#endif /* !OPENSSL_THREADS */ +#ifdef SSL_MODE_RELEASE_BUFFERS + fprintf(stderr, "Using SSL_MODE_RELEASE_BUFFERS\n"); +#else /* !SSL_MODE_RELEASE_BUFFERS */ + fprintf(stderr, "Not using SSL_MODE_RELEASE_BUFFERS\n"); +#endif /* !SSL_MODE_RELEASE_BUFFERS */ +#if (OPENSSL_VERSION_NUMBER == 0x0090819fL) || \ + (OPENSSL_VERSION_NUMBER == 0x100000bfL) || \ + (OPENSSL_VERSION_NUMBER == 0x1000105fL) + fprintf(stderr, "Using direct access workaround when loading certs\n"); +#endif /* OpenSSL 0.9.8y, 1.0.0k or 1.0.1e */ + + fprintf(stderr, "SSL/TLS protocol availability: %s\n", + SSL_PROTO_SUPPORT_S); + + fprintf(stderr, "SSL/TLS algorithm availability:"); +#ifndef OPENSSL_NO_SHA0 + fprintf(stderr, " SHA0"); +#else /* !OPENSSL_NO_SHA0 */ + fprintf(stderr, " !SHA0"); +#endif /* !OPENSSL_NO_SHA0 */ +#ifndef OPENSSL_NO_RSA + fprintf(stderr, " RSA"); +#else /* !OPENSSL_NO_RSA */ + fprintf(stderr, " !RSA"); +#endif /* !OPENSSL_NO_RSA */ +#ifndef OPENSSL_NO_DSA + fprintf(stderr, " DSA"); +#else /* !OPENSSL_NO_DSA */ + fprintf(stderr, " !DSA"); +#endif /* !OPENSSL_NO_DSA */ +#ifndef OPENSSL_NO_ECDSA + fprintf(stderr, " ECDSA"); +#else /* !OPENSSL_NO_ECDSA */ + fprintf(stderr, " !ECDSA"); +#endif /* !OPENSSL_NO_ECDSA */ +#ifndef OPENSSL_NO_DH + fprintf(stderr, " DH"); +#else /* !OPENSSL_NO_DH */ + fprintf(stderr, " !DH"); +#endif /* !OPENSSL_NO_DH */ +#ifndef OPENSSL_NO_ECDH + fprintf(stderr, " ECDH"); +#else /* !OPENSSL_NO_ECDH */ + fprintf(stderr, " !ECDH"); +#endif /* !OPENSSL_NO_ECDH */ +#ifndef OPENSSL_NO_EC + fprintf(stderr, " EC"); +#else /* !OPENSSL_NO_EC */ + fprintf(stderr, " !EC"); +#endif /* !OPENSSL_NO_EC */ + fprintf(stderr, "\n"); + + fprintf(stderr, "OpenSSL option availability:"); +#ifdef SSL_OP_NO_COMPRESSION + fprintf(stderr, " SSL_OP_NO_COMPRESSION"); +#else /* !SSL_OP_NO_COMPRESSION */ + fprintf(stderr, " !SSL_OP_NO_COMPRESSION"); +#endif /* SSL_OP_NO_COMPRESSION */ +#ifdef SSL_OP_NO_TICKET + fprintf(stderr, " SSL_OP_NO_TICKET"); +#else /* !SSL_OP_NO_TICKET */ + fprintf(stderr, " !SSL_OP_NO_TICKET"); +#endif /* SSL_OP_NO_TICKET */ +#ifdef SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION + fprintf(stderr, " SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION"); +#else /* !SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION */ + fprintf(stderr, " !SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION"); +#endif /* !SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION */ +#ifdef SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS + fprintf(stderr, " SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS"); +#else /* !SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS */ + fprintf(stderr, " !SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS"); +#endif /* !SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS */ +#ifdef SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION + fprintf(stderr, " SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION"); +#else /* !SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION */ + fprintf(stderr, " !SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION"); +#endif /* !SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION */ +#ifdef SSL_OP_TLS_ROLLBACK_BUG + fprintf(stderr, " SSL_OP_TLS_ROLLBACK_BUG"); +#else /* !SSL_OP_TLS_ROLLBACK_BUG */ + fprintf(stderr, " !SSL_OP_TLS_ROLLBACK_BUG"); +#endif /* !SSL_OP_TLS_ROLLBACK_BUG */ + fprintf(stderr, "\n"); +} + +/* + * 1 if OpenSSL has been initialized, 0 if not. When calling a _load() + * function the first time, OpenSSL will automatically be initialized. + * Not protected by a mutex and thus not thread-safe. + */ +static int ssl_initialized = 0; + +int ssl_init(void) +{ + int fd; + char buf[256]; + + if (ssl_initialized) return 0; + + /* general initialization */ + SSL_library_init(); + +#ifdef PURIFY + CRYPTO_malloc_init(); + CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ON); +#endif /* PURIFY */ + + SSL_load_error_strings(); + OpenSSL_add_all_algorithms(); + + if ((fd = open("/dev/urandom", O_RDONLY)) == -1) + { + TFE_LOG_ERROR(NULL, "Error opening /dev/urandom for reading: %s", strerror(errno)); + goto __errout; + } + + while (!RAND_status()) + { + if (read(fd, buf, sizeof(buf)) == -1) + { + TFE_LOG_ERROR(NULL, "Error reading from /dev/urandom: %s", strerror(errno)); + goto __errout; + } + + RAND_seed(buf, sizeof(buf)); + } + + if (!RAND_poll()) + { + TFE_LOG_ERROR(NULL, "RAND_poll() failed."); + return -1; + } + + ssl_initialized = 1; + close(fd); + + return 0; + +__errout: + if (fd > 0) close(fd); + return -1; +} + +/* + * Re-initialize OpenSSL after forking. Returns 0 on success, -1 on failure. + */ +int ssl_reinit(void) +{ + if (!ssl_initialized) + return 0; + return 0; +} + +/* + * Deinitialize OpenSSL and free as much memory as possible. + * Some 10k-100k will still remain resident no matter what. + */ +void +ssl_fini(void) +{ + if (!ssl_initialized) + return; + + ENGINE_cleanup(); + CONF_modules_finish(); + CONF_modules_unload(1); + CONF_modules_free(); + + EVP_cleanup(); + ERR_free_strings(); + CRYPTO_cleanup_all_ex_data(); +} + +/* + * Format raw SHA1 hash into newly allocated string, with or without colons. + */ +char * ssl_sha1_to_str(unsigned char * rawhash, int colons) +{ + char * str; + int rv; + + rv = asprintf(&str, colons ? + "%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X" + "%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X" : + "%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X" + "%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X", + rawhash[0], rawhash[1], rawhash[2], rawhash[3], + rawhash[4], rawhash[5], rawhash[6], rawhash[7], + rawhash[8], rawhash[9], rawhash[10], rawhash[11], + rawhash[12], rawhash[13], rawhash[14], rawhash[15], + rawhash[16], rawhash[17], rawhash[18], rawhash[19]); + + if (rv == -1) + return NULL; + return str; +} + +/* + * Format SSL state into newly allocated string. + * Returns pointer to string that must be freed by caller, or NULL on error. + */ +char * ssl_ssl_state_to_str(SSL * ssl) +{ + char * str = NULL; + int rv; + + rv = asprintf(&str, "%08x = %s%s%04x = %s (%s) [%s]", + SSL_get_state(ssl), + (SSL_get_state(ssl) & SSL_ST_CONNECT) ? "SSL_ST_CONNECT|" : "", + (SSL_get_state(ssl) & SSL_ST_ACCEPT) ? "SSL_ST_ACCEPT|" : "", + SSL_get_state(ssl) & SSL_ST_MASK, + SSL_state_string(ssl), + SSL_state_string_long(ssl), + SSL_is_server(ssl) ? "accept socket" : "connect socket"); + + return (rv < 0) ? NULL : str; +} + +/* + * Generates a NSS key log format compatible string containing the client + * random and the master key, intended to be used to decrypt externally + * captured network traffic using tools like Wireshark. + * + * Only supports the CLIENT_RANDOM method (SSL 3.0 - TLS 1.2). + * + * https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/Key_Log_Format + */ +char * ssl_ssl_masterkey_to_str(SSL * ssl) +{ + char * str = NULL; + int rv; + unsigned char * k, * r; + + unsigned char kbuf[48], rbuf[32]; + k = &kbuf[0]; + r = &rbuf[0]; + SSL_SESSION* sess=SSL_get0_session(ssl); + SSL_SESSION_get_master_key(sess, k, sizeof(kbuf)); + SSL_get_client_random(ssl, r, sizeof(rbuf)); + + rv = asprintf(&str, + "CLIENT_RANDOM " + "%02X%02X%02X%02X%02X%02X%02X%02X" + "%02X%02X%02X%02X%02X%02X%02X%02X" + "%02X%02X%02X%02X%02X%02X%02X%02X" + "%02X%02X%02X%02X%02X%02X%02X%02X" + " " + "%02X%02X%02X%02X%02X%02X%02X%02X" + "%02X%02X%02X%02X%02X%02X%02X%02X" + "%02X%02X%02X%02X%02X%02X%02X%02X" + "%02X%02X%02X%02X%02X%02X%02X%02X" + "%02X%02X%02X%02X%02X%02X%02X%02X" + "%02X%02X%02X%02X%02X%02X%02X%02X" + "\n", + r[0], r[1], r[2], r[3], r[4], r[5], r[6], r[7], + r[8], r[9], r[10], r[11], r[12], r[13], r[14], r[15], + r[16], r[17], r[18], r[19], r[20], r[21], r[22], r[23], + r[24], r[25], r[26], r[27], r[28], r[29], r[30], r[31], + k[0], k[1], k[2], k[3], k[4], k[5], k[6], k[7], + k[8], k[9], k[10], k[11], k[12], k[13], k[14], k[15], + k[16], k[17], k[18], k[19], k[20], k[21], k[22], k[23], + k[24], k[25], k[26], k[27], k[28], k[29], k[30], k[31], + k[32], k[33], k[34], k[35], k[36], k[37], k[38], k[39], + k[40], k[41], k[42], k[43], k[44], k[45], k[46], k[47]); + + return (rv < 0) ? NULL : str; +} + +#ifndef OPENSSL_NO_DH +static unsigned char dh_g[] = {0x02}; +static unsigned char dh512_p[] = { + 0xAB, 0xC0, 0x34, 0x16, 0x95, 0x8B, 0x57, 0xE5, 0x5C, 0xB3, 0x4E, 0x6E, + 0x16, 0x0B, 0x35, 0xC5, 0x6A, 0xCC, 0x4F, 0xD3, 0xE5, 0x46, 0xE2, 0x23, + 0x6A, 0x5B, 0xBB, 0x5D, 0x3D, 0x52, 0xEA, 0xCE, 0x4F, 0x7D, 0xCA, 0xFF, + 0xB4, 0x8B, 0xC9, 0x78, 0xDC, 0xA0, 0xFC, 0xBE, 0xF3, 0xB5, 0xE6, 0x61, + 0xA6, 0x6D, 0x58, 0xFC, 0xA0, 0x0F, 0xF7, 0x9B, 0x97, 0xE6, 0xC7, 0xE8, + 0x1F, 0xCD, 0x16, 0x73}; +static unsigned char dh1024_p[] = { + 0x99, 0x28, 0x34, 0x48, 0x9E, 0xB7, 0xD1, 0x4F, 0x0D, 0x17, 0x09, 0x97, + 0xB9, 0x9B, 0x20, 0xFE, 0xE5, 0x65, 0xE0, 0xE2, 0x56, 0x37, 0x80, 0xA2, + 0x9F, 0x2C, 0x2D, 0x87, 0x10, 0x58, 0x39, 0xAD, 0xF3, 0xC5, 0xA9, 0x08, + 0x24, 0xC7, 0xAA, 0xA9, 0x29, 0x3A, 0x13, 0xDF, 0x4E, 0x0A, 0x6D, 0x11, + 0x39, 0xB1, 0x1C, 0x3F, 0xFE, 0xFE, 0x0A, 0x5E, 0xAD, 0x2E, 0x5C, 0x10, + 0x97, 0x38, 0xAC, 0xE8, 0xEB, 0xAA, 0x4A, 0xA1, 0xC0, 0x5C, 0x1D, 0x27, + 0x65, 0x9C, 0xC8, 0x53, 0xAC, 0x35, 0xDD, 0x84, 0x1F, 0x47, 0x0E, 0x04, + 0xF1, 0x90, 0x61, 0x62, 0x2E, 0x29, 0x2C, 0xC6, 0x28, 0x91, 0x6D, 0xF0, + 0xE2, 0x5E, 0xCE, 0x60, 0x3E, 0xF7, 0xF8, 0x37, 0x99, 0x4D, 0x9F, 0xFB, + 0x68, 0xEC, 0x7F, 0x9D, 0x32, 0x74, 0xD1, 0xAA, 0xD4, 0x4C, 0xF5, 0xCD, + 0xC2, 0xD7, 0xD7, 0xAC, 0xDA, 0x69, 0xF5, 0x2B}; +static unsigned char dh2048_p[] = { + 0xAB, 0x88, 0x97, 0xCA, 0xF1, 0xE1, 0x60, 0x39, 0xFA, 0xB1, 0xA8, 0x7D, + 0xB3, 0x7A, 0x38, 0x08, 0xF0, 0x7A, 0x3D, 0x21, 0xC4, 0xE6, 0xB8, 0x32, + 0x3D, 0xAB, 0x0F, 0xE7, 0x8C, 0xA1, 0x59, 0x47, 0xB2, 0x0A, 0x7A, 0x3A, + 0x20, 0x2A, 0x1B, 0xD4, 0xBA, 0xFC, 0x4C, 0xC5, 0xEE, 0xA2, 0xB9, 0xB9, + 0x65, 0x47, 0xCC, 0x13, 0x99, 0xD7, 0xA6, 0xCA, 0xFF, 0x23, 0x05, 0x91, + 0xAB, 0x5C, 0x82, 0xB8, 0xB4, 0xFD, 0xB1, 0x2E, 0x5B, 0x0F, 0x8E, 0x03, + 0x3C, 0x23, 0xD6, 0x6A, 0xE2, 0x83, 0x95, 0xD2, 0x8E, 0xEB, 0xDF, 0x3A, + 0xAF, 0x89, 0xF0, 0xA0, 0x14, 0x09, 0x12, 0xF6, 0x54, 0x54, 0x93, 0xF4, + 0xD4, 0x41, 0x56, 0x7A, 0x0E, 0x56, 0x20, 0x1F, 0x1D, 0xBA, 0x3F, 0x07, + 0xD2, 0x89, 0x1B, 0x40, 0xD0, 0x1C, 0x08, 0xDF, 0x00, 0x7F, 0x34, 0xF4, + 0x28, 0x4E, 0xF7, 0x53, 0x8D, 0x4A, 0x00, 0xC3, 0xC0, 0x89, 0x9E, 0x63, + 0x96, 0xE9, 0x52, 0xDF, 0xA5, 0x2C, 0x00, 0x4E, 0xB0, 0x82, 0x6A, 0x10, + 0x28, 0x8D, 0xB9, 0xE7, 0x7A, 0xCB, 0xC3, 0xD6, 0xC1, 0xC0, 0x4D, 0x91, + 0xC4, 0x6F, 0xD3, 0x99, 0xD1, 0x86, 0x71, 0x67, 0x0A, 0xA1, 0xFC, 0xF4, + 0x7D, 0x40, 0x88, 0x8D, 0xAC, 0xCB, 0xBC, 0xEA, 0x17, 0x85, 0x0B, 0xC6, + 0x12, 0x3E, 0x4A, 0xB9, 0x60, 0x74, 0x93, 0x54, 0x14, 0x39, 0x10, 0xBF, + 0x21, 0xB0, 0x8B, 0xB1, 0x55, 0x3F, 0xBB, 0x6A, 0x1F, 0x42, 0x82, 0x0A, + 0x40, 0x3A, 0x15, 0xCD, 0xD3, 0x79, 0xD0, 0x02, 0xA4, 0xF5, 0x79, 0x78, + 0x03, 0xBD, 0x47, 0xCC, 0xD5, 0x08, 0x6A, 0x46, 0xAE, 0x36, 0xE4, 0xCD, + 0xB1, 0x17, 0x48, 0x30, 0xB4, 0x02, 0xBC, 0x50, 0x68, 0xE3, 0xA2, 0x76, + 0xD0, 0x5C, 0xB9, 0xE6, 0xBE, 0x4C, 0xFD, 0x50, 0xEF, 0xD0, 0x3F, 0x39, + 0x4F, 0x53, 0x16, 0x3B}; +static unsigned char dh4096_p[] = { + 0xB1, 0xCC, 0x09, 0x86, 0xEE, 0xF9, 0xB9, 0xC9, 0xB9, 0x87, 0xC4, 0xB9, + 0xD7, 0x31, 0x95, 0x84, 0x94, 0x65, 0xED, 0x82, 0x64, 0x11, 0xA7, 0x0A, + 0xFE, 0xC2, 0x60, 0xAE, 0x7C, 0x74, 0xFB, 0x72, 0x8F, 0x0D, 0xA6, 0xDD, + 0x02, 0x49, 0x5B, 0x69, 0xD6, 0x96, 0x05, 0xBE, 0x5E, 0x9B, 0x09, 0x83, + 0xD8, 0xF3, 0x91, 0x55, 0x30, 0x86, 0x97, 0x6C, 0x48, 0x7B, 0x99, 0x82, + 0xCC, 0x1E, 0x1E, 0x25, 0xE6, 0x25, 0xCC, 0xA3, 0x66, 0xDE, 0x8A, 0x78, + 0xEE, 0x7F, 0x4F, 0x86, 0x95, 0x06, 0xBE, 0x64, 0x86, 0xFD, 0x60, 0x6A, + 0x3F, 0x0D, 0x8F, 0x62, 0x17, 0x89, 0xDB, 0xE1, 0x01, 0xC1, 0x75, 0x3A, + 0x78, 0x42, 0xA8, 0x26, 0xEC, 0x00, 0x78, 0xF3, 0xDA, 0x40, 0x8D, 0x0D, + 0x4D, 0x53, 0x82, 0xD7, 0x21, 0xC8, 0x46, 0xC9, 0xE3, 0x80, 0xB4, 0xCF, + 0xEA, 0x46, 0x85, 0xE9, 0xC4, 0x9D, 0xD0, 0xC0, 0x4D, 0x27, 0x0F, 0xF8, + 0x34, 0x3B, 0x86, 0x8F, 0xFC, 0x40, 0x56, 0x49, 0x64, 0x76, 0x61, 0xBC, + 0x35, 0x6A, 0xB8, 0xC5, 0x32, 0x19, 0x00, 0x5E, 0x21, 0x1C, 0x34, 0xCB, + 0x74, 0x5B, 0x60, 0x85, 0x8C, 0x38, 0x52, 0x50, 0x4D, 0xAA, 0x25, 0xE4, + 0x1A, 0xE6, 0xE4, 0xDF, 0x0A, 0xD2, 0x8F, 0x2B, 0xD1, 0x35, 0xC7, 0x92, + 0x7D, 0x6F, 0x54, 0x61, 0x8E, 0x3F, 0xFB, 0xE2, 0xC8, 0x81, 0xD0, 0xAC, + 0x64, 0xE2, 0xA8, 0x30, 0xEA, 0x8E, 0xAD, 0xFE, 0xC0, 0x9E, 0x0B, 0xBF, + 0x34, 0xAC, 0x79, 0x96, 0x38, 0x31, 0x1E, 0xEA, 0xF2, 0x7E, 0xEE, 0x0A, + 0x10, 0x34, 0x7C, 0x1A, 0x30, 0x5F, 0xAF, 0x96, 0x2F, 0x7F, 0xB5, 0x1D, + 0xA7, 0x3D, 0x35, 0x7A, 0x30, 0x70, 0x40, 0xE7, 0xD6, 0x22, 0x1E, 0xD0, + 0x9A, 0x34, 0xC7, 0x6B, 0xE4, 0xF1, 0x78, 0xED, 0xD9, 0xCD, 0x18, 0xBF, + 0x2A, 0x1A, 0x98, 0xB7, 0x6C, 0x6E, 0x18, 0x40, 0xB5, 0xBE, 0xDF, 0xE4, + 0x78, 0x8E, 0x34, 0xB2, 0x7B, 0xE5, 0x88, 0xE6, 0xFD, 0x24, 0xBD, 0xBB, + 0x2E, 0x30, 0x72, 0x54, 0xC7, 0xF4, 0xA0, 0xF1, 0x25, 0xFF, 0xB1, 0x37, + 0x42, 0x07, 0x8C, 0xF2, 0xB9, 0xA1, 0xA4, 0xA7, 0x76, 0x39, 0xB8, 0x11, + 0x17, 0xF3, 0xA8, 0x2E, 0x78, 0x68, 0xF4, 0xBF, 0x98, 0x25, 0x59, 0x17, + 0x59, 0x9B, 0x0D, 0x0B, 0x9B, 0xE3, 0x0F, 0xFF, 0xDC, 0xC8, 0x47, 0x21, + 0xE1, 0x0B, 0x9A, 0x44, 0x79, 0xC7, 0x5F, 0x8E, 0x83, 0x1E, 0x04, 0xA1, + 0xB2, 0x9F, 0x9B, 0xFC, 0xB3, 0x4E, 0xD9, 0xF9, 0x8F, 0x03, 0xBC, 0x0A, + 0x04, 0x00, 0x5C, 0x59, 0xB7, 0x51, 0xAA, 0x75, 0xF8, 0x7A, 0x03, 0x07, + 0x81, 0x6D, 0x67, 0x3E, 0x28, 0x37, 0xE4, 0x74, 0x5B, 0x8C, 0x2A, 0x4B, + 0x6C, 0x10, 0x92, 0x75, 0xA5, 0x79, 0x4B, 0x6D, 0x30, 0xB7, 0x6E, 0xD6, + 0x9E, 0x16, 0xC2, 0x87, 0x69, 0x34, 0xFE, 0xD7, 0x2A, 0x4F, 0xD6, 0xC0, + 0xF3, 0xCD, 0x9C, 0x46, 0xED, 0xC0, 0xB2, 0x84, 0x8D, 0x7E, 0x93, 0xD2, + 0xE9, 0xBE, 0x59, 0x18, 0x92, 0xC1, 0x2C, 0xD6, 0x6C, 0x71, 0x50, 0xA1, + 0x98, 0xDA, 0xD1, 0xAC, 0xDB, 0x88, 0x40, 0x1F, 0x69, 0xDC, 0xDB, 0xB2, + 0xA0, 0x90, 0x01, 0x8E, 0x12, 0xD6, 0x40, 0x1A, 0x8E, 0xC5, 0x69, 0x9C, + 0x91, 0x67, 0xAC, 0xD8, 0x4C, 0x27, 0xCD, 0x08, 0xB8, 0x32, 0x97, 0xE1, + 0x13, 0x0C, 0xFF, 0xB1, 0x06, 0x65, 0x03, 0x98, 0x6F, 0x9E, 0xF7, 0xB8, + 0xA8, 0x75, 0xBA, 0x59, 0xFD, 0x23, 0x98, 0x94, 0x80, 0x9C, 0xA7, 0x46, + 0x32, 0x98, 0x28, 0x7A, 0x0A, 0x3A, 0xA6, 0x95, 0x16, 0x6A, 0x52, 0x8E, + 0x8F, 0x2C, 0xC9, 0x49, 0xB7, 0x59, 0x99, 0x2A, 0xE6, 0xCA, 0x82, 0x88, + 0x36, 0xD3, 0x2B, 0xA4, 0x73, 0xFA, 0x89, 0xBB, +}; + +/* + * OpenSSL temporary DH callback which loads DH parameters from static memory. + */ +DH * ssl_tmp_dh_callback(SSL * s, int is_export, int keylength) +{ + DH * dh; + int rv = 0; + + if (!(dh = DH_new())) + { + TFE_LOG_ERROR(NULL, "DH_new() failed\n"); + return NULL; + } + + switch (keylength) + { + case 512: + rv = DH_set0_pqg(dh, BN_bin2bn(dh512_p, sizeof(dh512_p), NULL), NULL, + BN_bin2bn(dh_g, sizeof(dh_g), NULL)); + break; + case 1024: + rv = DH_set0_pqg(dh, BN_bin2bn(dh1024_p, sizeof(dh1024_p), NULL), NULL, + BN_bin2bn(dh_g, sizeof(dh_g), NULL)); + break; + case 2048: + rv = DH_set0_pqg(dh, BN_bin2bn(dh2048_p, sizeof(dh2048_p), NULL), NULL, + BN_bin2bn(dh_g, sizeof(dh_g), NULL)); + break; + case 4096: + rv = DH_set0_pqg(dh, BN_bin2bn(dh4096_p, sizeof(dh4096_p), NULL), NULL, + BN_bin2bn(dh_g, sizeof(dh_g), NULL)); + break; + default: + TFE_LOG_ERROR(NULL, "Unhandled DH keylength %i%s", keylength, (is_export ? " (export)" : "")); + DH_free(dh); + return NULL; + } + + if (!rv) + { + TFE_LOG_ERROR(NULL, "Failed to load DH p and g from memory\n"); + DH_free(dh); + return NULL; + } + + return (dh); +} + +/* + * Load DH parameters from a PEM file. + * Not thread-safe. + */ +DH * ssl_dh_load(const char * filename) +{ + DH * dh; + FILE * fh; + + if (ssl_init() == -1) + return NULL; + + if (!(fh = fopen(filename, "r"))) + { + return NULL; + } + dh = PEM_read_DHparams(fh, NULL, NULL, NULL); + fclose(fh); + return dh; +} +#endif /* !OPENSSL_NO_DH */ + +#ifndef OPENSSL_NO_EC +/* + * Load an Elliptic Curve by name. If curvename is NULL, a default curve is + * loaded. + */ +EC_KEY * ssl_ec_by_name(const char * curvename) +{ + int nid; + + if (!curvename) curvename = "prime256v1"; + if ((nid = OBJ_sn2nid(curvename)) == NID_undef) + { + return NULL; + } + + return EC_KEY_new_by_curve_name(nid); +} +#endif /* !OPENSSL_NO_EC */ + +/* + * Add a X509v3 extension to a cert and handle errors. + * Returns -1 on errors, 0 on success. + */ +int ssl_x509_v3ext_add(X509V3_CTX * ctx, X509 * crt, const char * k, const char * v) +{ + X509_EXTENSION * ext; + + if (!(ext = X509V3_EXT_conf(NULL, ctx, k, v))) + { + return -1; + } + if (X509_add_ext(crt, ext, -1) != 1) + { + X509_EXTENSION_free(ext); + return -1; + } + X509_EXTENSION_free(ext); + return 0; +} + +/* + * Copy a X509v3 extension from one cert to another. + * If the extension is not present in the original cert, + * the extension will not be added to the destination cert. + * Returns 1 if ext was copied, 0 if not present in origcrt, -1 on error. + */ +int ssl_x509_v3ext_copy_by_nid(X509 * crt, X509 * origcrt, int nid) +{ + X509_EXTENSION * ext; + int pos; + + pos = X509_get_ext_by_NID(origcrt, nid, -1); + if (pos == -1) + return 0; + ext = X509_get_ext(origcrt, pos); + if (!ext) + return -1; + if (X509_add_ext(crt, ext, -1) != 1) + return -1; + return 1; +} + +/* + * Best effort randomness generator. + * Not for real life cryptography applications. + * Returns 0 on success, -1 on failure. + */ +int ssl_rand(void * p, size_t sz) +{ + int rv; + rv = RAND_bytes((unsigned char *) p, sz); + + if (rv == 1) return 0; + return -1; +} + +/* + * Copy the serial number from src cert to dst cert + * and modify it by a random offset. + * If reading the serial fails for some reason, generate a new + * random serial and store it in the dst cert. + * Using the same serial is not a good idea since some SSL stacks + * check for duplicate cert serials. + * Returns 0 on success, -1 on error. + */ +int ssl_x509_serial_copyrand(X509 * dstcrt, X509 * srccrt) +{ + ASN1_INTEGER * srcptr, * dstptr; + BIGNUM * bnserial; + unsigned int rand; + int rv; + +#ifndef PURIFY + rv = ssl_rand(&rand, sizeof(rand)); +#else /* PURIFY */ + rand = 0xF001; + rv = 0; +#endif /* PURIFY */ + dstptr = X509_get_serialNumber(dstcrt); + srcptr = X509_get_serialNumber(srccrt); + if ((rv == -1) || !dstptr || !srcptr) + return -1; + bnserial = ASN1_INTEGER_to_BN(srcptr, NULL); + if (!bnserial) + { + /* random 32-bit serial */ + ASN1_INTEGER_set(dstptr, rand); + } + else + { + /* original serial plus random 32-bit offset */ + BN_add_word(bnserial, rand); + BN_to_ASN1_INTEGER(bnserial, dstptr); + BN_free(bnserial); + } + return 0; +} + +/* + * Create a fake X509v3 cert, signed by the provided CA, + * based on the original cert retrieved from the real server. + * The returned cert is created using X509_new() and thus must + * be freed by the caller using X509_free(). + * The optional argument extraname is added to subjectAltNames if provided. + */ +X509 * ssl_x509_forge(X509 * cacrt, EVP_PKEY * cakey, X509 * origcrt, EVP_PKEY * key, + const char * extraname, const char * crlurl) +{ + X509_NAME * subject, * issuer; + GENERAL_NAMES * names; + GENERAL_NAME * gn; + X509 * crt; + int rv; + + subject = X509_get_subject_name(origcrt); + issuer = X509_get_subject_name(cacrt); + if (!subject || !issuer) + return NULL; + + crt = X509_new(); + if (!crt) + return NULL; + + if (!X509_set_version(crt, 0x02) || + !X509_set_subject_name(crt, subject) || + !X509_set_issuer_name(crt, issuer) || + ssl_x509_serial_copyrand(crt, origcrt) == -1 || + !X509_gmtime_adj(X509_get_notBefore(crt), (long) -60 * 60 * 24) || + !X509_gmtime_adj(X509_get_notAfter(crt), (long) 60 * 60 * 24 * 364) || + !X509_set_pubkey(crt, key)) + goto errout; + + /* add standard v3 extensions; cf. RFC 2459 */ + + X509V3_CTX ctx; + X509V3_set_ctx(&ctx, cacrt, crt, NULL, NULL, 0); + if (ssl_x509_v3ext_add(&ctx, crt, "subjectKeyIdentifier", "hash") == -1 || + ssl_x509_v3ext_add(&ctx, crt, "authorityKeyIdentifier", "keyid,issuer:always") == -1) + goto errout; + + rv = ssl_x509_v3ext_copy_by_nid(crt, origcrt, + NID_basic_constraints); + if (rv == 0) + rv = ssl_x509_v3ext_add(&ctx, crt, "basicConstraints", + "CA:FALSE"); + if (rv == -1) + goto errout; + + rv = ssl_x509_v3ext_copy_by_nid(crt, origcrt, + NID_key_usage); + if (rv == 0) + rv = ssl_x509_v3ext_add(&ctx, crt, "keyUsage", + "digitalSignature," + "keyEncipherment"); + if (rv == -1) + goto errout; + + rv = ssl_x509_v3ext_copy_by_nid(crt, origcrt, + NID_ext_key_usage); + if (rv == 0) + rv = ssl_x509_v3ext_add(&ctx, crt, "extendedKeyUsage", + "serverAuth"); + if (rv == -1) + goto errout; + + if (crlurl) + { + char * crlurlval; + if (asprintf(&crlurlval, "URI:%s", crlurl) < 0) + goto errout; + if (ssl_x509_v3ext_add(&ctx, crt, "crlDistributionPoints", + crlurlval) == -1) + { + free(crlurlval); + goto errout; + } + free(crlurlval); + } + + if (!extraname) + { + /* no extraname provided: copy original subjectAltName ext */ + if (ssl_x509_v3ext_copy_by_nid(crt, origcrt, + NID_subject_alt_name) == -1) + goto errout; + } + else + { + names = (GENERAL_NAMES *) X509_get_ext_d2i(origcrt, NID_subject_alt_name, 0, 0); + if (!names) + { + /* no subjectAltName present: add new one */ + char * cfval; + if (asprintf(&cfval, "DNS:%s", extraname) < 0) + goto errout; + if (ssl_x509_v3ext_add(&ctx, crt, "subjectAltName", + cfval) == -1) + { + free(cfval); + goto errout; + } + free(cfval); + } + else + { + /* add extraname to original subjectAltName + * and add it to the new cert */ + gn = GENERAL_NAME_new(); + if (!gn) + goto errout2; + gn->type = GEN_DNS; + gn->d.dNSName = ASN1_IA5STRING_new(); + if (!gn->d.dNSName) + goto errout3; + ASN1_STRING_set(gn->d.dNSName, + (unsigned char *) extraname, + strlen(extraname)); + sk_GENERAL_NAME_push(names, gn); + X509_EXTENSION * ext = X509V3_EXT_i2d( + NID_subject_alt_name, 0, names); + if (!X509_add_ext(crt, ext, -1)) + { + if (ext) + { + X509_EXTENSION_free(ext); + } + goto errout3; + } + X509_EXTENSION_free(ext); + sk_GENERAL_NAME_pop_free(names, GENERAL_NAME_free); + } + } +#ifdef DEBUG_CERTIFICATE + ssl_x509_v3ext_add(&ctx, crt, "nsComment", "Generated by " PKGLABEL); +#endif /* DEBUG_CERTIFICATE */ + + const EVP_MD * md; + switch (EVP_PKEY_type(EVP_PKEY_base_id(cakey))) + { +#ifndef OPENSSL_NO_RSA + case EVP_PKEY_RSA: + switch (X509_get_signature_nid(origcrt)) + { + case NID_md5WithRSAEncryption: md = EVP_md5(); + break; + case NID_ripemd160WithRSA: md = EVP_ripemd160(); + break; + case NID_sha1WithRSAEncryption: md = EVP_sha1(); + break; + case NID_sha224WithRSAEncryption: md = EVP_sha224(); + break; + case NID_sha256WithRSAEncryption: md = EVP_sha256(); + break; + case NID_sha384WithRSAEncryption: md = EVP_sha384(); + break; + case NID_sha512WithRSAEncryption: md = EVP_sha512(); + break; +#ifndef OPENSSL_NO_SHA0 + case NID_shaWithRSAEncryption: md = EVP_sha(); + break; +#endif /* !OPENSSL_NO_SHA0 */ + default: md = EVP_sha256(); + break; + } + break; +#endif /* !OPENSSL_NO_RSA */ +#ifndef OPENSSL_NO_DSA + case EVP_PKEY_DSA: + switch (X509_get_signature_nid(origcrt)) + { + case NID_dsaWithSHA1: + case NID_dsaWithSHA1_2: md = EVP_sha1(); + break; + case NID_dsa_with_SHA224: md = EVP_sha224(); + break; + case NID_dsa_with_SHA256: md = EVP_sha256(); + break; +#ifndef OPENSSL_NO_SHA0 + case NID_dsaWithSHA: md = EVP_sha(); + break; +#endif /* !OPENSSL_NO_SHA0 */ + default: md = EVP_sha256(); + break; + } + break; +#endif /* !OPENSSL_NO_DSA */ +#ifndef OPENSSL_NO_ECDSA + case EVP_PKEY_EC: + switch (X509_get_signature_nid(origcrt)) + { + case NID_ecdsa_with_SHA1: md = EVP_sha1(); + break; + case NID_ecdsa_with_SHA224: md = EVP_sha224(); + break; + case NID_ecdsa_with_SHA256: md = EVP_sha256(); + break; + case NID_ecdsa_with_SHA384: md = EVP_sha384(); + break; + case NID_ecdsa_with_SHA512: md = EVP_sha512(); + break; + default: md = EVP_sha256(); + break; + } + break; +#endif /* !OPENSSL_NO_ECDSA */ + default: goto errout; + } + if (!X509_sign(crt, cakey, md)) + goto errout; + + return crt; + +errout3: + GENERAL_NAME_free(gn); +errout2: + sk_GENERAL_NAME_pop_free(names, GENERAL_NAME_free); +errout: + X509_free(crt); + return NULL; +} + +/* + * Load a X509 cert chain from a PEM file. + * Returns the first cert in *crt and all subsequent certificates in + * *chain. If crt is NULL, the first cert is prepended to *chain + * instead of returned separately. If *chain is NULL, a new stack of X509* + * is created in *chain, else the certs are pushed onto an existing stack. + * Returns -1 on error. + * Not thread-safe. + * + * By accessing (SSLCTX*)->extra_certs directly on OpenSSL before 1.0.2, we + * depend on OpenSSL internals in this function. OpenSSL 1.0.2 introduced + * the SSL_get0_chain_certs() API for accessing the cert chain. + */ +int ssl_x509chain_load(X509 ** crt, STACK_OF(X509) ** chain, const char * filename) +{ + X509 * tmpcrt; + SSL_CTX * tmpctx; + SSL * tmpssl; + STACK_OF(X509) * tmpchain; + int rv; + + if (ssl_init() == -1) + return -1; + + tmpctx = SSL_CTX_new(SSLv23_server_method()); + if (!tmpctx) + goto leave1; + + rv = SSL_CTX_use_certificate_chain_file(tmpctx, filename); + if (rv != 1) + goto leave2; + tmpssl = SSL_new(tmpctx); + if (!tmpssl) + goto leave2; + + tmpcrt = SSL_get_certificate(tmpssl); + if (!tmpcrt) + goto leave3; + + if (!*chain) + { + *chain = sk_X509_new_null(); + if (!*chain) + goto leave3; + } + +#if (OPENSSL_VERSION_NUMBER < 0x1000200fL) || defined(LIBRESSL_VERSION_NUMBER) + tmpchain = tmpctx->extra_certs; +#else /* OpenSSL >= 1.0.2 */ + rv = SSL_CTX_get0_chain_certs(tmpctx, &tmpchain); + if (rv != 1) + goto leave3; +#endif /* OpenSSL >= 1.0.2 */ + + if (crt) + { + *crt = tmpcrt; + } + else + { + sk_X509_push(*chain, tmpcrt); + } + ssl_x509_refcount_inc(tmpcrt); + + for (int i = 0; i < sk_X509_num(tmpchain); i++) + { + tmpcrt = sk_X509_value(tmpchain, i); + ssl_x509_refcount_inc(tmpcrt); + sk_X509_push(*chain, tmpcrt); + } + SSL_free(tmpssl); + SSL_CTX_free(tmpctx); + return 0; + +leave3: + SSL_free(tmpssl); +leave2: + SSL_CTX_free(tmpctx); +leave1: + return -1; +} + +/* + * Use a X509 cert chain for an SSL context. + * Copies the cert stack to the SSL_CTX internal data structures + * and increases reference counts accordingly. + */ +void ssl_x509chain_use(SSL_CTX * sslctx, X509 * crt, STACK_OF(X509) * chain) +{ + SSL_CTX_use_certificate(sslctx, crt); + + for (int i = 0; i < sk_X509_num(chain); i++) + { + X509 * tmpcrt; + + tmpcrt = sk_X509_value(chain, i); + ssl_x509_refcount_inc(tmpcrt); + SSL_CTX_add_extra_chain_cert(sslctx, tmpcrt); + } +} + +/* + * Load a X509 cert from a PEM file. + * Returned X509 must be freed using X509_free() by the caller. + * Not thread-safe. + */ +X509 * ssl_x509_load(const char * filename) +{ + X509 * crt = NULL; + SSL_CTX * tmpctx; + SSL * tmpssl; + int rv; + + if (ssl_init() == -1) + return NULL; + + tmpctx = SSL_CTX_new(SSLv23_server_method()); + if (!tmpctx) + goto leave1; + rv = SSL_CTX_use_certificate_file(tmpctx, filename, SSL_FILETYPE_PEM); + if (rv != 1) + goto leave2; + tmpssl = SSL_new(tmpctx); + if (!tmpssl) + goto leave2; + crt = SSL_get_certificate(tmpssl); + if (crt) + ssl_x509_refcount_inc(crt); + SSL_free(tmpssl); +leave2: + SSL_CTX_free(tmpctx); +leave1: + return crt; +} + +/* + * Load a private key from a PEM file. + * Returned EVP_PKEY must be freed using EVP_PKEY_free() by the caller. + * Not thread-safe. + */ +EVP_PKEY * ssl_key_load(const char * filename) +{ + EVP_PKEY * key = NULL; + SSL_CTX * tmpctx; + SSL * tmpssl; + int rv; + + if (ssl_init() == -1) + return NULL; + + tmpctx = SSL_CTX_new(SSLv23_server_method()); + if (!tmpctx) + goto leave1; + rv = SSL_CTX_use_PrivateKey_file(tmpctx, filename, SSL_FILETYPE_PEM); + if (rv != 1) + goto leave2; + tmpssl = SSL_new(tmpctx); + if (!tmpssl) + goto leave2; + key = SSL_get_privatekey(tmpssl); + if (key) + ssl_key_refcount_inc(key); + SSL_free(tmpssl); +leave2: + SSL_CTX_free(tmpctx); +leave1: + return key; +} + +/* + * Generate a new RSA key. + * Returned EVP_PKEY must be freed using EVP_PKEY_free() by the caller. + */ +EVP_PKEY * ssl_key_genrsa(const int keysize) +{ + EVP_PKEY * pkey; + RSA * rsa; + +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + BIGNUM * bn; + int rv; + rsa = RSA_new(); + bn = BN_new(); + BN_dec2bn(&bn, "3"); + rv = RSA_generate_key_ex(rsa, keysize, bn, NULL); + BN_free(bn); + if (rv != 1) + { + RSA_free(rsa); + return NULL; + } +#else /* OPENSSL_VERSION_NUMBER < 0x10100000L */ + rsa = RSA_generate_key(keysize, 3, NULL, NULL); + if (!rsa) + return NULL; +#endif /* OPENSSL_VERSION_NUMBER < 0x10100000L */ + pkey = EVP_PKEY_new(); + EVP_PKEY_assign_RSA(pkey, rsa); /* does not increment refcount */ + return pkey; +} + +/* + * Returns the subjectKeyIdentifier compatible key id of the public key. + * keyid will receive a binary SHA-1 hash of SSL_KEY_IDSZ bytes. + * Returns 0 on success, -1 on failure. + */ +int ssl_key_identifier_sha1(EVP_PKEY * key, unsigned char * keyid) +{ + X509_PUBKEY * pubkey = NULL; + const unsigned char * pk; + int length; + + /* X509_PUBKEY_set() will attempt to free pubkey if != NULL */ + if (X509_PUBKEY_set(&pubkey, key) != 1 || !pubkey) + return -1; + if (!X509_PUBKEY_get0_param(NULL, &pk, &length, NULL, pubkey)) + goto errout; + if (!EVP_Digest(pk, length, keyid, NULL, EVP_sha1(), NULL)) + goto errout; + X509_PUBKEY_free(pubkey); + return 0; + +errout: + X509_PUBKEY_free(pubkey); + return -1; +} + +char* ssl_X509_print_name(X509_NAME* name) +{ + //ref: https://kahdev.wordpress.com/2008/11/23/a-certificates-name-issuer-and-its-keyusage/ + // Set up a BIO to put the name line into. + BIO *name_Bio = BIO_new(BIO_s_mem()); + + // Now, put the name line into the BIO. + X509_NAME_print_ex(name_Bio, name, 0, XN_FLAG_ONELINE); + + // Obtain a reference to the data and copy out + // just the length of the data. + char *data_start = NULL; + char *name_string = NULL; + long nameLength = BIO_get_mem_data(name_Bio, &data_start); + + name_string = ALLOC(char, nameLength + 1); + memset(name_string, 0, nameLength + 1); + memcpy(name_string, data_start, nameLength); + BIO_vfree(name_Bio); + return name_string; +} +/* + * Returns the result of ssl_key_identifier_sha1() as hex characters with or + * without colons in a newly allocated string. + */ +char * ssl_key_identifier(EVP_PKEY * key, int colons) +{ + unsigned char id[SSL_KEY_IDSZ]; + + if (ssl_key_identifier_sha1(key, id) == -1) + return NULL; + + return ssl_sha1_to_str(id, colons); +} + +/* + * Returns the one-line representation of the subject DN in a newly allocated + * string which must be freed by the caller. + */ +char * ssl_x509_subject(const X509 * crt) +{ + return ssl_X509_print_name(X509_get_subject_name(crt)); +} + +/* + * Returns the one-line representation of the issuer in a newly allocated + * string which must be freed by the caller. + */ +char * ssl_x509_issuer(const X509 * crt) +{ + return ssl_X509_print_name(X509_get_issuer_name(crt)); +} + + +/* + * Parse the common name from the subject distinguished name. + * Returns string allocated using malloc(), caller must free(). + * Returns NULL on errors. + */ +char * +ssl_x509_subject_cn(X509 * crt, size_t * psz) +{ + X509_NAME * ptr; + char * cn; + size_t sz; + + ptr = X509_get_subject_name(crt); /* does not inc refcounts */ + if (!ptr) + return NULL; + sz = (size_t) X509_NAME_get_text_by_NID(ptr, NID_commonName, NULL, 0) + 1; + + if ((sz == 0) || !(cn = (char *) malloc(sz))) + return NULL; + if (X509_NAME_get_text_by_NID(ptr, NID_commonName, cn, sz) == -1) + { + free(cn); + return NULL; + } + *psz = sz; + return cn; +} + +/* + * Write the SHA1 fingerprint of cert to fpr as SSL_X509_FPRSZ (20) + * bytes long binary buffer. + * Returns -1 on error, 0 on success. + */ +int +ssl_x509_fingerprint_sha1(X509 * crt, unsigned char * fpr) +{ + unsigned int sz = SSL_X509_FPRSZ; + + return X509_digest(crt, EVP_sha1(), fpr, &sz) ? 0 : -1; +} + +/* + * Returns the result of ssl_x509_fingerprint_sha1() as hex characters with or + * without colons in a newly allocated string. + */ +char * +ssl_x509_fingerprint(X509 * crt, int colons) +{ + unsigned char fpr[SSL_X509_FPRSZ]; + + if (ssl_x509_fingerprint_sha1(crt, fpr) == -1) + return NULL; + + return ssl_sha1_to_str(fpr, colons); +} + +#ifndef OPENSSL_NO_DH +/* + * Increment the reference count of DH parameters in a thread-safe + * manner. + */ +void +ssl_dh_refcount_inc(DH * dh) +{ +#if defined(OPENSSL_THREADS) && OPENSSL_VERSION_NUMBER < 0x10100000L + CRYPTO_add(&dh->references, 1, CRYPTO_LOCK_DH); +#else /* !OPENSSL_THREADS */ + DH_up_ref(dh); +#endif /* !OPENSSL_THREADS */ +} +#endif /* !OPENSSL_NO_DH */ + +/* + * Increment the reference count of an X509 cert in a thread-safe + * manner. + */ +void +ssl_key_refcount_inc(EVP_PKEY * key) +{ +#if defined(OPENSSL_THREADS) && OPENSSL_VERSION_NUMBER < 0x10100000L + CRYPTO_add(&key->references, 1, CRYPTO_LOCK_EVP_PKEY); +#else /* !OPENSSL_THREADS */ + EVP_PKEY_up_ref(key); +#endif /* !OPENSSL_THREADS */ +} + +/* + * Increment the reference count of an X509 cert in a thread-safe + * manner. This differs from X509_dup() in that it does not create new, + * full copy of the cert, but only increases the reference count. + */ +void +ssl_x509_refcount_inc(X509 * crt) +{ +#if defined(OPENSSL_THREADS) && OPENSSL_VERSION_NUMBER < 0x10100000L + CRYPTO_add(&crt->references, 1, CRYPTO_LOCK_X509); +#else /* !OPENSSL_THREADS */ + X509_up_ref(crt); +#endif /* !OPENSSL_THREADS */ +} + +/* + * Match a URL/URI hostname against a single cert DNS name + * using RFC 6125 rules (6.4.3 Checking of Wildcard Certificates): + * + * 1. The client SHOULD NOT attempt to match a presented identifier in + * which the wildcard character comprises a label other than the + * left-most label (e.g., do not match bar.*.example.net). + * + * 2. If the wildcard character is the only character of the left-most + * label in the presented identifier, the client SHOULD NOT compare + * against anything but the left-most label of the reference + * identifier (e.g., *.example.com would match foo.example.com but + * not bar.foo.example.com or example.com). + * + * 3. The client MAY match a presented identifier in which the wildcard + * character is not the only character of the label (e.g., + * baz*.example.net and *baz.example.net and b*z.example.net would + * be taken to match baz1.example.net and foobaz.example.net and + * buzz.example.net, respectively). However, the client SHOULD NOT + * attempt to match a presented identifier where the wildcard + * character is embedded within an A-label or U-label [IDNA-DEFS] of + * an internationalized domain name [IDNA-PROTO]. + * + * The optional partial matching in rule 3 is not implemented. + * Returns 1 on match, 0 on no match. + */ +int +ssl_dnsname_match( + const char * certname, size_t certnamesz, + const char * hostname, size_t hostnamesz) +{ + if (hostnamesz < certnamesz) + return 0; + if ((hostnamesz == certnamesz) && + !memcmp(certname, hostname, certnamesz)) + return 1; + if (!memcmp(certname, "xn--", 4)) + return 0; + if ((certnamesz == 1) && (certname[0] == '*') && + !memchr(hostname, '.', hostnamesz)) + return 1; + if ((certnamesz > 2) && (certname[0] == '*') && (certname[1] == '.') && + !memcmp(&certname[1], + &hostname[hostnamesz - (certnamesz - 1)], + certnamesz - 1) && + (memchr(hostname, '.', hostnamesz) == + &hostname[hostnamesz - (certnamesz - 1)])) + return 1; + return 0; +} + +/* + * Transform a NULL-terminated hostname into a matching wildcard hostname, + * e.g. "test.example.org" -> "*.example.org". + * Returns string which must be free()'d by the caller, or NULL on error. + */ +char * +ssl_wildcardify(const char * hostname) +{ + char * dot, * wildcarded; + size_t dotsz; + + if (!(dot = (char *) strchr(hostname, '.'))) + return strdup("*"); + dotsz = strlen(dot); + if (!(wildcarded = (char *) malloc(dotsz + 2))) + return NULL; + wildcarded[0] = '*'; + for (size_t i = 0; i < dotsz; i++) + { + wildcarded[i + 1] = dot[i]; + } + wildcarded[dotsz + 1] = '\0'; + return wildcarded; +} + +/* + * Match DNS name against cert subject CN and subjectAltNames DNS names. + * Returns 1 if any name matches, 0 if none matches. + */ +int +ssl_x509_names_match(X509 * crt, const char * dnsname) +{ + GENERAL_NAMES * altnames; + char * cn; + size_t cnsz, dnsnamesz; + + dnsnamesz = strlen(dnsname); + + cn = ssl_x509_subject_cn(crt, &cnsz); + if (cn && ssl_dnsname_match(cn, cnsz, dnsname, dnsnamesz)) + { + free(cn); + return 1; + } + if (cn) + { + free(cn); + } + + altnames = (GENERAL_NAMES *) X509_get_ext_d2i(crt, NID_subject_alt_name, 0, 0); + if (!altnames) + return 0; + for (int i = 0; i < sk_GENERAL_NAME_num(altnames); i++) + { + GENERAL_NAME * gn = sk_GENERAL_NAME_value(altnames, i); + if (gn->type == GEN_DNS) + { + unsigned char * altname; + int altnamesz; + ASN1_STRING_to_UTF8(&altname, gn->d.dNSName); + altnamesz = ASN1_STRING_length(gn->d.dNSName); + if (altnamesz < 0) + break; + if (ssl_dnsname_match((char *) altname, + (size_t) altnamesz, + dnsname, dnsnamesz)) + { + OPENSSL_free((char *) altname); + GENERAL_NAMES_free(altnames); + return 1; + } + OPENSSL_free((char *) altname); + } + } + GENERAL_NAMES_free(altnames); + return 0; +} + +/* + * Returns a NULL terminated array of pointers to all common names found + * in the Subject DN CN and subjectAltNames extension (DNSName only). + * Caller must free returned buffer and all pointers within. + * Embedded NULL characters in hostnames are replaced with '!'. + */ +char ** ssl_x509_names(X509 * crt) +{ + GENERAL_NAMES * altnames; + char * cn; + size_t cnsz; + char ** res, ** p; + size_t count; + + cn = ssl_x509_subject_cn(crt, &cnsz); + altnames = (GENERAL_NAMES *) X509_get_ext_d2i(crt, NID_subject_alt_name, NULL, NULL); + + count = (size_t) (altnames ? sk_GENERAL_NAME_num(altnames) : 0) + (cn ? 2 : 1); + res = (char **) malloc(count * sizeof(char *)); + + if (!res) + return NULL; + p = res; + if (cn) + *(p++) = cn; + if (!altnames) + { + *p = NULL; + return res; + } + for (int i = 0; i < sk_GENERAL_NAME_num(altnames); i++) + { + GENERAL_NAME * gn = sk_GENERAL_NAME_value(altnames, i); + if (gn->type == GEN_DNS) + { + unsigned char * altname; + int altnamesz; + + ASN1_STRING_to_UTF8(&altname, gn->d.dNSName); + if (!altname) + break; + altnamesz = ASN1_STRING_length(gn->d.dNSName); + if (altnamesz < 0) + { + OPENSSL_free((char *) altname); + break; + } + *p = (char *) malloc(altnamesz + 1); + if (!*p) + { + OPENSSL_free((char *) altname); + GENERAL_NAMES_free(altnames); + for (p = res; *p; p++) + free(*p); + free(res); + return NULL; + } + for (int j = 0; j < altnamesz; j++) + { + (*p)[j] = altname[j] ? altname[j] : '!'; + } + (*p)[altnamesz] = '\0'; + OPENSSL_free((char *) altname); + p++; + } + } + *p = NULL; + GENERAL_NAMES_free(altnames); + return res; +} + +/* + * Returns a printable representation of a cert's common names found + * in the Subject DN CN and subjectAltNames extension, separated by slashes. + * Caller must free returned buffer. + * Embedded NULL characters in hostnames are replaced with '!'. + * If no CN and no subjectAltNames are found, returns "-". + */ +char * ssl_x509_names_to_str(X509 * crt) +{ + char ** names; + size_t sz; + char * buf = NULL, * next; + + names = ssl_x509_names(crt); + if (!names) + return strdup("-"); + + sz = 0; + for (char ** p = names; *p; p++) + { + sz += strlen(*p) + 1; + } + if (!sz) + { + goto out1; + } + + if (!(buf = (char *) malloc(sz))) + goto out2; + next = buf; + for (char ** p = names; *p; p++) + { + char * src = *p; + while (*src) + { + *next++ = *src++; + } + *next++ = '/'; + } + *--next = '\0'; +out2: + for (char ** p = names; *p; p++) + free(*p); +out1: + free(names); + return buf; +} + +/* + * Returns a zero-terminated buffer containing the ASN1 IA5 string. + * Returned buffer must be free()'d by caller. + */ +static char * ssl_ia5string_strdup(ASN1_IA5STRING * ia5) +{ + char * str; + + if (!ia5 || !ia5->length) + return NULL; + + str = (char *) malloc(ia5->length + 1); + if (!str) + return NULL; + memcpy(str, ia5->data, ia5->length); + str[ia5->length] = 0; + return str; +} + +/* + * Returns a NULL terminated array of pointers to copies of Authority + * Information Access (AIA) URLs of a given type contained in the cert. + * Caller must free returned buffer and all pointers within. + */ +char ** ssl_x509_aias(X509 * crt, const int type) +{ + AUTHORITY_INFO_ACCESS * aias; + char ** res; + int count, i, j; + + aias = (AUTHORITY_INFO_ACCESS *) X509_get_ext_d2i(crt, NID_info_access, NULL, NULL); + if (!aias || !(count = sk_ACCESS_DESCRIPTION_num(aias))) + return NULL; + + res = (char **) malloc((count + 1) * sizeof(char *)); + if (!res) + { + sk_ACCESS_DESCRIPTION_pop_free(aias, ACCESS_DESCRIPTION_free); + return NULL; + } + + for (i = 0, j = 0; i < count; i++) + { + ACCESS_DESCRIPTION * aia; + + aia = sk_ACCESS_DESCRIPTION_value(aias, i); + if (aia && + OBJ_obj2nid(aia->method) == type && + aia->location->type == GEN_URI) + { + res[j] = ssl_ia5string_strdup(aia->location->d.ia5); + if (res[j]) + j++; + } + } + res[j] = NULL; + sk_ACCESS_DESCRIPTION_pop_free(aias, ACCESS_DESCRIPTION_free); + return res; +} + +/* + * Returns a NULL terminated array of pointers to copies of Authority + * Information Access (AIA) URLs of type OCSP contained in the cert. + * Caller must free returned buffer and all pointers within. + */ +char ** ssl_x509_ocsps(X509 * crt) +{ + return ssl_x509_aias(crt, NID_ad_OCSP); +} + +/* + * Check whether the cert is valid based on current time. + * Return 1 if valid, 0 otherwise. + */ +int ssl_x509_is_valid(X509 * crt) +{ + if (X509_cmp_current_time(X509_get_notAfter(crt)) <= 0) + return 0; + if (X509_cmp_current_time(X509_get_notBefore(crt)) > 0) + return 0; + return 1; +} + +/* + * Print X509 cert data to a newly allocated string. + * Caller must free returned string. + * Returns NULL on errors. + */ +char * +ssl_x509_to_str(X509 * crt) +{ + BIO * bio; + char * p, * ret; + size_t sz; + + bio = BIO_new(BIO_s_mem()); + if (!bio) + return NULL; + + X509_print(bio, crt); + sz = (size_t) BIO_get_mem_data(bio, &p); + + if (!(ret = (char *) malloc(sz + 1))) + { + BIO_free(bio); + return NULL; + } + memcpy(ret, p, sz); + ret[sz] = '\0'; + BIO_free(bio); + return ret; +} + +/* + * Convert X509 cert to PEM format in a newly allocated string. + * Caller must free returned string. + * Returns NULL on errors. + */ +char * ssl_x509_to_pem(X509 * crt) +{ + BIO * bio; + char * p, * ret; + size_t sz; + + bio = BIO_new(BIO_s_mem()); + if (!bio) + return NULL; + + PEM_write_bio_X509(bio, crt); + sz = (size_t) BIO_get_mem_data(bio, &p); + + if (!(ret = (char *) malloc(sz + 1))) + { + BIO_free(bio); + return NULL; + } + + memcpy(ret, p, sz); + ret[sz] = '\0'; + BIO_free(bio); + return ret; +} + +/* + * Print SSL_SESSION data to a newly allocated string. + * Caller must free returned string. + * Returns NULL on errors. + */ +char * ssl_session_to_str(SSL_SESSION * sess) +{ + BIO * bio; + char * p, * ret; + size_t sz; + + bio = BIO_new(BIO_s_mem()); + if (!bio) + return NULL; + + SSL_SESSION_print(bio, sess); + sz = (size_t) BIO_get_mem_data(bio, &p); /* sets p to internal buffer */ + + if (!(ret = (char *) malloc(sz + 1))) + { + BIO_free(bio); + return NULL; + } + memcpy(ret, p, sz); + ret[sz] = '\0'; + BIO_free(bio); + return ret; +} + +/* + * Returns non-zero if the session timeout has not expired yet, + * zero if the session has expired or an error occured. + */ +int ssl_session_is_valid(SSL_SESSION * sess) +{ + time_t curtimet; + long curtime, timeout; + + curtimet = time(NULL); + if (curtimet == (time_t) -1) + return 0; + curtime = curtimet; + if ((curtime < 0) || ((time_t) curtime != curtimet)) + return 0; + timeout = SSL_SESSION_get_timeout(sess); + if (curtime < timeout) + return 0; + return (SSL_SESSION_get_time(sess) > curtime - timeout); +} + +/* + * Returns 1 if buf contains a DER encoded OCSP request which can be parsed. + * Returns 0 otherwise. + */ +int ssl_is_ocspreq(const unsigned char * buf, size_t sz) +{ + OCSP_REQUEST * req; + const unsigned char * p; + + p = (const unsigned char *) buf; + req = d2i_OCSP_REQUEST(NULL, &p, sz); /* increments p */ + if (!req) + return 0; + OCSP_REQUEST_free(req); + return 1; +} + + +struct cipher_suite +{ + int value; + const char* name; +}; + +struct cipher_suite cipher_suite_list[] = +{ + {0xC030, "ECDHE-RSA-AES256-GCM-SHA384"}, + {0xC02C, "ECDHE-ECDSA-AES256-GCM-SHA384"}, + {0xC028, "ECDHE-RSA-AES256-SHA384"}, + {0xC024, "ECDHE-ECDSA-AES256-SHA384"}, + {0xC014, "ECDHE-RSA-AES256-SHA"}, + {0xC00A, "ECDHE-ECDSA-AES256-SHA"}, + {0x00A5, "DH-DSS-AES256-GCM-SHA384"}, + {0x00A3, "DHE-DSS-AES256-GCM-SHA384"}, + {0x00A1, "DH-RSA-AES256-GCM-SHA384"}, + {0x009F, "DHE-RSA-AES256-GCM-SHA384"}, + {0x006B, "DHE-RSA-AES256-SHA256"}, + {0x006A, "DHE-DSS-AES256-SHA256"}, + {0x0069, "DH-RSA-AES256-SHA256"}, + {0x0068, "DH-DSS-AES256-SHA256"}, + {0x0039, "DHE-RSA-AES256-SHA"}, + {0x0038, "DHE-DSS-AES256-SHA"}, + {0x0037, "DH-RSA-AES256-SHA"}, + {0x0036, "DH-DSS-AES256-SHA"}, + {0x0088, "DHE-RSA-CAMELLIA256-SHA"}, + {0x0087, "DHE-DSS-CAMELLIA256-SHA"}, + {0x0086, "DH-RSA-CAMELLIA256-SHA"}, + {0x0085, "DH-DSS-CAMELLIA256-SHA"}, + {0xC019, "AECDH-AES256-SHA"}, + {0x00A7, "ADH-AES256-GCM-SHA384"}, + {0x006D, "ADH-AES256-SHA256"}, + {0x003A, "ADH-AES256-SHA"}, + {0x0089, "ADH-CAMELLIA256-SHA"}, + {0xC032, "ECDH-RSA-AES256-GCM-SHA384"}, + {0xC02E, "ECDH-ECDSA-AES256-GCM-SHA384"}, + {0xC02A, "ECDH-RSA-AES256-SHA384"}, + {0xC026, "ECDH-ECDSA-AES256-SHA384"}, + {0xC00F, "ECDH-RSA-AES256-SHA"}, + {0xC005, "ECDH-ECDSA-AES256-SHA"}, + {0x009D, "AES256-GCM-SHA384"}, + {0x003D, "AES256-SHA256"}, + {0x0035, "AES256-SHA"}, + {0x0084, "CAMELLIA256-SHA"}, + {0x008D, "PSK-AES256-CBC-SHA"}, + {0xC02F, "ECDHE-RSA-AES128-GCM-SHA256"}, + {0xC02B, "ECDHE-ECDSA-AES128-GCM-SHA256"}, + {0xC027, "ECDHE-RSA-AES128-SHA256"}, + {0xC023, "ECDHE-ECDSA-AES128-SHA256"}, + {0xC013, "ECDHE-RSA-AES128-SHA"}, + {0xC009, "ECDHE-ECDSA-AES128-SHA"}, + {0x00A4, "DH-DSS-AES128-GCM-SHA256"}, + {0x00A2, "DHE-DSS-AES128-GCM-SHA256"}, + {0x00A0, "DH-RSA-AES128-GCM-SHA256"}, + {0x009E, "DHE-RSA-AES128-GCM-SHA256"}, + {0x0067, "DHE-RSA-AES128-SHA256"}, + {0x0040, "DHE-DSS-AES128-SHA256"}, + {0x003F, "DH-RSA-AES128-SHA256"}, + {0x003E, "DH-DSS-AES128-SHA256"}, + {0x0033, "DHE-RSA-AES128-SHA"}, + {0x0032, "DHE-DSS-AES128-SHA"}, + {0x0031, "DH-RSA-AES128-SHA"}, + {0x0030, "DH-DSS-AES128-SHA"}, + {0x009A, "DHE-RSA-SEED-SHA"}, + {0x0099, "DHE-DSS-SEED-SHA"}, + {0x0098, "DH-RSA-SEED-SHA"}, + {0x0097, "DH-DSS-SEED-SHA"}, + {0x0045, "DHE-RSA-CAMELLIA128-SHA"}, + {0x0044, "DHE-DSS-CAMELLIA128-SHA"}, + {0x0043, "DH-RSA-CAMELLIA128-SHA"}, + {0x0042, "DH-DSS-CAMELLIA128-SHA"}, + {0xC018, "AECDH-AES128-SHA"}, + {0x00A6, "ADH-AES128-GCM-SHA256"}, + {0x006C, "ADH-AES128-SHA256"}, + {0x0034, "ADH-AES128-SHA"}, + {0x009B, "ADH-SEED-SHA"}, + {0x0046, "ADH-CAMELLIA128-SHA"}, + {0xC031, "ECDH-RSA-AES128-GCM-SHA256"}, + {0xC02D, "ECDH-ECDSA-AES128-GCM-SHA256"}, + {0xC029, "ECDH-RSA-AES128-SHA256"}, + {0xC025, "ECDH-ECDSA-AES128-SHA256"}, + {0xC00E, "ECDH-RSA-AES128-SHA"}, + {0xC004, "ECDH-ECDSA-AES128-SHA"}, + {0x009C, "AES128-GCM-SHA256"}, + {0x003C, "AES128-SHA256"}, + {0x002F, "AES128-SHA"}, + {0x0096, "SEED-SHA"}, + {0x0041, "CAMELLIA128-SHA"}, + {0x008C, "PSK-AES128-CBC-SHA"}, + {0xC012, "ECDHE-RSA-DES-CBC3-SHA"}, + {0xC008, "ECDHE-ECDSA-DES-CBC3-SHA"}, + {0x0016, "EDH-RSA-DES-CBC3-SHA"}, + {0x0013, "EDH-DSS-DES-CBC3-SHA"}, + {0x0010, "DH-RSA-DES-CBC3-SHA"}, + {0x000D, "DH-DSS-DES-CBC3-SHA"}, + {0xC017, "AECDH-DES-CBC3-SHA"}, + {0x001B, "ADH-DES-CBC3-SHA"}, + {0xC00D, "ECDH-RSA-DES-CBC3-SHA"}, + {0xC003, "ECDH-ECDSA-DES-CBC3-SHA"}, + {0x000A, "DES-CBC3-SHA"}, + {0x0007, "IDEA-CBC-SHA"}, + {0x008B, "PSK-3DES-EDE-CBC-SHA"}, + {0x0021, "KRB5-IDEA-CBC-SHA"}, + {0x001F, "KRB5-DES-CBC3-SHA"}, + {0x0025, "KRB5-IDEA-CBC-MD5"}, + {0x0023, "KRB5-DES-CBC3-MD5"}, + {0xC011, "ECDHE-RSA-RC4-SHA"}, + {0xC007, "ECDHE-ECDSA-RC4-SHA"}, + {0xC016, "AECDH-RC4-SHA"}, + {0x0018, "ADH-RC4-MD5"}, + {0xC00C, "ECDH-RSA-RC4-SHA"}, + {0xC002, "ECDH-ECDSA-RC4-SHA"}, + {0x0005, "RC4-SHA"}, + {0x0004, "RC4-MD5"}, + {0x008A, "PSK-RC4-SHA"}, + {0x0020, "KRB5-RC4-SHA"}, + {0x0024, "KRB5-RC4-MD5"}, + {0xC010, "ECDHE-RSA-NULL-SHA"}, + {0xC006, "ECDHE-ECDSA-NULL-SHA"}, + {0xC015, "AECDH-NULL-SHA"}, + {0xC00B, "ECDH-RSA-NULL-SHA"}, + {0xC001, "ECDH-ECDSA-NULL-SHA"}, + {0x003B, "NULL-SHA256"}, + {0x0002, "NULL-SHA"}, + {0x0001, "NULL-MD5"} +}; + +struct cipher_suite cipher_suite_list_tls13[] = +{ + {0x1301, "TLS_AES_128_GCM_SHA256"}, + {0x1302, "TLS_AES_256_GCM_SHA384"}, + {0x1303, "TLS_CHACHA20_POLY1305_SHA256"}, + {0x1304, "TLS_AES_128_CCM_SHA256"}, + {0x1305, "TLS_AES_128_CCM_8_SHA256"} +}; + + +void ssl_chello_free(struct ssl_chello* chello) +{ + if(chello==NULL) + { + return; + } + free(chello->sni); + chello->sni = NULL; + free(chello->alpn); + chello->alpn = NULL; + free(chello->cipher_suites); + chello->cipher_suites = NULL; + free(chello->cipher_suites_tls13); + chello->cipher_suites_tls13 = NULL; + free(chello); +} + +static char* parse_alpn_extension(const unsigned char* buff, size_t buff_len, enum chello_parse_result* result) +{ + size_t pos = 0; + size_t len = ((size_t)buff[pos] << 8) + (size_t)buff[pos + 1]; + if(2 + len != buff_len) + { + *result = CHELLO_PARSE_INVALID_FORMAT; + return NULL; + } + char* alpn = ALLOC(char, len + 1); + strncpy((char*)alpn, (const char*)buff + 2, len); + alpn[len] = '\0'; + *result = CHELLO_PARSE_SUCCESS; + return alpn; +} + +static char* parse_server_name_extension(const unsigned char* buff, size_t buff_len, enum chello_parse_result* result) +{ + size_t pos = 2; /* skip server name list length */ + size_t len; + char* sni = NULL; + while (pos + 3 < buff_len) + { + len = ((size_t)buff[pos + 1] << 8) + (size_t)buff[pos + 2]; + if (pos + 3 + len > buff_len) + { + *result = CHELLO_PARSE_INVALID_FORMAT; + return NULL; + } + switch (buff[pos]) + { + case 0x00: /* host_name */ + sni = (char*)malloc(len + 1); + strncpy(sni, (const char*)buff + pos + 3, len); + sni[len] = '\0'; + *result = CHELLO_PARSE_SUCCESS; + } + pos += 3 + len; + } + if (pos != buff_len) + { + *result = CHELLO_PARSE_INVALID_FORMAT; + } + return sni; +} + +static enum chello_parse_result parse_extensions(const unsigned char* buff, size_t buff_len, struct ssl_chello* chello) { + size_t pos = 0; + /* Parse each 4 bytes for the extension header */ + while (pos + 4 <= buff_len) + { + size_t len = ((size_t)buff[pos + 2] << 8) + (size_t)buff[pos + 3]; + /* Check if it's a server name extension */ + if (buff[pos] == 0x00 && buff[pos + 1] == 0x00) + { + if (pos + 4 + len > buff_len) + { + return CHELLO_PARSE_INVALID_FORMAT; + } + enum chello_parse_result result = CHELLO_PARSE_SUCCESS; + chello->sni = parse_server_name_extension(buff + pos + 4, len, &result); + if(result != CHELLO_PARSE_SUCCESS) + { + return result; + } + } + /* Check if it's a alpn extension */ + if (buff[pos] == 0x00 && buff[pos + 1] == 0x10) + { + if (pos + 4 + len > buff_len) + { + return CHELLO_PARSE_INVALID_FORMAT; + } + enum chello_parse_result result = CHELLO_PARSE_SUCCESS; + chello->alpn = parse_alpn_extension(buff + pos + 4, len, &result); + if(result != CHELLO_PARSE_SUCCESS) + { + return result; + } + } + pos += (4 + len); + } + /* Check we ended where we expected to */ + if (pos != buff_len) + { + return CHELLO_PARSE_INVALID_FORMAT; + } + return CHELLO_PARSE_SUCCESS; +} + +static char* parse_cipher_suites(struct cipher_suite* _cipher_suite_list, int n, const unsigned char* buff, size_t buff_len, enum chello_parse_result* result) +{ + char* cipher_suites_str = (char* )malloc(TFE_STRING_MAX); + cipher_suites_str[0] = '\0'; + size_t pos = 0; + int flag = 0; + while(pos < buff_len) + { + int i = 0; + for(i = 0;i < n; i++) + { + int val = (buff[pos] << 8) + buff[pos + 1]; + if(_cipher_suite_list[i].value == val) + { + if(strnlen(_cipher_suite_list[i].name, TFE_STRING_MAX) + strnlen(cipher_suites_str, TFE_STRING_MAX) + 1 > TFE_STRING_MAX) + { + flag = 1; + break; + } + strncat(cipher_suites_str, _cipher_suite_list[i].name, TFE_STRING_MAX); + strncat(cipher_suites_str, ":", TFE_STRING_MAX); + } + } + pos += 2; + if(flag == 1) + { + break; + } + } + int len = strnlen(cipher_suites_str, TFE_STRING_MAX); + if(len > 0) + { + cipher_suites_str[len-1] = '\0'; + } + if(pos != buff_len && flag == 0) + { + *result = CHELLO_PARSE_INVALID_FORMAT; + free(cipher_suites_str); + return NULL; + } + *result = CHELLO_PARSE_SUCCESS; + return cipher_suites_str; +} + +struct ssl_chello* ssl_chello_parse(const unsigned char* buff, size_t buff_len, enum chello_parse_result* result) +{ + if(buff == NULL) + { + *result = CHELLO_PARSE_INVALID_FORMAT; + return NULL; + } + if(buff_len < 1) + { + *result = CHELLO_PARSE_NOT_ENOUGH_BUFF; + return NULL; + } + if(buff[0] != 0x80 && buff[0] != 0x16) + { + *result = CHELLO_PARSE_INVALID_FORMAT; + return NULL; + } + /* SSL 2.0 compatible Client Hello + * High bit of first byte (length) and content type is Client Hello + * See RFC5246 Appendix E.2 + * if it is SSL 2.0, only parse version + */ + if(buff[0] == 0x80) + { + struct ssl_chello* _chello = (struct ssl_chello*)ALLOC(struct ssl_chello, 1); + _chello->min_version.major = 0x02; + if(buff_len < 2) + { + *result = CHELLO_PARSE_NOT_ENOUGH_BUFF; + return _chello; + } + size_t len = (size_t)buff[1]; + if (buff_len < len + 2) + { + *result = CHELLO_PARSE_NOT_ENOUGH_BUFF; + return _chello; + } + buff_len = len + 2; + size_t pos = 2; + /* Handshark Message Type: Client Hello */ + if (pos + 1 > buff_len) + { + *result = CHELLO_PARSE_INVALID_FORMAT; + return _chello; + } + if (buff[pos] != 0x01) + { + *result = CHELLO_PARSE_INVALID_FORMAT; + return _chello; + } + pos += 1; + /* Version */ + if(pos + 2 > buff_len) + { + *result = CHELLO_PARSE_INVALID_FORMAT; + return _chello; + } + _chello->max_version.major = buff[pos]; + _chello->max_version.minor = buff[pos + 1]; + _chello->max_version.ossl_format=(uint16_t)_chello->max_version.major<<8|_chello->max_version.minor; + *result = CHELLO_PARSE_SUCCESS; + return _chello; + } + else + { + if (buff_len < 5) + { + *result = CHELLO_PARSE_NOT_ENOUGH_BUFF; + return NULL; + } + if(buff[1] != 3 || buff[2] > 4 || buff[2] < 0) + { + *result = CHELLO_PARSE_INVALID_FORMAT; + return NULL; + } + struct ssl_chello* _chello = (struct ssl_chello*)ALLOC(struct ssl_chello, 1); + _chello->min_version.major = buff[1]; + _chello->min_version.minor = buff[2]; + _chello->min_version.ossl_format=(uint16_t)_chello->min_version.major<<8|_chello->min_version.minor; + _chello->max_version.major = -1; + _chello->max_version.minor = -1; + _chello->sni = NULL; + _chello->alpn = NULL; + _chello->cipher_suites = NULL; + _chello->cipher_suites_tls13 = NULL; + /* TLS record length */ + size_t len = ((size_t)buff[3] << 8) + (size_t)buff[4] + 5; + if (buff_len < len) + { + *result = CHELLO_PARSE_NOT_ENOUGH_BUFF; + return _chello; + } + buff_len = len; + size_t pos = 5; + if (pos + 1 > buff_len) + { + *result = CHELLO_PARSE_INVALID_FORMAT; + return _chello; + } + if (buff[pos] != 0x01) + { + *result = CHELLO_PARSE_INVALID_FORMAT; + return _chello; + } + pos += 4; + if(pos + 2 > buff_len) + { + *result = CHELLO_PARSE_INVALID_FORMAT; + return _chello; + } + _chello->max_version.major = buff[pos]; + _chello->max_version.minor = buff[pos+1]; + _chello->max_version.ossl_format=(uint16_t)_chello->max_version.major<<8|_chello->max_version.minor; + + pos += 34; + /* Session ID */ + if (pos + 1 > buff_len) + { + *result = CHELLO_PARSE_INVALID_FORMAT; + return _chello; + } + len = (size_t)buff[pos]; + pos += 1 + len; + /* Cipher Suites */ + if (pos + 2 > buff_len) + { + *result = CHELLO_PARSE_INVALID_FORMAT; + return _chello; + } + len = ((size_t)buff[pos] << 8) + (size_t)buff[pos + 1]; + pos += 2; + if(pos + len > buff_len) + { + *result = CHELLO_PARSE_INVALID_FORMAT; + return _chello; + } + /* + int n = sizeof(cipher_suite_list) / sizeof(struct cipher_suite); + _chello->cipher_suites = parse_cipher_suites(cipher_suite_list, n, buff + pos, len, result); + if(*result != CHELLO_PARSE_SUCCESS) + { + return _chello; + } + n = sizeof(cipher_suite_list_tls13) / sizeof(struct cipher_suite); + _chello->cipher_suites_tls13 = parse_cipher_suites(cipher_suite_list_tls13, n, buff + pos, len, result); + if(*result != CHELLO_PARSE_SUCCESS) + { + return _chello; + }*/ + pos += len; + + /* Compression Methods */ + if (pos >= buff_len) + { + *result = CHELLO_PARSE_INVALID_FORMAT; + return _chello; + } + len = (size_t)buff[pos]; + pos += 1 + len; + /* ssl 3.0, no extensions */ + if(_chello->min_version.major == 3 && _chello->min_version.minor == 0) + { + if(pos == buff_len) + { + *result = CHELLO_PARSE_SUCCESS; + return _chello; + } + else + { + *result = CHELLO_PARSE_INVALID_FORMAT; + return _chello; + } + } + /* Extensions */ + if (pos + 2 > buff_len) + { + *result = CHELLO_PARSE_INVALID_FORMAT; + return _chello; + } + len = ((size_t)buff[pos] << 8) + (size_t)buff[pos + 1]; + pos += 2; + if (pos + len > buff_len) + { + *result = CHELLO_PARSE_INVALID_FORMAT; + return _chello; + } + enum chello_parse_result rtn = parse_extensions(buff + pos, len, _chello); + *result = rtn; + return _chello; + } +} diff --git a/plugin/business/pangu-http/src/pangu_http.cpp b/plugin/business/pangu-http/src/pangu_http.cpp index c671bc2..7470e3d 100644 --- a/plugin/business/pangu-http/src/pangu_http.cpp +++ b/plugin/business/pangu-http/src/pangu_http.cpp @@ -230,7 +230,7 @@ static void pangu_http_stat_init(struct pangu_rt * pangu_runtime) { if(spec[i]!=NULL) { - pangu_runtime->fs_id[i]=FS_register(pangu_runtime->fs_handle, FS_STYLE_STATUS, FS_CALC_CURRENT, spec[i]); + pangu_runtime->fs_id[i]=FS_register(pangu_runtime->fs_handle, FS_STYLE_FIELD, FS_CALC_CURRENT, spec[i]); } } g_pangu_rt->gcev = event_new(pangu_runtime->gc_evbase, -1, EV_PERSIST, pangu_http_gc_cb, NULL); diff --git a/vendor/CMakeLists.txt b/vendor/CMakeLists.txt index 51e96bd..a0c4e2f 100644 --- a/vendor/CMakeLists.txt +++ b/vendor/CMakeLists.txt @@ -29,7 +29,6 @@ set_property(TARGET openssl-ssl-static PROPERTY IMPORTED_LOCATION ${INSTALL_DIR} set_property(TARGET openssl-ssl-static PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${INSTALL_DIR}/include) ### Libevent 2.1.8 -### Use openssl 1.1.1 ExternalProject_Add(libevent PREFIX libevent URL ${CMAKE_CURRENT_SOURCE_DIR}/libevent-2.1.8-stable.tar.gz URL_MD5 f3eeaed018542963b7d2416ef1135ecc @@ -307,4 +306,20 @@ add_library(brotlienc-static STATIC IMPORTED GLOBAL) add_dependencies(brotlienc-static brotli) set_property(TARGET brotlienc-static PROPERTY IMPORTED_LOCATION ${INSTALL_DIR}/lib64/libbrotlienc-static.a) set_property(TARGET brotlienc-static PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${INSTALL_DIR}/include) -target_link_libraries(brotlienc-static INTERFACE brotlicommon-static) \ No newline at end of file +target_link_libraries(brotlienc-static INTERFACE brotlicommon-static) + +### gperftools +ExternalProject_Add(gperftools + PREFIX gperftools + URL ${CMAKE_CURRENT_SOURCE_DIR}/gperftools-2.7.tar.gz + URL_MD5 c6a852a817e9160c79bdb2d3101b4601 + CONFIGURE_COMMAND ./configure --prefix= --disable-shared + BUILD_IN_SOURCE 1) + +ExternalProject_Get_Property(gperftools INSTALL_DIR) +file(MAKE_DIRECTORY ${INSTALL_DIR}/include) + +add_library(gperftools-static STATIC IMPORTED GLOBAL) +add_dependencies(gperftools-static gperftools) +set_property(TARGET gperftools-static PROPERTY IMPORTED_LOCATION ${INSTALL_DIR}/lib/libtcmalloc.a) +set_property(TARGET gperftools-static PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${INSTALL_DIR}/include) diff --git a/vendor/gperftools-2.7.tar.gz b/vendor/gperftools-2.7.tar.gz new file mode 100644 index 0000000..e0a82a2 Binary files /dev/null and b/vendor/gperftools-2.7.tar.gz differ -- cgit v1.2.3