| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 3b1db7f commit 33c1e8b
11 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -631,6 +631,12 @@ Used when `Console` is instantiated without `stdout` stream or when `stdout` or | |||
| 631 | 631 | ||
| 632 | 632 | Used when the native call from `process.cpuUsage` cannot be processed properly. | |
| 633 | 633 | ||
| 634 | + <a id="ERR_CRYPTO_CUSTOM_ENGINE_NOT_SUPPORTED"></a> | ||
| 635 | + ### ERR_CRYPTO_CUSTOM_ENGINE_NOT_SUPPORTED | ||
| 636 | + | ||
| 637 | + Used when a client certificate engine is requested that is not supported by the | ||
| 638 | + version of OpenSSL being used. | ||
| 639 | + | ||
| 634 | 640 | <a id="ERR_CRYPTO_ECDH_INVALID_FORMAT"></a> | |
| 635 | 641 | ### ERR_CRYPTO_ECDH_INVALID_FORMAT | |
| 636 | 642 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -208,6 +208,18 @@ exports.createSecureContext = function createSecureContext(options, context) { | |||
| 208 | 208 | c.context.setFreeListLength(0); | |
| 209 | 209 | } | |
| 210 | 210 | ||
| 211 | + if (typeof options.clientCertEngine === 'string') { | ||
| 212 | + if (c.context.setClientCertEngine) | ||
| 213 | + c.context.setClientCertEngine(options.clientCertEngine); | ||
| 214 | + else | ||
| 215 | + throw new errors.Error('ERR_CRYPTO_CUSTOM_ENGINE_NOT_SUPPORTED'); | ||
| 216 | + } else if (options.clientCertEngine != null) { | ||
| 217 | + throw new errors.TypeError('ERR_INVALID_ARG_TYPE', | ||
| 218 | + 'options.clientCertEngine', | ||
| 219 | + ['string', 'null', 'undefined'], | ||
| 220 | + options.clientCertEngine); | ||
| 221 | + } | ||
| 222 | + | ||
| 211 | 223 | return c; | |
| 212 | 224 | }; | |
| 213 | 225 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -816,6 +816,7 @@ function tlsConnectionListener(rawSocket) { | |||
| 816 | 816 | // - rejectUnauthorized. Boolean, default to true. | |
| 817 | 817 | // - key. string. | |
| 818 | 818 | // - cert: string. | |
| 819 | + // - clientCertEngine: string. | ||
| 819 | 820 | // - ca: string or array of strings. | |
| 820 | 821 | // - sessionTimeout: integer. | |
| 821 | 822 | // | |
@@ -859,6 +860,7 @@ function Server(options, listener) { | |||
| 859 | 860 | key: this.key, | |
| 860 | 861 | passphrase: this.passphrase, | |
| 861 | 862 | cert: this.cert, | |
| 863 | + clientCertEngine: this.clientCertEngine, | ||
| 862 | 864 | ca: this.ca, | |
| 863 | 865 | ciphers: this.ciphers, | |
| 864 | 866 | ecdhCurve: this.ecdhCurve, | |
@@ -931,6 +933,8 @@ Server.prototype.setOptions = function(options) { | |||
| 931 | 933 | if (options.key) this.key = options.key; | |
| 932 | 934 | if (options.passphrase) this.passphrase = options.passphrase; | |
| 933 | 935 | if (options.cert) this.cert = options.cert; | |
| 936 | + if (options.clientCertEngine) | ||
| 937 | + this.clientCertEngine = options.clientCertEngine; | ||
| 934 | 938 | if (options.ca) this.ca = options.ca; | |
| 935 | 939 | if (options.secureProtocol) this.secureProtocol = options.secureProtocol; | |
| 936 | 940 | if (options.crl) this.crl = options.crl; | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -160,6 +160,10 @@ Agent.prototype.getName = function getName(options) { | |||
| 160 | 160 | if (options.cert) | |
| 161 | 161 | name += options.cert; | |
| 162 | 162 | ||
| 163 | + name += ':'; | ||
| 164 | + if (options.clientCertEngine) | ||
| 165 | + name += options.clientCertEngine; | ||
| 166 | + | ||
| 163 | 167 | name += ':'; | |
| 164 | 168 | if (options.ciphers) | |
| 165 | 169 | name += options.ciphers; | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -155,6 +155,8 @@ E('ERR_CHILD_CLOSED_BEFORE_REPLY', 'Child closed before reply received'); | |||
| 155 | 155 | E('ERR_CONSOLE_WRITABLE_STREAM', | |
| 156 | 156 | 'Console expects a writable stream instance for %s'); | |
| 157 | 157 | E('ERR_CPU_USAGE', 'Unable to obtain cpu usage %s'); | |
| 158 | + E('ERR_CRYPTO_CUSTOM_ENGINE_NOT_SUPPORTED', | ||
| 159 | + 'Custom engines not supported by this OpenSSL'); | ||
| 158 | 160 | E('ERR_CRYPTO_ECDH_INVALID_FORMAT', 'Invalid ECDH format: %s'); | |
| 159 | 161 | E('ERR_CRYPTO_ENGINE_UNKNOWN', 'Engine "%s" was not found'); | |
| 160 | 162 | E('ERR_CRYPTO_FIPS_FORCED', | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -361,6 +361,41 @@ static int PasswordCallback(char *buf, int size, int rwflag, void *u) { | |||
| 361 | 361 | return 0; | |
| 362 | 362 | } | |
| 363 | 363 | ||
| 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 | ||
| 364 | 399 | ||
| 365 | 400 | // This callback is used to avoid the default passphrase callback in OpenSSL | |
| 366 | 401 | // which will typically prompt for the passphrase. The prompting is designed | |
@@ -505,6 +540,10 @@ void SecureContext::Initialize(Environment* env, Local<Object> target) { | |||
| 505 | 540 | SecureContext::SetSessionTimeout); | |
| 506 | 541 | env->SetProtoMethod(t, "close", SecureContext::Close); | |
| 507 | 542 | env->SetProtoMethod(t, "loadPKCS12", SecureContext::LoadPKCS12); | |
| 543 | + #ifndef OPENSSL_NO_ENGINE | ||
| 544 | + env->SetProtoMethod(t, "setClientCertEngine", | ||
| 545 | + SecureContext::SetClientCertEngine); | ||
| 546 | + #endif // !OPENSSL_NO_ENGINE | ||
| 508 | 547 | env->SetProtoMethod(t, "getTicketKeys", SecureContext::GetTicketKeys); | |
| 509 | 548 | env->SetProtoMethod(t, "setTicketKeys", SecureContext::SetTicketKeys); | |
| 510 | 549 | env->SetProtoMethod(t, "setFreeListLength", SecureContext::SetFreeListLength); | |
@@ -1302,6 +1341,46 @@ void SecureContext::LoadPKCS12(const FunctionCallbackInfo<Value>& args) { | |||
| 1302 | 1341 | } | |
| 1303 | 1342 | ||
| 1304 | 1343 | ||
| 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 | + | ||
| 1305 | 1384 | void SecureContext::GetTicketKeys(const FunctionCallbackInfo<Value>& args) { | |
| 1306 | 1385 | #if !defined(OPENSSL_NO_TLSEXT) && defined(SSL_CTX_get_tlsext_ticket_keys) | |
| 1307 | 1386 | ||
@@ -6124,20 +6203,10 @@ void SetEngine(const FunctionCallbackInfo<Value>& args) { | |||
| 6124 | 6203 | ||
| 6125 | 6204 | ClearErrorOnReturn clear_error_on_return; | |
| 6126 | 6205 | ||
| 6206 | + // Load engine. | ||
| 6127 | 6207 | 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); | ||
| 6141 | 6210 | ||
| 6142 | 6211 | if (engine == nullptr) { | |
| 6143 | 6212 | int err = ERR_get_error(); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -93,6 +93,9 @@ class SecureContext : public BaseObject { | |||
| 93 | 93 | SSL_CTX* ctx_; | |
| 94 | 94 | X509* cert_; | |
| 95 | 95 | X509* issuer_; | |
| 96 | + #ifndef OPENSSL_NO_ENGINE | ||
| 97 | + bool client_cert_engine_provided_ = false; | ||
| 98 | + #endif // !OPENSSL_NO_ENGINE | ||
| 96 | 99 | ||
| 97 | 100 | static const int kMaxSessionSize = 10 * 1024; | |
| 98 | 101 | ||
@@ -135,6 +138,10 @@ class SecureContext : public BaseObject { | |||
| 135 | 138 | const v8::FunctionCallbackInfo<v8::Value>& args); | |
| 136 | 139 | static void Close(const v8::FunctionCallbackInfo<v8::Value>& args); | |
| 137 | 140 | static void LoadPKCS12(const v8::FunctionCallbackInfo<v8::Value>& args); | |
| 141 | + #ifndef OPENSSL_NO_ENGINE | ||
| 142 | + static void SetClientCertEngine( | ||
| 143 | + const v8::FunctionCallbackInfo<v8::Value>& args); | ||
| 144 | + #endif // !OPENSSL_NO_ENGINE | ||
| 138 | 145 | static void GetTicketKeys(const v8::FunctionCallbackInfo<v8::Value>& args); | |
| 139 | 146 | static void SetTicketKeys(const v8::FunctionCallbackInfo<v8::Value>& args); | |
| 140 | 147 | static void SetFreeListLength( | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,24 @@ | |||
| 1 | + { | ||
| 2 | + 'targets': [ | ||
| 3 | + { | ||
| 4 | + 'target_name': 'testengine', | ||
| 5 | + 'type': 'none', | ||
| 6 | + 'conditions': [ | ||
| 7 | + ['OS=="mac" and ' | ||
| 8 | + 'node_use_openssl=="true" and ' | ||
| 9 | + 'node_shared=="false" and ' | ||
| 10 | + 'node_shared_openssl=="false"', { | ||
| 11 | + 'type': 'shared_library', | ||
| 12 | + 'sources': [ 'testengine.cc' ], | ||
| 13 | + 'product_extension': 'engine', | ||
| 14 | + 'include_dirs': ['../../../deps/openssl/openssl/include'], | ||
| 15 | + 'link_settings': { | ||
| 16 | + 'libraries': [ | ||
| 17 | + '../../../../out/<(PRODUCT_DIR)/<(OPENSSL_PRODUCT)' | ||
| 18 | + ] | ||
| 19 | + }, | ||
| 20 | + }] | ||
| 21 | + ] | ||
| 22 | + } | ||
| 23 | + ] | ||
| 24 | + } | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,62 @@ | |||
| 1 | + 'use strict'; | ||
| 2 | + const common = require('../../common'); | ||
| 3 | + const fixture = require('../../common/fixtures'); | ||
| 4 | + | ||
| 5 | + if (!common.hasCrypto) | ||
| 6 | + common.skip('missing crypto'); | ||
| 7 | + | ||
| 8 | + const fs = require('fs'); | ||
| 9 | + const path = require('path'); | ||
| 10 | + | ||
| 11 | + const engine = path.join(__dirname, | ||
| 12 | + `/build/${common.buildType}/testengine.engine`); | ||
| 13 | + | ||
| 14 | + if (!fs.existsSync(engine)) | ||
| 15 | + common.skip('no client cert engine'); | ||
| 16 | + | ||
| 17 | + const assert = require('assert'); | ||
| 18 | + const https = require('https'); | ||
| 19 | + | ||
| 20 | + const agentKey = fs.readFileSync(fixture.path('/keys/agent1-key.pem')); | ||
| 21 | + const agentCert = fs.readFileSync(fixture.path('/keys/agent1-cert.pem')); | ||
| 22 | + const agentCa = fs.readFileSync(fixture.path('/keys/ca1-cert.pem')); | ||
| 23 | + | ||
| 24 | + const port = common.PORT; | ||
| 25 | + | ||
| 26 | + const serverOptions = { | ||
| 27 | + key: agentKey, | ||
| 28 | + cert: agentCert, | ||
| 29 | + ca: agentCa, | ||
| 30 | + requestCert: true, | ||
| 31 | + rejectUnauthorized: true | ||
| 32 | + }; | ||
| 33 | + | ||
| 34 | + const server = https.createServer(serverOptions, (req, res) => { | ||
| 35 | + res.writeHead(200); | ||
| 36 | + res.end('hello world'); | ||
| 37 | + }).listen(port, common.localhostIPv4, () => { | ||
| 38 | + const clientOptions = { | ||
| 39 | + method: 'GET', | ||
| 40 | + host: common.localhostIPv4, | ||
| 41 | + port: port, | ||
| 42 | + path: '/test', | ||
| 43 | + clientCertEngine: engine, // engine will provide key+cert | ||
| 44 | + rejectUnauthorized: false, // prevent failing on self-signed certificates | ||
| 45 | + headers: {} | ||
| 46 | + }; | ||
| 47 | + | ||
| 48 | + const req = https.request(clientOptions, common.mustCall(function(response) { | ||
| 49 | + let body = ''; | ||
| 50 | + response.setEncoding('utf8'); | ||
| 51 | + response.on('data', function(chunk) { | ||
| 52 | + body += chunk; | ||
| 53 | + }); | ||
| 54 | + | ||
| 55 | + response.on('end', common.mustCall(function() { | ||
| 56 | + assert.strictEqual(body, 'hello world'); | ||
| 57 | + server.close(); | ||
| 58 | + })); | ||
| 59 | + })); | ||
| 60 | + | ||
| 61 | + req.end(); | ||
| 62 | + }); | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments