44
55#include " caf/openssl/session.hpp"
66
7+ #include < optional>
8+ #include < openssl/decoder.h>
9+
710CAF_PUSH_WARNINGS
811#include < openssl/err.h>
912CAF_POP_WARNINGS
@@ -56,6 +59,33 @@ int pem_passwd_cb(char* buf, int size, int, void* ptr) {
5659 return static_cast <int >(strlen (buf));
5760}
5861
62+ // If the input is a string like `env:SOME_VARIABLE` and the environment variable
63+ // `SOME_VARIABLE` exists, returns a string with the value of `SOME_VARIABLE`.
64+ // Otherwise, returns `std::nullopt`.
65+ std::optional<std::string> contents_from_indirect_envvar (const std::string& str) {
66+ if (str.find (" env:" ) != 0 )
67+ return std::nullopt ;
68+ auto var = str.substr (4 );
69+ auto const * contents = ::getenv (var.c_str ());
70+ if (contents == nullptr )
71+ return std::nullopt ;
72+ return std::string{contents};
73+ }
74+
75+ // If the input is a string like `env:SOME_VARIABLE` and the environment variable
76+ // `SOME_VARIABLE` exists, returns a string with the value of `SOME_VARIABLE`.
77+ std::optional<std::string> tmpfile_from_indirect_envvar (const std::string& str) {
78+ auto contents = contents_from_indirect_envvar (str);
79+ if (!contents)
80+ return contents;
81+ auto filename = std::string{" /tmp/caf-openssl.XXXXXX" };
82+ ::mkstemp (&filename[0 ]);
83+ std::ofstream ofs (filename);
84+ ofs << *contents;
85+ ofs.close ();
86+ return filename;
87+ }
88+
5989} // namespace
6090
6191session::session (actor_system& sys)
@@ -207,16 +237,47 @@ SSL_CTX* session::create_ssl_context() {
207237 if (sys_.openssl_manager ().authentication_enabled ()) {
208238 // Require valid certificates on both sides.
209239 auto & cfg = sys_.config ();
210- if (!cfg.openssl_certificate .empty ()
240+ // OpenSSL doesn't expose an API to read a certificate chain PEM from
241+ // memory (as far as I can tell, there might be something in OSSL_DECODER)
242+ // and the implementation of `SSL_CTX_use_certificate_chain_file`
243+ // is huge, so we just write into a temporary file.
244+ if (auto filename = tmpfile_from_indirect_envvar (cfg.openssl_certificate )) {
245+ if (SSL_CTX_use_certificate_chain_file (ctx, filename->c_str ())
246+ != 1 )
247+ CAF_RAISE_ERROR (" cannot load certificate from environt variable" );
248+ } else if (!cfg.openssl_certificate .empty ()
211249 && SSL_CTX_use_certificate_chain_file (ctx,
212250 cfg.openssl_certificate .c_str ())
213- != 1 )
251+ != 1 ) {
214252 CAF_RAISE_ERROR (" cannot load certificate" );
253+ }
215254 if (!cfg.openssl_passphrase .empty ()) {
216255 openssl_passphrase_ = cfg.openssl_passphrase ;
217256 SSL_CTX_set_default_passwd_cb (ctx, pem_passwd_cb);
218257 SSL_CTX_set_default_passwd_cb_userdata (ctx, this );
219258 }
259+ if (auto var = contents_from_indirect_envvar (cfg.openssl_key )) {
260+ EVP_PKEY *pkey = nullptr ;
261+ const char *format = " PEM" ; // NULL for any format
262+ const char *structure = nullptr ; // Any structure
263+ const char *keytype = nullptr ; // Any key
264+ auto const * datap = reinterpret_cast <const unsigned char *>(var->data ());
265+ auto len = var->size ();
266+ int selection = 0 ; // Autodetect selection.
267+ // TODO: We might as well read `openssl_passphrase` here and pass it
268+ // to the decoder.
269+ auto * dctx = OSSL_DECODER_CTX_new_for_pkey (&pkey, format, structure,
270+ keytype,
271+ selection,
272+ nullptr , nullptr );
273+ if (dctx == nullptr )
274+ CAF_RAISE_ERROR (" couldn't create openssl decoder context" );
275+ if (OSSL_DECODER_from_data (dctx, &datap, &len) != 0 )
276+ CAF_RAISE_ERROR (" failed to decode private key" );
277+ // Use the private key.
278+ SSL_CTX_use_PrivateKey (ctx, pkey);
279+ OSSL_DECODER_CTX_free (dctx);
280+ }
220281 if (!cfg.openssl_key .empty ()
221282 && SSL_CTX_use_PrivateKey_file (ctx, cfg.openssl_key .c_str (),
222283 SSL_FILETYPE_PEM)
@@ -226,6 +287,9 @@ SSL_CTX* session::create_ssl_context() {
226287 : nullptr );
227288 auto capath = (!cfg.openssl_capath .empty () ? cfg.openssl_capath .c_str ()
228289 : nullptr );
290+ auto tmpfile = tmpfile_from_indirect_envvar (cafile);
291+ if (tmpfile)
292+ cafile = tmpfile->c_str ();
229293 if (cafile || capath) {
230294 if (SSL_CTX_load_verify_locations (ctx, cafile, capath) != 1 )
231295 CAF_RAISE_ERROR (" cannot load trusted CA certificates" );
0 commit comments