More for SCRAM authentication support, This commit also adds the support for
authorMuhammad Usama <m.usama@gmail.com>
Wed, 25 Apr 2018 20:24:16 +0000 (01:24 +0500)
committerMuhammad Usama <m.usama@gmail.com>
Mon, 6 Aug 2018 12:33:14 +0000 (17:33 +0500)
CERT authentication between client and Pgpool-II.
The authentication infrastructure of Pgpool-II is also enhanced by this commit
and now it is possible to use different authentication methods between client
to Pgpool-II and Pgpool-II to backend.

src/auth/pool_auth.c
src/auth/pool_hba.c
src/auth/pool_passwd.c
src/include/auth/pool_hba.h
src/include/auth/pool_passwd.h
src/include/pool.h
src/main/main.c
src/main/pgpool_main.c
src/protocol/child.c
src/utils/pool_ssl.c

index b187eff6f88cda3db10d4eea3ad5e057e0fbf365..ae5af2f39b399feae58a4648cd2059414b1ced4c 100644 (file)
@@ -30,6 +30,7 @@
 #include "utils/elog.h"
 #include "utils/palloc.h"
 #include "utils/memutils.h"
+#include "auth/md5.h"
 #ifdef HAVE_SYS_TYPES_H
 #include <sys/types.h>
 #endif
