@@ -361,6 +361,41 @@ static int PasswordCallback(char *buf, int size, int rwflag, void *u) {
361361 return 0 ;
362362}
363363
364+ // Loads OpenSSL engine by engine id and returns it. The loaded engine
365+ // gets a reference so remember the corresponding call to ENGINE_free.
366+ // In case of error the appropriate js exception is scheduled
367+ // and nullptr is returned.
368+ #ifndef OPENSSL_NO_ENGINE
369+ static ENGINE* LoadEngineById (const char * engine_id, char (*errmsg)[1024]) {
370+ MarkPopErrorOnReturn mark_pop_error_on_return;
371+
372+ ENGINE* engine = ENGINE_by_id (engine_id);
373+
374+ if (engine == nullptr ) {
375+ // Engine not found, try loading dynamically.
376+ engine = ENGINE_by_id (" dynamic" );
377+ if (engine != nullptr ) {
378+ if (!ENGINE_ctrl_cmd_string (engine, " SO_PATH" , engine_id, 0 ) ||
379+ !ENGINE_ctrl_cmd_string (engine, " LOAD" , nullptr , 0 )) {
380+ ENGINE_free (engine);
381+ engine = nullptr ;
382+ }
383+ }
384+ }
385+
386+ if (engine == nullptr ) {
387+ int err = ERR_get_error ();
388+ if (err != 0 ) {
389+ ERR_error_string_n (err, *errmsg, sizeof (*errmsg));
390+ } else {
391+ snprintf (*errmsg, sizeof (*errmsg),
392+ " Engine \" %s\" was not found" , engine_id);
393+ }
394+ }
395+
396+ return engine;
397+ }
398+ #endif // !OPENSSL_NO_ENGINE
364399
365400// This callback is used to avoid the default passphrase callback in OpenSSL
366401// which will typically prompt for the passphrase. The prompting is designed
@@ -505,6 +540,10 @@ void SecureContext::Initialize(Environment* env, Local<Object> target) {
505540 SecureContext::SetSessionTimeout);
506541 env->SetProtoMethod (t, " close" , SecureContext::Close);
507542 env->SetProtoMethod (t, " loadPKCS12" , SecureContext::LoadPKCS12);
543+ #ifndef OPENSSL_NO_ENGINE
544+ env->SetProtoMethod (t, " setClientCertEngine" ,
545+ SecureContext::SetClientCertEngine);
546+ #endif // !OPENSSL_NO_ENGINE
508547 env->SetProtoMethod (t, " getTicketKeys" , SecureContext::GetTicketKeys);
509548 env->SetProtoMethod (t, " setTicketKeys" , SecureContext::SetTicketKeys);
510549 env->SetProtoMethod (t, " setFreeListLength" , SecureContext::SetFreeListLength);
@@ -1302,6 +1341,46 @@ void SecureContext::LoadPKCS12(const FunctionCallbackInfo<Value>& args) {
13021341}
13031342
13041343
1344+ #ifndef OPENSSL_NO_ENGINE
1345+ void SecureContext::SetClientCertEngine (
1346+ const FunctionCallbackInfo<Value>& args) {
1347+ Environment* env = Environment::GetCurrent (args);
1348+ CHECK_EQ (args.Length (), 1 );
1349+ CHECK (args[0 ]->IsString ());
1350+
1351+ SecureContext* sc = Unwrap<SecureContext>(args.This ());
1352+
1353+ MarkPopErrorOnReturn mark_pop_error_on_return;
1354+
1355+ // SSL_CTX_set_client_cert_engine does not itself support multiple
1356+ // calls by cleaning up before overwriting the client_cert_engine
1357+ // internal context variable.
1358+ // Instead of trying to fix up this problem we in turn also do not
1359+ // support multiple calls to SetClientCertEngine.
1360+ if (sc->client_cert_engine_provided_ ) {
1361+ return env->ThrowError (
1362+ " Multiple calls to SetClientCertEngine are not allowed" );
1363+ }
1364+
1365+ const node::Utf8Value engine_id (env->isolate (), args[0 ]);
1366+ char errmsg[1024 ];
1367+ ENGINE* engine = LoadEngineById (*engine_id, &errmsg);
1368+
1369+ if (engine == nullptr ) {
1370+ return env->ThrowError (errmsg);
1371+ }
1372+
1373+ int r = SSL_CTX_set_client_cert_engine (sc->ctx_ , engine);
1374+ // Free reference (SSL_CTX_set_client_cert_engine took it via ENGINE_init).
1375+ ENGINE_free (engine);
1376+ if (r == 0 ) {
1377+ return ThrowCryptoError (env, ERR_get_error ());
1378+ }
1379+ sc->client_cert_engine_provided_ = true ;
1380+ }
1381+ #endif // !OPENSSL_NO_ENGINE
1382+
1383+
13051384void SecureContext::GetTicketKeys (const FunctionCallbackInfo<Value>& args) {
13061385#if !defined(OPENSSL_NO_TLSEXT) && defined(SSL_CTX_get_tlsext_ticket_keys)
13071386
@@ -6124,20 +6203,10 @@ void SetEngine(const FunctionCallbackInfo<Value>& args) {
61246203
61256204 ClearErrorOnReturn clear_error_on_return;
61266205
6206+ // Load engine.
61276207 const node::Utf8Value engine_id (env->isolate (), args[0 ]);
6128- ENGINE* engine = ENGINE_by_id (*engine_id);
6129-
6130- // Engine not found, try loading dynamically
6131- if (engine == nullptr ) {
6132- engine = ENGINE_by_id (" dynamic" );
6133- if (engine != nullptr ) {
6134- if (!ENGINE_ctrl_cmd_string (engine, " SO_PATH" , *engine_id, 0 ) ||
6135- !ENGINE_ctrl_cmd_string (engine, " LOAD" , nullptr , 0 )) {
6136- ENGINE_free (engine);
6137- engine = nullptr ;
6138- }
6139- }
6140- }
6208+ char errmsg[1024 ];
6209+ ENGINE* engine = LoadEngineById (*engine_id, &errmsg);
61416210
61426211 if (engine == nullptr ) {
61436212 int err = ERR_get_error ();
0 commit comments