bool TLSTicketKeyManager::setTLSTicketKeySeeds( const std::vector<std::string>& oldSeeds, const std::vector<std::string>& currentSeeds, const std::vector<std::string>& newSeeds) { recordTlsTicketRotation(oldSeeds, currentSeeds, newSeeds); bool result = true; activeKeys_.clear(); ticketKeys_.clear(); ticketSeeds_.clear(); const std::vector<string> *seedList = &oldSeeds; for (uint32_t i = 0; i < 3; i++) { TLSTicketSeedType type = (TLSTicketSeedType)i; if (type == SEED_CURRENT) { seedList = ¤tSeeds; } else if (type == SEED_NEW) { seedList = &newSeeds; } for (const auto& seedInput: *seedList) { TLSTicketSeed* seed = insertSeed(seedInput, type); if (seed == nullptr) { result = false; continue; } insertNewKey(seed, 1, nullptr); } } if (!result) { VLOG(2) << "One or more seeds failed to decode"; } if (ticketKeys_.size() == 0 || activeKeys_.size() == 0) { LOG(WARNING) << "No keys configured, falling back to default"; SSL_CTX_set_tlsext_ticket_key_cb(ctx_->getSSLCtx(), nullptr); return false; } SSL_CTX_set_tlsext_ticket_key_cb(ctx_->getSSLCtx(), TLSTicketKeyManager::callback); return true; }
/* * Configure callbacks and other properties that can't be set directly * in the server/client CONF. */ static void configure_handshake_ctx(SSL_CTX *server_ctx, SSL_CTX *client_ctx, const SSL_TEST_CTX *test_ctx) { switch (test_ctx->client_verify_callback) { case SSL_TEST_VERIFY_ACCEPT_ALL: SSL_CTX_set_cert_verify_callback(client_ctx, &verify_accept_callback, NULL); break; case SSL_TEST_VERIFY_REJECT_ALL: SSL_CTX_set_cert_verify_callback(client_ctx, &verify_reject_callback, NULL); break; default: break; } if (test_ctx->session_ticket_expected == SSL_TEST_SESSION_TICKET_BROKEN) { SSL_CTX_set_tlsext_ticket_key_cb(server_ctx, broken_session_ticket_callback); } }
/* * Configure callbacks and other properties that can't be set directly * in the server/client CONF. */ static void configure_handshake_ctx(SSL_CTX *server_ctx, SSL_CTX *server2_ctx, SSL_CTX *client_ctx, const SSL_TEST_CTX *test_ctx, CTX_DATA *server_ctx_data, CTX_DATA *server2_ctx_data, CTX_DATA *client_ctx_data) { unsigned char *ticket_keys; size_t ticket_key_len; switch (test_ctx->client_verify_callback) { case SSL_TEST_VERIFY_ACCEPT_ALL: SSL_CTX_set_cert_verify_callback(client_ctx, &verify_accept_cb, NULL); break; case SSL_TEST_VERIFY_REJECT_ALL: SSL_CTX_set_cert_verify_callback(client_ctx, &verify_reject_cb, NULL); break; default: break; } /* link the two contexts for SNI purposes */ switch (test_ctx->servername_callback) { case SSL_TEST_SERVERNAME_IGNORE_MISMATCH: SSL_CTX_set_tlsext_servername_callback(server_ctx, servername_ignore_cb); SSL_CTX_set_tlsext_servername_arg(server_ctx, server2_ctx); break; case SSL_TEST_SERVERNAME_REJECT_MISMATCH: SSL_CTX_set_tlsext_servername_callback(server_ctx, servername_reject_cb); SSL_CTX_set_tlsext_servername_arg(server_ctx, server2_ctx); break; default: break; } /* * The initial_ctx/session_ctx always handles the encrypt/decrypt of the * session ticket. This ticket_key callback is assigned to the second * session (assigned via SNI), and should never be invoked */ if (server2_ctx != NULL) SSL_CTX_set_tlsext_ticket_key_cb(server2_ctx, do_not_call_session_ticket_cb); if (test_ctx->session_ticket_expected == SSL_TEST_SESSION_TICKET_BROKEN) { SSL_CTX_set_tlsext_ticket_key_cb(server_ctx, broken_session_ticket_cb); } if (test_ctx->server_npn_protocols != NULL) { parse_protos(test_ctx->server_npn_protocols, &server_ctx_data->npn_protocols, &server_ctx_data->npn_protocols_len); SSL_CTX_set_next_protos_advertised_cb(server_ctx, server_npn_cb, server_ctx_data); } if (test_ctx->server2_npn_protocols != NULL) { parse_protos(test_ctx->server2_npn_protocols, &server2_ctx_data->npn_protocols, &server2_ctx_data->npn_protocols_len); OPENSSL_assert(server2_ctx != NULL); SSL_CTX_set_next_protos_advertised_cb(server2_ctx, server_npn_cb, server2_ctx_data); } if (test_ctx->client_npn_protocols != NULL) { parse_protos(test_ctx->client_npn_protocols, &client_ctx_data->npn_protocols, &client_ctx_data->npn_protocols_len); SSL_CTX_set_next_proto_select_cb(client_ctx, client_npn_cb, client_ctx_data); } if (test_ctx->server_alpn_protocols != NULL) { parse_protos(test_ctx->server_alpn_protocols, &server_ctx_data->alpn_protocols, &server_ctx_data->alpn_protocols_len); SSL_CTX_set_alpn_select_cb(server_ctx, server_alpn_cb, server_ctx_data); } if (test_ctx->server2_alpn_protocols != NULL) { OPENSSL_assert(server2_ctx != NULL); parse_protos(test_ctx->server2_alpn_protocols, &server2_ctx_data->alpn_protocols, &server2_ctx_data->alpn_protocols_len); SSL_CTX_set_alpn_select_cb(server2_ctx, server_alpn_cb, server2_ctx_data); } if (test_ctx->client_alpn_protocols != NULL) { unsigned char *alpn_protos = NULL; size_t alpn_protos_len; parse_protos(test_ctx->client_alpn_protocols, &alpn_protos, &alpn_protos_len); /* Reversed return value convention... */ OPENSSL_assert(SSL_CTX_set_alpn_protos(client_ctx, alpn_protos, alpn_protos_len) == 0); OPENSSL_free(alpn_protos); } /* * Use fixed session ticket keys so that we can decrypt a ticket created with * one CTX in another CTX. Don't address server2 for the moment. */ ticket_key_len = SSL_CTX_set_tlsext_ticket_keys(server_ctx, NULL, 0); ticket_keys = OPENSSL_zalloc(ticket_key_len); OPENSSL_assert(ticket_keys != NULL); OPENSSL_assert(SSL_CTX_set_tlsext_ticket_keys(server_ctx, ticket_keys, ticket_key_len) == 1); OPENSSL_free(ticket_keys); }
/* * Configure callbacks and other properties that can't be set directly * in the server/client CONF. */ static void configure_handshake_ctx(SSL_CTX *server_ctx, SSL_CTX *server2_ctx, SSL_CTX *client_ctx, const SSL_TEST_CTX *test, const SSL_TEST_EXTRA_CONF *extra, CTX_DATA *server_ctx_data, CTX_DATA *server2_ctx_data, CTX_DATA *client_ctx_data) { unsigned char *ticket_keys; size_t ticket_key_len; TEST_check(SSL_CTX_set_max_send_fragment(server_ctx, test->max_fragment_size) == 1); if (server2_ctx != NULL) { TEST_check(SSL_CTX_set_max_send_fragment(server2_ctx, test->max_fragment_size) == 1); } TEST_check(SSL_CTX_set_max_send_fragment(client_ctx, test->max_fragment_size) == 1); switch (extra->client.verify_callback) { case SSL_TEST_VERIFY_ACCEPT_ALL: SSL_CTX_set_cert_verify_callback(client_ctx, &verify_accept_cb, NULL); break; case SSL_TEST_VERIFY_REJECT_ALL: SSL_CTX_set_cert_verify_callback(client_ctx, &verify_reject_cb, NULL); break; default: break; } /* link the two contexts for SNI purposes */ switch (extra->server.servername_callback) { case SSL_TEST_SERVERNAME_IGNORE_MISMATCH: SSL_CTX_set_tlsext_servername_callback(server_ctx, servername_ignore_cb); SSL_CTX_set_tlsext_servername_arg(server_ctx, server2_ctx); break; case SSL_TEST_SERVERNAME_REJECT_MISMATCH: SSL_CTX_set_tlsext_servername_callback(server_ctx, servername_reject_cb); SSL_CTX_set_tlsext_servername_arg(server_ctx, server2_ctx); break; default: break; } /* * The initial_ctx/session_ctx always handles the encrypt/decrypt of the * session ticket. This ticket_key callback is assigned to the second * session (assigned via SNI), and should never be invoked */ if (server2_ctx != NULL) SSL_CTX_set_tlsext_ticket_key_cb(server2_ctx, do_not_call_session_ticket_cb); if (extra->server.broken_session_ticket) { SSL_CTX_set_tlsext_ticket_key_cb(server_ctx, broken_session_ticket_cb); } #ifndef OPENSSL_NO_NEXTPROTONEG if (extra->server.npn_protocols != NULL) { parse_protos(extra->server.npn_protocols, &server_ctx_data->npn_protocols, &server_ctx_data->npn_protocols_len); SSL_CTX_set_next_protos_advertised_cb(server_ctx, server_npn_cb, server_ctx_data); } if (extra->server2.npn_protocols != NULL) { parse_protos(extra->server2.npn_protocols, &server2_ctx_data->npn_protocols, &server2_ctx_data->npn_protocols_len); TEST_check(server2_ctx != NULL); SSL_CTX_set_next_protos_advertised_cb(server2_ctx, server_npn_cb, server2_ctx_data); } if (extra->client.npn_protocols != NULL) { parse_protos(extra->client.npn_protocols, &client_ctx_data->npn_protocols, &client_ctx_data->npn_protocols_len); SSL_CTX_set_next_proto_select_cb(client_ctx, client_npn_cb, client_ctx_data); } #endif if (extra->server.alpn_protocols != NULL) { parse_protos(extra->server.alpn_protocols, &server_ctx_data->alpn_protocols, &server_ctx_data->alpn_protocols_len); SSL_CTX_set_alpn_select_cb(server_ctx, server_alpn_cb, server_ctx_data); } if (extra->server2.alpn_protocols != NULL) { TEST_check(server2_ctx != NULL); parse_protos(extra->server2.alpn_protocols, &server2_ctx_data->alpn_protocols, &server2_ctx_data->alpn_protocols_len); SSL_CTX_set_alpn_select_cb(server2_ctx, server_alpn_cb, server2_ctx_data); } if (extra->client.alpn_protocols != NULL) { unsigned char *alpn_protos = NULL; size_t alpn_protos_len; parse_protos(extra->client.alpn_protocols, &alpn_protos, &alpn_protos_len); /* Reversed return value convention... */ TEST_check(SSL_CTX_set_alpn_protos(client_ctx, alpn_protos, alpn_protos_len) == 0); OPENSSL_free(alpn_protos); } /* * Use fixed session ticket keys so that we can decrypt a ticket created with * one CTX in another CTX. Don't address server2 for the moment. */ ticket_key_len = SSL_CTX_set_tlsext_ticket_keys(server_ctx, NULL, 0); ticket_keys = OPENSSL_zalloc(ticket_key_len); TEST_check(ticket_keys != NULL); TEST_check(SSL_CTX_set_tlsext_ticket_keys(server_ctx, ticket_keys, ticket_key_len) == 1); OPENSSL_free(ticket_keys); /* The default log list includes EC keys, so CT can't work without EC. */ #if !defined(OPENSSL_NO_CT) && !defined(OPENSSL_NO_EC) TEST_check(SSL_CTX_set_default_ctlog_list_file(client_ctx)); switch (extra->client.ct_validation) { case SSL_TEST_CT_VALIDATION_PERMISSIVE: TEST_check(SSL_CTX_enable_ct(client_ctx, SSL_CT_VALIDATION_PERMISSIVE)); break; case SSL_TEST_CT_VALIDATION_STRICT: TEST_check(SSL_CTX_enable_ct(client_ctx, SSL_CT_VALIDATION_STRICT)); break; case SSL_TEST_CT_VALIDATION_NONE: break; } #endif }
TLS_APPL_STATE *tls_server_init(const TLS_SERVER_INIT_PROPS *props) { SSL_CTX *server_ctx; long off = 0; int verify_flags = SSL_VERIFY_NONE; int cachable; int scache_timeout; int ticketable = 0; int protomask; TLS_APPL_STATE *app_ctx; int log_mask; /* * Convert user loglevel to internal logmask. */ log_mask = tls_log_mask(props->log_param, props->log_level); if (log_mask & TLS_LOG_VERBOSE) msg_info("initializing the server-side TLS engine"); /* * Load (mostly cipher related) TLS-library internal main.cf parameters. */ tls_param_init(); /* * Detect mismatch between compile-time headers and run-time library. */ tls_check_version(); /* * Initialize the OpenSSL library by the book! To start with, we must * initialize the algorithms. We want cleartext error messages instead of * just error codes, so we load the error_strings. */ SSL_load_error_strings(); OpenSSL_add_ssl_algorithms(); /* * First validate the protocols. If these are invalid, we can't continue. */ protomask = tls_protocol_mask(props->protocols); if (protomask == TLS_PROTOCOL_INVALID) { /* tls_protocol_mask() logs no warning. */ msg_warn("Invalid TLS protocol list \"%s\": disabling TLS support", props->protocols); return (0); } /* * Create an application data index for SSL objects, so that we can * attach TLScontext information; this information is needed inside * tls_verify_certificate_callback(). */ if (TLScontext_index < 0) { if ((TLScontext_index = SSL_get_ex_new_index(0, 0, 0, 0, 0)) < 0) { msg_warn("Cannot allocate SSL application data index: " "disabling TLS support"); return (0); } } /* * If the administrator specifies an unsupported digest algorithm, fail * now, rather than in the middle of a TLS handshake. */ if (!tls_validate_digest(props->mdalg)) { msg_warn("disabling TLS support"); return (0); } /* * Initialize the PRNG (Pseudo Random Number Generator) with some seed * from external and internal sources. Don't enable TLS without some real * entropy. */ if (tls_ext_seed(var_tls_daemon_rand_bytes) < 0) { msg_warn("no entropy for TLS key generation: disabling TLS support"); return (0); } tls_int_seed(); /* * The SSL/TLS specifications require the client to send a message in the * oldest specification it understands with the highest level it * understands in the message. Netscape communicator can still * communicate with SSLv2 servers, so it sends out a SSLv2 client hello. * To deal with it, our server must be SSLv2 aware (even if we don't like * SSLv2), so we need to have the SSLv23 server here. If we want to limit * the protocol level, we can add an option to not use SSLv2/v3/TLSv1 * later. */ ERR_clear_error(); if ((server_ctx = SSL_CTX_new(SSLv23_server_method())) == 0) { msg_warn("cannot allocate server SSL_CTX: disabling TLS support"); tls_print_errors(); return (0); } /* * See the verify callback in tls_verify.c */ SSL_CTX_set_verify_depth(server_ctx, props->verifydepth + 1); /* * The session cache is implemented by the tlsmgr(8) server. * * XXX 200502 Surprise: when OpenSSL purges an entry from the in-memory * cache, it also attempts to purge the entry from the on-disk cache. * This is undesirable, especially when we set the in-memory cache size * to 1. For this reason we don't allow OpenSSL to purge on-disk cache * entries, and leave it up to the tlsmgr process instead. Found by * Victor Duchovni. */ if (tls_mgr_policy(props->cache_type, &cachable, &scache_timeout) != TLS_MGR_STAT_OK) scache_timeout = 0; if (scache_timeout <= 0) cachable = 0; /* * Protocol work-arounds, OpenSSL version dependent. */ off |= tls_bug_bits(); /* * Add SSL_OP_NO_TICKET when the timeout is zero or library support is * incomplete. The SSL_CTX_set_tlsext_ticket_key_cb feature was added in * OpenSSL 0.9.8h, while SSL_NO_TICKET was added in 0.9.8f. */ #ifdef SSL_OP_NO_TICKET #if !defined(OPENSSL_NO_TLSEXT) && OPENSSL_VERSION_NUMBER >= 0x0090808fL ticketable = (scache_timeout > 0 && !(off & SSL_OP_NO_TICKET)); if (ticketable) SSL_CTX_set_tlsext_ticket_key_cb(server_ctx, ticket_cb); #endif if (!ticketable) off |= SSL_OP_NO_TICKET; #endif SSL_CTX_set_options(server_ctx, off); /* * Global protocol selection. */ if (protomask != 0) SSL_CTX_set_options(server_ctx, ((protomask & TLS_PROTOCOL_TLSv1) ? SSL_OP_NO_TLSv1 : 0L) | ((protomask & TLS_PROTOCOL_TLSv1_1) ? SSL_OP_NO_TLSv1_1 : 0L) | ((protomask & TLS_PROTOCOL_TLSv1_2) ? SSL_OP_NO_TLSv1_2 : 0L) | ((protomask & TLS_PROTOCOL_SSLv3) ? SSL_OP_NO_SSLv3 : 0L) | ((protomask & TLS_PROTOCOL_SSLv2) ? SSL_OP_NO_SSLv2 : 0L)); /* * Some sites may want to give the client less rope. On the other hand, * this could trigger inter-operability issues, the client should not * offer ciphers it implements poorly, but this hasn't stopped some * vendors from getting it wrong. * * XXX: Given OpenSSL's security history, nobody should still be using * 0.9.7, let alone 0.9.6 or earlier. Warning added to TLS_README.html. */ if (var_tls_preempt_clist) SSL_CTX_set_options(server_ctx, SSL_OP_CIPHER_SERVER_PREFERENCE); /* * Set the call-back routine to debug handshake progress. */ if (log_mask & TLS_LOG_DEBUG) SSL_CTX_set_info_callback(server_ctx, tls_info_callback); /* * Load the CA public key certificates for both the server cert and for * the verification of client certificates. As provided by OpenSSL we * support two types of CA certificate handling: One possibility is to * add all CA certificates to one large CAfile, the other possibility is * a directory pointed to by CApath, containing separate files for each * CA with softlinks named after the hash values of the certificate. The * first alternative has the advantage that the file is opened and read * at startup time, so that you don't have the hassle to maintain another * copy of the CApath directory for chroot-jail. */ if (tls_set_ca_certificate_info(server_ctx, props->CAfile, props->CApath) < 0) { /* tls_set_ca_certificate_info() already logs a warning. */ SSL_CTX_free(server_ctx); /* 200411 */ return (0); } /* * Load the server public key certificate and private key from file and * check whether the cert matches the key. We can use RSA certificates * ("cert") DSA certificates ("dcert") or ECDSA certificates ("eccert"). * All three can be made available at the same time. The CA certificates * for all three are handled in the same setup already finished. Which * one is used depends on the cipher negotiated (that is: the first * cipher listed by the client which does match the server). A client * with RSA only (e.g. Netscape) will use the RSA certificate only. A * client with openssl-library will use RSA first if not especially * changed in the cipher setup. */ if (tls_set_my_certificate_key_info(server_ctx, props->cert_file, props->key_file, props->dcert_file, props->dkey_file, props->eccert_file, props->eckey_file) < 0) { /* tls_set_my_certificate_key_info() already logs a warning. */ SSL_CTX_free(server_ctx); /* 200411 */ return (0); } /* * According to OpenSSL documentation, a temporary RSA key is needed when * export ciphers are in use, because the certified key cannot be * directly used. */ SSL_CTX_set_tmp_rsa_callback(server_ctx, tls_tmp_rsa_cb); /* * Diffie-Hellman key generation parameters can either be loaded from * files (preferred) or taken from compiled in values. First, set the * callback that will select the values when requested, then load the * (possibly) available DH parameters from files. We are generous with * the error handling, since we do have default values compiled in, so we * will not abort but just log the error message. */ SSL_CTX_set_tmp_dh_callback(server_ctx, tls_tmp_dh_cb); if (*props->dh1024_param_file != 0) tls_set_dh_from_file(props->dh1024_param_file, 1024); if (*props->dh512_param_file != 0) tls_set_dh_from_file(props->dh512_param_file, 512); /* * Enable EECDH if available, errors are not fatal, we just keep going * with any remaining key-exchange algorithms. */ (void) tls_set_eecdh_curve(server_ctx, props->eecdh_grade); /* * If we want to check client certificates, we have to indicate it in * advance. By now we only allow to decide on a global basis. If we want * to allow certificate based relaying, we must ask the client to provide * one with SSL_VERIFY_PEER. The client now can decide, whether it * provides one or not. We can enforce a failure of the negotiation with * SSL_VERIFY_FAIL_IF_NO_PEER_CERT, if we do not allow a connection * without one. In the "server hello" following the initialization by the * "client hello" the server must provide a list of CAs it is willing to * accept. Some clever clients will then select one from the list of * available certificates matching these CAs. Netscape Communicator will * present the list of certificates for selecting the one to be sent, or * it will issue a warning, if there is no certificate matching the * available CAs. * * With regard to the purpose of the certificate for relaying, we might like * a later negotiation, maybe relaying would already be allowed for other * reasons, but this would involve severe changes in the internal postfix * logic, so we have to live with it the way it is. */ if (props->ask_ccert) verify_flags = SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE; SSL_CTX_set_verify(server_ctx, verify_flags, tls_verify_certificate_callback); if (*props->CAfile) SSL_CTX_set_client_CA_list(server_ctx, SSL_load_client_CA_file(props->CAfile)); /* * Initialize our own TLS server handle, before diving into the details * of TLS session cache management. */ app_ctx = tls_alloc_app_context(server_ctx, log_mask); if (cachable || ticketable || props->set_sessid) { /* * Initialize the session cache. * * With a large number of concurrent smtpd(8) processes, it is not a * good idea to cache multiple large session objects in each process. * We set the internal cache size to 1, and don't register a * "remove_cb" so as to avoid deleting good sessions from the * external cache prematurely (when the internal cache is full, * OpenSSL removes sessions from the external cache also)! * * This makes SSL_CTX_remove_session() not useful for flushing broken * sessions from the external cache, so we must delete them directly * (not via a callback). * * Set a session id context to identify to what type of server process * created a session. In our case, the context is simply the name of * the mail system: "Postfix/TLS". */ SSL_CTX_sess_set_cache_size(server_ctx, 1); SSL_CTX_set_session_id_context(server_ctx, (void *) &server_session_id_context, sizeof(server_session_id_context)); SSL_CTX_set_session_cache_mode(server_ctx, SSL_SESS_CACHE_SERVER | SSL_SESS_CACHE_NO_AUTO_CLEAR); if (cachable) { app_ctx->cache_type = mystrdup(props->cache_type); SSL_CTX_sess_set_get_cb(server_ctx, get_server_session_cb); SSL_CTX_sess_set_new_cb(server_ctx, new_server_session_cb); } /* * OpenSSL ignores timed-out sessions. We need to set the internal * cache timeout at least as high as the external cache timeout. This * applies even if no internal cache is used. We set the session * lifetime to twice the cache lifetime, which is also the issuing * and retired key validation lifetime of session tickets keys. This * way a session always lasts longer than the server's ability to * decrypt its session ticket. Otherwise, a bug in OpenSSL may fail * to re-issue tickets when sessions decrypt, but are expired. */ SSL_CTX_set_timeout(server_ctx, 2 * scache_timeout); } else { /* * If we have no external cache, disable all caching. No use wasting * server memory resources with sessions they are unlikely to be able * to reuse. */ SSL_CTX_set_session_cache_mode(server_ctx, SSL_SESS_CACHE_OFF); } return (app_ctx); }