[pgpool-hackers: 137] SSL mutual authentication (with patch)

Warren Armstrong wa at quintessencelabs.com
Thu Oct 4 10:47:40 JST 2012


-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
 
Hi,

We recently encountered a problem using pgpool with mutual
authentication between a
client (pgpool) and a server (postgres).  We determined that the problem
was due to pgpool
not loading client certificates & private keys when connecting to a
backend - while pgpool loaded
a CA certificate to authenticate the backend, it did not provide its own
credentials to said backend.

We were unsure whether or not this was a deliberate omission, and so we
changed the pgpool
codebase to allow for mutual authentication.  The changes provide for 
additional per-backend
configuration directives to set certificates, keys, etc.  These
directives are then used when configuring
the OpenSSL context.

I have attached a patch against Git revision
3f89a334fe08dfcd199d9e45728a04ddb1d2ec85.

Cheers,
Warren Armstrong
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2.0.17 (MingW32)
Comment: Using GnuPG with Mozilla - http://www.enigmail.net/
 
iEYEARECAAYFAlBs6rsACgkQIZlA5/+bUwn3eQCgjtbapglXoRX/jPle4aMeDOzu
3moAoJC9eqIBVAI+Nm1UtwApuHnKWFyR
=SFLK
-----END PGP SIGNATURE-----

-------------- next part --------------
diff --git a/pcp/libpcp_ext.h b/pcp/libpcp_ext.h
index 84fca61..02f2f2c 100644
--- a/pcp/libpcp_ext.h
+++ b/pcp/libpcp_ext.h
@@ -59,6 +59,10 @@ typedef struct {
 	char backend_data_directory[MAX_PATH_LENGTH];
 	unsigned short flag;		/* various flags */
 	unsigned long long int standby_delay;		/* The replication delay against the primary */
+	char backend_cert[MAX_PATH_LENGTH];
+	char backend_key[MAX_PATH_LENGTH];
+	char backend_ca[MAX_PATH_LENGTH];
+	char backend_ca_cert_dir[MAX_PATH_LENGTH];
 } BackendInfo;
 
 typedef struct {
diff --git a/pool_config.c b/pool_config.c
index ebfbfb6..16940ef 100644
--- a/pool_config.c
+++ b/pool_config.c
@@ -3553,6 +3553,107 @@ int pool_get_config(char *confpath, POOL_CONFIG_CONTEXT context)
 			pool_debug("pool_config: slot number %d flag: %04x", slot, flag);
 		}
 
+		else if (!strncmp(key, "backend_cert", 12) &&
+				 CHECK_CONTEXT(INIT_CONFIG|RELOAD_CONFIG, context) &&
+				 mypid == getpid()) /* this parameter must be modified by parent pid */
+		{
+			int slot;
+			char *str;
+
+			slot = atoi(key + 12);
+			if (slot < 0 || slot >= MAX_CONNECTION_SLOTS)
+			{
+				pool_error("pool_config: backend number %s for backend_cert out of range", key);
+				fclose(fd);
+				return(-1);
+			}
+
+			str = extract_string(yytext, token);
+			if (str == NULL)
+			{
+				fclose(fd);
+				return(-1);
+			}
+			if (context == INIT_CONFIG ||
+				(context == RELOAD_CONFIG && BACKEND_INFO(slot).backend_status == CON_UNUSED))
+				strlcpy(BACKEND_INFO(slot).backend_cert, str, MAX_PATH_LENGTH);
+		}
+		else if (!strncmp(key, "backend_key", 11) &&
+				 CHECK_CONTEXT(INIT_CONFIG|RELOAD_CONFIG, context) &&
+				 mypid == getpid()) /* this parameter must be modified by parent pid */
+		{
+			int slot;
+			char *str;
+
+			slot = atoi(key + 11);
+			if (slot < 0 || slot >= MAX_CONNECTION_SLOTS)
+			{
+				pool_error("pool_config: backend number %s for backend_key out of range", key);
+				fclose(fd);
+				return(-1);
+			}
+
+			str = extract_string(yytext, token);
+			if (str == NULL)
+			{
+				fclose(fd);
+				return(-1);
+			}
+			if (context == INIT_CONFIG ||
+				(context == RELOAD_CONFIG && BACKEND_INFO(slot).backend_status == CON_UNUSED))
+				strlcpy(BACKEND_INFO(slot).backend_key, str, MAX_PATH_LENGTH);
+		}
+		else if (!strncmp(key, "backend_ca", 10) &&
+				 CHECK_CONTEXT(INIT_CONFIG|RELOAD_CONFIG, context) &&
+				 mypid == getpid()) /* this parameter must be modified by parent pid */
+		{
+			int slot;
+			char *str;
+
+			slot = atoi(key + 10);
+			if (slot < 0 || slot >= MAX_CONNECTION_SLOTS)
+			{
+				pool_error("pool_config: backend number %s for backend_ca out of range", key);
+				fclose(fd);
+				return(-1);
+			}
+
+			str = extract_string(yytext, token);
+			if (str == NULL)
+			{
+				fclose(fd);
+				return(-1);
+			}
+			if (context == INIT_CONFIG ||
+				(context == RELOAD_CONFIG && BACKEND_INFO(slot).backend_status == CON_UNUSED))
+				strlcpy(BACKEND_INFO(slot).backend_ca, str, MAX_PATH_LENGTH);
+		}
+		else if (!strncmp(key, "backend_ca_cert_dir", 19) &&
+				 CHECK_CONTEXT(INIT_CONFIG|RELOAD_CONFIG, context) &&
+				 mypid == getpid()) /* this parameter must be modified by parent pid */
+		{
+			int slot;
+			char *str;
+
+			slot = atoi(key + 19);
+			if (slot < 0 || slot >= MAX_CONNECTION_SLOTS)
+			{
+				pool_error("pool_config: backend number %s for backend_ca_cert_dir out of range", key);
+				fclose(fd);
+				return(-1);
+			}
+
+			str = extract_string(yytext, token);
+			if (str == NULL)
+			{
+				fclose(fd);
+				return(-1);
+			}
+			if (context == INIT_CONFIG ||
+				(context == RELOAD_CONFIG && BACKEND_INFO(slot).backend_status == CON_UNUSED))
+				strlcpy(BACKEND_INFO(slot).backend_ca_cert_dir, str, MAX_PATH_LENGTH);
+		}
+
        	else if (!strcmp(key, "log_statement") && CHECK_CONTEXT(INIT_CONFIG|RELOAD_CONFIG, context))
 		{
 			int v = eval_logical(yytext);
@@ -4611,6 +4712,10 @@ static void clear_host_entry(int slot)
 	pool_config->backend_desc->backend_info[slot].backend_port = 0;
 	pool_config->backend_desc->backend_info[slot].backend_status = CON_UNUSED;
 	pool_config->backend_desc->backend_info[slot].backend_weight = 0.0;
+	*pool_config->backend_desc->backend_info[slot].backend_cert = '\0';
+	*pool_config->backend_desc->backend_info[slot].backend_key = '\0';
+	*pool_config->backend_desc->backend_info[slot].backend_ca = '\0';
+	*pool_config->backend_desc->backend_info[slot].backend_ca_cert_dir = '\0';
 }
 
 #ifdef DEBUG
