ibmdb / node-ibm_db

IBM DB2 and IBM Informix bindings for node
MIT License
190 stars 151 forks source link

Certificates may be incorrectly cached #929

Closed fterui closed 1 year ago

fterui commented 1 year ago

Basic information:

Terminal output:

$ uname
Linux
$ uname -a
Linux 56e04a2fbf84 4.15.0-211-generic #222-Ubuntu SMP Tue Apr 18 18:55:06 UTC 2023 x86_64 x86_64 x86_64 GNU/Linux
$ node -v
v16.19.1
$ npm ls ibm_db
ibmdbtest@ /home/fterui/github/ibmdbtest
└── ibm_db@3.2.1

(Db2 server is not installed on this machine)

Problem summary

If a certificate is used to connect to a Db2 server, that certificate seems cached somewhere and the behavior of subsequent connection attempts to the same server may vary even if different certificate is specified.

Code to reproduce the problem


const ibmdb = require("ibm_db");

function connect(connectionUri) {
        return new Promise((resolve, reject) => {
                ibmdb.open(connectionUri, function(err, db) {
                        if (err) {
                                reject(err);
                        } else {
                                resolve("Connected");
                        }
                });
        });
}

async function call(url) {
        try {
                const result = await connect(url);
                console.log(`Success: ${result}`);
        } catch (e) {
                console.log(e);
        }
}

const DB2HOST = "xxx";
const PASSWORD = "yyy";
const CORRECT_CERT = "cert1";
const INCORRECT_CERT = "cert2";

const correct   = `DATABASE=SAMPLE;HOSTNAME=${DB2HOST};PORT=50001;PROTOCOL=TCPIP;UID=db2inst1;PWD=${PASSWORD};Security=SSL;SSLServerCertificate=${CORRECT_CERT};`;
const incorrect = `DATABASE=SAMPLE;HOSTNAME=${DB2HOST};PORT=50001;PROTOCOL=TCPIP;UID=db2inst1;PWD=${PASSWORD};Security=SSL;SSLServerCertificate=${INCORRECT_CERT};`;

async function sequence(url1, url2) {
        await call(url1);
        await call(url2);
        await call(url1);
        await call(url2);
}

async function correctFirst() {
        console.log("Correct -> Incorrect -> Correct -> Incorrect");
        await sequence(correct, incorrect, correct);
}
async function incorrectFirst() {
        console.log("Incorrect -> Correct -> Incorrect -> Correct");
        await sequence(incorrect, correct, incorrect);
}

let test;
if (process.argv[2] === "1") {
        test = incorrectFirst;
} else {
        test = correctFirst;
}

test().then(() => console.log("Finished"));

Steps to reproduce:

  1. Prepare two certificates for the particular Db2 server, one is correct certificate for the server and the other is incorrect (such as certificate for another Db2 server)
  2. Update the constants DB2HOST, PASSWORD, CORRECT_CERT and INCORRECT_CERT
  3. Run npm i ibm_db
  4. Run node certtest.js 0 and node certtest.js 1

Expected behavior

The certtest.js (above test code) tries to establish the connection to the server in this sequence:

In either way, we expect the connection should be established with correct certificate, and error should happen with incorrect certificate.

Actual results

$ node certtest.js 0
Correct -> Incorrect -> Correct -> Incorrect
Success: Connected
[Error: [IBM][CLI Driver] SQL10013N  The specified library "GSKit Error: 2" could not be loaded.  SQLSTATE=42724
] {
  error: '[ibm_db] SQL_ERROR',
  sqlcode: -10013,
  state: '42724'
}
Success: Connected
Success: Connected
Finished

The last trial (with incorrect certificate) should have failed but the connection is established.

$ node certtest.js 1
Incorrect -> Correct -> Incorrect -> Correct
[Error: [IBM][CLI Driver] SQL30081N  A communication error has been detected. Communication protocol being used: "SSL".  Communication API being used: "SOCKETS".  Location where the error was detected: "".  Communication function detecting the error: "sqlccSSLSocketSetup".  Protocol specific error code(s): "414", "*", "*".  SQLSTATE=08001
] {
  error: '[ibm_db] SQL_ERROR',
  sqlcode: -30081,
  state: '08001'
}
[Error: [IBM][CLI Driver] SQL10013N  The specified library "GSKit Error: 2" could not be loaded.  SQLSTATE=42724
] {
  error: '[ibm_db] SQL_ERROR',
  sqlcode: -10013,
  state: '42724'
}
[Error: [IBM][CLI Driver] SQL30081N  A communication error has been detected. Communication protocol being used: "SSL".  Communication API being used: "SOCKETS".  Location where the error was detected: "".  Communication function detecting the error: "sqlccSSLSocketSetup".  Protocol specific error code(s): "414", "*", "*".  SQLSTATE=08001
] {
  error: '[ibm_db] SQL_ERROR',
  sqlcode: -30081,
  state: '08001'
}
[Error: [IBM][CLI Driver] SQL30081N  A communication error has been detected. Communication protocol being used: "SSL".  Communication API being used: "SOCKETS".  Location where the error was detected: "".  Communication function detecting the error: "sqlccSSLSocketSetup".  Protocol specific error code(s): "414", "*", "*".  SQLSTATE=08001
] {
  error: '[ibm_db] SQL_ERROR',
  sqlcode: -30081,
  state: '08001'
}
Finished

