Encrypted password support for Pgpool-II internal backend connections.
authorMuhammad Usama <m.usama@gmail.com>
Wed, 8 Aug 2018 15:34:30 +0000 (20:34 +0500)
committerMuhammad Usama <m.usama@gmail.com>
Wed, 8 Aug 2018 15:34:30 +0000 (20:34 +0500)
The commit allows to specify the AES encrypted password in the pgpool.conf file
for healh_check_user, sr_check_user, wd_lifecheck_user and recovery_user users,
Additionally if the password field for any of these users is left blank in
pgpool conf then Pgpool-II will first try to get the password for that user
from pool_passwd file before using the empty password for the connection.
So now pgpool.conf can be made password free and single pool_passwd file can be
used to store all passwords for internal and external user connection

src/auth/pool_passwd.c
src/include/auth/pool_passwd.h
src/main/health_check.c
src/main/pgpool_main.c
src/pcp_con/recovery.c
src/streaming_replication/pool_worker_child.c
src/watchdog/wd_lifecheck.c

index 051f18534ac1756b1433e590ac900ebd89b3acfd..6670f0d722085a8084716377d113cd34970f176c 100644 (file)
@@ -327,15 +327,16 @@ PasswordMapping *pool_get_user_credentials(char *username)
        PasswordMapping *pwdMapping = NULL;
        char        buf[1024];
 
-
        if (!username)
                ereport(ERROR,
                                (errmsg("unable to get password, username is NULL")));
 
        if (!passwd_fd)
-               ereport(ERROR,
+       {
+               ereport(WARNING,
                                (errmsg("unable to get password, password file descriptor is NULL")));
-
+               return NULL;
+       }
        rewind(passwd_fd);
 
        while (!feof(passwd_fd) && !ferror(passwd_fd))
@@ -361,10 +362,10 @@ PasswordMapping *pool_get_user_credentials(char *username)
                t = getNextToken(t, &tok);
                if (tok)
                {
-                       pwdMapping = palloc(sizeof(PasswordMapping));
+                       pwdMapping = palloc0(sizeof(PasswordMapping));
                        pwdMapping->pgpoolUser.password = tok;
                        pwdMapping->pgpoolUser.passwordType = get_password_type(pwdMapping->pgpoolUser.password);
-                       pwdMapping->pgpoolUser.userName = pstrdup(username);
+                       pwdMapping->pgpoolUser.userName = (char*) pstrdup(username);
                        pwdMapping->mappedUser = false;
                }
                else
@@ -389,6 +390,23 @@ PasswordMapping *pool_get_user_credentials(char *username)
        return pwdMapping;
 }
 
