static void log_verify_details(as_socket* sock) { long vr = SSL_get_verify_result(sock->ssl); if (vr != X509_V_OK) { as_log_info("TLS verify result: %s", X509_verify_cert_error_string(vr)); } }
static void as_cluster_find_nodes_to_add(as_cluster* cluster, as_vector* /* <as_host> */ friends, as_vector* /* <as_node*> */ nodes_to_add) { as_error err; as_error_init(&err); as_vector addresses; as_vector_inita(&addresses, sizeof(struct sockaddr_in), 5); as_node_info node_info; for (uint32_t i = 0; i < friends->size; i++) { as_host* friend = as_vector_get(friends, i); as_vector_clear(&addresses); as_status status = as_lookup(&err, friend->name, friend->port, &addresses); if (status != AEROSPIKE_OK) { as_log_warn("%s %s", as_error_string(status), err.message); continue; } for (uint32_t i = 0; i < addresses.size; i++) { struct sockaddr_in* addr = as_vector_get(&addresses, i); status = as_lookup_node(cluster, &err, addr, &node_info); if (status == AEROSPIKE_OK) { as_node* node = as_cluster_find_node(cluster->nodes, nodes_to_add, node_info.name); if (node) { // Duplicate node name found. This usually occurs when the server // services list contains both internal and external IP addresses // for the same node. Add new host to list of alias filters // and do not add new node. as_close(node_info.fd); as_address* a = as_node_get_address_full(node); as_log_info("Node %s:%d already exists with nodeid %s and address %s:%d", friend->name, friend->port, node->name, a->name, (int)cf_swap_from_be16(a->addr.sin_port)); node->friends++; as_node_add_address(node, friend, addr); continue; } node = as_node_create(cluster, friend, addr, &node_info); as_address* a = as_node_get_address_full(node); as_log_info("Add node %s %s:%d", node_info.name, a->name, (int)cf_swap_from_be16(a->addr.sin_port)); as_vector_append(nodes_to_add, &node); } else { as_log_warn("Failed to connect to friend %s:%d. %s %s", friend->name, friend->port, as_error_string(status), err.message); } }
static void log_session_info(as_socket* sock) { if (! sock->ctx->log_session_info) return; SSL_CIPHER const* cipher = SSL_get_current_cipher(sock->ssl); if (cipher) { char desc[1024]; SSL_CIPHER_description(cipher, desc, sizeof(desc)); size_t len = strlen(desc); if (len > 0) { desc[len-1] = '\0'; // Trim trailing \n } as_log_info("TLS cipher: %s", desc); } else { as_log_warn("TLS no current cipher"); } }
static as_status as_cluster_seed_nodes(as_cluster* cluster, as_error* err, bool enable_warnings) { // Add all nodes at once to avoid copying entire array multiple times. as_vector nodes_to_add; as_vector_inita(&nodes_to_add, sizeof(as_node*), 64); as_vector addresses; as_vector_inita(&addresses, sizeof(struct sockaddr_in), 5); as_node_info node_info; as_error error_local; as_error_init(&error_local); // AEROSPIKE_ERR_TIMEOUT doesn't come with a message; make sure it's initialized. as_status status = AEROSPIKE_OK; as_seeds* seeds = as_seeds_reserve(cluster); for (uint32_t i = 0; i < seeds->size; i++) { as_seed* seed = &seeds->array[i]; as_vector_clear(&addresses); status = as_lookup(&error_local, seed->name, seed->port, &addresses); if (status != AEROSPIKE_OK) { if (enable_warnings) { as_log_warn("Failed to lookup %s:%d. %s %s", seed->name, seed->port, as_error_string(status), error_local.message); } continue; } for (uint32_t i = 0; i < addresses.size; i++) { struct sockaddr_in* addr = as_vector_get(&addresses, i); status = as_lookup_node(cluster, &error_local, addr, &node_info); if (status == AEROSPIKE_OK) { as_host host; if (as_strncpy(host.name, seed->name, sizeof(host.name))) { as_log_warn("Hostname has been truncated: %s", host.name); } host.port = seed->port; as_node* node = as_cluster_find_node_in_vector(&nodes_to_add, node_info.name); if (node) { as_close(node_info.fd); as_node_add_address(node, &host, addr); } else { node = as_node_create(cluster, &host, addr, &node_info); as_address* a = as_node_get_address_full(node); as_log_info("Add node %s %s:%d", node->name, a->name, (int)cf_swap_from_be16(a->addr.sin_port)); as_vector_append(&nodes_to_add, &node); } } else { if (enable_warnings) { as_log_warn("Failed to connect to seed %s:%d. %s %s", seed->name, seed->port, as_error_string(status), error_local.message); } } } } as_seeds_release(seeds); if (nodes_to_add.size > 0) { as_cluster_add_nodes(cluster, &nodes_to_add); status = AEROSPIKE_OK; } else { status = as_error_set_message(err, AEROSPIKE_ERR_CLIENT, "Failed to seed cluster"); } as_vector_destroy(&nodes_to_add); as_vector_destroy(&addresses); return status; }
as_status as_tls_context_setup(as_config_tls* tlscfg, as_tls_context* ctx, as_error* errp) { // Clear the destination, in case we don't make it. ctx->ssl_ctx = NULL; ctx->pkey = NULL; ctx->cert_blacklist = NULL; ctx->log_session_info = tlscfg->log_session_info; ctx->for_login_only = tlscfg->for_login_only; as_tls_check_init(); pthread_mutex_init(&ctx->lock, NULL); if (tlscfg->cert_blacklist) { ctx->cert_blacklist = cert_blacklist_read(tlscfg->cert_blacklist); if (! ctx->cert_blacklist) { as_tls_context_destroy(ctx); return as_error_update(errp, AEROSPIKE_ERR_TLS_ERROR, "Failed to read certificate blacklist: %s", tlscfg->cert_blacklist); } } uint16_t protocols = AS_TLS_PROTOCOL_NONE; as_status status = protocols_parse(tlscfg, &protocols, errp); if (status != AEROSPIKE_OK) { as_tls_context_destroy(ctx); return status; } const SSL_METHOD* method = NULL; // If the selected protocol set is a single protocol we can use a specific method. if (protocols == AS_TLS_PROTOCOL_TLSV1) { #if OPENSSL_VERSION_NUMBER >= 0x10100000L method = TLS_client_method(); #else method = TLSv1_client_method(); #endif } else if (protocols == AS_TLS_PROTOCOL_TLSV1_1) { #if OPENSSL_VERSION_NUMBER >= 0x10100000L method = TLS_client_method(); #else method = TLSv1_1_client_method(); #endif } else if (protocols == AS_TLS_PROTOCOL_TLSV1_2) { #if OPENSSL_VERSION_NUMBER >= 0x10100000L method = TLS_client_method(); #else method = TLSv1_2_client_method(); #endif } else { // Multiple protocols are enabled, use a flexible method. #if OPENSSL_VERSION_NUMBER >= 0x10100000L method = TLS_client_method(); #else method = SSLv23_client_method(); #endif } ctx->ssl_ctx = SSL_CTX_new(method); if (ctx->ssl_ctx == NULL) { as_tls_context_destroy(ctx); unsigned long errcode = ERR_get_error(); char errbuf[1024]; ERR_error_string_n(errcode, errbuf, sizeof(errbuf)); return as_error_update(errp, AEROSPIKE_ERR_TLS_ERROR, "Failed to create new TLS context: %s", errbuf); } /* always disable SSLv2, as per RFC 6176 */ SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_NO_SSLv2); SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_NO_SSLv3); // Turn off non-enabled protocols. if (! (protocols & AS_TLS_PROTOCOL_TLSV1)) { SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_NO_TLSv1); } if (! (protocols & AS_TLS_PROTOCOL_TLSV1_1)) { SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_NO_TLSv1_1); } if (! (protocols & AS_TLS_PROTOCOL_TLSV1_2)) { SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_NO_TLSv1_2); } if (tlscfg->cafile || tlscfg->capath) { int rv = SSL_CTX_load_verify_locations(ctx->ssl_ctx, tlscfg->cafile, tlscfg->capath); if (rv != 1) { as_tls_context_destroy(ctx); char errbuf[1024]; unsigned long errcode = ERR_get_error(); if (errcode != 0) { ERR_error_string_n(errcode, errbuf, sizeof(errbuf)); return as_error_update(errp, AEROSPIKE_ERR_TLS_ERROR, "Failed to load CAFile: %s", errbuf); } return as_error_set_message(errp, AEROSPIKE_ERR_TLS_ERROR, "Unknown failure loading CAFile"); } } if (tlscfg->certfile) { int rv = SSL_CTX_use_certificate_chain_file(ctx->ssl_ctx, tlscfg->certfile); if (rv != 1) { // We seem to be seeing this bug: // https://groups.google.com/ // forum/#!topic/mailing.openssl.users/nRvRzmKnEQA // If the rv is not 1 check the error stack; if it doesn't have an // error assume we are OK. // unsigned long errcode = ERR_peek_error(); if (errcode != SSL_ERROR_NONE) { // There *was* an error after all. as_tls_context_destroy(ctx); unsigned long errcode = ERR_get_error(); char errbuf[1024]; ERR_error_string_n(errcode, errbuf, sizeof(errbuf)); return as_error_update(errp, AEROSPIKE_ERR_TLS_ERROR, "SSL_CTX_use_certificate_chain_file failed: %s", errbuf); } } } if (tlscfg->keyfile) { bool ok = false; FILE *fh = fopen(tlscfg->keyfile, "r"); if (fh == NULL) { as_error_update(errp, AEROSPIKE_ERR_TLS_ERROR, "failed to open key file %s: %s", tlscfg->keyfile, strerror(errno)); } else { EVP_PKEY *pkey = PEM_read_PrivateKey(fh, NULL, password_cb, tlscfg->keyfile_pw); if (pkey == NULL) { unsigned long errcode = ERR_get_error(); if (ERR_GET_REASON(errcode) == PEM_R_BAD_PASSWORD_READ) { if (tlscfg->keyfile_pw == NULL) { as_error_update(errp, AEROSPIKE_ERR_TLS_ERROR, "key file %s requires a password", tlscfg->keyfile); } else { as_error_update(errp, AEROSPIKE_ERR_TLS_ERROR, "password for key file %s too long", tlscfg->keyfile); } } else if (ERR_GET_REASON(errcode) == EVP_R_BAD_DECRYPT) { as_error_update(errp, AEROSPIKE_ERR_TLS_ERROR, "invalid password for key file %s", tlscfg->keyfile); } else { char errbuf[1024]; ERR_error_string_n(errcode, errbuf, sizeof(errbuf)); as_error_update(errp, AEROSPIKE_ERR_TLS_ERROR, "PEM_read_PrivateKey failed: %s", errbuf); } } else { ctx->pkey = pkey; SSL_CTX_use_PrivateKey(ctx->ssl_ctx, pkey); ok = true; } fclose(fh); } if (!ok) { as_tls_context_destroy(ctx); return AEROSPIKE_ERR_TLS_ERROR; } } if (tlscfg->cipher_suite) { int rv = SSL_CTX_set_cipher_list(ctx->ssl_ctx, tlscfg->cipher_suite); if (rv != 1) { as_tls_context_destroy(ctx); return as_error_set_message(errp, AEROSPIKE_ERR_TLS_ERROR, "no compatible cipher found"); } // It's bogus that we have to create an SSL just to get the // cipher list, but SSL_CTX_get_ciphers doesn't appear to // exist ... SSL* ssl = SSL_new(ctx->ssl_ctx); for (int prio = 0; true; ++prio) { char const * cipherstr = SSL_get_cipher_list(ssl, prio); if (!cipherstr) { break; } as_log_info("cipher %d: %s", prio+1, cipherstr); } SSL_free(ssl); } if (tlscfg->crl_check || tlscfg->crl_check_all) { X509_VERIFY_PARAM* param = X509_VERIFY_PARAM_new(); unsigned long flags = X509_V_FLAG_CRL_CHECK; if (tlscfg->crl_check_all) { flags |= X509_V_FLAG_CRL_CHECK_ALL; } X509_VERIFY_PARAM_set_flags(param, flags); SSL_CTX_set1_param(ctx->ssl_ctx, param); X509_VERIFY_PARAM_free(param); } SSL_CTX_set_verify(ctx->ssl_ctx, SSL_VERIFY_PEER, verify_callback); manage_sigpipe(); return AEROSPIKE_OK; }
static int verify_callback(int preverify_ok, X509_STORE_CTX* ctx) { // If the cert has already failed we're done. if (! preverify_ok) { return preverify_ok; } SSL* ssl = X509_STORE_CTX_get_ex_data( ctx, SSL_get_ex_data_X509_STORE_CTX_idx()); // The verify callback is called for each cert in the chain. X509* current_cert = X509_STORE_CTX_get_current_cert(ctx); as_tls_context* asctxt = SSL_get_ex_data(ssl, s_ex_ctxt_index); if (! asctxt) { as_log_warn("Missing as_tls_context in TLS verify callback"); return 0; } pthread_mutex_lock(&asctxt->lock); if (asctxt->cert_blacklist) { // Is this cert blacklisted? char name[256]; X509_NAME* iname = X509_get_issuer_name(current_cert); X509_NAME_oneline(iname, name, sizeof(name)); ASN1_INTEGER* sn = X509_get_serialNumber(current_cert); BIGNUM* snbn = ASN1_INTEGER_to_BN(sn, NULL); char* snhex = BN_bn2hex(snbn); as_log_info("CERT: %s %s", snhex, name); bool blacklisted = cert_blacklist_check(asctxt->cert_blacklist, snhex, name); OPENSSL_free(snhex); BN_free(snbn); if (blacklisted) { as_log_warn("CERT: BLACKLISTED"); pthread_mutex_unlock(&asctxt->lock); return 0; } } pthread_mutex_unlock(&asctxt->lock); // If this is the peer cert, check the name #if OPENSSL_VERSION_NUMBER >= 0x10100000L X509* cert = X509_STORE_CTX_get0_cert(ctx); #else X509* cert = ctx->cert; #endif if (current_cert == cert) { char * hostname = SSL_get_ex_data(ssl, s_ex_name_index); if (! hostname) { as_log_warn("Missing hostname in TLS verify callback"); return 0; } bool allow_wildcard = true; bool matched = as_tls_match_name(cert, hostname, allow_wildcard); if (matched) { as_log_debug("TLS name '%s' matches", hostname); } else { as_log_warn("TLS name '%s' mismatch", hostname); } return matched ? 1 : 0; } // If we make it here we are a root or chain cert and are not // blacklisted. return 1; }