#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
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);
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
ereport(DEBUG1,
(errmsg("authentication backend"),
errdetail("auth kind:%d", authkind)));
-
/* trust? */
if (authkind == AUTH_REQ_OK)
{
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? */
/* 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++)
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)
{
/* 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++)
{
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
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,
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;
}
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;
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"),
* 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")));
* 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);
/*
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)));
(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);
}
(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;
}
/*
pool_write(frontend, &kind, sizeof(kind));
if (extralen > 0)
pool_write_and_flush(frontend, extradata, extralen);
+ else
+ pool_flush(frontend);
}
/*
}
-/* 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 */
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:
* 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,
}
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;
* 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")));
#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) ) { \
/* 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;
}
-/* 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);
}
}
}
}
+/*
+ * 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
*/
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) {
return -1; /* never reached */
}
+int SSL_ServerSide_init(void)
+{
+ return 0;
+}
+
bool pool_ssl_pending(POOL_CONNECTION *cp)
{
return false;