+void delete_passwordMapping(PasswordMapping *pwdMapping)
+{
+       if (!pwdMapping)
+               return;
+       if (pwdMapping->pgpoolUser.password)
+               pfree(pwdMapping->pgpoolUser.password);
+       if (pwdMapping->pgpoolUser.userName)
+               pfree(pwdMapping->pgpoolUser.userName);
+
+       if (pwdMapping->backendUser.password)
+               pfree(pwdMapping->backendUser.password);
+       if (pwdMapping->backendUser.userName)
+               pfree(pwdMapping->backendUser.userName);
+
+       pfree(pwdMapping);
+}
+
 /*
  * Delete the entry by username. If specified entry does not exist,
  * does nothing.
@@ -415,6 +433,75 @@ void pool_reopen_passwd_file(void)
        pool_init_pool_passwd(saved_passwd_filename, pool_passwd_mode);
 }
 
+/*
+ * function first uses the password in the argument, if the
+ * argument is empty string or NULL, it looks for the password
+ * for uset in pool_passwd file.
+ * The returned password is always in plain text and palloc'd (if not null)
+ */
+char *get_pgpool_config_user_password(char *username, char *password_in_config)
+{
+       PasswordType passwordType = PASSWORD_TYPE_UNKNOWN;
+       char *password = NULL;
+       PasswordMapping*  password_mapping = NULL;
+       /*
+        * if the password specified in confg is empty strin or NULL
+        * look for the password in pool_passwd file
+        */
+       if (password_in_config == NULL || strlen(password_in_config) == 0)
+       {
+               password_mapping = pool_get_user_credentials(username);
+               if (password_mapping == NULL)
+               {
+                       return NULL;
+               }
+               passwordType = password_mapping->pgpoolUser.passwordType;
+               password = password_mapping->pgpoolUser.password;
+       }
+       else
+       {
+               passwordType = get_password_type(password_in_config);
+               password = password_in_config;
+       }
+
+       if (passwordType == PASSWORD_TYPE_AES)
+       {
+               /*
+                * decrypt the stored AES password
+                * for comparing it
+                */
+               password = get_decrypted_password(password);
+               if (password == NULL)
+               {
+                       ereport(WARNING,
+                               (errmsg("could not get the password for user:%s",username),
+                                        errdetail("unable to decrypt password from pool_passwd"),
+                                        errhint("verify the valid pool_key exists")));
+               }
+               else
+               {
+                       delete_passwordMapping(password_mapping);
+                       /* the password returned by get_decrypted_password() is
+                        * already palloc'd */
+                       return password;
+               }
+       }
+
+       if (passwordType != PASSWORD_TYPE_PLAINTEXT)
+       {
+               ereport(WARNING,
+                               (errmsg("could not get the password for user:%s",username),
+                                errdetail("username \"%s\" has invalid password type",username)));
+               password = NULL;
+       }
+       if (password)
+               password = (char*)pstrdup(password);
+
+       delete_passwordMapping(password_mapping);
+
+       return password;
+}
+
 #ifndef POOL_PRIVATE
 char *get_decrypted_password(const char *shadow_pass)
 {
@@ -460,9 +547,6 @@ char *get_decrypted_password(const char *shadow_pass)
 }
 #endif
 
-/*
-* What kind of a password verifier is 'shadow_pass'?
-*/
 PasswordType
 get_password_type(const char *shadow_pass)
 {
@@ -472,6 +556,7 @@ get_password_type(const char *shadow_pass)
                return PASSWORD_TYPE_AES;
        if (strncmp(shadow_pass, PASSWORD_SCRAM_PREFIX, strlen(PASSWORD_SCRAM_PREFIX)) == 0)
                return PASSWORD_TYPE_SCRAM_SHA_256;
+
        return PASSWORD_TYPE_PLAINTEXT;
 }
 
index 9005f904ca7af1bbc1d024c5f806ebcf97cc5f11..71f059379b805b2e550e74526cdcdbe3b1902bc6 100644 (file)
@@ -38,6 +38,7 @@
 #define PASSWORD_MD5_PREFIX    "md5"
 #define PASSWORD_AES_PREFIX    "AES"
 #define PASSWORD_SCRAM_PREFIX  "SCRAM-SHA-256$"