diff --git a/pool_ssl.c b/pool_ssl.c
index e8e15f6..c675dac 100644
--- a/pool_ssl.c
+++ b/pool_ssl.c
@@ -230,35 +230,53 @@ retry:
 static int init_ssl_ctx(POOL_CONNECTION *cp, enum ssl_conn_type conntype) {
 	int error = 0;
 	char *cacert = NULL, *cacert_dir = NULL;
+        char *priv_key = NULL, *cert = NULL;
+	BackendInfo *backend = NULL;
 
 	/* initialize SSL members */
 	cp->ssl_ctx = SSL_CTX_new(TLSv1_method());
 	SSL_RETURN_ERROR_IF( (! cp->ssl_ctx), "SSL_CTX_new" );
 
 	if ( conntype == ssl_conn_serverclient) {
-		error = SSL_CTX_use_certificate_file(cp->ssl_ctx,
-		                                     pool_config->ssl_cert,
-		                                     SSL_FILETYPE_PEM);
-		SSL_RETURN_ERROR_IF( (error <= 0), "Loading SSL certificate");
-
-		error = SSL_CTX_use_PrivateKey_file(cp->ssl_ctx,
-		                                    pool_config->ssl_key,
-		                                    SSL_FILETYPE_PEM);
-		SSL_RETURN_ERROR_IF( (error <= 0), "Loading SSL private key");
+	    if (strlen(pool_config->ssl_cert))
+		cert = pool_config->ssl_cert;
+            if (strlen(pool_config->ssl_key))
+                priv_key = pool_config->ssl_key;
+            if (strlen(pool_config->ssl_ca_cert))
+		cacert = pool_config->ssl_ca_cert;
+	    if (strlen(pool_config->ssl_ca_cert_dir))
+		cacert_dir = pool_config->ssl_ca_cert_dir;
 	} else {
+	    backend = &BACKEND_INFO(cp->db_node_id);
+	    if (strlen(backend->backend_cert))
+		cert = backend->backend_cert;
+            if (strlen(backend->backend_key))
+                priv_key = backend->backend_key;
+            if (strlen(backend->backend_ca))
+		cacert = backend->backend_ca;
+	    if (strlen(backend->backend_ca_cert_dir))
+		cacert_dir = backend->backend_ca_cert_dir;
+	}
+
+	if (cert != NULL ) {
+	    error = SSL_CTX_use_certificate_file(cp->ssl_ctx,
+	                                         cert, 
+                                                 SSL_FILETYPE_PEM);
+	    SSL_RETURN_ERROR_IF( (error <= 0), "Loading SSL certificate");
+	}
+	if ( priv_key != NULL ) {
+	    error = SSL_CTX_use_PrivateKey_file(cp->ssl_ctx,
+		                                priv_key,
+  		                                SSL_FILETYPE_PEM);
+	    SSL_RETURN_ERROR_IF( (error <= 0), "Loading SSL private key");
+	}
 		/* set extra verification if ssl_ca_cert or ssl_ca_cert_dir are set */
-		if (strlen(pool_config->ssl_ca_cert))
-			cacert = pool_config->ssl_ca_cert;
-		if (strlen(pool_config->ssl_ca_cert_dir))
-			cacert_dir = pool_config->ssl_ca_cert_dir;
-    
-		if ( cacert || cacert_dir ) {
-			error = (!SSL_CTX_load_verify_locations(cp->ssl_ctx,
-			                                        cacert,
-			                                        cacert_dir));
-			SSL_RETURN_ERROR_IF(error, "SSL verification setup");
-			SSL_CTX_set_verify(cp->ssl_ctx, SSL_VERIFY_PEER, NULL);
-		}
+	if ( cacert || cacert_dir ) {
+	    error = (!SSL_CTX_load_verify_locations(cp->ssl_ctx,
+		                                    cacert,
+			                            cacert_dir));
+	    SSL_RETURN_ERROR_IF(error, "SSL verification setup");
+	    SSL_CTX_set_verify(cp->ssl_ctx, SSL_VERIFY_PEER, NULL);
 	}
 
 	cp->ssl = SSL_new(cp->ssl_ctx);


More information about the pgpool-hackers mailing list