@@ -53,7 +54,9 @@ static POOL_STATUS pool_send_backend_key_data(POOL_CONNECTION *frontend, int pid
 static int do_clear_text_password(POOL_CONNECTION *backend, POOL_CONNECTION *frontend, int reauth, int protoMajor);
 static void pool_send_auth_fail(POOL_CONNECTION *frontend, POOL_CONNECTION_POOL *cp);
 static int do_crypt(POOL_CONNECTION *backend, POOL_CONNECTION *frontend, int reauth, int protoMajor);
-static int do_md5(POOL_CONNECTION *backend, POOL_CONNECTION *frontend, int reauth, int protoMajor);
+static int do_md5(POOL_CONNECTION *backend, POOL_CONNECTION *frontend, int reauth, int protoMajor,
+                                 char *storedPassword, PasswordType passwordType);
+static int do_md5_single_backend(POOL_CONNECTION *backend, POOL_CONNECTION *frontend, int reauth, int protoMajor);
 static void send_md5auth_request(POOL_CONNECTION *frontend, int protoMajor, char *salt);
 static int read_password_packet(POOL_CONNECTION *frontend, int protoMajor,     char *password, int *pwdSize);
 static int send_password_packet(POOL_CONNECTION *backend, int protoMajor, char *password);
@@ -61,10 +64,16 @@ static int send_auth_ok(POOL_CONNECTION *frontend, int protoMajor);
 static void sendAuthRequest(POOL_CONNECTION *frontend, int protoMajor, int32 auth_req_type, char *extradata, int extralen);
 static long PostmasterRandom(void);
 
-static int doSCRAMAuth(POOL_CONNECTION *frontend, int reauth, int protoMajor);
 static int pg_SASL_continue(POOL_CONNECTION *frontend, POOL_CONNECTION *backend, char *payload, int payloadlen, void *sasl_state, bool final);
-static void* pg_SASL_init(POOL_CONNECTION *frontend, POOL_CONNECTION *backend, char *payload, int payloadlen);
-static bool doSCRAMBackendAuth(POOL_CONNECTION *frontend, POOL_CONNECTION *backend, int protoMajor, int message_length);
+static void* pg_SASL_init(POOL_CONNECTION *frontend, POOL_CONNECTION *backend, char *payload, int payloadlen, char* storedPassword);
+static bool do_SCRAM(POOL_CONNECTION *frontend, POOL_CONNECTION *backend, int protoMajor, int message_length,
+                                        char *storedPassword, PasswordType passwordType);
+static void authenticate_frontend_md5(POOL_CONNECTION *backend, POOL_CONNECTION *frontend, int reauth, int protoMajor);
+static void authenticate_frontend_cert(POOL_CONNECTION *frontend);
+static void authenticate_frontend_SCRAM(POOL_CONNECTION *backend, POOL_CONNECTION *frontend, int reauth);
+static void authenticate_frontend_clear_text(POOL_CONNECTION *frontend);
+static bool get_auth_password(POOL_CONNECTION *backend, POOL_CONNECTION *frontend, int reauth,
+                                                         char** password, PasswordType *passwordType);
 
 /*
  * After sending the start up packet to the backend, do the
@@ -155,7 +164,6 @@ int pool_do_auth(POOL_CONNECTION *frontend, POOL_CONNECTION_POOL *cp)
        ereport(DEBUG1,
                (errmsg("authentication backend"),
                         errdetail("auth kind:%d", authkind)));
-
        /* trust? */
        if (authkind == AUTH_REQ_OK)
        {
@@ -171,7 +179,7 @@ int pool_do_auth(POOL_CONNECTION *frontend, POOL_CONNECTION_POOL *cp)
 
                msglen = htonl(0);
                pool_write_and_flush(frontend, &msglen, sizeof(msglen));
-               MASTER(cp)->auth_kind = 0;
+               MASTER(cp)->auth_kind = AUTH_REQ_OK;
        }
 
        /* clear text password authentication? */
@@ -228,20 +236,45 @@ int pool_do_auth(POOL_CONNECTION *frontend, POOL_CONNECTION_POOL *cp)
        /* md5 authentication? */
        else if (authkind == AUTH_REQ_MD5)
        {
-               /* If MD5 auth is not active in pool_hba.conf, it cannot be
-                * used with other than raw mode.
+               char *password;
+               PasswordType passwordType;
+
+               /*
+                * check if we can use md5 authentication.
                 */
-               if ((frontend->pool_hba == NULL || frontend->pool_hba->auth_method != uaMD5) && !RAW_MODE && NUM_BACKENDS > 1)
+               if (NUM_BACKENDS >  1)
                {
-                       pool_send_error_message(frontend, protoMajor, AUTHFAIL_ERRORCODE,
-                                                                       "MD5 authentication is unsupported in replication and master-slave modes.",
-                                                                       "",
-                                                                       "check pg_hba.conf",
-                                                                       __FILE__, __LINE__);
-                       ereport(ERROR,
-                               (errmsg("failed to authenticate with backend"),
-                                       errdetail("MD5 authentication is not supported in replication and master-slave modes."),
-                                               errhint("check pg_hba.conf settings on backend node")));
+                       if (get_auth_password(MASTER(cp), frontend, 0,
+                                                                 &password, &passwordType) == false)
+                       {
+                               /*
+                                * We do not have any passeord,
+                                * we can still get the password from client using plain text authentication
+                                * if it is allowed by user
+                                */
+                               if (frontend->pool_hba == NULL /*&& config allows */)
+                               {
+                                       ereport(LOG,
+                                                       (errmsg("usign clear text authentication with frontend"),
+                                                        errdetail("backend will still use md5 auth")));
+                                       authenticate_frontend_clear_text(frontend);
+                                       /* now check again if we have a password now */
+                                       if (get_auth_password(MASTER(cp), frontend, 0,
+                                                                                 &password, &passwordType) == false)
+                                       {
+                                               ereport(ERROR,
+                                                       (errmsg("failed to authenticate with backend using md5"),
+                                                                errdetail("unable to get the password")));
+                                       }
+                               }
+                       }
+                       /* we have a password to use, validate the password type */
+                       if (passwordType != PASSWORD_TYPE_PLAINTEXT && passwordType != PASSWORD_TYPE_MD5)
+                       {
+                               ereport(ERROR,
+                                       (errmsg("failed to authenticate with backend using md5"),
+                                                errdetail("password type is not valid")));
+                       }
                }
 
                for (i=0;i<NUM_BACKENDS;i++)
@@ -250,10 +283,10 @@ int pool_do_auth(POOL_CONNECTION *frontend, POOL_CONNECTION_POOL *cp)
                                continue;
 
                        ereport(DEBUG1,
-                               (errmsg("authentication backend"),
+                                       (errmsg("authentication backend: %d",i),
                                         errdetail("trying md5 authentication")));
 
-                       authkind = do_md5(CONNECTION(cp, i), frontend, 0, protoMajor);
+                       authkind = do_md5(CONNECTION(cp, i), frontend, 0, protoMajor, password, passwordType);
 
                        if (authkind < 0)
                        {
@@ -268,23 +301,40 @@ int pool_do_auth(POOL_CONNECTION *frontend, POOL_CONNECTION_POOL *cp)
        /* SCRAM authentication? */
        else if (authkind == AUTH_REQ_SASL)
        {
-               /* If SCRAM auth is not active in pool_hba.conf, it cannot be
-                * used with other than raw mode.
-                */
-               if (frontend->pool_hba == NULL || frontend->pool_hba->auth_method != uaSCRAM)
+               char *password;
+               PasswordType passwordType;
+
+               if (get_auth_password(MASTER(cp), frontend, 0,
+                                                         &password, &passwordType) == false)
+               {
+                       /*
+                        * We do not have any passeord,
+                        * we can still get the password from client using plain text authentication
+                        * if it is allowed by user
+                        */
+                       if (frontend->pool_hba == NULL /*&& config allows */)
+                       {
+                               ereport(LOG,
+                                               (errmsg("usign clear text authentication with frontend"),
+                                                errdetail("backend will still use SCRAM auth")));
+                               authenticate_frontend_clear_text(frontend);
+                               /* now check again if we have a password now */
+                               if (get_auth_password(MASTER(cp), frontend, 0,
+                                                                         &password, &passwordType) == false)
+                               {
+                                       ereport(ERROR,
+                                                       (errmsg("failed to authenticate with backend using SCRAM"),
+                                                        errdetail("unable to get the password")));
+                               }
+                       }
+               }
+               /* we have a password to use, validate the password type */
+               if (passwordType != PASSWORD_TYPE_PLAINTEXT)
                {
-                       pool_send_error_message(frontend, protoMajor, AUTHFAIL_ERRORCODE,
-                                                                       "SCRAM authentication is unsupported in replication and master-slave modes.",
-                                                                       "",
-                                                                       "check pg_hba.conf",
-                                                                       __FILE__, __LINE__);
                        ereport(ERROR,
-                                       (errmsg("failed to authenticate with backend"),
-                                        errdetail("SCRAM authentication is not supported in replication and master-slave modes."),
-                                        errhint("check pg_hba.conf settings on backend node")));
+                                       (errmsg("failed to authenticate with backend using SCRAM"),
+                                        errdetail("password type is not valid")));
                }
-               /* Do frontend SCRAM auth first */
-               authkind = doSCRAMAuth(frontend, 0, protoMajor);
 
                for (i=0;i<NUM_BACKENDS;i++)
                {
@@ -292,20 +342,23 @@ int pool_do_auth(POOL_CONNECTION *frontend, POOL_CONNECTION_POOL *cp)
                                continue;
                        
                        ereport(DEBUG1,
-                               (errmsg("authentication backend"),
+                               (errmsg("authentication backend %d",i),
                                         errdetail("trying SCRAM authentication")));
 
-                       if (doSCRAMBackendAuth(frontend, CONNECTION(cp, i), protoMajor, message_length) == false)
+                       if (do_SCRAM(frontend, CONNECTION(cp, i), protoMajor, message_length, password, passwordType) == false)
                        {
                                pool_send_auth_fail(frontend, cp);
                                ereport(ERROR,
                                                (errmsg("failed to authenticate with backend"),
                                                 errdetail("SCRAM authentication failed in slot [%d].",i)));
                        }
+                       ereport(DEBUG1,
+                               (errmsg("SCRAM authentication successful for backend %d",i)));
+
                }
 
                send_auth_ok(frontend, protoMajor);
-
+               authkind = 0;
        }
 
        else
@@ -513,11 +566,11 @@ int pool_do_reauth(POOL_CONNECTION *frontend, POOL_CONNECTION_POOL *cp)
 
                case AUTH_REQ_MD5:
                        /* md5 password */
-                       do_md5(MASTER(cp), frontend, 1, protoMajor);
+                       authenticate_frontend_md5(MASTER(cp), frontend, 1, protoMajor);
                        break;
                case AUTH_REQ_SASL:
                        /* SCRAM */
-                       doSCRAMAuth( frontend, 1, protoMajor);
+                       authenticate_frontend_SCRAM(MASTER(cp), frontend, 1);
                        break;
                default:
             ereport(ERROR,
@@ -590,131 +643,136 @@ static POOL_STATUS pool_send_backend_key_data(POOL_CONNECTION *frontend, int pid
        return 0;
 }
 
+static void authenticate_frontend_clear_text(POOL_CONNECTION *frontend)
+{
+       static int size;
+       char password[MAX_PASSWORD_SIZE];
+       char userPassword[MAX_PASSWORD_SIZE];
+       char *pwd;
+
+
+       sendAuthRequest(frontend, frontend->protoVersion, AUTH_REQ_PASSWORD, NULL, 0);
+
+       /* Read password packet */
+       read_password_packet(frontend, frontend->protoVersion, password, &size);
+
+       /* save the password in frontend */
+       frontend->auth_kind = AUTH_REQ_PASSWORD;
+       frontend->pwd_size = size;
+       memcpy(frontend->password, password, frontend->pwd_size);
+       frontend->password[size] = 0; /* Null terminate the password string */
+       frontend->passwordType = PASSWORD_TYPE_PLAINTEXT;
+
+       if (!frontend->passwordMapping)
+       {
+               /*
+                * if the password is not saved in pool_passwd
+                * just bail out from here
+                */
+               return;
+       }
+       /*
+        * If we have md5 password stored in pool_passwd, convert the user
+        * supplied password to md5 for comparison
+        */
+       if (frontend->passwordMapping->pgpoolUser.passwordType == PASSWORD_TYPE_MD5)
+       {
+               pg_md5_encrypt(password,
+                                          frontend->username,
+                                          strlen(frontend->username), userPassword);
+               
+               pwd = userPassword;
+       }
+       else if (frontend->passwordMapping->pgpoolUser.passwordType == PASSWORD_TYPE_PLAINTEXT)
+       {
+               pwd = password;
+       }
+       else
+       {
+               ereport(LOG,
+                               (errmsg("password type stored in pool_passwd can't be used for clear text authentication")));
+               return;
+       }
+
+       if (memcmp(password, frontend->passwordMapping->pgpoolUser.password, size))
+       {
+               /* Password does not match */
+               ereport(ERROR,
+                       (errmsg("clear text authentication failed"),
+                                errdetail("password does not match")));
+       }
+       ereport(LOG,
+                       (errmsg("clear text authentication successfull with frontend")));
+
+       frontend->frontend_authenticated = true;
+}
+
 /*
  * perform clear text password authentication
  */
 static int do_clear_text_password(POOL_CONNECTION *backend, POOL_CONNECTION *frontend, int reauth, int protoMajor)
 {
        static int size;
-       static char password[MAX_PASSWORD_SIZE];
-       char response;
+       char *pwd = NULL;
        int kind;
-       int len;
 
-       /* master? */
-       if (IS_MASTER_NODE_ID(backend->db_node_id))
+       if (reauth && frontend->frontend_authenticated)
        {
-               pool_write(frontend, "R", 1);   /* authentication */
-               if (protoMajor == PROTO_MAJOR_V3)
-               {
-                       len = htonl(8);
-                       pool_write(frontend, &len, sizeof(len));
-               }
-               kind = htonl(3);                /* clear text password authentication */
-               pool_write_and_flush(frontend, &kind, sizeof(kind));    /* indicating clear text password authentication */
-
-               /* read password packet */
-               if (protoMajor == PROTO_MAJOR_V2)
-               {
-                       pool_read(frontend, &size, sizeof(size));
-               }
-               else
-               {
-                       char k;
-
-                       pool_read(frontend, &k, sizeof(k));
-                       if (k != 'p')
-                ereport(ERROR,
-                        (errmsg("clear text password authentication failed"),
-                         errdetail("invalid password packet. Packet does not starts with \"p\"")));
-                       pool_read(frontend, &size, sizeof(size));
-               }
+               /* frontend and backend are both authenticated already */
+               return 0;
+       }
 
-               if ((ntohl(size) - 4) > sizeof(password))
-            ereport(ERROR,
-                    (errmsg("clear text password authentication failed"),
-                     errdetail("password is too long. password size = %d", ntohl(size) - 4)));
+       /* get the password */
+       if (frontend->pwd_size > 0)
+       {
+               pwd = frontend->password;
+               size = frontend->pwd_size;
+       }
+       else if (frontend->passwordMapping && frontend->passwordMapping->pgpoolUser.passwordType == PASSWORD_TYPE_PLAINTEXT)
+       {
+               pwd = frontend->passwordMapping->pgpoolUser.password;
+               size = pwd? strlen(pwd): 0;
+       }
 
-               pool_read(frontend, password, ntohl(size) - 4);
+       if (pwd == NULL)
+       {
+               /* If we still do not have a password. we can't proceed */
+               ereport(ERROR,
+                       (errmsg("clear text password authentication failed"),
+                                errdetail("unable to get the password")));
+               return 0;
        }
 
        /* connection reusing? */
        if (reauth)
        {
-               if ((ntohl(size) - 4) != backend->pwd_size)
+               if (size != backend->pwd_size)
             ereport(ERROR,
-                    (errmsg("clear text password authentication failed"),
+                               (errmsg("clear text password authentication failed"),
                      errdetail("password size does not match")));
 
-               if (memcmp(password, backend->password, backend->pwd_size) != 0)
+               if (memcmp(pwd, backend->password, backend->pwd_size) != 0)
             ereport(ERROR,
-                    (errmsg("clear text password authentication failed"),
+                               (errmsg("clear text password authentication failed"),
                      errdetail("password does not match")));
 
                return 0;
        }
 
-       /* send password packet to backend */
-       if (protoMajor == PROTO_MAJOR_V3)
-               pool_write(backend, "p", 1);
-       pool_write(backend, &size, sizeof(size));
-       pool_write_and_flush(backend, password, ntohl(size) -4);
-       
-    pool_read(backend, &response, sizeof(response));
-
-       if (response != 'R')
-       {
-               if(response == 'E') /* Backend has thrown an error instead */
-               {
-                       char* message = NULL;
-                       if (pool_extract_error_message(false, backend, protoMajor, false, &message) == 1)
-                       {
-                               ereport(ERROR,
-                                       (errmsg("clear text password authentication failed"),
-                                        errdetail("%s",message?message:"backend throws authentication error")));
-                       }
-                       if (message)
-                               pfree(message);
-               }
-        ereport(ERROR,
-            (errmsg("clear text password authentication failed"),
-                 errdetail("invalid packet from backend. backend does not return R while processing clear text password authentication")));
-       }
-       if (protoMajor == PROTO_MAJOR_V3)
-       {
-               pool_read(backend, &len, sizeof(len));
-
-               if (ntohl(len) != 8)
-            ereport(ERROR,
-                    (errmsg("clear text password authentication failed"),
-                     errdetail("invalid packet from backend. incorrect authentication packet size (%d)", ntohl(len))));
-       }
-
-       /* expect to read "Authentication OK" response. kind should be 0... */
-       pool_read(backend, &kind, sizeof(kind));
+       kind = send_password_packet(backend, protoMajor, pwd);
 
        /* if authenticated, save info */
-       if (!reauth && kind == 0)
+       if (!reauth && kind == AUTH_REQ_OK)
        {
                if (IS_MASTER_NODE_ID(backend->db_node_id))
                {
-                       int msglen;
-
-                       pool_write(frontend, "R", 1);
-
-                       if (protoMajor == PROTO_MAJOR_V3)
-                       {
-                               msglen = htonl(8);
-                               pool_write(frontend, &msglen, sizeof(msglen));
-                       }
-
-                       msglen = htonl(0);
-                       pool_write_and_flush(frontend, &msglen, sizeof(msglen));
+                       send_auth_ok(frontend, protoMajor);
                }
 
-               backend->auth_kind = 3;
-               backend->pwd_size = ntohl(size) - 4;
-               memcpy(backend->password, password, backend->pwd_size);
+               backend->auth_kind = AUTH_REQ_PASSWORD;
+               backend->pwd_size = size;
+               memcpy(backend->password, pwd, backend->pwd_size);
+               backend->passwordType = PASSWORD_TYPE_PLAINTEXT;
        }
        return kind;
 }
@@ -867,9 +925,12 @@ static int do_crypt(POOL_CONNECTION *backend, POOL_CONNECTION *frontend, int rea
        return kind;
 }
 
-
-static int
-doSCRAMAuth(POOL_CONNECTION *frontend, int reauth, int protoMajor)
+/*
+ * Do the SCRAM authentication with the frontend using the stored
+ * password in the pool_passwd file.
+ */
+static void
+authenticate_frontend_SCRAM(POOL_CONNECTION *backend, POOL_CONNECTION *frontend, int reauth)
 {
        void       *scram_opaq;
        char       *output = NULL;
@@ -880,14 +941,44 @@ doSCRAMAuth(POOL_CONNECTION *frontend, int reauth, int protoMajor)
        bool            initial;
        char            *logdetail = NULL;
        char            *shadow_pass;
-       char            *pool_passwd = pool_get_passwd(frontend->username);
 
-       if (!pool_passwd)
+       
+       PasswordType storedPasswordType = PASSWORD_TYPE_UNKNOWN;
+       char *storedPassword = NULL;
+
+       if (!frontend->passwordMapping)
+               frontend->passwordMapping = pool_get_user_credentials(frontend->username);
+
+       if (!frontend->passwordMapping)
+       {
+               /* see if we have a password stored in the backend for this */
+               if (reauth && backend->pwd_size)
+               {
+                       storedPasswordType = backend->passwordType;
+                       storedPassword = backend->password;
+               }
+               else
+               {
+                       ereport(FATAL,
+                               (return_code(2),
+                                        errmsg("SCRAM authentication failed"),
+                                        errdetail("pool_passwd file does not contain an entry for \"%s\"",frontend->username)));
+               }
+       }
+       else
+       {
+               storedPasswordType = frontend->passwordMapping->pgpoolUser.passwordType;
+               storedPassword = frontend->passwordMapping->pgpoolUser.password;
+       }
+
+       if (storedPasswordType != PASSWORD_TYPE_PLAINTEXT)
+       {
                ereport(ERROR,
-                       (errmsg("authentication failed"),
-                                errdetail("username \"%s\" does not exist in pool_passwd",frontend->username)));
+                       (errmsg("SCRAM authentication failed"),
+                                errdetail("username \"%s\" has invalid password type",frontend->username)));
+       }
 
-       shadow_pass = pg_be_scram_build_verifier(pool_passwd);
+       shadow_pass = pg_be_scram_build_verifier(storedPassword);
        if (!shadow_pass)
                ereport(ERROR,
                        (errmsg("authentication failed"),
@@ -902,7 +993,7 @@ doSCRAMAuth(POOL_CONNECTION *frontend, int reauth, int protoMajor)
         * without relying on the length word, but we hardly care about older
         * protocol version anymore.)
         */
-       if (protoMajor < 3)
+       if (frontend->protoVersion < PROTO_MAJOR_V3)
                ereport(FATAL,
                                (errmsg("SASL authentication is not supported in protocol version 2")));
 
@@ -913,7 +1004,7 @@ doSCRAMAuth(POOL_CONNECTION *frontend, int reauth, int protoMajor)
         * terminate the list.
         */
 
-       sendAuthRequest(frontend, protoMajor, AUTH_REQ_SASL, SCRAM_SHA_256_NAME "\0",
+       sendAuthRequest(frontend, frontend->protoVersion, AUTH_REQ_SASL, SCRAM_SHA_256_NAME "\0",
                                        strlen(SCRAM_SHA_256_NAME) + 2);
        
        /*
@@ -942,7 +1033,7 @@ doSCRAMAuth(POOL_CONNECTION *frontend, int reauth, int protoMajor)
                static char data[MAX_PASSWORD_SIZE];
 
                /* Read password packet */
-               read_password_packet(frontend, protoMajor, data, &size);
+               read_password_packet(frontend, frontend->protoVersion, data, &size);
 
                ereport(DEBUG4,
                                (errmsg("Processing received SASL response of length %d", size)));
@@ -1005,9 +1096,9 @@ doSCRAMAuth(POOL_CONNECTION *frontend, int reauth, int protoMajor)
                                (errmsg("sending SASL challenge of length %u", outputlen)));
 
                        if (result == SASL_EXCHANGE_SUCCESS)
-                               sendAuthRequest(frontend, protoMajor, AUTH_REQ_SASL_FIN, output, outputlen);
+                               sendAuthRequest(frontend, frontend->protoVersion, AUTH_REQ_SASL_FIN, output, outputlen);
                        else
-                               sendAuthRequest(frontend, protoMajor, AUTH_REQ_SASL_CONT, output, outputlen);
+                               sendAuthRequest(frontend, frontend->protoVersion, AUTH_REQ_SASL_CONT, output, outputlen);
 
                        pfree(output);
                }
@@ -1020,154 +1111,347 @@ doSCRAMAuth(POOL_CONNECTION *frontend, int reauth, int protoMajor)
                        (errmsg("authentication failed"),
                                 errdetail("username \"%s\" or password does not exist in backend",frontend->username)));
        }
-       
-       return 0;
+       frontend->frontend_authenticated = true;
 }
 
+void authenticate_frontend(POOL_CONNECTION *frontend)
+{
+       switch (frontend->pool_hba->auth_method)
+       {
+               case uaMD5:
+                       authenticate_frontend_md5(NULL,frontend,0,frontend->protoVersion);
+                       break;
+               case uaCert:
+                       authenticate_frontend_cert(frontend);
+                       break;
+               case uaSCRAM:
+                       authenticate_frontend_SCRAM(NULL, frontend, 0);
+                       break;
+               case uaPassword:
+                       authenticate_frontend_clear_text(frontend);
+       }
+}
 
-/*
- * perform MD5 authentication
- */
-static int do_md5(POOL_CONNECTION *backend, POOL_CONNECTION *frontend, int reauth, int protoMajor)
+#ifdef USE_SSL
+static void authenticate_frontend_cert(POOL_CONNECTION *frontend)
+{
+       if (frontend->client_cert_loaded == true && frontend->cert_cn)
+       {
+               ereport(DEBUG1,
+                               (errmsg("connecting user is \"%s\" and ssl certificate CN is \"%s\"",frontend->username,frontend->cert_cn)));
+               if (strcasecmp(frontend->username,frontend->cert_cn) == 0)
+               {
+                       frontend->frontend_authenticated = true;
+                       ereport(LOG,
+                                       (errmsg("SSL certificate authentication for user \"%s\" with Pgpool-II is successful",frontend->username)));
+                       return POOL_CONTINUE;
+               }
+               else
+               {
+                       frontend->frontend_authenticated = false;
+                       ereport(LOG,
+                                       (errmsg("SSL certificate authentication for user \"%s\" failed",frontend->username)));
+               }
+       }
+       ereport(ERROR,
+               (errmsg("CERT authentication failed"),
+                        errdetail("no valid certificate presented")));
+
+}
+#else
+static void authenticate_frontend_cert(POOL_CONNECTION *frontend)
+{
+       ereport(ERROR,
+               (errmsg("CERT authentication failed"),
+                        errdetail("CERT authentication is not supported without SSL")));
+}
+#endif
+
+static void authenticate_frontend_md5(POOL_CONNECTION *backend, POOL_CONNECTION *frontend, int reauth, int protoMajor)
 {
        char salt[4];
        static int size;
-       static char password[MAX_PASSWORD_SIZE];
-       int kind;
+       char password[MAX_PASSWORD_SIZE];
+       char userPassword[MAX_PASSWORD_SIZE];
        char encbuf[POOL_PASSWD_LEN+1];
-       char *pool_passwd = NULL;
+       char *md5;
+       
+       PasswordType storedPasswordType = PASSWORD_TYPE_UNKNOWN;
+       char *storedPassword = NULL;
 
-       if (!RAW_MODE && NUM_BACKENDS > 1)
+       if (RAW_MODE || NUM_BACKENDS == 1)
        {
-               /* Read password entry from pool_passwd */
-               pool_passwd = pool_get_passwd(frontend->username);
-               if (!pool_passwd)
-            ereport(ERROR,
-                               (errmsg("md5 authentication failed"),
-                     errdetail("username \"%s\" does not exist in pool_passwd",frontend->username)));
-
+               if (backend)
+                       do_md5_single_backend(backend, frontend, reauth, protoMajor);
+               return; /* This will be handled later */
+       }
+       if (!frontend->passwordMapping)
+               frontend->passwordMapping = pool_get_user_credentials(frontend->username);
 
-               /* master? */
-               if (IS_MASTER_NODE_ID(backend->db_node_id))
+       if (!frontend->passwordMapping)
+       {
+               /* see if we have a password stored in the backend for this */
+               if (reauth && backend->pwd_size)
                {
-                       /* Send md5 auth request to frontend with my own salt */
-                       pool_random_salt(salt);
-                       send_md5auth_request(frontend, protoMajor, salt);
-
-                       /* Read password packet */
-                       read_password_packet(frontend, protoMajor, password, &size);
-
-                       /* Check the password using my salt + pool_passwd */
-                       pg_md5_encrypt(pool_passwd+strlen("md5"), salt, sizeof(salt), encbuf);
-                       if (strcmp(password, encbuf))
-                       {
-                               /* Password does not match */
-                ereport(ERROR,
-                    (errmsg("md5 authentication failed"),
-                         errdetail("password does not match")));
-                       }
+                       storedPasswordType = backend->passwordType;
+                       storedPassword = backend->password;
                }
-               kind = 0;
-
-               if (!reauth)
+               else
                {
-                       /*
-                        * If ok, authenticate against backends using pool_passwd
-                        */
-                       /* Read salt */
-                       pool_read(backend, salt, sizeof(salt));
+                       ereport(FATAL,
+                               (return_code(2),
+                                       errmsg("md5 authentication failed"),
+                                        errdetail("pool_passwd file does not contain an entry for \"%s\"",frontend->username)));
+               }
+       }
+       else
+       {
+               storedPasswordType = frontend->passwordMapping->pgpoolUser.passwordType;
+               storedPassword = frontend->passwordMapping->pgpoolUser.password;
+       }
 
-                       ereport(DEBUG1,
-                               (errmsg("performing md5 authentication"),
-                                       errdetail("DB node id: %d salt: %hhx%hhx%hhx%hhx", backend->db_node_id,
-                                                  salt[0], salt[1], salt[2], salt[3])));
+       pool_random_salt(salt);
+       send_md5auth_request(frontend, frontend->protoVersion, salt);
 
-                       /* Encrypt password in pool_passwd using the salt */
-                       pg_md5_encrypt(pool_passwd+strlen("md5"), salt, sizeof(salt), encbuf);
+       /* Read password packet */
+       read_password_packet(frontend, frontend->protoVersion, password, &size);
 
-                       /* Send password packet to backend and receive auth response */
-                       kind = send_password_packet(backend, protoMajor, encbuf);
-                       if (kind < 0)
-                ereport(ERROR,
-                    (errmsg("md5 authentication failed"),
-                         errdetail("backend replied with invalid kind")));
-               }
+       /*If we have clear text password stored in pool_passwd, convert it to md5 */
+       if (storedPasswordType == PASSWORD_TYPE_PLAINTEXT)
+       {
+               pg_md5_encrypt(storedPassword,
+                                          frontend->username,
+                                          strlen(frontend->username), userPassword);
 
-               if (!reauth && kind == 0)
-               {
-                       if (IS_MASTER_NODE_ID(backend->db_node_id))
-                       {
-                               /* Send auth ok to frontend */
-                               send_auth_ok(frontend, protoMajor);
-                       }
+               md5 = userPassword;
+       }
+       else if (storedPasswordType == PASSWORD_TYPE_MD5)
+       {
+               md5 = storedPassword;
+       }
+       else
+       {
+               ereport(FATAL,
+                       (return_code(2),
+                               errmsg("md5 authentication failed"),
+                                errdetail("unable to get the password for \"%s\"",frontend->username)));
 
-                       /* Save the auth info */
-                       backend->auth_kind = AUTH_REQ_MD5;
-               }
-               return kind;
        }
+       /* Check the password using my salt + pool_passwd */
+       pg_md5_encrypt(md5+strlen("md5"), salt, sizeof(salt), encbuf);
+       if (strcmp(password, encbuf))
+       {
+               /* Password does not match */
+               ereport(ERROR,
+                       (errmsg("md5 authentication failed"),
+                                errdetail("password does not match")));
+       }
+       ereport(LOG,
+                       (errmsg("md5 authentication successfull with frontend")));
 
-       /*
-        * Followings are NUM_BACKEND == 1 case.
-        */
+       frontend->frontend_authenticated = true;
+}
+
+/*
+ * perform MD5 authentication
+ */
+static int do_md5_single_backend(POOL_CONNECTION *backend, POOL_CONNECTION *frontend, int reauth, int protoMajor)
+{
+       char salt[4];
+       static int size;
+       static char password[MAX_PASSWORD_SIZE];
+       int kind;
+       
        if (!reauth)
        {
-               /* read salt */
+               /* read salt from backend */
                pool_read(backend, salt, sizeof(salt));
                ereport(DEBUG1,
-                       (errmsg("performing md5 authentication"),
-                               errdetail("DB node id: %d salt: %hhx%hhx%hhx%hhx", backend->db_node_id,
-                                          salt[0], salt[1], salt[2], salt[3])));
+                               (errmsg("performing md5 authentication"),
+                                errdetail("DB node id: %d salt: %hhx%hhx%hhx%hhx", backend->db_node_id,
+                                                  salt[0], salt[1], salt[2], salt[3])));
        }
        else
        {
+               /* Use the saved salt */
                memcpy(salt, backend->salt, sizeof(salt));
        }
+       
+       /* Send md5 auth request to frontend */
+       send_md5auth_request(frontend, protoMajor, salt);
 
-       /* master? */
-       if (IS_MASTER_NODE_ID(backend->db_node_id))
-       {
-               /* Send md5 auth request to frontend */
-               send_md5auth_request(frontend, protoMajor, salt);
-
-               /* Read password packet */
-               read_password_packet(frontend, protoMajor, password, &size);
-       }
+       /* Read password packet */
+       read_password_packet(frontend, protoMajor, password, &size);
 
-       /* connection reusing? */
+       /* connection reusing? compare it with saved password */
        if (reauth)
        {
+               if (backend->passwordType != PASSWORD_TYPE_MD5)
+                       ereport(ERROR,
+                                       (errmsg("md5 authentication failed"),
+                                        errdetail("invalid password type")));
+
                if (size != backend->pwd_size)
-            ereport(ERROR,
-                               (errmsg("md5 authentication failed"),
-                     errdetail("password does not match")));
+                       ereport(ERROR,
+                                       (errmsg("md5 authentication failed"),
+                                        errdetail("password does not match")));
 
                if (memcmp(password, backend->password, backend->pwd_size) != 0)
-            ereport(ERROR,
-                               (errmsg("md5 authentication failed"),
-                     errdetail("password does not match")));
-
+                       ereport(ERROR,
+                                       (errmsg("md5 authentication failed"),
+                                        errdetail("password does not match")));
                return 0;
        }
+       else
+       {
+               /* Send password packet to backend and receive auth response */
+               kind = send_password_packet(backend, protoMajor, password);
+               if (kind < 0)
+                       ereport(ERROR,
+                                       (errmsg("md5 authentication failed"),
+                                        errdetail("backend replied with invalid kind")));
 
-       /* Send password packet to backend and receive auth response */
-       kind = send_password_packet(backend, protoMajor, password);
-       if (kind < 0)
-        ereport(ERROR,
+               /* If authenticated, reply back to frontend and save info */
+               if (kind == 0)
+               {
+                       send_auth_ok(frontend, protoMajor);
+                       backend->passwordType = PASSWORD_TYPE_MD5;
+                       backend->auth_kind = AUTH_REQ_MD5;
+                       backend->pwd_size = size;
+                       memcpy(backend->password, password, backend->pwd_size);
+                       memcpy(backend->salt, salt, sizeof(salt));
+               }
+       }
+       return kind;
+}
+
+static bool get_auth_password(POOL_CONNECTION *backend, POOL_CONNECTION *frontend, int reauth,
+                                                         char** password, PasswordType *passwordType)
+{
+       /* First preference is to use the pool_passwd file */
+       if (frontend->passwordMapping == NULL)
+               frontend->passwordMapping = pool_get_user_credentials(frontend->username);
+
+       if (frontend->passwordMapping == NULL)
+       {
+               /*
+                * check if we have password stored in the
+                * frontend connection.
+                * that could come by using the clear text auth
+                */
+               if (frontend->pwd_size > 0 && frontend->passwordType == PASSWORD_TYPE_PLAINTEXT)
+               {
+                       *password = frontend->password;
+                       *passwordType = frontend->passwordType;
+                       return true;
+               }
+               else if (reauth && backend && backend->pwd_size > 0)
+               {
+                       *password = backend->password;
+                       *passwordType = backend->passwordType;
+                       return true;
+               }
+       }
+       else
+       {
+               *password = frontend->passwordMapping->pgpoolUser.password;
+               *passwordType = frontend->passwordMapping->pgpoolUser.passwordType;
+               return true;
+       }
+       return false;
+}
+
+/*
+ * perform MD5 authentication
+ */
+static int do_md5(POOL_CONNECTION *backend, POOL_CONNECTION *frontend, int reauth, int protoMajor,
+                                 char *storedPassword, PasswordType passwordType)
+{
+       char salt[4];
+       static char userPassword[MAX_PASSWORD_SIZE];
+       int kind;
+       char encbuf[POOL_PASSWD_LEN+1];
+       char *pool_passwd = NULL;
+       
+       if (NUM_BACKENDS == 1)
+               return do_md5_single_backend(backend, frontend, reauth, protoMajor);
+
+       if (passwordType == PASSWORD_TYPE_PLAINTEXT)
+       {
+               pg_md5_encrypt(storedPassword,
+                                          frontend->username,
+                                          strlen(frontend->username), userPassword);
+               pool_passwd = userPassword;
+       }
+       else if (frontend->passwordMapping->pgpoolUser.passwordType == PASSWORD_TYPE_MD5)
+       {
+               pool_passwd = storedPassword;
+       }
+       else
+       {
+               ereport(ERROR,
                        (errmsg("md5 authentication failed"),
-                 errdetail("backend replied with invalid kind")));
+                                errdetail("unable to get the password")));
+       }
+
+       /* master? */
+       if (IS_MASTER_NODE_ID(backend->db_node_id) && frontend->frontend_authenticated == false )
+       {
+               /* frontend is not yet authenticated, Do it now */
+               if (frontend->passwordType != PASSWORD_TYPE_UNKNOWN &&
+                       frontend->auth_kind != AUTH_REQ_PASSWORD)
+               {
+                       authenticate_frontend_md5(backend, frontend, reauth, protoMajor);
+               }
+               else
+               {
+                       ereport(DEBUG2,
+                               (errmsg("md5 authentication using the password from frontend")));
+                       /* save this password in backend for the re-auth */
+                       backend->pwd_size = frontend->pwd_size;
+                       memcpy(backend->password, frontend->password, frontend->pwd_size);
+                       backend->password[backend->pwd_size] = 0; /* null terminate */
+                       backend->passwordType = frontend->passwordType;
+               }
+       }
+
+       if (!reauth)
+       {
+               /*
+                * now authenticate the backend
+                */
+
+               /* Read salt */
+               pool_read(backend, salt, sizeof(salt));
+
+               ereport(DEBUG2,
+                       (errmsg("performing md5 authentication"),
+                               errdetail("DB node id: %d salt: %hhx%hhx%hhx%hhx", backend->db_node_id,
+                                          salt[0], salt[1], salt[2], salt[3])));
+
+               /* Encrypt password in pool_passwd using the salt */
+               pg_md5_encrypt(pool_passwd+strlen("md5"), salt, sizeof(salt), encbuf);
+
+               /* Send password packet to backend and receive auth response */
+               kind = send_password_packet(backend, protoMajor, encbuf);
+               if (kind < 0)
+                       ereport(ERROR,
+                               (errmsg("md5 authentication failed"),
+                                        errdetail("backend replied with invalid kind")));
+       }
 
-       /* If authenticated, reply back to frontend and save info */
        if (!reauth && kind == 0)
        {
-               send_auth_ok(frontend, protoMajor);
+               if (IS_MASTER_NODE_ID(backend->db_node_id))
+               {
+                       /* Send auth ok to frontend */
+                       send_auth_ok(frontend, protoMajor);
+               }
 
-               backend->auth_kind = 5;
-               backend->pwd_size = size;
-               memcpy(backend->password, password, backend->pwd_size);
-               memcpy(backend->salt, salt, sizeof(salt));
+               /* Save the auth info */
+               backend->auth_kind = AUTH_REQ_MD5;
        }
-       return kind;
+       return 0;
 }
 
 /*
@@ -1188,6 +1472,8 @@ sendAuthRequest(POOL_CONNECTION *frontend, int protoMajor, int32 auth_req_type,
        pool_write(frontend, &kind, sizeof(kind));
        if (extralen > 0)
                pool_write_and_flush(frontend, extradata, extralen);
+       else
+               pool_flush(frontend);
 }
 
 /*
@@ -1577,17 +1863,49 @@ PostmasterRandom(void)
 }
 
 
-/* SCRAM  CLIENT */
-/*
- * Initialize SASL authentication exchange.
- */
-static bool doSCRAMBackendAuth(POOL_CONNECTION *frontend, POOL_CONNECTION *backend, int protoMajor, int message_length)
+static bool do_SCRAM(POOL_CONNECTION *frontend, POOL_CONNECTION *backend, int protoMajor, int message_length,
+          char *storedPassword, PasswordType passwordType)
 {
        /* read the packet first */
        void *sasl_state = NULL;
        int payload_len = message_length - 4 - 4;
        int  auth_kind = AUTH_REQ_SASL;
        char payload[1024];
+       
+       if (passwordType != PASSWORD_TYPE_PLAINTEXT)
+       {
+               ereport(ERROR,
+                       (errmsg("SCRAM authentication failed"),
+                                errdetail("invalid password type")));
+       }
+       if (storedPassword == NULL)
+       {
+               ereport(ERROR,
+                       (errmsg("SCRAM authentication failed"),
+                                errdetail("password not found")));
+       }
+
+       /* master? */
+       if (IS_MASTER_NODE_ID(backend->db_node_id) && frontend->frontend_authenticated == false )
+       {
+               /* frontend is not yet authenticated, Do it now */
+               if (frontend->passwordType != PASSWORD_TYPE_UNKNOWN &&
+                       frontend->auth_kind != AUTH_REQ_PASSWORD)
+               {
+                       authenticate_frontend_SCRAM(backend, frontend, 0);
+               }
+               else
+               {
+                       ereport(DEBUG2,
+                                       (errmsg("SCRAM authentication using the password from frontend")));
+                       /* save this password in backend for the re-auth */
+                       backend->pwd_size = frontend->pwd_size;
+                       memcpy(backend->password, frontend->password, frontend->pwd_size);
+                       backend->password[backend->pwd_size] = 0; /* null terminate */
+                       backend->passwordType = frontend->passwordType;
+               }
+       }
+
        for(;;)
        {
                /* at this point we have already read kind, message length and authkind */
@@ -1597,10 +1915,10 @@ static bool doSCRAMBackendAuth(POOL_CONNECTION *frontend, POOL_CONNECTION *backe
 
                pool_read(backend, payload, payload_len);
 
-               //char kind = read_packet_from_connection(backend, protoMajor, data, &len);
-
                switch (auth_kind) {
                        case AUTH_REQ_OK:
+                               /* Save the auth info in backend */
+                               backend->auth_kind = AUTH_REQ_SASL;
                                return true;
                                break;
                        case AUTH_REQ_SASL:
@@ -1609,7 +1927,7 @@ static bool doSCRAMBackendAuth(POOL_CONNECTION *frontend, POOL_CONNECTION *backe
                                 * The request contains the name (as assigned by IANA) of the
                                 * authentication mechanism.
                                 */
-                               sasl_state = pg_SASL_init(frontend, backend, payload, payload_len);
+                               sasl_state = pg_SASL_init(frontend, backend, payload, payload_len, storedPassword);
                                if (!sasl_state)
                                {
                                        ereport(ERROR,
@@ -1661,7 +1979,7 @@ static bool doSCRAMBackendAuth(POOL_CONNECTION *frontend, POOL_CONNECTION *backe
 }
 
 static void*
-pg_SASL_init(POOL_CONNECTION *frontend, POOL_CONNECTION *backend, char *payload, int payloadlen)
+pg_SASL_init(POOL_CONNECTION *frontend, POOL_CONNECTION *backend, char *payload, int payloadlen, char* storedPassword)
 {
        char       *initialresponse = NULL;
        int                     initialresponselen;
@@ -1701,16 +2019,13 @@ pg_SASL_init(POOL_CONNECTION *frontend, POOL_CONNECTION *backend, char *payload,
                         * for authentication.
                         * It is stored in the file
                         */
-                       /* Read the password from the pool_passwd file */
-                       char       *password = pool_get_passwd(frontend->username);
-
-                       if (password == NULL || password[0] == '\0')
+                       if (storedPassword == NULL || storedPassword[0] == '\0')
                        {
                                ereport(ERROR,
                                                (errmsg("password not found")));
                        }
 
-                       sasl_state = pg_fe_scram_init(frontend->username, password);
+                       sasl_state = pg_fe_scram_init(frontend->username, storedPassword);
                        if (!sasl_state)
                                ereport(ERROR,
                                                (errmsg("SASL authentication error\n")));
index 6d5f082b6d121a74125d8653393e5bfe8107b0ec..f5728fc03295b4cca15852e2b9eda46a489b1ff2 100644 (file)
@@ -615,6 +615,10 @@ parse_hba_line(TokenizedLine *tok_line, int elevel)
                parsedline->auth_method = uaTrust;
        else if (strcmp(token->string, "reject") == 0)
                parsedline->auth_method = uaReject;
+       else if (strcmp(token->string, "cert") == 0)
+               parsedline->auth_method = uaCert;
+       else if (strcmp(token->string, "password") == 0)
+               parsedline->auth_method = uaPassword;
        else if (strcmp(token->string, "md5") == 0)
                parsedline->auth_method = uaMD5;
        else if (strcmp(token->string, "scram-sha-256") == 0)
@@ -769,7 +773,11 @@ void ClientAuthentication(POOL_CONNECTION *frontend)
                         errdetail("missing or erroneous pool_hba.conf file"),
                             errhint("see pgpool log for details")));
 
-
+               /*
+                * Get the password for the user if it is stored
+                * in the pool_password file
+                */
+               frontend->passwordMapping = pool_get_user_credentials(frontend->username);
         switch (frontend->pool_hba->auth_method)
         {
                        case uaImplicitReject:
@@ -818,27 +826,51 @@ void ClientAuthentication(POOL_CONNECTION *frontend)
             case uaKrb5:
             case uaIdent:
             case uaCrypt:
+                        */
             case uaPassword:
-                break; 
-            */
+                               ereport(DEBUG1,
+                                               (errmsg("password authentication required")));
+                               status = POOL_CONTINUE;
+                break;
+                       case uaCert:
+                               ereport(DEBUG1,
+                                               (errmsg("SSL certificate authentication required")));
+                               status = POOL_CONTINUE;
+                               break;
 
             case uaMD5:
-                status = CheckUserExist(frontend->username);
-                               if (status != POOL_CONTINUE)
+                               status = POOL_CONTINUE;
+
+                               if (NUM_BACKENDS <= 1)
+                                       break;
+
+                               if (!frontend->passwordMapping)
                                        ereport(FATAL,
                                                (return_code(2),
-                                                       errmsg("md5 authentication failed"),
+                                                       errmsg("md5 authentication failed"),
                                                                errdetail("pool_passwd file does not contain an entry for \"%s\"",frontend->username)));
+                               if (frontend->passwordMapping->pgpoolUser.passwordType != PASSWORD_TYPE_PLAINTEXT &&
+                                       frontend->passwordMapping->pgpoolUser.passwordType != PASSWORD_TYPE_MD5)
+                                       ereport(FATAL,
+                                               (return_code(2),
+                                                       errmsg("md5 authentication failed"),
+                                                        errdetail("pool_passwd file does not contain valid md5 entry for \"%s\"",frontend->username)));
                 break;
 
                        case uaSCRAM:
-                               status = CheckUserExist(frontend->username);
-                               if (status != POOL_CONTINUE)
+                               if (!frontend->passwordMapping)
                                        ereport(FATAL,
                                                (return_code(2),
                                                        errmsg("SCRAM authentication failed"),
                                                         errdetail("pool_passwd file does not contain an entry for \"%s\"",frontend->username)));
+                               if (frontend->passwordMapping->pgpoolUser.passwordType != PASSWORD_TYPE_PLAINTEXT &&
+                                       frontend->passwordMapping->pgpoolUser.passwordType != PASSWORD_TYPE_SCRAM_SHA_256)
+                                       ereport(FATAL,
+                                               (return_code(2),
+                                                       errmsg("SCRAM authentication failed"),
+                                                        errdetail("pool_passwd file does not contain valid SCRAM entry for \"%s\"",frontend->username)));
 
+                               status = POOL_CONTINUE;
                                break;
 
 
@@ -853,6 +885,7 @@ void ClientAuthentication(POOL_CONNECTION *frontend)
                 status = POOL_CONTINUE;
                 break;
         }
+               authenticate_frontend(frontend);
     }
     PG_CATCH();
     {
@@ -1012,6 +1045,12 @@ static void auth_failed(POOL_CONNECTION *frontend)
                                         frontend->username);
                        break;
 
+               case uaCert:
+                       snprintf(errmessage, messagelen,
+                                        "\"CERT\" authentication with pgpool failed for user \"%s\"",
+                                        frontend->username);
+                       break;
+
 /*             case uaCrypt: */
 /*             case uaPassword: */
 /*                     snprintf(errmessage, messagelen, */
@@ -2006,6 +2045,8 @@ static POOL_STATUS CheckPAMAuth(POOL_CONNECTION *frontend, char *user, char *pas
 
 #endif   /* USE_PAM */
 
+
+
 static POOL_STATUS CheckUserExist(char *username)
 {
        char *passwd;
index 4dd30d1601e4661124f6e75b162f2e7d3b6eac1f..dab815d1c1c6f96ba3d68787998bd96c752b4960 100644 (file)
@@ -233,6 +233,158 @@ char *pool_get_passwd(char *username)
        return NULL;
 }
 
+/*
+ * return the next token if the current token matches the
+ * user
+ */
+static char *
+getNextToken(char *buf, char **token)
+{
+#define MAX_TOKEN_LEN 128
+       char    *tbuf,*p;
+       bool    bslash = false;
+       char    tok[MAX_TOKEN_LEN+1];
+       int readlen = 0;
+
+       *token = NULL;
+       if (buf == NULL)
+               return NULL;
+
+       tbuf = buf;
+       p = tok;
+       while (*tbuf != 0 && readlen < MAX_TOKEN_LEN)
+       {
+               if (*tbuf == '\\' && !bslash)
+               {
+                       tbuf++;
+                       bslash = true;
+               }
+
+               if (*tbuf == ':' && !bslash)
+               {
+                       *p = '\0';
+                       if(readlen)
+                               *token = pstrdup(tok);
+                       return tbuf + 1;
+               }
+               /*  just copy to the tok */
+               bslash = false;
+               *p++ = *tbuf++;
+               readlen++;
+       }
+       *p = '\0';
+       if(readlen)
+               *token = pstrdup(tok);
+       return NULL;
+}
+/*
+ * return the next token if the current token matches the
+ * user
+ */
+static char *
+userMatchesString(char *buf, char *user)
+{
+       char       *tbuf,
+       *ttok;
+       bool            bslash = false;
+
+       if (buf == NULL || user == NULL)
+               return NULL;
+       tbuf = buf;
+       ttok = user;
+       while (*tbuf != 0)
+       {
+               if (*tbuf == '\\' && !bslash)
+               {
+                       tbuf++;
+                       bslash = true;
+               }
+               if (*tbuf == ':' && *ttok == 0 && !bslash)
+                       return tbuf + 1;
+               bslash = false;
+               if (*ttok == 0)
+                       return NULL;
+               if (*tbuf == *ttok)
+               {
+                       tbuf++;
+                       ttok++;
+               }
+               else
+                       return NULL;
+       }
+       return NULL;
+}
+
+/*
+ * user:passwod[:user:password]
+ */
+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,
+                               (errmsg("unable to get password, password file descriptor is NULL")));
+
+       rewind(passwd_fd);
+
+       while (!feof(passwd_fd) && !ferror(passwd_fd))
+       {
+               char    *t = buf;
+               char    *tok;
+               int     len;
+
+               if (fgets(buf, sizeof(buf), passwd_fd) == NULL)
+                       break;
+
+               len = strlen(buf);
+               if (len == 0)
+                       continue;
+
+               /* Remove trailing newline */
+               if (buf[len - 1] == '\n')
+                       buf[len - 1] = 0;
+
+               if ((t = userMatchesString(t, username)) == NULL)
+                       continue;
+               /* Get the password */
+               t = getNextToken(t, &tok);
+               if (tok)
+               {
+                       pwdMapping = palloc(sizeof(PasswordMapping));
+                       pwdMapping->pgpoolUser.password = tok;
+                       pwdMapping->pgpoolUser.passwordType = get_password_type(pwdMapping->pgpoolUser.password);
+                       pwdMapping->pgpoolUser.userName = pstrdup(username);
+                       pwdMapping->mappedUser = false;
+               }
+               else
+                       continue;
+               /* Get backend user*/
+               t = getNextToken(t, &tok);
+               if (tok)
+               {
+                       /* check if we also have the password */
+                       char *pwd;
+                       t = getNextToken(t, &pwd);
+                       if (tok)
+                       {
+                               pwdMapping->backendUser.password = pwd;
+                               pwdMapping->backendUser.userName = tok;
+                               pwdMapping->backendUser.passwordType = get_password_type(pwdMapping->backendUser.password);
+                               pwdMapping->mappedUser = true;
+                       }
+               }
+               break;
+       }
+       return pwdMapping;
+}
+
 /*
  * Delete the entry by username. If specified entry does not exist,
  * does nothing.
index ba5cf2de34550a672587e3a2e074039574e35b32..a4588a8a7ad55cbeae1ce41d981d1d4371135d6b 100644 (file)
@@ -38,8 +38,9 @@ typedef enum UserAuth
        /*  uaKrb5, */
        uaTrust,
        /*  uaIdent, */
-       /*  uaPassword, */
+       uaPassword,
        /*  uaCrypt, */
+       uaCert,
        uaMD5,
        uaSCRAM
 #ifdef USE_PAM
index 2368364b4688427c814f57f19109c103b91a78f5..45f82dcac3440351d0ef3e14f270c68c870af3a1 100644 (file)
@@ -6,7 +6,7 @@
  * pgpool: a language independent connection pool server for PostgreSQL 
  * written by Tatsuo Ishii
  *
- * Copyright (c) 2003-2015     PgPool Global Development Group
+ * Copyright (c) 2003-2018     PgPool Global Development Group
  *
  * Permission to use, copy, modify, and distribute this software and
  * its documentation for any purpose and without fee is hereby
@@ -26,7 +26,6 @@
 #ifndef POOL_PASSWD_H
 #define POOL_PASSWD_H
 
-#include "pool.h"
 
 #define POOL_PASSWD_FILENAME "pool_passwd"
 #define POOL_PASSWD_LEN 35
@@ -38,11 +37,27 @@ typedef enum {
 
 typedef enum PasswordType
 {
-       PASSWORD_TYPE_PLAINTEXT = 0,
+       PASSWORD_TYPE_UNKNOWN = 0,
+       PASSWORD_TYPE_PLAINTEXT,
        PASSWORD_TYPE_MD5,
        PASSWORD_TYPE_SCRAM_SHA_256
 } PasswordType;
 
+typedef struct UserPassword
+{
+       char *userName;
+       char *password;
+       PasswordType passwordType;
+}UserPassword;
+
+typedef struct PasswordMapping
+{
+       UserPassword pgpoolUser;
+       UserPassword backendUser;
+       bool mappedUser;
+}PasswordMapping;
+
+extern PasswordMapping *pool_get_user_credentials(char *username);
 extern PasswordType get_password_type(const char *shadow_pass);
 extern void pool_init_pool_passwd(char *pool_passwd_filename, POOL_PASSWD_MODE mode);
 extern int pool_create_passwdent(char *username, char *passwd);
index d9c6ff2c3d6653e76868930789db33110593f573..7c8671228659cb512ee1aac460dcab47da67422f 100644 (file)
@@ -28,6 +28,7 @@
 #include "pool_type.h"
 #include "pcp/libpcp_ext.h"
 #include "utils/pool_signal.h"
+#include "auth/pool_passwd.h"
 #include "parser/nodes.h"
 #include <stdio.h>
 #include <time.h>
@@ -181,6 +182,13 @@ typedef struct {
 #ifdef USE_SSL
        SSL_CTX *ssl_ctx; /* SSL connection context */
        SSL *ssl;       /* SSL connection */
+       X509 *peer;
+       char *cert_cn;                  /* common in the ssl certificate
+                                                        * presented by frontend connection
+                                                        * Used for cert authentication
+                                                        */
+       bool client_cert_loaded;
+
 #endif
        int ssl_active; /* SSL is failed if < 0, off if 0, on if > 0 */
 
@@ -215,8 +223,9 @@ typedef struct {
         */
        int auth_kind;          /* 3: clear text password, 4: crypt password, 5: md5 password */
        int pwd_size;           /* password (sent back from frontend) size in host order */
-       char password[MAX_PASSWORD_SIZE];               /* password (sent back from frontend) */
+       char password[MAX_PASSWORD_SIZE +1 ];           /* password (sent back from frontend) */
        char salt[4];           /* password salt */
+       PasswordType    passwordType;
 
        /*
         * following are used to remember current session parameter status.
@@ -240,6 +249,8 @@ typedef struct {
        char *username;
        char *remote_hostname;
        int remote_hostname_resolv;
+       bool frontend_authenticated;
+       PasswordMapping *passwordMapping;
        ConnectionInfo *con_info; /* shared memory coninfo used
                                                   * for handling the query containing
                                                   * pg_terminate_backend*/
@@ -555,6 +566,7 @@ extern POOL_STATUS pool_process_query(POOL_CONNECTION *frontend,
 
 extern int pool_do_auth(POOL_CONNECTION *frontend, POOL_CONNECTION_POOL *backend);
 extern int pool_do_reauth(POOL_CONNECTION *frontend, POOL_CONNECTION_POOL *cp);
+extern void authenticate_frontend(POOL_CONNECTION *frontend);
 
 extern bool is_backend_cache_empty(POOL_CONNECTION_POOL *backend);
 
@@ -565,6 +577,7 @@ extern void pool_ssl_close(POOL_CONNECTION *cp);
 extern int pool_ssl_read(POOL_CONNECTION *cp, void *buf, int size);
 extern int pool_ssl_write(POOL_CONNECTION *cp, const void *buf, int size);
 extern bool pool_ssl_pending(POOL_CONNECTION *cp);
+extern int SSL_ServerSide_init(void);
 
 extern POOL_STATUS ErrorResponse(POOL_CONNECTION *frontend, 
                                                                  POOL_CONNECTION_POOL *backend);
index a87fa2965df69076326be675643a44dfd04ff518..21123cff853d634b34132022e152df4151b99152 100644 (file)
@@ -184,15 +184,15 @@ int main(int argc, char **argv)
                                exit(1);
                }
        }
-#ifdef USE_SSL
-       /* global ssl init */
-#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined (LIBRESSL_VERSION_NUMBER))
-       OPENSSL_init_ssl(0, NULL);
-#else
-       SSL_library_init();
-#endif
-       SSL_load_error_strings();
-#endif /* USE_SSL */
+//#ifdef USE_SSL
+//     /* global ssl init */
+//#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined (LIBRESSL_VERSION_NUMBER))
+//     OPENSSL_init_ssl(0, NULL);
+//#else
+//     SSL_library_init();
+//#endif
+//     SSL_load_error_strings();
+//#endif /* USE_SSL */
 
        myargv = save_ps_display_args(myargc, myargv);
        /* create MemoryContexts */
@@ -284,6 +284,10 @@ int main(int argc, char **argv)
        if (pool_config->enable_pool_hba)
                load_hba(hba_file);
 
+#ifdef USE_SSL
+       SSL_ServerSide_init();
+#endif /* USE_SSL */
+
        /* check effective user id for watchdog */
        /* watchdog must be started under the privileged user */
        wd_check_network_command_configurations();
index 5b73dbf8d393d413f4f900e332254d764ccabdda..9814924619a2815747ea61e1d77412915591799a 100644 (file)
@@ -46,7 +46,6 @@
 
 #include <libgen.h>
 #include "utils/elog.h"
-#include "utils/palloc.h"
 
 #include "pool.h"
 #include "utils/palloc.h"
index 4a970c525077800f7922613b9ceceb2cf2c1bba3..2f18a7eaa25b00ac4e9ed122f271ed7a4f17ae83 100644 (file)
@@ -2317,7 +2317,7 @@ retry_startup:
        found = 0;
 
        backend = pool_get_cp(sp->user, sp->database, sp->major, 1);
-       
+
        if (backend != NULL)
        {
                found = 1;
index 1b2de402e197fa2c0dce592e9ff790cc39328384..ae01da6068e0af5046e133eb675ae124dde809bd 100644 (file)
 #include "config.h"
 #include "pool.h"
 #include "utils/elog.h"
+#include "utils/palloc.h"
+#include "utils/memutils.h"
 #include "utils/pool_stream.h"
 #include "pool_config.h"
+#include <sys/stat.h>
 
 #ifdef USE_SSL
 
+static SSL_CTX *SSL_frontend_context = NULL;
+static bool SSL_initialized = false;
+static bool ssl_passwd_cb_called = false;
+static int     ssl_passwd_cb(char *buf, int size, int rwflag, void *userdata);
+static int  verify_cb(int ok, X509_STORE_CTX *ctx);
+static const char * SSLerrmessage(unsigned long ecode);
+static void fetch_pool_ssl_cert(POOL_CONNECTION *cp);
+
 #define SSL_RETURN_VOID_IF(cond, msg) \
        do { \
                if ( (cond) ) { \
@@ -62,7 +73,9 @@ static int init_ssl_ctx(POOL_CONNECTION *cp, enum ssl_conn_type conntype);
 /* OpenSSL error message */
 static void perror_ssl(const char *context);
 
-/* attempt to negotiate a secure connection */
+/* Attempt to negotiate a secure connection
+ * between pgpool-II and PostgreSQL backends
+ */
 void pool_ssl_negotiate_clientserver(POOL_CONNECTION *cp) {
        int ssl_packet[2] = { htonl(sizeof(int)*2), htonl(NEGOTIATE_SSL_CODE) };
        char server_response;
@@ -112,21 +125,27 @@ void pool_ssl_negotiate_clientserver(POOL_CONNECTION *cp) {
 }
 
 
-/* attempt to negotiate a secure connection */
+/* attempt to negotiate a secure connection
+ * between frontend and Pgpool-II
+ */
 void pool_ssl_negotiate_serverclient(POOL_CONNECTION *cp) {
 
        cp->ssl_active = -1;
+       if ( (!pool_config->ssl) || !SSL_frontend_context) {
 
-       if ( (!pool_config->ssl) || init_ssl_ctx(cp, ssl_conn_serverclient)) {
+//     if ( (!pool_config->ssl) || init_ssl_ctx(cp, ssl_conn_serverclient)) {
                /* write back an "SSL reject" response before returning */
                pool_write_and_flush(cp, "N", 1);
        } else {
+               cp->ssl = SSL_new(SSL_frontend_context);
+
                /* write back an "SSL accept" response */
                pool_write_and_flush(cp, "S", 1);
 
                SSL_set_fd(cp->ssl, cp->fd);
                SSL_RETURN_VOID_IF( (SSL_accept(cp->ssl) < 0), "SSL_accept");
                cp->ssl_active = 1;
+               fetch_pool_ssl_cert(cp);
        }
 }
 
@@ -318,6 +337,31 @@ static void perror_ssl(const char *context) {
        }
 }
 
+/*
+ * Obtain reason string for passed SSL errcode
+ *
+ * ERR_get_error() is used by caller to get errcode to pass here.
+ *
+ * Some caution is needed here since ERR_reason_error_string will
+ * return NULL if it doesn't recognize the error code.  We don't
+ * want to return NULL ever.
+ */
+static const char *
+SSLerrmessage(unsigned long ecode)
+{
+       const char *errreason;
+       static char errbuf[32];
+       
+       if (ecode == 0)
+               return _("no SSL error reported");
+       errreason = ERR_reason_error_string(ecode);
+       if (errreason != NULL)
+               return errreason;
+       snprintf(errbuf, sizeof(errbuf), _("SSL error code %lu"), ecode);
+       return errbuf;
+}
+
+
 /*
  * Return true if SSL layer has any pending data in buffer
  */
@@ -328,6 +372,315 @@ bool pool_ssl_pending(POOL_CONNECTION *cp)
        return false;
 }
 
+static void fetch_pool_ssl_cert(POOL_CONNECTION *cp)
+{
+       int         len;
+       X509 *peer = SSL_get_peer_certificate(cp->ssl);
+       cp->peer = peer;
+       if (peer)
+       {
+               ereport(DEBUG1,
+                               (errmsg("got the SSL certificate")));
+               len = X509_NAME_get_text_by_NID(X509_get_subject_name(peer),NID_commonName, NULL, 0);
+               if (len != -1)
+               {
+                       char       *peer_cn;
+                       peer_cn = palloc(len + 1);
+                       int r = X509_NAME_get_text_by_NID(X509_get_subject_name(peer), NID_commonName, peer_cn, len + 1);
+                       peer_cn[len] = '\0';
+                       if (r != len)
+                       {
+                               /* shouldn't happen */
+                               pfree(peer_cn);
+                               return;
+                       }
+                       cp->client_cert_loaded = true;
+                       cp->cert_cn = MemoryContextStrdup(TopMemoryContext,peer_cn);
+                       pfree(peer_cn);
+               }
+               else
+               {
+                       cp->client_cert_loaded = false;
+               }
+       }
+       else
+       {
+               cp->client_cert_loaded = false;
+       }
+}
+
+/*
+ *     Passphrase collection callback
+ *
+ * If OpenSSL is told to use a passphrase-protected server key, by default
+ * it will issue a prompt on /dev/tty and try to read a key from there.
+ * That's no good during a postmaster SIGHUP cycle, not to mention SSL context
+ * reload in an EXEC_BACKEND postmaster child.  So override it with this dummy
+ * function that just returns an empty passphrase, guaranteeing failure.
+ */
+static int
+ssl_passwd_cb(char *buf, int size, int rwflag, void *userdata)
+{
+       /* Set flag to change the error message we'll report */
+       ssl_passwd_cb_called = true;
+       /* And return empty string */
+       Assert(size > 0);
+       buf[0] = '\0';
+       return 0;
+}
+/*
+ *     Certificate verification callback
+ *
+ *     This callback allows us to log intermediate problems during
+ *     verification, but for now we'll see if the final error message
+ *     contains enough information.
+ *
+ *     This callback also allows us to override the default acceptance
+ *     criteria (e.g., accepting self-signed or expired certs), but
+ *     for now we accept the default checks.
+ */
+static int
+verify_cb(int ok, X509_STORE_CTX *ctx)
+{
+       return ok;
+}
+
+/*
+ *     Initialize global SSL context.
+ *
+ * If isServerStart is true, report any errors as FATAL (so we don't return).
+ * Otherwise, log errors at LOG level and return -1 to indicate trouble,
+ * preserving the old SSL state if any.  Returns 0 if OK.
+ */
+int
+SSL_ServerSide_init(void)
+{
+       STACK_OF(X509_NAME) *root_cert_list = NULL;
+       SSL_CTX    *context;
+       struct stat buf;
+       
+       /* This stuff need be done only once. */
+       if (!SSL_initialized)
+       {
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined (LIBRESSL_VERSION_NUMBER))
+               OPENSSL_init_ssl(0, NULL);
+#else
+               SSL_library_init();
+#endif
+               SSL_load_error_strings();
+
+               SSL_initialized = true;
+       }
+       
+       /*
+        * We use SSLv23_method() because it can negotiate use of the highest
+        * mutually supported protocol version, while alternatives like
+        * TLSv1_2_method() permit only one specific version.  Note that we don't
+        * actually allow SSL v2 or v3, only TLS protocols (see below).
+        */
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined (LIBRESSL_VERSION_NUMBER))
+       context = SSL_CTX_new(TLS_method());
+#else
+       context = SSL_CTX_new(SSLv23_method());
+#endif
+
+       if (!context)
+       {
+               ereport(FATAL,
+                               (errmsg("could not create SSL context: %s",
+                                               SSLerrmessage(ERR_get_error()))));
+               goto error;
+       }
+
+       /*
+        * Disable OpenSSL's moving-write-buffer sanity check, because it causes
+        * unnecessary failures in nonblocking send cases.
+        */
+       SSL_CTX_set_mode(context, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
+
+       /*
+        * prompt for password for passphrase-protected files
+        */
+       SSL_CTX_set_default_passwd_cb(context, ssl_passwd_cb);
+
+       /*
+        * Load and verify server's certificate and private key
+        */
+       if (SSL_CTX_use_certificate_chain_file(context, pool_config->ssl_cert) != 1)
+       {
+               ereport(FATAL,
+                               (errmsg("could not load server certificate file \"%s\": %s",
+                                               pool_config->ssl_cert, SSLerrmessage(ERR_get_error()))));
+               goto error;
+       }
+
+       if (stat(pool_config->ssl_key, &buf) != 0)
+       {
+               ereport(FATAL,
+                               (errmsg("could not access private key file \"%s\": %m",
+                                               pool_config->ssl_key)));
+               goto error;
+       }
+       
+       if (!S_ISREG(buf.st_mode))
+       {
+               ereport(FATAL,
+                               (errmsg("private key file \"%s\" is not a regular file",
+                                               pool_config->ssl_key)));
+               goto error;
+       }
+       
+       /*
+        * Refuse to load key files owned by users other than us or root.
+        *
+        * XXX surely we can check this on Windows somehow, too.
+        */
+#if !defined(WIN32) && !defined(__CYGWIN__)
+       if (buf.st_uid != geteuid() && buf.st_uid != 0)
+       {
+               ereport(FATAL,
+                               (errmsg("private key file \"%s\" must be owned by the database user or root",
+                                               pool_config->ssl_key)));
+               goto error;
+       }
+#endif
+       
+       /*
+        * Require no public access to key file. If the file is owned by us,
+        * require mode 0600 or less. If owned by root, require 0640 or less to
+        * allow read access through our gid, or a supplementary gid that allows
+        * to read system-wide certificates.
+        *
+        * XXX temporarily suppress check when on Windows, because there may not
+        * be proper support for Unix-y file permissions.  Need to think of a
+        * reasonable check to apply on Windows.  (See also the data directory
+        * permission check in postmaster.c)
+        */
+#if !defined(WIN32) && !defined(__CYGWIN__)
+       if ((buf.st_uid == geteuid() && buf.st_mode & (S_IRWXG | S_IRWXO)) ||
+               (buf.st_uid == 0 && buf.st_mode & (S_IWGRP | S_IXGRP | S_IRWXO)))
+       {
+               ereport(FATAL,
+                               (errmsg("private key file \"%s\" has group or world access",
+                                               pool_config->ssl_key),
+                                errdetail("File must have permissions u=rw (0600) or less if owned by the database user, or permissions u=rw,g=r (0640) or less if owned by root.")));
+               goto error;
+       }
+#endif
+       
+       /*
+        * OK, try to load the private key file.
+        */
+       ssl_passwd_cb_called = false;
+       
+       if (SSL_CTX_use_PrivateKey_file(context,
+                                                                       pool_config->ssl_key,
+                                                                       SSL_FILETYPE_PEM) != 1)
+       {
+               if (ssl_passwd_cb_called)
+                       ereport(FATAL,
+                                       (errmsg("private key file \"%s\" cannot be reloaded because it requires a passphrase",
+                                                       pool_config->ssl_key)));
+               else
+                       ereport(FATAL,
+                                       (errmsg("could not load private key file \"%s\": %s",
+                                                       pool_config->ssl_key, SSLerrmessage(ERR_get_error()))));
+               goto error;
+       }
+       
+       if (SSL_CTX_check_private_key(context) != 1)
+       {
+               ereport(FATAL,
+                               (errmsg("check of private key failed: %s",
+                                               SSLerrmessage(ERR_get_error()))));
+               goto error;
+       }
+       
+       /* disallow SSL v2/v3 */
+       SSL_CTX_set_options(context, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
+       
+       /* disallow SSL session tickets */
+#ifdef SSL_OP_NO_TICKET                        /* added in openssl 0.9.8f */
+       SSL_CTX_set_options(context, SSL_OP_NO_TICKET);
+#endif
+       
+       /* disallow SSL session caching, too */
+       SSL_CTX_set_session_cache_mode(context, SSL_SESS_CACHE_OFF);
+       
+//     /* set up ephemeral DH and ECDH keys */
+//     if (!initialize_dh(context, isServerStart))
+//             goto error;
+//     if (!initialize_ecdh(context, isServerStart))
+//             goto error;
+       
+//     /* set up the allowed cipher list */
+//     if (SSL_CTX_set_cipher_list(context, SSLCipherSuites) != 1)
+//     {
+//             ereport(FATAL,
+//                             (errcode(ERRCODE_CONFIG_FILE_ERROR),
+//                              errmsg("could not set the cipher list (no valid ciphers available)")));
+//             goto error;
+//     }
+       
+       /* Let server choose order */
+//     if (SSLPreferServerCiphers)
+//             SSL_CTX_set_options(context, SSL_OP_CIPHER_SERVER_PREFERENCE);
+       
+       /*
+        * Load CA store, so we can verify client certificates if needed.
+        */
+       if (pool_config->ssl_ca_cert)
+       {
+               if (SSL_CTX_load_verify_locations(context, pool_config->ssl_ca_cert, NULL) != 1 ||
+                       (root_cert_list = SSL_load_client_CA_file(pool_config->ssl_ca_cert)) == NULL)
+               {
+                       ereport(FATAL,
+                                       (errmsg("could not load root certificate file \"%s\": %s",
+                                                       pool_config->ssl_ca_cert, SSLerrmessage(ERR_get_error()))));
+                       goto error;
+               }
+               /*
+                * Always ask for SSL client cert, but don't fail if it's not
+                * presented.  We might fail such connections later, depending on what
+                * we find in pg_hba.conf.
+                */
+               SSL_CTX_set_verify(context,
+                                                  (SSL_VERIFY_PEER |
+                                                       SSL_VERIFY_CLIENT_ONCE),
+                                                  verify_cb);
+               
+               /*
+                * Tell OpenSSL to send the list of root certs we trust to clients in
+                * CertificateRequests.  This lets a client with a keystore select the
+                * appropriate client certificate to send to us.
+                */
+               SSL_CTX_set_client_CA_list(context, root_cert_list);
+       }
+       
+       /*
+        * Success!  Replace any existing SSL_context.
+        */
+       if (SSL_frontend_context)
+               SSL_CTX_free(SSL_frontend_context);
+       
+       SSL_frontend_context = context;
+       
+       /*
+        * Set flag to remember whether CA store has been loaded into SSL_context.
+        */
+//     if (pool_config->ssl_ca_cert)
+//             ssl_loaded_verify_locations = true;
+//     else
+//             ssl_loaded_verify_locations = false;
+//
+       return 0;
+       
+error:
+       if (context)
+               SSL_CTX_free(context);
+       return -1;
+}
+
 #else /* USE_SSL: wrap / no-op ssl functionality if it's not available */
 
 void pool_ssl_negotiate_serverclient(POOL_CONNECTION *cp) {
@@ -363,6 +716,11 @@ int pool_ssl_write(POOL_CONNECTION *cp, const void *buf, int size) {
        return -1; /* never reached */
 }
 
+int SSL_ServerSide_init(void)
+{
+       return 0;
+}
+
 bool pool_ssl_pending(POOL_CONNECTION *cp)
 {
        return false;