From b4289224de9cdabf204fe896063cb4b9459212df Mon Sep 17 00:00:00 2001 From: Esa Korhonen Date: Wed, 26 Jun 2019 16:21:21 +0300 Subject: [PATCH] Avoid using SQLITE_OPEN_URI Centos6 uses a very old version of SQLite without support for URI filenames. PAM authenticator must use a file-based database. --- .../authenticator/PAM/PAMAuth/pam_auth.cc | 3 ++ .../authenticator/PAM/PAMAuth/pam_auth.hh | 5 ++- .../PAM/PAMAuth/pam_client_session.cc | 25 ++++++++++-- .../authenticator/PAM/PAMAuth/pam_instance.cc | 39 +++++++++++++------ 4 files changed, 56 insertions(+), 16 deletions(-) diff --git a/server/modules/authenticator/PAM/PAMAuth/pam_auth.cc b/server/modules/authenticator/PAM/PAMAuth/pam_auth.cc index 4c5e5d56d..015be96f3 100644 --- a/server/modules/authenticator/PAM/PAMAuth/pam_auth.cc +++ b/server/modules/authenticator/PAM/PAMAuth/pam_auth.cc @@ -28,6 +28,9 @@ const string FIELD_ANYDB = "anydb"; const string FIELD_AUTHSTR = "authentication_string"; const int NUM_FIELDS = 5; +const char* SQLITE_OPEN_FAIL = "Failed to open SQLite3 handle for file '%s': '%s'"; +const char* SQLITE_OPEN_OOM = "Failed to allocate memory for SQLite3 handle for file '%s'."; + /** * Initialize PAM authenticator * diff --git a/server/modules/authenticator/PAM/PAMAuth/pam_auth.hh b/server/modules/authenticator/PAM/PAMAuth/pam_auth.hh index 9b1d50bcf..97de3419e 100644 --- a/server/modules/authenticator/PAM/PAMAuth/pam_auth.hh +++ b/server/modules/authenticator/PAM/PAMAuth/pam_auth.hh @@ -32,4 +32,7 @@ extern const string FIELD_HOST; extern const string FIELD_DB; extern const string FIELD_ANYDB; extern const string FIELD_AUTHSTR; -extern const int NUM_FIELDS; \ No newline at end of file +extern const int NUM_FIELDS; + +extern const char* SQLITE_OPEN_FAIL; +extern const char* SQLITE_OPEN_OOM; diff --git a/server/modules/authenticator/PAM/PAMAuth/pam_client_session.cc b/server/modules/authenticator/PAM/PAMAuth/pam_client_session.cc index 9b049404e..25008c099 100644 --- a/server/modules/authenticator/PAM/PAMAuth/pam_client_session.cc +++ b/server/modules/authenticator/PAM/PAMAuth/pam_client_session.cc @@ -204,17 +204,34 @@ PamClientSession* PamClientSession::create(const PamInstance& inst) { // This handle is only used from one thread, can define no_mutex. sqlite3* dbhandle = NULL; - int db_flags = SQLITE_OPEN_URI | SQLITE_OPEN_READONLY | SQLITE_OPEN_SHAREDCACHE | SQLITE_OPEN_NOMUTEX; - if (sqlite3_open_v2(inst.m_dbname.c_str(), &dbhandle, db_flags, NULL) == SQLITE_OK) + bool error = false; + int db_flags = SQLITE_OPEN_READONLY | SQLITE_OPEN_SHAREDCACHE | SQLITE_OPEN_NOMUTEX; + const char* filename = inst.m_dbname.c_str(); + if (sqlite3_open_v2(filename, &dbhandle, db_flags, NULL) == SQLITE_OK) { sqlite3_busy_timeout(dbhandle, 1000); } else { - MXS_ERROR("Failed to open SQLite3 handle."); + if (dbhandle) + { + MXS_ERROR(SQLITE_OPEN_FAIL, filename, sqlite3_errmsg(dbhandle)); + } + else + { + // This means memory allocation failed. + MXS_ERROR(SQLITE_OPEN_OOM, filename); + } + error = true; } + PamClientSession* rval = NULL; - if (!dbhandle || (rval = new (std::nothrow) PamClientSession(dbhandle, inst)) == NULL) + if (!error && ((rval = new (std::nothrow) PamClientSession(dbhandle, inst)) == NULL)) + { + error = true; + } + + if (error) { sqlite3_close_v2(dbhandle); } diff --git a/server/modules/authenticator/PAM/PAMAuth/pam_instance.cc b/server/modules/authenticator/PAM/PAMAuth/pam_instance.cc index 4f7135028..0eeec7212 100644 --- a/server/modules/authenticator/PAM/PAMAuth/pam_instance.cc +++ b/server/modules/authenticator/PAM/PAMAuth/pam_instance.cc @@ -17,11 +17,10 @@ #include #include #include +#include #include #include -#define DEFAULT_PAM_DATABASE_NAME "file:pam.db?mode=memory&cache=shared" -#define DEFAULT_PAM_TABLE_NAME "pam_users" using std::string; /** @@ -32,10 +31,12 @@ using std::string; */ PamInstance* PamInstance::create(char **options) { - /** Name of the in-memory database */ - const string pam_db_name = DEFAULT_PAM_DATABASE_NAME; - /** The table name where we store the users */ - const string pam_table_name = DEFAULT_PAM_TABLE_NAME; + // Name of the in-memory database. + // TODO: Once Centos6 is no longer needed and Sqlite version 3.7+ can be assumed, + // use a memory-only db with a URI filename (e.g. file:pam.db?mode=memory&cache=shared) + const string pam_db_fname = string(get_cachedir()) + "/pam_db.sqlite3"; + // The table name where we store the users + const string pam_table_name = "pam_users"; /** CREATE TABLE statement for the in-memory table */ const string create_sql = string("CREATE TABLE IF NOT EXISTS ") + pam_table_name + " (" + FIELD_USER + " varchar(255), " + FIELD_HOST + " varchar(255), " + @@ -49,15 +50,25 @@ PamInstance* PamInstance::create(char **options) bool error = false; /* This handle may be used from multiple threads, set full mutex. */ sqlite3* dbhandle = NULL; - int db_flags = SQLITE_OPEN_URI | SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | + int db_flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_SHAREDCACHE | SQLITE_OPEN_FULLMUTEX; - if (sqlite3_open_v2(pam_db_name.c_str(), &dbhandle, db_flags, NULL) != SQLITE_OK) + const char* filename = pam_db_fname.c_str(); + if (sqlite3_open_v2(filename, &dbhandle, db_flags, NULL) != SQLITE_OK) { - MXS_ERROR("Failed to open SQLite3 handle."); + // Even if the open failed, the handle may exist and an error message can be read. + if (dbhandle) + { + MXS_ERROR(SQLITE_OPEN_FAIL, filename, sqlite3_errmsg(dbhandle)); + } + else + { + // This means memory allocation failed. + MXS_ERROR(SQLITE_OPEN_OOM, filename); + } error = true; } - char *err; + char *err = NULL; if (!error && sqlite3_exec(dbhandle, create_sql.c_str(), NULL, NULL, &err) != SQLITE_OK) { MXS_ERROR("Failed to create database: '%s'", err); @@ -67,8 +78,14 @@ PamInstance* PamInstance::create(char **options) PamInstance *instance = NULL; if (!error && - ((instance = new (std::nothrow) PamInstance(dbhandle, pam_db_name, pam_table_name)) == NULL)) + ((instance = new (std::nothrow) PamInstance(dbhandle, pam_db_fname, pam_table_name)) == NULL)) { + error = true; + } + + if (error) + { + // Close the handle even if never opened. sqlite3_close_v2(dbhandle); } return instance;