The second and forth attempts (both with correct certificate) should have been successful but failed, and please also note that the error code for second attempt is not the same as others.

Any help is really appreciated. Thanks in advance.

bimalkjha commented 1 year ago

@fterui Please install ibm_db using npm install ibm_db -clidriver=v11.5.4 command after uninstalling it. Then run your test program and let us know the result. Thanks.

fterui commented 1 year ago

Thanks @bimalkjha. The results changed:

$ node certtest.js 0
Correct -> Incorrect -> Correct -> Incorrect
Success: Connected
Success: Connected
Success: Connected
Success: Connected
Finished
$ node certtest.js 1
Incorrect -> Correct -> Incorrect -> Correct
[Error: [IBM][CLI Driver] SQL30081N  A communication error has been detected. Communication protocol being used: "SSL".  Communication API being used: "SOCKETS".  Location where the error was detected: "".  Communication function detecting the error: "sqlccSSLSocketSetup".  Protocol specific error code(s): "414", "*", "*".  SQLSTATE=08001
] {
  error: '[ibm_db] SQL_ERROR',
  sqlcode: -30081,
  state: '08001'
}
Success: Connected
Success: Connected
Success: Connected
Finished

In either case, connection was established with correct certificate. The only issue I see with this version is that the connection is established even with incorrect certificate (second and forth attempt in the first case, and third attempt in the second case), once it was established with correct certificate.

bimalkjha commented 1 year ago

@fterui The last behavior using npm install ibm_db -clidriver=v11.5.4 command seems correct. Please find explanation as below: When we pass SSLServerCertificate is connection string, the ibm_db process creates a keystoredb in memory and adds certificate to it. Later during the handshaking with server for connection, client checks for valid certificate in keystoredb. If certificate found in keystoredb; connection succeeds. In case of second connection request with wrong certificate, the wrong certificate also get added to the pre-existing keystoredb that already has correct certificate. So, during the handshake with server for connection, client is able to find a valid server certificate in the keystoredb which was added during first connection. So, irrespective of correct or incorrect certificate, the second connection also succeeds. The keystoredb get created per process in memory and get cleaned when process exits. Since, both connection request is within the same process, so single keystoredb get used by both connections and hence this behavior.

The previous "GSKit Error:102" was due to new gskit library used in the default clidriver which is v11.5.8.0. So, when you installed ibm_db using clidriver v11.5.4.0 which has old gskit library, the issue disappeared. We'll fix this gskit bug in next upgrade of clidriver. Till then you can continue using clidriver v11.5.4.0. Thanks.

fterui commented 1 year ago

@bimalkjha Thanks for the explanation. Yes, we'll use v11.5.4 so far.

By the way, our application is using ibm_db to validate the credentials (including certificate), so successful connection with incorrect certificate may result in accepting incorrect credentials... Is there any way to clean up the keystoredb in memory other than terminating the process? Or is it better to use our own keystoredb for that purpose?

bimalkjha commented 1 year ago

@fterui There is no way of cleaning of in-memory keystoredb without terminating the process. The keystoredb is per process so multiple instance of ibm_db under single process will use same keydb. So, I think calling require(ibm_db) per connection in your application will still use same keystoredb, but you can verify it.

Using own keystoredb can serve the purpose and you'll have better control. Make sure, you are using new keystoredb for every certificate for validation. In that case the connection string would change and keystoredb will keep changing in every connection string. Make sure one keystoredb has only one certificate for validation.

For this purpose, you need to install latest version of GSKit in your system. You need to use gskit commands to create keystoredb and add certificate to it before attempting the connection. Make sure your GSKit version is 8.0.55.31 or later. Or gskit version 8.0.55.21 is also fine. Db2 v11.5.4.0 uses gskit v8.0.55.21. Thanks.

fterui commented 1 year ago

Thanks @bimalkjha. As you expected, calling require("ibm_db") per connection doesn't change the behavior, so it seems there is no way to clear the in-memory keystoredb. I'll then try the other solution.

I guess this behavior won't be fixed. If so, please go ahead and close this issue. Thanks for your support for this matter.

bimalkjha commented 1 year ago

@fterui Yes, this is the design for in-memory keystoredb that it can get cleaned only when process terminates. I am closing the issue now. Thanks.