+#define PASSWORD_TEXT_PREFIX   "TEXT"
 
 typedef enum {
        POOL_PASSWD_R,          /* open pool_passwd in read only mode. used by pgpool-II child main process */
@@ -77,5 +78,6 @@ extern void pool_finish_pool_passwd(void);
 extern void pool_reopen_passwd_file(void);
 extern char *get_decrypted_password(const char *shadow_pass);
 extern char *read_pool_key(char *key_file_path);
-
+extern char *get_pgpool_config_user_password(char *username, char *password_in_config);
+extern void delete_passwordMapping(PasswordMapping *pwdMapping);
 #endif /* POOL_PASSWD_H */
index de3eda8e8218eee19fac1cd9126f378f97b94352..835970817c39febbb7c3baeec8ed28966c3b75d5 100644 (file)
@@ -245,6 +245,9 @@ static bool establish_persistent_connection(int node)
        {
                retry_cnt = pool_config->health_check_params[node].health_check_max_retries;
 
+               char *password = get_pgpool_config_user_password(pool_config->health_check_params[node].health_check_user,
+                                                                                                                pool_config->health_check_params[node].health_check_password);
+
                do
                {
                        /*
@@ -265,7 +268,7 @@ static bool establish_persistent_connection(int node)
                                                                                                                 bkinfo->backend_port,
                                                                                                                 pool_config->health_check_params[node].health_check_database,
                                                                                                                 pool_config->health_check_params[node].health_check_user,
-                                                                                                                pool_config->health_check_params[node].health_check_password, false);
+                                                                                                                password?password:"", false);
 
                        if (pool_config->health_check_params[node].health_check_timeout > 0)
                        {
@@ -297,6 +300,9 @@ static bool establish_persistent_connection(int node)
                                sleep(pool_config->health_check_params[node].health_check_retry_delay);
                        }
                } while (retry_cnt >= 0);
+
+               if (password)
+                       pfree(password);
        }
        return true;
 }
index 9814924619a2815747ea61e1d77412915591799a..7329f0fb1d36ee81477e2ca6d840d5e7ca233ac9 100644 (file)
@@ -2301,7 +2301,8 @@ do_health_check(bool use_template_db, volatile int *health_check_node_id)
        static char *dbname;
        int i;
        bool all_nodes_healthy = false;
-
+       char *password = get_pgpool_config_user_password(pool_config->health_check_user,
+                                                                                                        pool_config->health_check_password);
        /* Do not execute health check during recovery */
        if (*InRecovery)
                return false;
@@ -2352,13 +2353,15 @@ do_health_check(bool use_template_db, volatile int *health_check_node_id)
                                                                                         bkinfo->backend_port,
                                                                                         dbname,
                                                                                         pool_config->health_check_user,
-                                                                                        pool_config->health_check_password, false);
+                                                                                        password?password:"", false);
 
                ereport(DEBUG1,
                        (errmsg("persistent DB connection to backend node %d having status %d is successful", i, bkinfo->backend_status)));
 
                discard_persistent_db_connection(slot);
        }
+       if (password)
+               pfree(password);
        return all_nodes_healthy;
 }
 #endif
@@ -3238,6 +3241,9 @@ static int find_primary_node(void)
        int i;
        POOL_NODE_STATUS *status;
        int primary = -1;
+       char *password = get_pgpool_config_user_password(pool_config->sr_check_user,
+                                                                                                        pool_config->sr_check_password);
+
 
        /* Streaming replication mode? */
        if (!SL_MODE)
@@ -3280,7 +3286,7 @@ static int find_primary_node(void)
                                                                                                                 bkinfo->backend_port,
                                                                                                                 pool_config->sr_check_database,
                                                                                                                 pool_config->sr_check_user,
-                                                                                                                pool_config->sr_check_password, true);
+                                                                                                                password?password:"", true);
                if (!slots[i])
                {
                        ereport(LOG,
@@ -3288,6 +3294,9 @@ static int find_primary_node(void)
                }
        }
 
+       if(password)
+               pfree(password);
+
        /* Verify backend status */
        status = verify_backend_node_status(slots);
 
index 67098a3caa9d523140b848f8e143a6a2de6eeafb..0d30e4a99e1799fe31a7eaec15ec99a8743a4df3 100644 (file)
@@ -331,6 +331,8 @@ static void check_postmaster_started(BackendInfo *backend)
        char port_str[16];
        PGconn *conn;
        char *dbname;
+       char *password = get_pgpool_config_user_password(pool_config->recovery_user,
+                                                                                                        pool_config->recovery_password);
 
        snprintf(port_str, sizeof(port_str),"%d", backend->backend_port);
 
@@ -353,13 +355,16 @@ static void check_postmaster_started(BackendInfo *backend)
                                                        NULL,
                                                        dbname,
                                                        pool_config->recovery_user,
-                                                       pool_config->recovery_password);
+                                                       password?password:NULL);
 
                r = PQstatus(conn);
                PQfinish(conn);
                if (r == CONNECTION_OK)
-                       return;
-
+               {
+                       if (password)
+                               pfree(password);
+                               return;
+               }
                ereport(LOG,
                        (errmsg("checking if postmaster is started"),
                                errdetail("failed to connect to postmaster on hostname:%s database:%s user:%s",
@@ -388,12 +393,16 @@ static void check_postmaster_started(BackendInfo *backend)
                                                        NULL,
                                                        dbname,
                                                        pool_config->recovery_user,
-                                                       pool_config->recovery_password);
+                                                       password?password:NULL);
 
                r = PQstatus(conn);
                PQfinish(conn);
                if (r == CONNECTION_OK)
+               {
+                       if (password)
+                               pfree(password);
                        return;
+               }
 
                ereport(LOG,
                        (errmsg("checking if postmaster is started"),
@@ -404,6 +413,9 @@ static void check_postmaster_started(BackendInfo *backend)
                        sleep(3);
        } while (i++ < WAIT_RETRY_COUNT);
 
+       if (password)
+               pfree(password);
+
        ereport(ERROR,
                (errmsg("recovery is checking if postmaster is started"),
                         errdetail("postmaster on hostname:\"%s\" database:\"%s\" user:\"%s\" failed to start in %d second",
@@ -414,6 +426,8 @@ static PGconn *connect_backend_libpq(BackendInfo *backend)
 {
        char port_str[16];
        PGconn *conn;
+       char *password = get_pgpool_config_user_password(pool_config->recovery_user,
+                                                                                                        pool_config->recovery_password);
 
        snprintf(port_str, sizeof(port_str),
                         "%d", backend->backend_port);
@@ -423,7 +437,10 @@ static PGconn *connect_backend_libpq(BackendInfo *backend)
                                                NULL,
                                                "template1",
                                                pool_config->recovery_user,
-                                               pool_config->recovery_password);
+                                               password?password:"");
+
+       if (password)
+               pfree(password);
 
        if (PQstatus(conn) != CONNECTION_OK)
        {
index 4189fee004f502717327aebcfa6792c47c9c15f3..58e95adad66a9810be2ddda0541147e66f4cdf57 100644 (file)
@@ -214,7 +214,10 @@ static void establish_persistent_connection(void)
 {
        int i;
        BackendInfo *bkinfo;
-       
+
+       char *password = get_pgpool_config_user_password(pool_config->sr_check_user,
+                                                                                                        pool_config->sr_check_password);
+
        for (i=0;i<NUM_BACKENDS;i++)
        {
                if (!VALID_BACKEND(i))
@@ -227,9 +230,12 @@ static void establish_persistent_connection(void)
                                                                                          bkinfo->backend_port,
                                                                                          pool_config->sr_check_database,
                                                                                          pool_config->sr_check_user,
-                                                                                         pool_config->sr_check_password, true);
+                                                                                         password?password:"", true);
                }
        }
+
+       if(password)
+               pfree(password);
 }
 
 /*
index b34f08b9a3a941ec1648f09ab9ecd31d3a8161ac..4312787b6bb77626d240b4c1a7e499ae04a209ae 100644 (file)
@@ -870,6 +870,9 @@ create_conn(char * hostname, int port)
 {
        static char conninfo[1024];
        PGconn *conn;
+       char *password = get_pgpool_config_user_password(pool_config->wd_lifecheck_user,
+                                                                                                        pool_config->wd_lifecheck_password);
+
 
        if (strlen(pool_config->wd_lifecheck_dbname) == 0)
        {
@@ -891,10 +894,13 @@ create_conn(char * hostname, int port)
                port,
                pool_config->wd_lifecheck_dbname,
                pool_config->wd_lifecheck_user,
-               pool_config->wd_lifecheck_password,
+               password?password:"",
                pool_config->wd_interval / 2 + 1);
        conn = PQconnectdb(conninfo);
 
+       if (password)
+               pfree(password);
+
        if (PQstatus(conn) != CONNECTION_OK)
        {
                ereport(DEBUG1,