SSL information view

Started by Magnus Haganderover 11 years ago29 messages
#1Magnus Hagander
magnus@hagander.net

As an administrator, I find that you fairly often want to know what
your current connections are actually using as SSL parameters, and
there is currently no other way than gdb to find that out - something
we definitely should fix.

You can find it out today through libpq (the PQgetssl functions), the
psql banner, or contrib/sslinfo. All of them are client side (the
sslinfo module runs on the server, but it's just SQL functions that
can show information about the current connection, nothing that can be
used to inspect other connections).

I recently put together a quick hack
(https://github.com/mhagander/pg_sslstatus) that exposes a view with
this information, but it's definitely hacky, and it really is
functionality that we should include in core. Thus, I'll provide a
version of that hack for 9.5.

Before doing that, however, I'd like to ask for opinions :) The hack
currently exposes a separate view that you can join to
pg_stat_activity (or pg_stat_replication) on the pid -- this is sort
of the same way that pg_stat_replication works in the first place. Do
we want something similar to that for a builtin SSL view as well, or
do we want to include the fields directly in pg_stat_activity and
pg_stat_replication?

Second, I was planning to implement it by adding fields to
PgBackendStatus and thus to BackendStatusArray, booleans directly in
the struct and strings similar to how we track for example hostnames.
Anybody see a problem with that?

--
Magnus Hagander
Me: http://www.hagander.net/
Work: http://www.redpill-linpro.com/

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#2Tom Lane
tgl@sss.pgh.pa.us
In reply to: Magnus Hagander (#1)
Re: SSL information view

Magnus Hagander <magnus@hagander.net> writes:

As an administrator, I find that you fairly often want to know what
your current connections are actually using as SSL parameters, and
there is currently no other way than gdb to find that out - something
we definitely should fix.

I'm wondering whether it's such a great idea that everybody can see
everybody else's client DN. Other than that, no objection to the
concept.

Second, I was planning to implement it by adding fields to
PgBackendStatus and thus to BackendStatusArray, booleans directly in
the struct and strings similar to how we track for example hostnames.
Anybody see a problem with that?

Space in that array is at a premium, and again the client DN seems
problematic, in that it's not short and has no clear upper bound.

If you were to drop the DN from the proposed view then I'd be fine
with this.

regards, tom lane

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#3Magnus Hagander
magnus@hagander.net
In reply to: Tom Lane (#2)
Re: SSL information view

On Sat, Jul 12, 2014 at 4:36 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:

Magnus Hagander <magnus@hagander.net> writes:

As an administrator, I find that you fairly often want to know what
your current connections are actually using as SSL parameters, and
there is currently no other way than gdb to find that out - something
we definitely should fix.

I'm wondering whether it's such a great idea that everybody can see
everybody else's client DN. Other than that, no objection to the
concept.

I was thinking that's mostly the equivalent of a username, which we do
let everybody see in pg_stat_activity.

Second, I was planning to implement it by adding fields to
PgBackendStatus and thus to BackendStatusArray, booleans directly in
the struct and strings similar to how we track for example hostnames.
Anybody see a problem with that?

Space in that array is at a premium, and again the client DN seems
problematic, in that it's not short and has no clear upper bound.

If you were to drop the DN from the proposed view then I'd be fine
with this.

The text fields, like hostname, are tracked in separate parts of
shared memory with just a pointer in the main array - I assume that's
why, and was planning to do the same. We'd have to cap the length oft
he DN at something though (and document as such), to make it fixed
length.

--
Magnus Hagander
Me: http://www.hagander.net/
Work: http://www.redpill-linpro.com/

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#4Stefan Kaltenbrunner
stefan@kaltenbrunner.cc
In reply to: Magnus Hagander (#1)
Re: SSL information view

On 07/12/2014 03:08 PM, Magnus Hagander wrote:

As an administrator, I find that you fairly often want to know what
your current connections are actually using as SSL parameters, and
there is currently no other way than gdb to find that out - something
we definitely should fix.

Yeah that would be handy - however I often wish to be able to figure
that out based on the logfile as well, any chance of getting these into
connection-logging/log_line_prefix as well?

Stefan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#5Magnus Hagander
magnus@hagander.net
In reply to: Stefan Kaltenbrunner (#4)
Re: SSL information view

On Sun, Jul 13, 2014 at 10:32 PM, Stefan Kaltenbrunner
<stefan@kaltenbrunner.cc> wrote:

On 07/12/2014 03:08 PM, Magnus Hagander wrote:

As an administrator, I find that you fairly often want to know what
your current connections are actually using as SSL parameters, and
there is currently no other way than gdb to find that out - something
we definitely should fix.

Yeah that would be handy - however I often wish to be able to figure
that out based on the logfile as well, any chance of getting these into
connection-logging/log_line_prefix as well?

We do already log some of it if you have enabled log_connections -
protocol and cipher. Anything else in particular you'd be looking for
- compression info?

--
Magnus Hagander
Me: http://www.hagander.net/
Work: http://www.redpill-linpro.com/

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#6Stefan Kaltenbrunner
stefan@kaltenbrunner.cc
In reply to: Magnus Hagander (#5)
Re: SSL information view

On 07/13/2014 10:35 PM, Magnus Hagander wrote:

On Sun, Jul 13, 2014 at 10:32 PM, Stefan Kaltenbrunner
<stefan@kaltenbrunner.cc> wrote:

On 07/12/2014 03:08 PM, Magnus Hagander wrote:

As an administrator, I find that you fairly often want to know what
your current connections are actually using as SSL parameters, and
there is currently no other way than gdb to find that out - something
we definitely should fix.

Yeah that would be handy - however I often wish to be able to figure
that out based on the logfile as well, any chance of getting these into
connection-logging/log_line_prefix as well?

We do already log some of it if you have enabled log_connections -
protocol and cipher. Anything else in particular you'd be looking for
- compression info?

DN mostly, not sure I care about compression info...

Stefan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#7Magnus Hagander
magnus@hagander.net
In reply to: Stefan Kaltenbrunner (#6)
Re: SSL information view

On Mon, Jul 14, 2014 at 7:54 PM, Stefan Kaltenbrunner
<stefan@kaltenbrunner.cc> wrote:

On 07/13/2014 10:35 PM, Magnus Hagander wrote:

On Sun, Jul 13, 2014 at 10:32 PM, Stefan Kaltenbrunner
<stefan@kaltenbrunner.cc> wrote:

On 07/12/2014 03:08 PM, Magnus Hagander wrote:

As an administrator, I find that you fairly often want to know what
your current connections are actually using as SSL parameters, and
there is currently no other way than gdb to find that out - something
we definitely should fix.

Yeah that would be handy - however I often wish to be able to figure
that out based on the logfile as well, any chance of getting these into
connection-logging/log_line_prefix as well?

We do already log some of it if you have enabled log_connections -
protocol and cipher. Anything else in particular you'd be looking for
- compression info?

DN mostly, not sure I care about compression info...

Compression fitted more neatly in with the format that was there now.

I wonder if we shuold add a DETAIL field on that error message that
has the DN in case there is a client certificate. Would that make
sense?

--
Magnus Hagander
Me: http://www.hagander.net/
Work: http://www.redpill-linpro.com/

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#8Bernd Helmle
mailings@oopsware.de
In reply to: Magnus Hagander (#1)
Re: SSL information view

--On 12. Juli 2014 15:08:01 +0200 Magnus Hagander <magnus@hagander.net>
wrote:

Before doing that, however, I'd like to ask for opinions :) The hack
currently exposes a separate view that you can join to
pg_stat_activity (or pg_stat_replication) on the pid -- this is sort
of the same way that pg_stat_replication works in the first place. Do
we want something similar to that for a builtin SSL view as well, or
do we want to include the fields directly in pg_stat_activity and
pg_stat_replication?

I've heard more than once the wish to get this information without
contrib..especially for the SSL version used (client and server likewise).
So ++1 for this feature.

I'd vote for a special view, that will keep the information into a single
place and someone can easily join extra information together.

--
Thanks

Bernd

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#9Magnus Hagander
magnus@hagander.net
In reply to: Bernd Helmle (#8)
1 attachment(s)
Re: SSL information view

On Mon, Jul 21, 2014 at 5:24 PM, Bernd Helmle <mailings@oopsware.de> wrote:

--On 12. Juli 2014 15:08:01 +0200 Magnus Hagander <magnus@hagander.net>
wrote:

Before doing that, however, I'd like to ask for opinions :) The hack
currently exposes a separate view that you can join to
pg_stat_activity (or pg_stat_replication) on the pid -- this is sort
of the same way that pg_stat_replication works in the first place. Do
we want something similar to that for a builtin SSL view as well, or
do we want to include the fields directly in pg_stat_activity and
pg_stat_replication?

I've heard more than once the wish to get this information without
contrib..especially for the SSL version used (client and server likewise).
So ++1 for this feature.

I'd vote for a special view, that will keep the information into a single
place and someone can easily join extra information together.

Here's a patch that implements that.

Docs are currently ont included because I'm waiting for the
restructuring of tha section to be done (started by me in a separate
thread) first, but the code is there for review.

Right now it just truncates the dn at NAMEDATALEN - so treating it the
same as we do with hostnames. My guess is this is not a big problem
because in the case of long DNs, most of the time the important stuff
is at the beginning anyway... (And it's not like it's actually used
for authentication, in which case it would of course be a problem).

--
Magnus Hagander
Me: http://www.hagander.net/
Work: http://www.redpill-linpro.com/

Attachments:

pg_stat_ssl.patchtext/x-patch; charset=US-ASCII; name=pg_stat_ssl.patchDownload
*** a/src/backend/catalog/system_views.sql
--- b/src/backend/catalog/system_views.sql
***************
*** 648,653 **** CREATE VIEW pg_stat_replication AS
--- 648,664 ----
      WHERE S.usesysid = U.oid AND
              S.pid = W.pid;
  
+ CREATE VIEW pg_stat_ssl AS
+     SELECT
+             I.pid,
+             I.ssl,
+             I.bits,
+             I.compression,
+             I.version,
+             I.cipher,
+             I.clientdn
+     FROM pg_stat_get_sslstatus() AS I;
+ 
  CREATE VIEW pg_replication_slots AS
      SELECT
              L.slot_name,
*** a/src/backend/libpq/be-secure-openssl.c
--- b/src/backend/libpq/be-secure-openssl.c
***************
*** 88,93 **** static void info_cb(const SSL *ssl, int type, int args);
--- 88,95 ----
  static void initialize_ecdh(void);
  static const char *SSLerrmessage(void);
  
+ static char *X509_NAME_to_cstring(X509_NAME *name);
+ 
  /* are we in the middle of a renegotiation? */
  static bool in_ssl_renegotiation = false;
  
***************
*** 1053,1055 **** SSLerrmessage(void)
--- 1055,1159 ----
  	snprintf(errbuf, sizeof(errbuf), _("SSL error code %lu"), errcode);
  	return errbuf;
  }
+ 
+ /*
+  * Return information about the SSL connection
+  */
+ int
+ be_tls_get_cipher_bits(Port *port)
+ {
+ 	int bits;
+ 
+ 	if (port->ssl)
+ 	{
+ 		SSL_get_cipher_bits(port->ssl, &bits);
+ 		return bits;
+ 	}
+ 	else
+ 		return 0;
+ }
+ 
+ bool
+ be_tls_get_compression(Port *port)
+ {
+ 	if (port->ssl)
+ 		return (SSL_get_current_compression(port->ssl) != NULL);
+ 	else
+ 		return false;
+ }
+ 
+ void
+ be_tls_get_version(Port *port, char *ptr, size_t len)
+ {
+ 	if (port->ssl)
+ 		strlcpy(ptr, SSL_get_version(port->ssl), len);
+ 	else
+ 		ptr[0] = '\0';
+ }
+ 
+ void
+ be_tls_get_cipher(Port *port, char *ptr, size_t len)
+ {
+ 	if (port->ssl)
+ 		strlcpy(ptr, SSL_get_cipher(port->ssl), NAMEDATALEN);
+ 	else
+ 		ptr[0] = '\0';
+ }
+ 
+ void
+ be_tls_get_peerdn_name(Port *port, char *ptr, size_t len)
+ {
+ 	if (port->peer)
+ 		strlcpy(ptr, X509_NAME_to_cstring(X509_get_subject_name(port->peer)), NAMEDATALEN);
+ 	else
+ 		ptr[0] = '\0';
+ }
+ 
+ /*
+  * Convert an X509 subject name to a cstring.
+  *
+  */
+ static char *
+ X509_NAME_to_cstring(X509_NAME *name)
+ {
+ 	BIO		   *membuf = BIO_new(BIO_s_mem());
+ 	int			i,
+ 				nid,
+ 				count = X509_NAME_entry_count(name);
+ 	X509_NAME_ENTRY *e;
+ 	ASN1_STRING *v;
+ 	const char *field_name;
+ 	size_t		size;
+ 	char		nullterm;
+ 	char	   *sp;
+ 	char	   *dp;
+ 	char	   *result;
+ 
+ 	(void) BIO_set_close(membuf, BIO_CLOSE);
+ 	for (i = 0; i < count; i++)
+ 	{
+ 		e = X509_NAME_get_entry(name, i);
+ 		nid = OBJ_obj2nid(X509_NAME_ENTRY_get_object(e));
+ 		v = X509_NAME_ENTRY_get_data(e);
+ 		field_name = OBJ_nid2sn(nid);
+ 		if (!field_name)
+ 			field_name = OBJ_nid2ln(nid);
+ 		BIO_printf(membuf, "/%s=", field_name);
+ 		ASN1_STRING_print_ex(membuf, v,
+ 							 ((ASN1_STRFLGS_RFC2253 & ~ASN1_STRFLGS_ESC_MSB)
+ 							  | ASN1_STRFLGS_UTF8_CONVERT));
+ 	}
+ 
+ 	/* ensure null termination of the BIO's content */
+ 	nullterm = '\0';
+ 	BIO_write(membuf, &nullterm, 1);
+ 	size = BIO_get_mem_data(membuf, &sp);
+ 	dp = pg_any_to_server(sp, size - 1, PG_UTF8);
+ 
+ 	result = pstrdup(dp);
+ 	if (dp != sp)
+ 		pfree(dp);
+ 	BIO_free(membuf);
+ 
+ 	return result;
+ }
*** a/src/backend/postmaster/pgstat.c
--- b/src/backend/postmaster/pgstat.c
***************
*** 2386,2391 **** pgstat_fetch_global(void)
--- 2386,2394 ----
  static PgBackendStatus *BackendStatusArray = NULL;
  static PgBackendStatus *MyBEEntry = NULL;
  static char *BackendClientHostnameBuffer = NULL;
+ static char *BackendSslVersionBuffer = NULL;
+ static char *BackendSslCipherBuffer = NULL;
+ static char *BackendSslClientDNBuffer = NULL;
  static char *BackendAppnameBuffer = NULL;
  static char *BackendActivityBuffer = NULL;
  static Size BackendActivityBufferSize = 0;
***************
*** 2470,2475 **** CreateSharedBackendStatus(void)
--- 2473,2531 ----
  		}
  	}
  
+ 	/* Create or attach to the shared SSL status buffers */
+ 	size = mul_size(NAMEDATALEN, MaxBackends);
+ 	BackendSslVersionBuffer = (char *)
+ 		ShmemInitStruct("Backend SSL Version Buffer", size, &found);
+ 
+ 	if (!found)
+ 	{
+ 		MemSet(BackendSslVersionBuffer, 0, size);
+ 
+ 		/* Initialize st_ssl_version pointers. */
+ 		buffer = BackendSslVersionBuffer;
+ 		for (i = 0; i < MaxBackends; i++)
+ 		{
+ 			BackendStatusArray[i].st_ssl_version = buffer;
+ 			buffer += NAMEDATALEN;
+ 		}
+ 	}
+ 
+ 	size = mul_size(NAMEDATALEN, MaxBackends);
+ 	BackendSslCipherBuffer = (char *)
+ 		ShmemInitStruct("Backend SSL Cipher Buffer", size, &found);
+ 
+ 	if (!found)
+ 	{
+ 		MemSet(BackendSslCipherBuffer, 0, size);
+ 
+ 		/* Initialize st_ssl_cipher pointers. */
+ 		buffer = BackendSslCipherBuffer;
+ 		for (i = 0; i < MaxBackends; i++)
+ 		{
+ 			BackendStatusArray[i].st_ssl_cipher = buffer;
+ 			buffer += NAMEDATALEN;
+ 		}
+ 	}
+ 
+ 	size = mul_size(NAMEDATALEN, MaxBackends);
+ 	BackendSslClientDNBuffer = (char *)
+ 		ShmemInitStruct("Backend SSL Client DN Buffer", size, &found);
+ 
+ 	if (!found)
+ 	{
+ 		MemSet(BackendSslClientDNBuffer, 0, size);
+ 
+ 		/* Initialize st_ssl_clientdn pointers. */
+ 		buffer = BackendSslClientDNBuffer;
+ 		for (i = 0; i < MaxBackends; i++)
+ 		{
+ 			BackendStatusArray[i].st_ssl_clientdn = buffer;
+ 			buffer += NAMEDATALEN;
+ 		}
+ 	}
+ 
+ 
  	/* Create or attach to the shared activity buffer */
  	BackendActivityBufferSize = mul_size(pgstat_track_activity_query_size,
  										 MaxBackends);
***************
*** 2579,2584 **** pgstat_bestart(void)
--- 2635,2653 ----
  				NAMEDATALEN);
  	else
  		beentry->st_clienthostname[0] = '\0';
+ #ifdef USE_SSL
+ 	beentry->st_ssl = (MyProcPort && MyProcPort->ssl != NULL);
+ 	if (beentry->st_ssl)
+ 	{
+ 		beentry->st_ssl_bits = be_tls_get_cipher_bits(MyProcPort);
+ 		beentry->st_ssl_compression = be_tls_get_compression(MyProcPort);
+ 		be_tls_get_version(MyProcPort, beentry->st_ssl_version, NAMEDATALEN);
+ 		be_tls_get_cipher(MyProcPort, beentry->st_ssl_cipher, NAMEDATALEN);
+ 		be_tls_get_peerdn_name(MyProcPort, beentry->st_ssl_clientdn, NAMEDATALEN);
+ 	}
+ #else
+ 	beentry->st_ssl = false;
+ #endif
  	beentry->st_waiting = false;
  	beentry->st_state = STATE_UNDEFINED;
  	beentry->st_appname[0] = '\0';
***************
*** 2806,2811 **** pgstat_read_current_status(void)
--- 2875,2883 ----
  	LocalPgBackendStatus *localtable;
  	LocalPgBackendStatus *localentry;
  	char	   *localappname,
+ 			   *localsslversion,
+ 			   *localsslcipher,
+ 			   *localsslclientdn,
  			   *localactivity;
  	int			i;
  
***************
*** 2821,2826 **** pgstat_read_current_status(void)
--- 2893,2907 ----
  	localappname = (char *)
  		MemoryContextAlloc(pgStatLocalContext,
  						   NAMEDATALEN * MaxBackends);
+ 	localsslversion = (char *)
+ 		MemoryContextAlloc(pgStatLocalContext,
+ 						   NAMEDATALEN * MaxBackends);
+ 	localsslcipher = (char *)
+ 		MemoryContextAlloc(pgStatLocalContext,
+ 						   NAMEDATALEN * MaxBackends);
+ 	localsslclientdn = (char *)
+ 		MemoryContextAlloc(pgStatLocalContext,
+ 						   NAMEDATALEN * MaxBackends);
  	localactivity = (char *)
  		MemoryContextAlloc(pgStatLocalContext,
  						   pgstat_track_activity_query_size * MaxBackends);
***************
*** 2854,2859 **** pgstat_read_current_status(void)
--- 2935,2946 ----
  				localentry->backendStatus.st_appname = localappname;
  				strcpy(localactivity, (char *) beentry->st_activity);
  				localentry->backendStatus.st_activity = localactivity;
+ 				strcpy(localsslversion, (char *) beentry->st_ssl_version);
+ 				localentry->backendStatus.st_ssl_version = localsslversion;
+ 				strcpy(localsslcipher, (char *) beentry->st_ssl_cipher);
+ 				localentry->backendStatus.st_ssl_cipher = localsslcipher;
+ 				strcpy(localsslclientdn, (char *) beentry->st_ssl_clientdn);
+ 				localentry->backendStatus.st_ssl_clientdn = localsslclientdn;
  			}
  
  			if (save_changecount == beentry->st_changecount &&
***************
*** 2874,2879 **** pgstat_read_current_status(void)
--- 2961,2969 ----
  
  			localentry++;
  			localappname += NAMEDATALEN;
+ 			localsslversion += NAMEDATALEN;
+ 			localsslcipher += NAMEDATALEN;
+ 			localsslclientdn += NAMEDATALEN;
  			localactivity += pgstat_track_activity_query_size;
  			localNumBackends++;
  		}
*** a/src/backend/utils/adt/pgstatfuncs.c
--- b/src/backend/utils/adt/pgstatfuncs.c
***************
*** 821,826 **** pg_stat_get_activity(PG_FUNCTION_ARGS)
--- 821,905 ----
  	}
  }
  
+ /*
+  * Returns SSL information for all connections, both regular backend and
+  * WAL senders.
+  */
+ Datum
+ pg_stat_get_sslstatus(PG_FUNCTION_ARGS)
+ {
+ #define PG_STAT_GET_SSLSTATUS_COLS 7
+ 	ReturnSetInfo  *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
+ 	TupleDesc		tupdesc;
+ 	Tuplestorestate *tupstore;
+ 	MemoryContext	per_query_ctx;
+ 	MemoryContext	oldcontext;
+ 	int				i;
+ 
+ 	/* check to see if caller supports us returning a tuplestore */
+ 	if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ 				 errmsg("set-valued function called in context that cannot accept a set")));
+ 	if (!(rsinfo->allowedModes & SFRM_Materialize))
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ 				 errmsg("materialize mode required, but it is not "		\
+ 						"allowed in this context")));
+ 
+ 	/* Build a tuple descriptor for our result type */
+ 	if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
+ 		elog(ERROR, "return type must be a row type");
+ 
+ 	per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
+ 	oldcontext = MemoryContextSwitchTo(per_query_ctx);
+ 
+ 	tupstore = tuplestore_begin_heap(true, false, work_mem);
+ 	rsinfo->returnMode = SFRM_Materialize;
+ 	rsinfo->setResult = tupstore;
+ 	rsinfo->setDesc = tupdesc;
+ 
+ 	MemoryContextSwitchTo(oldcontext);
+ 
+ 	for (i = 0; i < pgstat_fetch_stat_numbackends(); i++)
+ 	{
+ 		Datum			values[PG_STAT_GET_SSLSTATUS_COLS];
+ 		bool			nulls[PG_STAT_GET_SSLSTATUS_COLS];
+ 		PgBackendStatus *beentry = &pgstat_fetch_stat_local_beentry(i+1)->backendStatus;
+ 
+ 		if (beentry->st_procpid == 0)
+ 			continue;
+ 
+ 		MemSet(nulls, 0, sizeof(nulls));
+ 		/* XXX: somethign superuser only? */
+ 
+ 		values[0] = Int32GetDatum(beentry->st_procpid);
+ 		values[1] = BoolGetDatum(beentry->st_ssl);
+ 		if (beentry->st_ssl)
+ 		{
+ 			values[2] = Int32GetDatum(beentry->st_ssl_bits);
+ 			values[3] = BoolGetDatum(beentry->st_ssl_compression);
+ 			values[4] = CStringGetTextDatum(beentry->st_ssl_version);
+ 			values[5] = CStringGetTextDatum(beentry->st_ssl_cipher);
+ 			values[6] = CStringGetTextDatum(beentry->st_ssl_clientdn);
+ 		}
+ 		else
+ 		{
+ 			nulls[2] = true; /* bits */
+ 			nulls[3] = true; /* compression */
+ 			nulls[4] = true; /* version */
+ 			nulls[5] = true; /* cipher */
+ 			nulls[6] = true; /* clientdn */
+ 		}
+ 
+ 		tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+ 	}
+ 
+ 	/* clean up and return the tuplestore */
+ 	tuplestore_donestoring(tupstore);
+ 
+ 	return (Datum) 0;
+ }
  
  Datum
  pg_backend_pid(PG_FUNCTION_ARGS)
*** a/src/include/catalog/pg_proc.h
--- b/src/include/catalog/pg_proc.h
***************
*** 2693,2698 **** DATA(insert OID = 2022 (  pg_stat_get_activity			PGNSP PGUID 12 1 100 0 0 f f f
--- 2693,2700 ----
  DESCR("statistics: information about currently active backends");
  DATA(insert OID = 3099 (  pg_stat_get_wal_senders	PGNSP PGUID 12 1 10 0 0 f f f f f t s 0 0 2249 "" "{23,25,3220,3220,3220,3220,23,25}" "{o,o,o,o,o,o,o,o}" "{pid,state,sent_location,write_location,flush_location,replay_location,sync_priority,sync_state}" _null_ pg_stat_get_wal_senders _null_ _null_ _null_ ));
  DESCR("statistics: information about currently active replication");
+ DATA(insert OID = 3259 ( pg_stat_get_sslstatus	PGNSP PGUID 12 1 10 0 0 f f f f f t s 0 0 2249 "" "{23,16,23,16,25,25,25}" "{o,o,o,o,o,o,o}" "{pid,ssl,bits,compression,version,cipher,clientdn}" _null_ pg_stat_get_sslstatus _null_ _null_ _null_ ));
+ DESCR("statistics: information about SSL connections");
  DATA(insert OID = 2026 (  pg_backend_pid				PGNSP PGUID 12 1 0 0 0 f f f f t f s 0 0 23 "" _null_ _null_ _null_ _null_ pg_backend_pid _null_ _null_ _null_ ));
  DESCR("statistics: current backend PID");
  DATA(insert OID = 1937 (  pg_stat_get_backend_pid		PGNSP PGUID 12 1 0 0 0 f f f f t f s 1 0 23 "23" _null_ _null_ _null_ _null_ pg_stat_get_backend_pid _null_ _null_ _null_ ));
*** a/src/include/libpq/libpq-be.h
--- b/src/include/libpq/libpq-be.h
***************
*** 210,215 **** extern void be_tls_close(Port *port);
--- 210,220 ----
  extern ssize_t be_tls_read(Port *port, void *ptr, size_t len);
  extern ssize_t be_tls_write(Port *port, void *ptr, size_t len);
  
+ extern int be_tls_get_cipher_bits(Port *port);
+ extern bool be_tls_get_compression(Port *port);
+ extern void be_tls_get_version(Port *port, char *ptr, size_t len);
+ extern void be_tls_get_cipher(Port *port, char *ptr, size_t len);
+ extern void be_tls_get_peerdn_name(Port *port, char *ptr, size_t len);
  #endif
  
  extern ProtocolVersion FrontendProtocol;
*** a/src/include/pgstat.h
--- b/src/include/pgstat.h
***************
*** 732,737 **** typedef struct PgBackendStatus
--- 732,745 ----
  	SockAddr	st_clientaddr;
  	char	   *st_clienthostname;		/* MUST be null-terminated */
  
+ 	/* Information about SSL connection */
+ 	bool		st_ssl;
+ 	int			st_ssl_bits;
+ 	bool		st_ssl_compression;
+ 	char	   *st_ssl_version;  /* MUST be null-terminated */
+ 	char	   *st_ssl_cipher;   /* MUST be null-terminated */
+ 	char	   *st_ssl_clientdn; /* MUST be null-terminated */
+ 
  	/* Is backend currently waiting on an lmgr lock? */
  	bool		st_waiting;
  
#10Michael Paquier
michael.paquier@gmail.com
In reply to: Magnus Hagander (#9)
Re: SSL information view

On Tue, Nov 11, 2014 at 1:43 AM, Magnus Hagander <magnus@hagander.net>
wrote:

Right now it just truncates the dn at NAMEDATALEN - so treating it the
same as we do with hostnames. My guess is this is not a big problem
because in the case of long DNs, most of the time the important stuff
is at the beginning anyway... (And it's not like it's actually used
for authentication, in which case it would of course be a problem).

You should add it to the next CF for proper tracking, there are already
many patches in the queue waiting for reviews :)
--
Michael

#11Magnus Hagander
magnus@hagander.net
In reply to: Michael Paquier (#10)
1 attachment(s)
Re: SSL information view

On Tue, Nov 11, 2014 at 1:04 AM, Michael Paquier
<michael.paquier@gmail.com> wrote:

On Tue, Nov 11, 2014 at 1:43 AM, Magnus Hagander <magnus@hagander.net>
wrote:

Right now it just truncates the dn at NAMEDATALEN - so treating it the
same as we do with hostnames. My guess is this is not a big problem
because in the case of long DNs, most of the time the important stuff
is at the beginning anyway... (And it's not like it's actually used
for authentication, in which case it would of course be a problem).

You should add it to the next CF for proper tracking, there are already many
patches in the queue waiting for reviews :)

Absolutely - I just wanted those that were already involved in the
thread to get a chance to look at it early :) didn't want to submit it
until it was complete.

Which it is now - attached is a new version that includes docs.

--
Magnus Hagander
Me: http://www.hagander.net/
Work: http://www.redpill-linpro.com/

Attachments:

pg_stat_ssl.patchtext/x-patch; charset=US-ASCII; name=pg_stat_ssl.patchDownload
*** a/doc/src/sgml/monitoring.sgml
--- b/doc/src/sgml/monitoring.sgml
***************
*** 300,305 **** postgres   27093  0.0  0.0  30096  2752 ?        Ss   11:34   0:00 postgres: ser
--- 300,313 ----
        </entry>
       </row>
  
+      <row>
+       <entry><structname>pg_stat_ssl</><indexterm><primary>pg_stat_ssl</primary></indexterm></entry>
+       <entry>One row per connection (regular and replication), showing statistics about
+        SSL used on this connection.
+        See <xref linkend="pg-stat-ssl-view"> for details.
+       </entry>
+      </row>
+ 
      </tbody>
     </tgroup>
    </table>
***************
*** 825,830 **** postgres   27093  0.0  0.0  30096  2752 ?        Ss   11:34   0:00 postgres: ser
--- 833,907 ----
     listed; no information is available about downstream standby servers.
    </para>
  
+   <table id="pg-stat-ssl-view" xreflabel="pg_stat_ssl">
+    <title><structname>pg_stat_ssl</structname> View</title>
+    <tgroup cols="3">
+     <thead>
+     <row>
+       <entry>Column</entry>
+       <entry>Type</entry>
+       <entry>Description</entry>
+      </row>
+     </thead>
+ 
+    <tbody>
+     <row>
+      <entry><structfield>pid</></entry>
+      <entry><type>integer</></entry>
+      <entry>Process ID of a backend or WAL sender process</entry>
+     </row>
+     <row>
+      <entry><structfield>ssl</></entry>
+      <entry><type>boolean</></entry>
+      <entry>True if SSL is used on this connection</entry>
+     </row>
+     <row>
+      <entry><structfield>bits</></entry>
+      <entry><type>integer</></entry>
+      <entry>Number of bits in the encryption algorithm used, or NULL
+      if SSL is not used on this connection</entry>
+     </row>
+     <row>
+      <entry><structfield>compression</></entry>
+      <entry><type>boolean</></entry>
+      <entry>True if SSL compression is in use, false if not,
+       or NULL if SSL is not in use on this connection</entry>
+     </row>
+     <row>
+      <entry><structfield>version</></entry>
+      <entry><type>text</></entry>
+      <entry>Version of SSL in use, or NULL if SSL is not in use
+       on this connection</entry>
+     </row>
+     <row>
+      <entry><structfield>cipher</></entry>
+      <entry><type>text</></entry>
+      <entry>Name of SSL cipher in use, or NULL if SSL is not in use
+       on this connection</entry>
+     </row>
+     <row>
+      <entry><structfield>clientdn</></entry>
+      <entry><type>text</></entry>
+      <entry>Distinguished Name (DN) field from the client certificate
+       used, or NULL if no client certificate was supplied or if SSL
+       is not in use on this connection. This field is truncated if the
+       DN field is longer than <symbol>NAMEDATALEN</symbol> (64 characters
+       in a standard build)
+      </entry>
+     </row>
+    </tbody>
+    </tgroup>
+   </table>
+ 
+   <para>
+    The <structname>pg_stat_ssl</structname> view will contain one row per
+    backend or WAL sender process, showing statistics about SSL usage on
+    this connection. It can be joined to <structname>pg_stat_activity</structname>
+    or <structname>pg_stat_replication</structname> on the
+    <structfield>pid</structfield> column to get more details about the
+    connection.
+   </para>
+ 
  
    <table id="pg-stat-archiver-view" xreflabel="pg_stat_archiver">
     <title><structname>pg_stat_archiver</structname> View</title>
*** a/src/backend/catalog/system_views.sql
--- b/src/backend/catalog/system_views.sql
***************
*** 648,653 **** CREATE VIEW pg_stat_replication AS
--- 648,664 ----
      WHERE S.usesysid = U.oid AND
              S.pid = W.pid;
  
+ CREATE VIEW pg_stat_ssl AS
+     SELECT
+             I.pid,
+             I.ssl,
+             I.bits,
+             I.compression,
+             I.version,
+             I.cipher,
+             I.clientdn
+     FROM pg_stat_get_sslstatus() AS I;
+ 
  CREATE VIEW pg_replication_slots AS
      SELECT
              L.slot_name,
*** a/src/backend/libpq/be-secure-openssl.c
--- b/src/backend/libpq/be-secure-openssl.c
***************
*** 88,93 **** static void info_cb(const SSL *ssl, int type, int args);
--- 88,95 ----
  static void initialize_ecdh(void);
  static const char *SSLerrmessage(void);
  
+ static char *X509_NAME_to_cstring(X509_NAME *name);
+ 
  /* are we in the middle of a renegotiation? */
  static bool in_ssl_renegotiation = false;
  
***************
*** 1053,1055 **** SSLerrmessage(void)
--- 1055,1159 ----
  	snprintf(errbuf, sizeof(errbuf), _("SSL error code %lu"), errcode);
  	return errbuf;
  }
+ 
+ /*
+  * Return information about the SSL connection
+  */
+ int
+ be_tls_get_cipher_bits(Port *port)
+ {
+ 	int bits;
+ 
+ 	if (port->ssl)
+ 	{
+ 		SSL_get_cipher_bits(port->ssl, &bits);
+ 		return bits;
+ 	}
+ 	else
+ 		return 0;
+ }
+ 
+ bool
+ be_tls_get_compression(Port *port)
+ {
+ 	if (port->ssl)
+ 		return (SSL_get_current_compression(port->ssl) != NULL);
+ 	else
+ 		return false;
+ }
+ 
+ void
+ be_tls_get_version(Port *port, char *ptr, size_t len)
+ {
+ 	if (port->ssl)
+ 		strlcpy(ptr, SSL_get_version(port->ssl), len);
+ 	else
+ 		ptr[0] = '\0';
+ }
+ 
+ void
+ be_tls_get_cipher(Port *port, char *ptr, size_t len)
+ {
+ 	if (port->ssl)
+ 		strlcpy(ptr, SSL_get_cipher(port->ssl), NAMEDATALEN);
+ 	else
+ 		ptr[0] = '\0';
+ }
+ 
+ void
+ be_tls_get_peerdn_name(Port *port, char *ptr, size_t len)
+ {
+ 	if (port->peer)
+ 		strlcpy(ptr, X509_NAME_to_cstring(X509_get_subject_name(port->peer)), NAMEDATALEN);
+ 	else
+ 		ptr[0] = '\0';
+ }
+ 
+ /*
+  * Convert an X509 subject name to a cstring.
+  *
+  */
+ static char *
+ X509_NAME_to_cstring(X509_NAME *name)
+ {
+ 	BIO		   *membuf = BIO_new(BIO_s_mem());
+ 	int			i,
+ 				nid,
+ 				count = X509_NAME_entry_count(name);
+ 	X509_NAME_ENTRY *e;
+ 	ASN1_STRING *v;
+ 	const char *field_name;
+ 	size_t		size;
+ 	char		nullterm;
+ 	char	   *sp;
+ 	char	   *dp;
+ 	char	   *result;
+ 
+ 	(void) BIO_set_close(membuf, BIO_CLOSE);
+ 	for (i = 0; i < count; i++)
+ 	{
+ 		e = X509_NAME_get_entry(name, i);
+ 		nid = OBJ_obj2nid(X509_NAME_ENTRY_get_object(e));
+ 		v = X509_NAME_ENTRY_get_data(e);
+ 		field_name = OBJ_nid2sn(nid);
+ 		if (!field_name)
+ 			field_name = OBJ_nid2ln(nid);
+ 		BIO_printf(membuf, "/%s=", field_name);
+ 		ASN1_STRING_print_ex(membuf, v,
+ 							 ((ASN1_STRFLGS_RFC2253 & ~ASN1_STRFLGS_ESC_MSB)
+ 							  | ASN1_STRFLGS_UTF8_CONVERT));
+ 	}
+ 
+ 	/* ensure null termination of the BIO's content */
+ 	nullterm = '\0';
+ 	BIO_write(membuf, &nullterm, 1);
+ 	size = BIO_get_mem_data(membuf, &sp);
+ 	dp = pg_any_to_server(sp, size - 1, PG_UTF8);
+ 
+ 	result = pstrdup(dp);
+ 	if (dp != sp)
+ 		pfree(dp);
+ 	BIO_free(membuf);
+ 
+ 	return result;
+ }
*** a/src/backend/postmaster/pgstat.c
--- b/src/backend/postmaster/pgstat.c
***************
*** 2386,2391 **** pgstat_fetch_global(void)
--- 2386,2394 ----
  static PgBackendStatus *BackendStatusArray = NULL;
  static PgBackendStatus *MyBEEntry = NULL;
  static char *BackendClientHostnameBuffer = NULL;
+ static char *BackendSslVersionBuffer = NULL;
+ static char *BackendSslCipherBuffer = NULL;
+ static char *BackendSslClientDNBuffer = NULL;
  static char *BackendAppnameBuffer = NULL;
  static char *BackendActivityBuffer = NULL;
  static Size BackendActivityBufferSize = 0;
***************
*** 2470,2475 **** CreateSharedBackendStatus(void)
--- 2473,2531 ----
  		}
  	}
  
+ 	/* Create or attach to the shared SSL status buffers */
+ 	size = mul_size(NAMEDATALEN, MaxBackends);
+ 	BackendSslVersionBuffer = (char *)
+ 		ShmemInitStruct("Backend SSL Version Buffer", size, &found);
+ 
+ 	if (!found)
+ 	{
+ 		MemSet(BackendSslVersionBuffer, 0, size);
+ 
+ 		/* Initialize st_ssl_version pointers. */
+ 		buffer = BackendSslVersionBuffer;
+ 		for (i = 0; i < MaxBackends; i++)
+ 		{
+ 			BackendStatusArray[i].st_ssl_version = buffer;
+ 			buffer += NAMEDATALEN;
+ 		}
+ 	}
+ 
+ 	size = mul_size(NAMEDATALEN, MaxBackends);
+ 	BackendSslCipherBuffer = (char *)
+ 		ShmemInitStruct("Backend SSL Cipher Buffer", size, &found);
+ 
+ 	if (!found)
+ 	{
+ 		MemSet(BackendSslCipherBuffer, 0, size);
+ 
+ 		/* Initialize st_ssl_cipher pointers. */
+ 		buffer = BackendSslCipherBuffer;
+ 		for (i = 0; i < MaxBackends; i++)
+ 		{
+ 			BackendStatusArray[i].st_ssl_cipher = buffer;
+ 			buffer += NAMEDATALEN;
+ 		}
+ 	}
+ 
+ 	size = mul_size(NAMEDATALEN, MaxBackends);
+ 	BackendSslClientDNBuffer = (char *)
+ 		ShmemInitStruct("Backend SSL Client DN Buffer", size, &found);
+ 
+ 	if (!found)
+ 	{
+ 		MemSet(BackendSslClientDNBuffer, 0, size);
+ 
+ 		/* Initialize st_ssl_clientdn pointers. */
+ 		buffer = BackendSslClientDNBuffer;
+ 		for (i = 0; i < MaxBackends; i++)
+ 		{
+ 			BackendStatusArray[i].st_ssl_clientdn = buffer;
+ 			buffer += NAMEDATALEN;
+ 		}
+ 	}
+ 
+ 
  	/* Create or attach to the shared activity buffer */
  	BackendActivityBufferSize = mul_size(pgstat_track_activity_query_size,
  										 MaxBackends);
***************
*** 2579,2584 **** pgstat_bestart(void)
--- 2635,2653 ----
  				NAMEDATALEN);
  	else
  		beentry->st_clienthostname[0] = '\0';
+ #ifdef USE_SSL
+ 	beentry->st_ssl = (MyProcPort && MyProcPort->ssl != NULL);
+ 	if (beentry->st_ssl)
+ 	{
+ 		beentry->st_ssl_bits = be_tls_get_cipher_bits(MyProcPort);
+ 		beentry->st_ssl_compression = be_tls_get_compression(MyProcPort);
+ 		be_tls_get_version(MyProcPort, beentry->st_ssl_version, NAMEDATALEN);
+ 		be_tls_get_cipher(MyProcPort, beentry->st_ssl_cipher, NAMEDATALEN);
+ 		be_tls_get_peerdn_name(MyProcPort, beentry->st_ssl_clientdn, NAMEDATALEN);
+ 	}
+ #else
+ 	beentry->st_ssl = false;
+ #endif
  	beentry->st_waiting = false;
  	beentry->st_state = STATE_UNDEFINED;
  	beentry->st_appname[0] = '\0';
***************
*** 2806,2811 **** pgstat_read_current_status(void)
--- 2875,2883 ----
  	LocalPgBackendStatus *localtable;
  	LocalPgBackendStatus *localentry;
  	char	   *localappname,
+ 			   *localsslversion,
+ 			   *localsslcipher,
+ 			   *localsslclientdn,
  			   *localactivity;
  	int			i;
  
***************
*** 2821,2826 **** pgstat_read_current_status(void)
--- 2893,2907 ----
  	localappname = (char *)
  		MemoryContextAlloc(pgStatLocalContext,
  						   NAMEDATALEN * MaxBackends);
+ 	localsslversion = (char *)
+ 		MemoryContextAlloc(pgStatLocalContext,
+ 						   NAMEDATALEN * MaxBackends);
+ 	localsslcipher = (char *)
+ 		MemoryContextAlloc(pgStatLocalContext,
+ 						   NAMEDATALEN * MaxBackends);
+ 	localsslclientdn = (char *)
+ 		MemoryContextAlloc(pgStatLocalContext,
+ 						   NAMEDATALEN * MaxBackends);
  	localactivity = (char *)
  		MemoryContextAlloc(pgStatLocalContext,
  						   pgstat_track_activity_query_size * MaxBackends);
***************
*** 2854,2859 **** pgstat_read_current_status(void)
--- 2935,2946 ----
  				localentry->backendStatus.st_appname = localappname;
  				strcpy(localactivity, (char *) beentry->st_activity);
  				localentry->backendStatus.st_activity = localactivity;
+ 				strcpy(localsslversion, (char *) beentry->st_ssl_version);
+ 				localentry->backendStatus.st_ssl_version = localsslversion;
+ 				strcpy(localsslcipher, (char *) beentry->st_ssl_cipher);
+ 				localentry->backendStatus.st_ssl_cipher = localsslcipher;
+ 				strcpy(localsslclientdn, (char *) beentry->st_ssl_clientdn);
+ 				localentry->backendStatus.st_ssl_clientdn = localsslclientdn;
  			}
  
  			if (save_changecount == beentry->st_changecount &&
***************
*** 2874,2879 **** pgstat_read_current_status(void)
--- 2961,2969 ----
  
  			localentry++;
  			localappname += NAMEDATALEN;
+ 			localsslversion += NAMEDATALEN;
+ 			localsslcipher += NAMEDATALEN;
+ 			localsslclientdn += NAMEDATALEN;
  			localactivity += pgstat_track_activity_query_size;
  			localNumBackends++;
  		}
*** a/src/backend/utils/adt/pgstatfuncs.c
--- b/src/backend/utils/adt/pgstatfuncs.c
***************
*** 821,826 **** pg_stat_get_activity(PG_FUNCTION_ARGS)
--- 821,905 ----
  	}
  }
  
+ /*
+  * Returns SSL information for all connections, both regular backend and
+  * WAL senders.
+  */
+ Datum
+ pg_stat_get_sslstatus(PG_FUNCTION_ARGS)
+ {
+ #define PG_STAT_GET_SSLSTATUS_COLS 7
+ 	ReturnSetInfo  *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
+ 	TupleDesc		tupdesc;
+ 	Tuplestorestate *tupstore;
+ 	MemoryContext	per_query_ctx;
+ 	MemoryContext	oldcontext;
+ 	int				i;
+ 
+ 	/* check to see if caller supports us returning a tuplestore */
+ 	if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ 				 errmsg("set-valued function called in context that cannot accept a set")));
+ 	if (!(rsinfo->allowedModes & SFRM_Materialize))
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ 				 errmsg("materialize mode required, but it is not "		\
+ 						"allowed in this context")));
+ 
+ 	/* Build a tuple descriptor for our result type */
+ 	if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
+ 		elog(ERROR, "return type must be a row type");
+ 
+ 	per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
+ 	oldcontext = MemoryContextSwitchTo(per_query_ctx);
+ 
+ 	tupstore = tuplestore_begin_heap(true, false, work_mem);
+ 	rsinfo->returnMode = SFRM_Materialize;
+ 	rsinfo->setResult = tupstore;
+ 	rsinfo->setDesc = tupdesc;
+ 
+ 	MemoryContextSwitchTo(oldcontext);
+ 
+ 	for (i = 0; i < pgstat_fetch_stat_numbackends(); i++)
+ 	{
+ 		Datum			values[PG_STAT_GET_SSLSTATUS_COLS];
+ 		bool			nulls[PG_STAT_GET_SSLSTATUS_COLS];
+ 		PgBackendStatus *beentry = &pgstat_fetch_stat_local_beentry(i+1)->backendStatus;
+ 
+ 		if (beentry->st_procpid == 0)
+ 			continue;
+ 
+ 		MemSet(nulls, 0, sizeof(nulls));
+ 		/* XXX: somethign superuser only? */
+ 
+ 		values[0] = Int32GetDatum(beentry->st_procpid);
+ 		values[1] = BoolGetDatum(beentry->st_ssl);
+ 		if (beentry->st_ssl)
+ 		{
+ 			values[2] = Int32GetDatum(beentry->st_ssl_bits);
+ 			values[3] = BoolGetDatum(beentry->st_ssl_compression);
+ 			values[4] = CStringGetTextDatum(beentry->st_ssl_version);
+ 			values[5] = CStringGetTextDatum(beentry->st_ssl_cipher);
+ 			values[6] = CStringGetTextDatum(beentry->st_ssl_clientdn);
+ 		}
+ 		else
+ 		{
+ 			nulls[2] = true; /* bits */
+ 			nulls[3] = true; /* compression */
+ 			nulls[4] = true; /* version */
+ 			nulls[5] = true; /* cipher */
+ 			nulls[6] = true; /* clientdn */
+ 		}
+ 
+ 		tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+ 	}
+ 
+ 	/* clean up and return the tuplestore */
+ 	tuplestore_donestoring(tupstore);
+ 
+ 	return (Datum) 0;
+ }
  
  Datum
  pg_backend_pid(PG_FUNCTION_ARGS)
*** a/src/include/catalog/pg_proc.h
--- b/src/include/catalog/pg_proc.h
***************
*** 2722,2727 **** DATA(insert OID = 2022 (  pg_stat_get_activity			PGNSP PGUID 12 1 100 0 0 f f f
--- 2722,2729 ----
  DESCR("statistics: information about currently active backends");
  DATA(insert OID = 3099 (  pg_stat_get_wal_senders	PGNSP PGUID 12 1 10 0 0 f f f f f t s 0 0 2249 "" "{23,25,3220,3220,3220,3220,23,25}" "{o,o,o,o,o,o,o,o}" "{pid,state,sent_location,write_location,flush_location,replay_location,sync_priority,sync_state}" _null_ pg_stat_get_wal_senders _null_ _null_ _null_ ));
  DESCR("statistics: information about currently active replication");
+ DATA(insert OID = 3261 ( pg_stat_get_sslstatus	PGNSP PGUID 12 1 10 0 0 f f f f f t s 0 0 2249 "" "{23,16,23,16,25,25,25}" "{o,o,o,o,o,o,o}" "{pid,ssl,bits,compression,version,cipher,clientdn}" _null_ pg_stat_get_sslstatus _null_ _null_ _null_ ));
+ DESCR("statistics: information about SSL connections");
  DATA(insert OID = 2026 (  pg_backend_pid				PGNSP PGUID 12 1 0 0 0 f f f f t f s 0 0 23 "" _null_ _null_ _null_ _null_ pg_backend_pid _null_ _null_ _null_ ));
  DESCR("statistics: current backend PID");
  DATA(insert OID = 1937 (  pg_stat_get_backend_pid		PGNSP PGUID 12 1 0 0 0 f f f f t f s 1 0 23 "23" _null_ _null_ _null_ _null_ pg_stat_get_backend_pid _null_ _null_ _null_ ));
*** a/src/include/libpq/libpq-be.h
--- b/src/include/libpq/libpq-be.h
***************
*** 210,215 **** extern void be_tls_close(Port *port);
--- 210,220 ----
  extern ssize_t be_tls_read(Port *port, void *ptr, size_t len);
  extern ssize_t be_tls_write(Port *port, void *ptr, size_t len);
  
+ extern int be_tls_get_cipher_bits(Port *port);
+ extern bool be_tls_get_compression(Port *port);
+ extern void be_tls_get_version(Port *port, char *ptr, size_t len);
+ extern void be_tls_get_cipher(Port *port, char *ptr, size_t len);
+ extern void be_tls_get_peerdn_name(Port *port, char *ptr, size_t len);
  #endif
  
  extern ProtocolVersion FrontendProtocol;
*** a/src/include/pgstat.h
--- b/src/include/pgstat.h
***************
*** 732,737 **** typedef struct PgBackendStatus
--- 732,745 ----
  	SockAddr	st_clientaddr;
  	char	   *st_clienthostname;		/* MUST be null-terminated */
  
+ 	/* Information about SSL connection */
+ 	bool		st_ssl;
+ 	int			st_ssl_bits;
+ 	bool		st_ssl_compression;
+ 	char	   *st_ssl_version;  /* MUST be null-terminated */
+ 	char	   *st_ssl_cipher;   /* MUST be null-terminated */
+ 	char	   *st_ssl_clientdn; /* MUST be null-terminated */
+ 
  	/* Is backend currently waiting on an lmgr lock? */
  	bool		st_waiting;
  
*** a/src/test/regress/expected/rules.out
--- b/src/test/regress/expected/rules.out
***************
*** 1747,1752 **** pg_stat_replication| SELECT s.pid,
--- 1747,1760 ----
      pg_authid u,
      pg_stat_get_wal_senders() w(pid, state, sent_location, write_location, flush_location, replay_location, sync_priority, sync_state)
    WHERE ((s.usesysid = u.oid) AND (s.pid = w.pid));
+ pg_stat_ssl| SELECT i.pid,
+     i.ssl,
+     i.bits,
+     i.compression,
+     i.version,
+     i.cipher,
+     i.clientdn
+    FROM pg_stat_get_sslstatus() i(pid, ssl, bits, compression, version, cipher, clientdn);
  pg_stat_sys_indexes| SELECT pg_stat_all_indexes.relid,
      pg_stat_all_indexes.indexrelid,
      pg_stat_all_indexes.schemaname,
#12Alex Shulgin
ash@commandprompt.com
In reply to: Magnus Hagander (#11)
Re: SSL information view

Magnus Hagander <magnus@hagander.net> writes:

You should add it to the next CF for proper tracking, there are already many
patches in the queue waiting for reviews :)

Absolutely - I just wanted those that were already involved in the
thread to get a chance to look at it early :) didn't want to submit it
until it was complete.

Which it is now - attached is a new version that includes docs.

Here's my review of the patch:

* Applies to the current HEAD, no failed hunks.
* Compiles and works as advertised.
* Docs included.

The following catches my eye:

psql (9.5devel)
SSL connection (protocol: TLSv1.2, cipher: ECDHE-RSA-AES256-GCM-SHA384, bits: 256, compression: off)
Type "help" for help.

postgres=# select * from pg_stat_ssl;
pid | ssl | bits | compression | version | cipher | clientdn
------+-----+------+-------------+---------+-----------------------------+----------
1343 | t | 256 | f | TLSv1.2 | ECDHE-RSA-AES256-GCM-SHA384 |
(1 row)

I think the order of details in the psql prompt makes more sense,
because it puts more important details first.

I suggest we reorder the view columns, while also renaming 'version' to
'protocol' (especially since we have another patch in the works that
adds a GUC named 'ssl_protocols'):

pid, ssl, protocol, cipher, bits, compression, clientdn.

Next, this one:

+ be_tls_get_cipher(Port *port, char *ptr, size_t len)
+ {
+ 	if (port->ssl)
+ 		strlcpy(ptr, SSL_get_cipher(port->ssl), NAMEDATALEN);

should be this:

+ strlcpy(ptr, SSL_get_cipher(port->ssl), len);

The same goes for this one:

+ be_tls_get_peerdn_name(Port *port, char *ptr, size_t len)
+ {
+ 	if (port->peer)
+ 		strlcpy(ptr, X509_NAME_to_cstring(X509_get_subject_name(port->peer)), NAMEDATALEN);

The NAMEDATALEN constant is passed in the calling code in
pgstat_bestart().

Other than that, looks good to me.

--
Alex

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#13Heikki Linnakangas
hlinnakangas@vmware.com
In reply to: Magnus Hagander (#11)
Re: SSL information view

On 11/19/2014 02:36 PM, Magnus Hagander wrote:

+ 	/* Create or attach to the shared SSL status buffers */
+ 	size = mul_size(NAMEDATALEN, MaxBackends);
+ 	BackendSslVersionBuffer = (char *)
+ 		ShmemInitStruct("Backend SSL Version Buffer", size, &found);
+
+ 	if (!found)
+ 	{
+ 		MemSet(BackendSslVersionBuffer, 0, size);
+
+ 		/* Initialize st_ssl_version pointers. */
+ 		buffer = BackendSslVersionBuffer;
+ 		for (i = 0; i < MaxBackends; i++)
+ 		{
+ 			BackendStatusArray[i].st_ssl_version = buffer;
+ 			buffer += NAMEDATALEN;
+ 		}
+ 	}
+
+ 	size = mul_size(NAMEDATALEN, MaxBackends);
+ 	BackendSslCipherBuffer = (char *)
+ 		ShmemInitStruct("Backend SSL Cipher Buffer", size, &found);
+
+ 	if (!found)
+ 	{
+ 		MemSet(BackendSslCipherBuffer, 0, size);
+
+ 		/* Initialize st_ssl_cipher pointers. */
+ 		buffer = BackendSslCipherBuffer;
+ 		for (i = 0; i < MaxBackends; i++)
+ 		{
+ 			BackendStatusArray[i].st_ssl_cipher = buffer;
+ 			buffer += NAMEDATALEN;
+ 		}
+ 	}
+
+ 	size = mul_size(NAMEDATALEN, MaxBackends);
+ 	BackendSslClientDNBuffer = (char *)
+ 		ShmemInitStruct("Backend SSL Client DN Buffer", size, &found);
+
+ 	if (!found)
+ 	{
+ 		MemSet(BackendSslClientDNBuffer, 0, size);
+
+ 		/* Initialize st_ssl_clientdn pointers. */
+ 		buffer = BackendSslClientDNBuffer;
+ 		for (i = 0; i < MaxBackends; i++)
+ 		{
+ 			BackendStatusArray[i].st_ssl_clientdn = buffer;
+ 			buffer += NAMEDATALEN;
+ 		}
+ 	}

This pattern gets a bit tedious. We do that already for
application_names, client hostnames, and activity status but this adds
three more such strings. Why are these not just regular char arrays in
PgBackendStatus struct, anyway? The activity status is not, because its
size is configurable with the pgstat_track_activity_query_size GUC, but
all those other things are fixed-size.

Also, it would be nice if you didn't allocate the memory for all those
SSL strings, when SSL is disabled altogether. Perhaps put the
SSL-related information into a separate struct:

struct
{
/* Information about SSL connection */
int st_ssl_bits;
bool st_ssl_compression;
char st_ssl_version[NAMEDATALEN]; /* MUST be null-terminated */
char st_ssl_cipher[NAMEDATALEN]; /* MUST be null-terminated */
char st_ssl_clientdn[NAMEDATALEN]; /* MUST be null-terminated */
} PgBackendSSLStatus;

Those structs could be allocated like you allocate the string buffers
now, with a pointer to that struct from PgBackendStatus. When SSL is
disabled, the structs are not allocated and the pointers in
PgBackendStatus structs are NULL.

- Heikki

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#14Peter Eisentraut
peter_e@gmx.net
In reply to: Magnus Hagander (#11)
Re: SSL information view

On 11/19/14 7:36 AM, Magnus Hagander wrote:

+      <row>
+       <entry><structname>pg_stat_ssl</><indexterm><primary>pg_stat_ssl</primary></indexterm></entry>
+       <entry>One row per connection (regular and replication), showing statistics about
+        SSL used on this connection.
+        See <xref linkend="pg-stat-ssl-view"> for details.
+       </entry>
+      </row>
+ 

It doesn't really show "statistics". It shows information or data.

We should make contrib/sslinfo a wrapper around this view as much as
possible.

Is it useful to include rows for sessions not using SSL?

Should we perpetuate the "ssl"-naming? Is there a more general term?

Will this work for non-OpenSSL implementations?

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#15Magnus Hagander
magnus@hagander.net
In reply to: Peter Eisentraut (#14)
Re: SSL information view

On Mon, Jan 5, 2015 at 9:56 PM, Peter Eisentraut <peter_e@gmx.net> wrote:

On 11/19/14 7:36 AM, Magnus Hagander wrote:

+ <row>
+

<entry><structname>pg_stat_ssl</><indexterm><primary>pg_stat_ssl</primary></indexterm></entry>

+ <entry>One row per connection (regular and replication), showing

statistics about

+        SSL used on this connection.
+        See <xref linkend="pg-stat-ssl-view"> for details.
+       </entry>
+      </row>
+

It doesn't really show "statistics". It shows information or data.

Good point.

We should make contrib/sslinfo a wrapper around this view as much as

possible.

Agreed - but let's do that as a separate patch.

Is it useful to include rows for sessions not using SSL?

I think so, mainly because it makes things "more obvious" that you are
querying it the right way. Sure you could do a LEFT JOIN from
pg_stat_activity and a CASE to get the same results, but that makes it a
lot less in-your-face.

Should we perpetuate the "ssl"-naming? Is there a more general term?

tls? :)

We call it ssl everywhere else, I think being consistent is important.

Will this work for non-OpenSSL implementations?

Yes, it uses (and declares new) the new internal APIs that Heikki created.

--
Magnus Hagander
Me: http://www.hagander.net/
Work: http://www.redpill-linpro.com/

#16Michael Paquier
michael.paquier@gmail.com
In reply to: Magnus Hagander (#15)
Re: SSL information view

Where are we on this patch? No new version has been provided and there
have been comments provided by Heikki here
(5491E547.4040705@vmware.com) and by Alexei here
(87ppbqz00h.fsf@commandprompt.com).
--
Michael

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#17Magnus Hagander
magnus@hagander.net
In reply to: Michael Paquier (#16)
Re: SSL information view

On Tue, Feb 3, 2015 at 6:42 AM, Michael Paquier <michael.paquier@gmail.com>
wrote:

Where are we on this patch? No new version has been provided and there
have been comments provided by Heikki here
(5491E547.4040705@vmware.com) and by Alexei here
(87ppbqz00h.fsf@commandprompt.com).

Yeah, it's on my shame list. I'm definitely planning to get a new version
in before the next CF and to try to work quicker with it that time to
finish it off on time.

Thanks for the reminder!

--
Magnus Hagander
Me: http://www.hagander.net/
Work: http://www.redpill-linpro.com/

#18Michael Paquier
michael.paquier@gmail.com
In reply to: Magnus Hagander (#17)
Re: SSL information view

On Tue, Feb 3, 2015 at 9:36 PM, Magnus Hagander <magnus@hagander.net> wrote:

On Tue, Feb 3, 2015 at 6:42 AM, Michael Paquier <michael.paquier@gmail.com

wrote:

Where are we on this patch? No new version has been provided and there
have been comments provided by Heikki here
(5491E547.4040705@vmware.com) and by Alexei here
(87ppbqz00h.fsf@commandprompt.com).

Yeah, it's on my shame list. I'm definitely planning to get a new version
in before the next CF and to try to work quicker with it that time to
finish it off on time.

Thanks for the reminder!

Magnus, are you planning to work on this item of your shame list soon?
Could you clarify the status of this patch?
--
Michael

#19Magnus Hagander
magnus@hagander.net
In reply to: Michael Paquier (#18)
Re: SSL information view

On Fri, Feb 13, 2015 at 9:07 AM, Michael Paquier <michael.paquier@gmail.com>
wrote:

On Tue, Feb 3, 2015 at 9:36 PM, Magnus Hagander <magnus@hagander.net>
wrote:

On Tue, Feb 3, 2015 at 6:42 AM, Michael Paquier <
michael.paquier@gmail.com> wrote:

Where are we on this patch? No new version has been provided and there
have been comments provided by Heikki here
(5491E547.4040705@vmware.com) and by Alexei here
(87ppbqz00h.fsf@commandprompt.com).

Yeah, it's on my shame list. I'm definitely planning to get a new version
in before the next CF and to try to work quicker with it that time to
finish it off on time.

Thanks for the reminder!

Magnus, are you planning to work on this item of your shame list soon?
Could you clarify the status of this patch?

I do, and I hope to work on it over the next week or two. However, feel
free to bump it to the next CF -- I'll pick it up halfway in between if
necessary.

--
Magnus Hagander
Me: http://www.hagander.net/
Work: http://www.redpill-linpro.com/

#20Michael Paquier
michael.paquier@gmail.com
In reply to: Magnus Hagander (#19)
Re: SSL information view

On Fri, Feb 13, 2015 at 5:31 PM, Magnus Hagander <magnus@hagander.net>wrote:

On Fri, Feb 13, 2015 at 9:07 AM, Michael Paquier
<michael.paquier@gmail.com>wrote:

Magnus, are you planning to work on this item of your shame list soon?
Could you clarify the status of this patch?

I do, and I hope to work on it over the next week or two. However, feel
free to bump it to the next CF -- I'll pick it up halfway in between if
necessary.

OK, I moved it to 2015-02 with the same status "Waiting on Author".
--
Michael

#21Magnus Hagander
magnus@hagander.net
In reply to: Heikki Linnakangas (#13)
1 attachment(s)
Re: SSL information view

On Wed, Dec 17, 2014 at 9:19 PM, Heikki Linnakangas <hlinnakangas@vmware.com

wrote:

On 11/19/2014 02:36 PM, Magnus Hagander wrote:

+       /* Create or attach to the shared SSL status buffers */
+       size = mul_size(NAMEDATALEN, MaxBackends);
+       BackendSslVersionBuffer = (char *)
+               ShmemInitStruct("Backend SSL Version Buffer", size,
&found);
+
+       if (!found)
+       {
+               MemSet(BackendSslVersionBuffer, 0, size);
+
+               /* Initialize st_ssl_version pointers. */
+               buffer = BackendSslVersionBuffer;
+               for (i = 0; i < MaxBackends; i++)
+               {
+                       BackendStatusArray[i].st_ssl_version = buffer;
+                       buffer += NAMEDATALEN;
+               }
+       }
+
+       size = mul_size(NAMEDATALEN, MaxBackends);
+       BackendSslCipherBuffer = (char *)
+               ShmemInitStruct("Backend SSL Cipher Buffer", size,
&found);
+
+       if (!found)
+       {
+               MemSet(BackendSslCipherBuffer, 0, size);
+
+               /* Initialize st_ssl_cipher pointers. */
+               buffer = BackendSslCipherBuffer;
+               for (i = 0; i < MaxBackends; i++)
+               {
+                       BackendStatusArray[i].st_ssl_cipher = buffer;
+                       buffer += NAMEDATALEN;
+               }
+       }
+
+       size = mul_size(NAMEDATALEN, MaxBackends);
+       BackendSslClientDNBuffer = (char *)
+               ShmemInitStruct("Backend SSL Client DN Buffer", size,
&found);
+
+       if (!found)
+       {
+               MemSet(BackendSslClientDNBuffer, 0, size);
+
+               /* Initialize st_ssl_clientdn pointers. */
+               buffer = BackendSslClientDNBuffer;
+               for (i = 0; i < MaxBackends; i++)
+               {
+                       BackendStatusArray[i].st_ssl_clientdn = buffer;
+                       buffer += NAMEDATALEN;
+               }
+       }

This pattern gets a bit tedious. We do that already for application_names,
client hostnames, and activity status but this adds three more such
strings. Why are these not just regular char arrays in PgBackendStatus
struct, anyway? The activity status is not, because its size is
configurable with the pgstat_track_activity_query_size GUC, but all those
other things are fixed-size.

Also, it would be nice if you didn't allocate the memory for all those SSL
strings, when SSL is disabled altogether. Perhaps put the SSL-related
information into a separate struct:

struct
{
/* Information about SSL connection */
int st_ssl_bits;
bool st_ssl_compression;
char st_ssl_version[NAMEDATALEN]; /* MUST be
null-terminated */
char st_ssl_cipher[NAMEDATALEN]; /* MUST be
null-terminated */
char st_ssl_clientdn[NAMEDATALEN]; /* MUST be
null-terminated */
} PgBackendSSLStatus;

Those structs could be allocated like you allocate the string buffers now,
with a pointer to that struct from PgBackendStatus. When SSL is disabled,
the structs are not allocated and the pointers in PgBackendStatus structs
are NULL.

Finally, I found time to do this. PFA a new version of this patch.

It takes into account the changes suggested by Heikki and Alex (minus the
renaming of fields - I think that's a separate thing to do, and we should
stick to existing naming conventions for now - but I changed the order of
the fields). Also the documentation changes suggested by Peter (but still
not the contrib/sslinfo part, as that should be a separate patch - but I
can look at that once we agree on this one). And resolves the inevitable
oid conflict for a patch that's been delayed that long.

--
Magnus Hagander
Me: http://www.hagander.net/
Work: http://www.redpill-linpro.com/

Attachments:

pg_stat_ssl_v2.patchtext/x-patch; charset=US-ASCII; name=pg_stat_ssl_v2.patchDownload
*** a/doc/src/sgml/monitoring.sgml
--- b/doc/src/sgml/monitoring.sgml
***************
*** 300,305 **** postgres   27093  0.0  0.0  30096  2752 ?        Ss   11:34   0:00 postgres: ser
--- 300,313 ----
        </entry>
       </row>
  
+      <row>
+       <entry><structname>pg_stat_ssl</><indexterm><primary>pg_stat_ssl</primary></indexterm></entry>
+       <entry>One row per connection (regular and replication), showing information about
+        SSL used on this connection.
+        See <xref linkend="pg-stat-ssl-view"> for details.
+       </entry>
+      </row>
+ 
      </tbody>
     </tgroup>
    </table>
***************
*** 825,830 **** postgres   27093  0.0  0.0  30096  2752 ?        Ss   11:34   0:00 postgres: ser
--- 833,907 ----
     listed; no information is available about downstream standby servers.
    </para>
  
+   <table id="pg-stat-ssl-view" xreflabel="pg_stat_ssl">
+    <title><structname>pg_stat_ssl</structname> View</title>
+    <tgroup cols="3">
+     <thead>
+     <row>
+       <entry>Column</entry>
+       <entry>Type</entry>
+       <entry>Description</entry>
+      </row>
+     </thead>
+ 
+    <tbody>
+     <row>
+      <entry><structfield>pid</></entry>
+      <entry><type>integer</></entry>
+      <entry>Process ID of a backend or WAL sender process</entry>
+     </row>
+     <row>
+      <entry><structfield>ssl</></entry>
+      <entry><type>boolean</></entry>
+      <entry>True if SSL is used on this connection</entry>
+     </row>
+     <row>
+      <entry><structfield>version</></entry>
+      <entry><type>text</></entry>
+      <entry>Version of SSL in use, or NULL if SSL is not in use
+       on this connection</entry>
+     </row>
+     <row>
+      <entry><structfield>cipher</></entry>
+      <entry><type>text</></entry>
+      <entry>Name of SSL cipher in use, or NULL if SSL is not in use
+       on this connection</entry>
+     </row>
+     <row>
+      <entry><structfield>bits</></entry>
+      <entry><type>integer</></entry>
+      <entry>Number of bits in the encryption algorithm used, or NULL
+      if SSL is not used on this connection</entry>
+     </row>
+     <row>
+      <entry><structfield>compression</></entry>
+      <entry><type>boolean</></entry>
+      <entry>True if SSL compression is in use, false if not,
+       or NULL if SSL is not in use on this connection</entry>
+     </row>
+     <row>
+      <entry><structfield>clientdn</></entry>
+      <entry><type>text</></entry>
+      <entry>Distinguished Name (DN) field from the client certificate
+       used, or NULL if no client certificate was supplied or if SSL
+       is not in use on this connection. This field is truncated if the
+       DN field is longer than <symbol>NAMEDATALEN</symbol> (64 characters
+       in a standard build)
+      </entry>
+     </row>
+    </tbody>
+    </tgroup>
+   </table>
+ 
+   <para>
+    The <structname>pg_stat_ssl</structname> view will contain one row per
+    backend or WAL sender process, showing statistics about SSL usage on
+    this connection. It can be joined to <structname>pg_stat_activity</structname>
+    or <structname>pg_stat_replication</structname> on the
+    <structfield>pid</structfield> column to get more details about the
+    connection.
+   </para>
+ 
  
    <table id="pg-stat-archiver-view" xreflabel="pg_stat_archiver">
     <title><structname>pg_stat_archiver</structname> View</title>
*** a/src/backend/catalog/system_views.sql
--- b/src/backend/catalog/system_views.sql
***************
*** 646,651 **** CREATE VIEW pg_stat_replication AS
--- 646,662 ----
      WHERE S.usesysid = U.oid AND
              S.pid = W.pid;
  
+ CREATE VIEW pg_stat_ssl AS
+     SELECT
+             I.pid,
+             I.ssl,
+             I.version,
+             I.cipher,
+             I.bits,
+             I.compression,
+             I.clientdn
+     FROM pg_stat_get_sslstatus() AS I;
+ 
  CREATE VIEW pg_replication_slots AS
      SELECT
              L.slot_name,
*** a/src/backend/libpq/be-secure-openssl.c
--- b/src/backend/libpq/be-secure-openssl.c
***************
*** 90,95 **** static void info_cb(const SSL *ssl, int type, int args);
--- 90,97 ----
  static void initialize_ecdh(void);
  static const char *SSLerrmessage(void);
  
+ static char *X509_NAME_to_cstring(X509_NAME *name);
+ 
  /* are we in the middle of a renegotiation? */
  static bool in_ssl_renegotiation = false;
  
***************
*** 1040,1042 **** SSLerrmessage(void)
--- 1042,1146 ----
  	snprintf(errbuf, sizeof(errbuf), _("SSL error code %lu"), errcode);
  	return errbuf;
  }
+ 
+ /*
+  * Return information about the SSL connection
+  */
+ int
+ be_tls_get_cipher_bits(Port *port)
+ {
+ 	int bits;
+ 
+ 	if (port->ssl)
+ 	{
+ 		SSL_get_cipher_bits(port->ssl, &bits);
+ 		return bits;
+ 	}
+ 	else
+ 		return 0;
+ }
+ 
+ bool
+ be_tls_get_compression(Port *port)
+ {
+ 	if (port->ssl)
+ 		return (SSL_get_current_compression(port->ssl) != NULL);
+ 	else
+ 		return false;
+ }
+ 
+ void
+ be_tls_get_version(Port *port, char *ptr, size_t len)
+ {
+ 	if (port->ssl)
+ 		strlcpy(ptr, SSL_get_version(port->ssl), len);
+ 	else
+ 		ptr[0] = '\0';
+ }
+ 
+ void
+ be_tls_get_cipher(Port *port, char *ptr, size_t len)
+ {
+ 	if (port->ssl)
+ 		strlcpy(ptr, SSL_get_cipher(port->ssl), len);
+ 	else
+ 		ptr[0] = '\0';
+ }
+ 
+ void
+ be_tls_get_peerdn_name(Port *port, char *ptr, size_t len)
+ {
+ 	if (port->peer)
+ 		strlcpy(ptr, X509_NAME_to_cstring(X509_get_subject_name(port->peer)), len);
+ 	else
+ 		ptr[0] = '\0';
+ }
+ 
+ /*
+  * Convert an X509 subject name to a cstring.
+  *
+  */
+ static char *
+ X509_NAME_to_cstring(X509_NAME *name)
+ {
+ 	BIO		   *membuf = BIO_new(BIO_s_mem());
+ 	int			i,
+ 				nid,
+ 				count = X509_NAME_entry_count(name);
+ 	X509_NAME_ENTRY *e;
+ 	ASN1_STRING *v;
+ 	const char *field_name;
+ 	size_t		size;
+ 	char		nullterm;
+ 	char	   *sp;
+ 	char	   *dp;
+ 	char	   *result;
+ 
+ 	(void) BIO_set_close(membuf, BIO_CLOSE);
+ 	for (i = 0; i < count; i++)
+ 	{
+ 		e = X509_NAME_get_entry(name, i);
+ 		nid = OBJ_obj2nid(X509_NAME_ENTRY_get_object(e));
+ 		v = X509_NAME_ENTRY_get_data(e);
+ 		field_name = OBJ_nid2sn(nid);
+ 		if (!field_name)
+ 			field_name = OBJ_nid2ln(nid);
+ 		BIO_printf(membuf, "/%s=", field_name);
+ 		ASN1_STRING_print_ex(membuf, v,
+ 							 ((ASN1_STRFLGS_RFC2253 & ~ASN1_STRFLGS_ESC_MSB)
+ 							  | ASN1_STRFLGS_UTF8_CONVERT));
+ 	}
+ 
+ 	/* ensure null termination of the BIO's content */
+ 	nullterm = '\0';
+ 	BIO_write(membuf, &nullterm, 1);
+ 	size = BIO_get_mem_data(membuf, &sp);
+ 	dp = pg_any_to_server(sp, size - 1, PG_UTF8);
+ 
+ 	result = pstrdup(dp);
+ 	if (dp != sp)
+ 		pfree(dp);
+ 	BIO_free(membuf);
+ 
+ 	return result;
+ }
*** a/src/backend/postmaster/pgstat.c
--- b/src/backend/postmaster/pgstat.c
***************
*** 2479,2484 **** pgstat_fetch_global(void)
--- 2479,2485 ----
  static PgBackendStatus *BackendStatusArray = NULL;
  static PgBackendStatus *MyBEEntry = NULL;
  static char *BackendClientHostnameBuffer = NULL;
+ static PgBackendSSLStatus *BackendSslStatusBuffer = NULL;
  static char *BackendAppnameBuffer = NULL;
  static char *BackendActivityBuffer = NULL;
  static Size BackendActivityBufferSize = 0;
***************
*** 2563,2568 **** CreateSharedBackendStatus(void)
--- 2564,2591 ----
  		}
  	}
  
+ #ifdef USE_SSL
+ 	/* Create or attach to the shared SSL status buffer */
+ 	size = mul_size(sizeof(PgBackendSSLStatus), MaxBackends);
+ 	BackendSslStatusBuffer = (PgBackendSSLStatus *)
+ 		ShmemInitStruct("Backend SSL Status Buffer", size, &found);
+ 
+ 	if (!found)
+ 	{
+ 		MemSet(BackendSslStatusBuffer, 0, size);
+ 
+ 		/* Initialize st_ssl pointers. */
+ 		buffer = (char *) BackendSslStatusBuffer;
+ 		for (i = 0; i < MaxBackends; i++)
+ 		{
+ 			BackendStatusArray[i].st_ssl = (PgBackendSSLStatus *)buffer;
+ 			buffer += sizeof(PgBackendSSLStatus);
+ 		}
+ 	}
+ 
+ 	/* In the non-SSL case, st_ssl is already initialized to NULL by the MemSet */
+ #endif
+ 
  	/* Create or attach to the shared activity buffer */
  	BackendActivityBufferSize = mul_size(pgstat_track_activity_query_size,
  										 MaxBackends);
***************
*** 2672,2677 **** pgstat_bestart(void)
--- 2695,2716 ----
  				NAMEDATALEN);
  	else
  		beentry->st_clienthostname[0] = '\0';
+ #ifdef USE_SSL
+ 	if (MyProcPort && MyProcPort->ssl != NULL)
+ 	{
+ 		beentry->st_ssl->ssl_bits = be_tls_get_cipher_bits(MyProcPort);
+ 		beentry->st_ssl->ssl_compression = be_tls_get_compression(MyProcPort);
+ 		be_tls_get_version(MyProcPort, beentry->st_ssl->ssl_version, NAMEDATALEN);
+ 		be_tls_get_cipher(MyProcPort, beentry->st_ssl->ssl_cipher, NAMEDATALEN);
+ 		be_tls_get_peerdn_name(MyProcPort, beentry->st_ssl->ssl_clientdn, NAMEDATALEN);
+ 	}
+ 	else
+ 	{
+ 		beentry->st_ssl = NULL;
+ 	}
+ #else
+ 	beentry->st_ssl = NULL;
+ #endif
  	beentry->st_waiting = false;
  	beentry->st_state = STATE_UNDEFINED;
  	beentry->st_appname[0] = '\0';
***************
*** 2892,2897 **** pgstat_read_current_status(void)
--- 2931,2937 ----
  	volatile PgBackendStatus *beentry;
  	LocalPgBackendStatus *localtable;
  	LocalPgBackendStatus *localentry;
+ 	PgBackendSSLStatus *localsslstatus;
  	char	   *localappname,
  			   *localactivity;
  	int			i;
***************
*** 2908,2913 **** pgstat_read_current_status(void)
--- 2948,2956 ----
  	localappname = (char *)
  		MemoryContextAlloc(pgStatLocalContext,
  						   NAMEDATALEN * MaxBackends);
+ 	localsslstatus = (PgBackendSSLStatus *)
+ 		MemoryContextAlloc(pgStatLocalContext,
+ 						   sizeof(PgBackendSSLStatus) * MaxBackends);
  	localactivity = (char *)
  		MemoryContextAlloc(pgStatLocalContext,
  						   pgstat_track_activity_query_size * MaxBackends);
***************
*** 2944,2949 **** pgstat_read_current_status(void)
--- 2987,2999 ----
  				localentry->backendStatus.st_appname = localappname;
  				strcpy(localactivity, (char *) beentry->st_activity);
  				localentry->backendStatus.st_activity = localactivity;
+ 				if (beentry->st_ssl)
+ 				{
+ 					memcpy(localsslstatus, beentry->st_ssl, sizeof(PgBackendSSLStatus));
+ 					localentry->backendStatus.st_ssl = localsslstatus;
+ 				}
+ 				else
+ 					localentry->backendStatus.st_ssl = NULL;
  			}
  
  			pgstat_save_changecount_after(beentry, after_changecount);
***************
*** 2966,2971 **** pgstat_read_current_status(void)
--- 3016,3022 ----
  			localentry++;
  			localappname += NAMEDATALEN;
  			localactivity += pgstat_track_activity_query_size;
+ 			localsslstatus += sizeof(PgBackendSSLStatus);
  			localNumBackends++;
  		}
  	}
*** a/src/backend/utils/adt/pgstatfuncs.c
--- b/src/backend/utils/adt/pgstatfuncs.c
***************
*** 53,58 **** extern Datum pg_stat_get_function_self_time(PG_FUNCTION_ARGS);
--- 53,59 ----
  
  extern Datum pg_stat_get_backend_idset(PG_FUNCTION_ARGS);
  extern Datum pg_stat_get_activity(PG_FUNCTION_ARGS);
+ extern Datum pg_stat_get_sslstatus(PG_FUNCTION_ARGS);
  extern Datum pg_backend_pid(PG_FUNCTION_ARGS);
  extern Datum pg_stat_get_backend_pid(PG_FUNCTION_ARGS);
  extern Datum pg_stat_get_backend_dbid(PG_FUNCTION_ARGS);
***************
*** 823,828 **** pg_stat_get_activity(PG_FUNCTION_ARGS)
--- 824,907 ----
  	}
  }
  
+ /*
+  * Returns SSL information for all connections, both regular backend and
+  * WAL senders.
+  */
+ Datum
+ pg_stat_get_sslstatus(PG_FUNCTION_ARGS)
+ {
+ #define PG_STAT_GET_SSLSTATUS_COLS 7
+ 	ReturnSetInfo  *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
+ 	TupleDesc		tupdesc;
+ 	Tuplestorestate *tupstore;
+ 	MemoryContext	per_query_ctx;
+ 	MemoryContext	oldcontext;
+ 	int				i;
+ 
+ 	/* check to see if caller supports us returning a tuplestore */
+ 	if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ 				 errmsg("set-valued function called in context that cannot accept a set")));
+ 	if (!(rsinfo->allowedModes & SFRM_Materialize))
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ 				 errmsg("materialize mode required, but it is not "		\
+ 						"allowed in this context")));
+ 
+ 	/* Build a tuple descriptor for our result type */
+ 	if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
+ 		elog(ERROR, "return type must be a row type");
+ 
+ 	per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
+ 	oldcontext = MemoryContextSwitchTo(per_query_ctx);
+ 
+ 	tupstore = tuplestore_begin_heap(true, false, work_mem);
+ 	rsinfo->returnMode = SFRM_Materialize;
+ 	rsinfo->setResult = tupstore;
+ 	rsinfo->setDesc = tupdesc;
+ 
+ 	MemoryContextSwitchTo(oldcontext);
+ 
+ 	for (i = 0; i < pgstat_fetch_stat_numbackends(); i++)
+ 	{
+ 		Datum			values[PG_STAT_GET_SSLSTATUS_COLS];
+ 		bool			nulls[PG_STAT_GET_SSLSTATUS_COLS];
+ 		PgBackendStatus *beentry = &pgstat_fetch_stat_local_beentry(i+1)->backendStatus;
+ 
+ 		if (beentry->st_procpid == 0)
+ 			continue;
+ 
+ 		MemSet(nulls, 0, sizeof(nulls));
+ 
+ 		values[0] = Int32GetDatum(beentry->st_procpid);
+ 		values[1] = BoolGetDatum(beentry->st_ssl != NULL);
+ 		if (beentry->st_ssl)
+ 		{
+ 			values[2] = CStringGetTextDatum(beentry->st_ssl->ssl_version);
+ 			values[3] = CStringGetTextDatum(beentry->st_ssl->ssl_cipher);
+ 			values[4] = Int32GetDatum(beentry->st_ssl->ssl_bits);
+ 			values[5] = BoolGetDatum(beentry->st_ssl->ssl_compression);
+ 			values[6] = CStringGetTextDatum(beentry->st_ssl->ssl_clientdn);
+ 		}
+ 		else
+ 		{
+ 			nulls[2] = true; /* version */
+ 			nulls[3] = true; /* cipher */
+ 			nulls[4] = true; /* bits */
+ 			nulls[5] = true; /* compression */
+ 			nulls[6] = true; /* clientdn */
+ 		}
+ 
+ 		tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+ 	}
+ 
+ 	/* clean up and return the tuplestore */
+ 	tuplestore_donestoring(tupstore);
+ 
+ 	return (Datum) 0;
+ }
  
  Datum
  pg_backend_pid(PG_FUNCTION_ARGS)
*** a/src/include/catalog/pg_proc.h
--- b/src/include/catalog/pg_proc.h
***************
*** 2760,2765 **** DATA(insert OID = 2022 (  pg_stat_get_activity			PGNSP PGUID 12 1 100 0 0 f f f
--- 2760,2767 ----
  DESCR("statistics: information about currently active backends");
  DATA(insert OID = 3099 (  pg_stat_get_wal_senders	PGNSP PGUID 12 1 10 0 0 f f f f f t s 0 0 2249 "" "{23,25,3220,3220,3220,3220,23,25}" "{o,o,o,o,o,o,o,o}" "{pid,state,sent_location,write_location,flush_location,replay_location,sync_priority,sync_state}" _null_ pg_stat_get_wal_senders _null_ _null_ _null_ ));
  DESCR("statistics: information about currently active replication");
+ DATA(insert OID = 3284 ( pg_stat_get_sslstatus	PGNSP PGUID 12 1 10 0 0 f f f f f t s 0 0 2249 "" "{23,16,25,25,23,16,25}" "{o,o,o,o,o,o,o}" "{pid,ssl,version,cipher,bits,compression,clientdn}" _null_ pg_stat_get_sslstatus _null_ _null_ _null_ ));
+ DESCR("statistics: information about SSL connections");
  DATA(insert OID = 2026 (  pg_backend_pid				PGNSP PGUID 12 1 0 0 0 f f f f t f s 0 0 23 "" _null_ _null_ _null_ _null_ pg_backend_pid _null_ _null_ _null_ ));
  DESCR("statistics: current backend PID");
  DATA(insert OID = 1937 (  pg_stat_get_backend_pid		PGNSP PGUID 12 1 0 0 0 f f f f t f s 1 0 23 "23" _null_ _null_ _null_ _null_ pg_stat_get_backend_pid _null_ _null_ _null_ ));
*** a/src/include/libpq/libpq-be.h
--- b/src/include/libpq/libpq-be.h
***************
*** 212,217 **** extern void be_tls_close(Port *port);
--- 212,222 ----
  extern ssize_t be_tls_read(Port *port, void *ptr, size_t len, int *waitfor);
  extern ssize_t be_tls_write(Port *port, void *ptr, size_t len, int *waitfor);
  
+ extern int be_tls_get_cipher_bits(Port *port);
+ extern bool be_tls_get_compression(Port *port);
+ extern void be_tls_get_version(Port *port, char *ptr, size_t len);
+ extern void be_tls_get_cipher(Port *port, char *ptr, size_t len);
+ extern void be_tls_get_peerdn_name(Port *port, char *ptr, size_t len);
  #endif
  
  extern ProtocolVersion FrontendProtocol;
*** a/src/include/pgstat.h
--- b/src/include/pgstat.h
***************
*** 701,706 **** typedef enum BackendState
--- 701,723 ----
   */
  
  
+ /*
+  * PgBackendSSLStatus
+  *
+  * For each backend, we keep the SSL status in a separate struct, that
+  * is only filled in if SSL is enabled.
+  */
+ typedef struct PgBackendSSLStatus
+ {
+         /* Information about SSL connection */
+         int             ssl_bits;
+         bool            ssl_compression;
+         char            ssl_version[NAMEDATALEN];  /* MUST be null-terminated */
+         char            ssl_cipher[NAMEDATALEN];   /* MUST be null-terminated */
+         char            ssl_clientdn[NAMEDATALEN]; /* MUST be null-terminated */
+ } PgBackendSSLStatus;
+ 
+ 
  /* ----------
   * PgBackendStatus
   *
***************
*** 744,749 **** typedef struct PgBackendStatus
--- 761,769 ----
  	SockAddr	st_clientaddr;
  	char	   *st_clienthostname;		/* MUST be null-terminated */
  
+ 	/* Information about SSL connection */
+ 	PgBackendSSLStatus *st_ssl;
+ 
  	/* Is backend currently waiting on an lmgr lock? */
  	bool		st_waiting;
  
*** a/src/test/regress/expected/rules.out
--- b/src/test/regress/expected/rules.out
***************
*** 1743,1748 **** pg_stat_replication| SELECT s.pid,
--- 1743,1756 ----
      pg_authid u,
      pg_stat_get_wal_senders() w(pid, state, sent_location, write_location, flush_location, replay_location, sync_priority, sync_state)
    WHERE ((s.usesysid = u.oid) AND (s.pid = w.pid));
+ pg_stat_ssl| SELECT i.pid,
+     i.ssl,
+     i.bits,
+     i.compression,
+     i.version,
+     i.cipher,
+     i.clientdn
+    FROM pg_stat_get_sslstatus() i(pid, ssl, bits, compression, version, cipher, clientdn);
  pg_stat_sys_indexes| SELECT pg_stat_all_indexes.relid,
      pg_stat_all_indexes.indexrelid,
      pg_stat_all_indexes.schemaname,
#22Andres Freund
andres@anarazel.de
In reply to: Magnus Hagander (#21)
Re: SSL information view

Hi,

On 2015-04-09 13:31:55 +0200, Magnus Hagander wrote:

+      <row>
+       <entry><structname>pg_stat_ssl</><indexterm><primary>pg_stat_ssl</primary></indexterm></entry>
+       <entry>One row per connection (regular and replication), showing information about
+        SSL used on this connection.
+        See <xref linkend="pg-stat-ssl-view"> for details.
+       </entry>
+      </row>
+ 

I kinda wonder why this even separate from pg_stat_activity, at least
from the POV of the function gathering the result. This way you have to
join between pg_stat_activity and pg_stat_ssl which will mean that the
two don't necessarily correspond to each other.

Greetings,

Andres Freund

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#23Magnus Hagander
magnus@hagander.net
In reply to: Andres Freund (#22)
Re: SSL information view

On Thu, Apr 9, 2015 at 3:20 PM, Andres Freund <andres@anarazel.de> wrote:

Hi,

On 2015-04-09 13:31:55 +0200, Magnus Hagander wrote:

+ <row>
+

<entry><structname>pg_stat_ssl</><indexterm><primary>pg_stat_ssl</primary></indexterm></entry>

+ <entry>One row per connection (regular and replication), showing

information about

+        SSL used on this connection.
+        See <xref linkend="pg-stat-ssl-view"> for details.
+       </entry>
+      </row>
+

I kinda wonder why this even separate from pg_stat_activity, at least
from the POV of the function gathering the result. This way you have to
join between pg_stat_activity and pg_stat_ssl which will mean that the
two don't necessarily correspond to each other.

To keep from "cluttering" pg_stat_activity for the majority of users who
are the ones not actually using SSL.

--
Magnus Hagander
Me: http://www.hagander.net/
Work: http://www.redpill-linpro.com/

#24Andres Freund
andres@anarazel.de
In reply to: Magnus Hagander (#23)
Re: SSL information view

On 2015-04-09 15:56:00 +0200, Magnus Hagander wrote:

On Thu, Apr 9, 2015 at 3:20 PM, Andres Freund <andres@anarazel.de> wrote:

Hi,

On 2015-04-09 13:31:55 +0200, Magnus Hagander wrote:

+ <row>
+

<entry><structname>pg_stat_ssl</><indexterm><primary>pg_stat_ssl</primary></indexterm></entry>

+ <entry>One row per connection (regular and replication), showing

information about

+        SSL used on this connection.
+        See <xref linkend="pg-stat-ssl-view"> for details.
+       </entry>
+      </row>
+

I kinda wonder why this even separate from pg_stat_activity, at least
from the POV of the function gathering the result. This way you have to
join between pg_stat_activity and pg_stat_ssl which will mean that the
two don't necessarily correspond to each other.

To keep from "cluttering" pg_stat_activity for the majority of users who
are the ones not actually using SSL.

I'm not sure that's actually a problem. But even if, it seems a bit
better to return the data for both views from one SRF and just define
the views differently. That way there's a way to query without the
danger of matching the wrong rows between pg_stat_activity & stat_ssl
due to pid reuse.

Greetings,

Andres Freund

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#25Magnus Hagander
magnus@hagander.net
In reply to: Andres Freund (#24)
1 attachment(s)
Re: SSL information view

On Thu, Apr 9, 2015 at 5:46 PM, Andres Freund <andres@anarazel.de> wrote:

On 2015-04-09 15:56:00 +0200, Magnus Hagander wrote:

On Thu, Apr 9, 2015 at 3:20 PM, Andres Freund <andres@anarazel.de>

wrote:

Hi,

On 2015-04-09 13:31:55 +0200, Magnus Hagander wrote:

+ <row>
+

<entry><structname>pg_stat_ssl</><indexterm><primary>pg_stat_ssl</primary></indexterm></entry>

+ <entry>One row per connection (regular and replication),

showing

information about

+        SSL used on this connection.
+        See <xref linkend="pg-stat-ssl-view"> for details.
+       </entry>
+      </row>
+

I kinda wonder why this even separate from pg_stat_activity, at least
from the POV of the function gathering the result. This way you have to
join between pg_stat_activity and pg_stat_ssl which will mean that the
two don't necessarily correspond to each other.

To keep from "cluttering" pg_stat_activity for the majority of users who
are the ones not actually using SSL.

I'm not sure that's actually a problem. But even if, it seems a bit
better to return the data for both views from one SRF and just define
the views differently. That way there's a way to query without the
danger of matching the wrong rows between pg_stat_activity & stat_ssl
due to pid reuse.

Ah, now I see your point.

Attached is a patch which does this, at least the way I think you meant.
And also fixes a nasty crash bug in the previous one that for some reason
my testing missed (can't set a pointer to null and then expect to use it
later, no... When it's in shared memory, it survives into a new backend.)

--
Magnus Hagander
Me: http://www.hagander.net/
Work: http://www.redpill-linpro.com/

Attachments:

pg_stat_ssl_v3.patchtext/x-patch; charset=US-ASCII; name=pg_stat_ssl_v3.patchDownload
*** a/doc/src/sgml/monitoring.sgml
--- b/doc/src/sgml/monitoring.sgml
***************
*** 300,305 **** postgres   27093  0.0  0.0  30096  2752 ?        Ss   11:34   0:00 postgres: ser
--- 300,313 ----
        </entry>
       </row>
  
+      <row>
+       <entry><structname>pg_stat_ssl</><indexterm><primary>pg_stat_ssl</primary></indexterm></entry>
+       <entry>One row per connection (regular and replication), showing information about
+        SSL used on this connection.
+        See <xref linkend="pg-stat-ssl-view"> for details.
+       </entry>
+      </row>
+ 
      </tbody>
     </tgroup>
    </table>
***************
*** 825,830 **** postgres   27093  0.0  0.0  30096  2752 ?        Ss   11:34   0:00 postgres: ser
--- 833,907 ----
     listed; no information is available about downstream standby servers.
    </para>
  
+   <table id="pg-stat-ssl-view" xreflabel="pg_stat_ssl">
+    <title><structname>pg_stat_ssl</structname> View</title>
+    <tgroup cols="3">
+     <thead>
+     <row>
+       <entry>Column</entry>
+       <entry>Type</entry>
+       <entry>Description</entry>
+      </row>
+     </thead>
+ 
+    <tbody>
+     <row>
+      <entry><structfield>pid</></entry>
+      <entry><type>integer</></entry>
+      <entry>Process ID of a backend or WAL sender process</entry>
+     </row>
+     <row>
+      <entry><structfield>ssl</></entry>
+      <entry><type>boolean</></entry>
+      <entry>True if SSL is used on this connection</entry>
+     </row>
+     <row>
+      <entry><structfield>version</></entry>
+      <entry><type>text</></entry>
+      <entry>Version of SSL in use, or NULL if SSL is not in use
+       on this connection</entry>
+     </row>
+     <row>
+      <entry><structfield>cipher</></entry>
+      <entry><type>text</></entry>
+      <entry>Name of SSL cipher in use, or NULL if SSL is not in use
+       on this connection</entry>
+     </row>
+     <row>
+      <entry><structfield>bits</></entry>
+      <entry><type>integer</></entry>
+      <entry>Number of bits in the encryption algorithm used, or NULL
+      if SSL is not used on this connection</entry>
+     </row>
+     <row>
+      <entry><structfield>compression</></entry>
+      <entry><type>boolean</></entry>
+      <entry>True if SSL compression is in use, false if not,
+       or NULL if SSL is not in use on this connection</entry>
+     </row>
+     <row>
+      <entry><structfield>clientdn</></entry>
+      <entry><type>text</></entry>
+      <entry>Distinguished Name (DN) field from the client certificate
+       used, or NULL if no client certificate was supplied or if SSL
+       is not in use on this connection. This field is truncated if the
+       DN field is longer than <symbol>NAMEDATALEN</symbol> (64 characters
+       in a standard build)
+      </entry>
+     </row>
+    </tbody>
+    </tgroup>
+   </table>
+ 
+   <para>
+    The <structname>pg_stat_ssl</structname> view will contain one row per
+    backend or WAL sender process, showing statistics about SSL usage on
+    this connection. It can be joined to <structname>pg_stat_activity</structname>
+    or <structname>pg_stat_replication</structname> on the
+    <structfield>pid</structfield> column to get more details about the
+    connection.
+   </para>
+ 
  
    <table id="pg-stat-archiver-view" xreflabel="pg_stat_archiver">
     <title><structname>pg_stat_archiver</structname> View</title>
*** a/src/backend/catalog/system_views.sql
--- b/src/backend/catalog/system_views.sql
***************
*** 646,651 **** CREATE VIEW pg_stat_replication AS
--- 646,662 ----
      WHERE S.usesysid = U.oid AND
              S.pid = W.pid;
  
+ CREATE VIEW pg_stat_ssl AS
+     SELECT
+             S.pid,
+             S.ssl,
+             S.sslversion AS version,
+             S.sslcipher AS cipher,
+             S.sslbits AS bits,
+             S.sslcompression AS compression,
+             S.sslclientdn AS clientdn
+     FROM pg_stat_get_activity(NULL) AS S;
+ 
  CREATE VIEW pg_replication_slots AS
      SELECT
              L.slot_name,
*** a/src/backend/libpq/be-secure-openssl.c
--- b/src/backend/libpq/be-secure-openssl.c
***************
*** 90,95 **** static void info_cb(const SSL *ssl, int type, int args);
--- 90,97 ----
  static void initialize_ecdh(void);
  static const char *SSLerrmessage(void);
  
+ static char *X509_NAME_to_cstring(X509_NAME *name);
+ 
  /* are we in the middle of a renegotiation? */
  static bool in_ssl_renegotiation = false;
  
***************
*** 1040,1042 **** SSLerrmessage(void)
--- 1042,1146 ----
  	snprintf(errbuf, sizeof(errbuf), _("SSL error code %lu"), errcode);
  	return errbuf;
  }
+ 
+ /*
+  * Return information about the SSL connection
+  */
+ int
+ be_tls_get_cipher_bits(Port *port)
+ {
+ 	int bits;
+ 
+ 	if (port->ssl)
+ 	{
+ 		SSL_get_cipher_bits(port->ssl, &bits);
+ 		return bits;
+ 	}
+ 	else
+ 		return 0;
+ }
+ 
+ bool
+ be_tls_get_compression(Port *port)
+ {
+ 	if (port->ssl)
+ 		return (SSL_get_current_compression(port->ssl) != NULL);
+ 	else
+ 		return false;
+ }
+ 
+ void
+ be_tls_get_version(Port *port, char *ptr, size_t len)
+ {
+ 	if (port->ssl)
+ 		strlcpy(ptr, SSL_get_version(port->ssl), len);
+ 	else
+ 		ptr[0] = '\0';
+ }
+ 
+ void
+ be_tls_get_cipher(Port *port, char *ptr, size_t len)
+ {
+ 	if (port->ssl)
+ 		strlcpy(ptr, SSL_get_cipher(port->ssl), len);
+ 	else
+ 		ptr[0] = '\0';
+ }
+ 
+ void
+ be_tls_get_peerdn_name(Port *port, char *ptr, size_t len)
+ {
+ 	if (port->peer)
+ 		strlcpy(ptr, X509_NAME_to_cstring(X509_get_subject_name(port->peer)), len);
+ 	else
+ 		ptr[0] = '\0';
+ }
+ 
+ /*
+  * Convert an X509 subject name to a cstring.
+  *
+  */
+ static char *
+ X509_NAME_to_cstring(X509_NAME *name)
+ {
+ 	BIO		   *membuf = BIO_new(BIO_s_mem());
+ 	int			i,
+ 				nid,
+ 				count = X509_NAME_entry_count(name);
+ 	X509_NAME_ENTRY *e;
+ 	ASN1_STRING *v;
+ 	const char *field_name;
+ 	size_t		size;
+ 	char		nullterm;
+ 	char	   *sp;
+ 	char	   *dp;
+ 	char	   *result;
+ 
+ 	(void) BIO_set_close(membuf, BIO_CLOSE);
+ 	for (i = 0; i < count; i++)
+ 	{
+ 		e = X509_NAME_get_entry(name, i);
+ 		nid = OBJ_obj2nid(X509_NAME_ENTRY_get_object(e));
+ 		v = X509_NAME_ENTRY_get_data(e);
+ 		field_name = OBJ_nid2sn(nid);
+ 		if (!field_name)
+ 			field_name = OBJ_nid2ln(nid);
+ 		BIO_printf(membuf, "/%s=", field_name);
+ 		ASN1_STRING_print_ex(membuf, v,
+ 							 ((ASN1_STRFLGS_RFC2253 & ~ASN1_STRFLGS_ESC_MSB)
+ 							  | ASN1_STRFLGS_UTF8_CONVERT));
+ 	}
+ 
+ 	/* ensure null termination of the BIO's content */
+ 	nullterm = '\0';
+ 	BIO_write(membuf, &nullterm, 1);
+ 	size = BIO_get_mem_data(membuf, &sp);
+ 	dp = pg_any_to_server(sp, size - 1, PG_UTF8);
+ 
+ 	result = pstrdup(dp);
+ 	if (dp != sp)
+ 		pfree(dp);
+ 	BIO_free(membuf);
+ 
+ 	return result;
+ }
*** a/src/backend/postmaster/pgstat.c
--- b/src/backend/postmaster/pgstat.c
***************
*** 2479,2484 **** pgstat_fetch_global(void)
--- 2479,2485 ----
  static PgBackendStatus *BackendStatusArray = NULL;
  static PgBackendStatus *MyBEEntry = NULL;
  static char *BackendClientHostnameBuffer = NULL;
+ static PgBackendSSLStatus *BackendSslStatusBuffer = NULL;
  static char *BackendAppnameBuffer = NULL;
  static char *BackendActivityBuffer = NULL;
  static Size BackendActivityBufferSize = 0;
***************
*** 2563,2568 **** CreateSharedBackendStatus(void)
--- 2564,2589 ----
  		}
  	}
  
+ #ifdef USE_SSL
+ 	/* Create or attach to the shared SSL status buffer */
+ 	size = mul_size(sizeof(PgBackendSSLStatus), MaxBackends);
+ 	BackendSslStatusBuffer = (PgBackendSSLStatus *)
+ 		ShmemInitStruct("Backend SSL Status Buffer", size, &found);
+ 
+ 	if (!found)
+ 	{
+ 		MemSet(BackendSslStatusBuffer, 0, size);
+ 
+ 		/* Initialize st_sslstatus pointers. */
+ 		buffer = (char *) BackendSslStatusBuffer;
+ 		for (i = 0; i < MaxBackends; i++)
+ 		{
+ 			BackendStatusArray[i].st_sslstatus = (PgBackendSSLStatus *)buffer;
+ 			buffer += sizeof(PgBackendSSLStatus);
+ 		}
+ 	}
+ #endif
+ 
  	/* Create or attach to the shared activity buffer */
  	BackendActivityBufferSize = mul_size(pgstat_track_activity_query_size,
  										 MaxBackends);
***************
*** 2672,2677 **** pgstat_bestart(void)
--- 2693,2715 ----
  				NAMEDATALEN);
  	else
  		beentry->st_clienthostname[0] = '\0';
+ #ifdef USE_SSL
+ 	if (MyProcPort && MyProcPort->ssl != NULL)
+ 	{
+ 		beentry->st_ssl = true;
+ 		beentry->st_sslstatus->ssl_bits = be_tls_get_cipher_bits(MyProcPort);
+ 		beentry->st_sslstatus->ssl_compression = be_tls_get_compression(MyProcPort);
+ 		be_tls_get_version(MyProcPort, beentry->st_sslstatus->ssl_version, NAMEDATALEN);
+ 		be_tls_get_cipher(MyProcPort, beentry->st_sslstatus->ssl_cipher, NAMEDATALEN);
+ 		be_tls_get_peerdn_name(MyProcPort, beentry->st_sslstatus->ssl_clientdn, NAMEDATALEN);
+ 	}
+ 	else
+ 	{
+ 		beentry->st_ssl = false;
+ 	}
+ #else
+ 	beentry->st_ssl = false;
+ #endif
  	beentry->st_waiting = false;
  	beentry->st_state = STATE_UNDEFINED;
  	beentry->st_appname[0] = '\0';
***************
*** 2892,2897 **** pgstat_read_current_status(void)
--- 2930,2936 ----
  	volatile PgBackendStatus *beentry;
  	LocalPgBackendStatus *localtable;
  	LocalPgBackendStatus *localentry;
+ 	PgBackendSSLStatus *localsslstatus;
  	char	   *localappname,
  			   *localactivity;
  	int			i;
***************
*** 2908,2913 **** pgstat_read_current_status(void)
--- 2947,2955 ----
  	localappname = (char *)
  		MemoryContextAlloc(pgStatLocalContext,
  						   NAMEDATALEN * MaxBackends);
+ 	localsslstatus = (PgBackendSSLStatus *)
+ 		MemoryContextAlloc(pgStatLocalContext,
+ 						   sizeof(PgBackendSSLStatus) * MaxBackends);
  	localactivity = (char *)
  		MemoryContextAlloc(pgStatLocalContext,
  						   pgstat_track_activity_query_size * MaxBackends);
***************
*** 2944,2949 **** pgstat_read_current_status(void)
--- 2986,2997 ----
  				localentry->backendStatus.st_appname = localappname;
  				strcpy(localactivity, (char *) beentry->st_activity);
  				localentry->backendStatus.st_activity = localactivity;
+ 				localentry->backendStatus.st_ssl = beentry->st_ssl;
+ 				if (beentry->st_ssl)
+ 				{
+ 					memcpy(localsslstatus, beentry->st_sslstatus, sizeof(PgBackendSSLStatus));
+ 					localentry->backendStatus.st_sslstatus = localsslstatus;
+ 				}
  			}
  
  			pgstat_save_changecount_after(beentry, after_changecount);
***************
*** 2966,2971 **** pgstat_read_current_status(void)
--- 3014,3020 ----
  			localentry++;
  			localappname += NAMEDATALEN;
  			localactivity += pgstat_track_activity_query_size;
+ 			localsslstatus += sizeof(PgBackendSSLStatus);
  			localNumBackends++;
  		}
  	}
*** a/src/backend/utils/adt/pgstatfuncs.c
--- b/src/backend/utils/adt/pgstatfuncs.c
***************
*** 538,544 **** pg_stat_get_activity(PG_FUNCTION_ARGS)
  
  		oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
  
! 		tupdesc = CreateTemplateTupleDesc(16, false);
  		TupleDescInitEntry(tupdesc, (AttrNumber) 1, "datid",
  						   OIDOID, -1, 0);
  		TupleDescInitEntry(tupdesc, (AttrNumber) 2, "pid",
--- 538,544 ----
  
  		oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
  
! 		tupdesc = CreateTemplateTupleDesc(22, false);
  		TupleDescInitEntry(tupdesc, (AttrNumber) 1, "datid",
  						   OIDOID, -1, 0);
  		TupleDescInitEntry(tupdesc, (AttrNumber) 2, "pid",
***************
*** 571,576 **** pg_stat_get_activity(PG_FUNCTION_ARGS)
--- 571,588 ----
  						   XIDOID, -1, 0);
  		TupleDescInitEntry(tupdesc, (AttrNumber) 16, "backend_xmin",
  						   XIDOID, -1, 0);
+ 		TupleDescInitEntry(tupdesc, (AttrNumber) 17, "ssl",
+ 						   BOOLOID, -1, 0);
+ 		TupleDescInitEntry(tupdesc, (AttrNumber) 18, "sslversion",
+ 						   TEXTOID, -1, 0);
+ 		TupleDescInitEntry(tupdesc, (AttrNumber) 19, "sslcipher",
+ 						   TEXTOID, -1, 0);
+ 		TupleDescInitEntry(tupdesc, (AttrNumber) 20, "sslbits",
+ 						   INT4OID, -1, 0);
+ 		TupleDescInitEntry(tupdesc, (AttrNumber) 21, "sslcompression",
+ 						   BOOLOID, -1, 0);
+ 		TupleDescInitEntry(tupdesc, (AttrNumber) 22, "sslclientdn",
+ 						   TEXTOID, -1, 0);
  
  		funcctx->tuple_desc = BlessTupleDesc(tupdesc);
  
***************
*** 622,629 **** pg_stat_get_activity(PG_FUNCTION_ARGS)
  	if (funcctx->call_cntr < funcctx->max_calls)
  	{
  		/* for each row */
! 		Datum		values[16];
! 		bool		nulls[16];
  		HeapTuple	tuple;
  		LocalPgBackendStatus *local_beentry;
  		PgBackendStatus *beentry;
--- 634,641 ----
  	if (funcctx->call_cntr < funcctx->max_calls)
  	{
  		/* for each row */
! 		Datum		values[22];
! 		bool		nulls[22];
  		HeapTuple	tuple;
  		LocalPgBackendStatus *local_beentry;
  		PgBackendStatus *beentry;
***************
*** 676,681 **** pg_stat_get_activity(PG_FUNCTION_ARGS)
--- 688,708 ----
  		else
  			nulls[15] = true;
  
+ 		if (beentry->st_ssl)
+ 		{
+ 			values[16] = BoolGetDatum(true); /* ssl */
+ 			values[17] = CStringGetTextDatum(beentry->st_sslstatus->ssl_version);
+ 			values[18] = CStringGetTextDatum(beentry->st_sslstatus->ssl_cipher);
+ 			values[19] = Int32GetDatum(beentry->st_sslstatus->ssl_bits);
+ 			values[20] = BoolGetDatum(beentry->st_sslstatus->ssl_compression);
+ 			values[21] = CStringGetTextDatum(beentry->st_sslstatus->ssl_clientdn);
+ 		}
+ 		else
+ 		{
+ 			values[16] = BoolGetDatum(false); /* ssl */
+ 			nulls[17] = nulls[18] = nulls[19] = nulls[20] = nulls[21] = true;
+ 		}
+ 
  		/* Values only available to role member */
  		if (has_privs_of_role(GetUserId(), beentry->st_userid))
  		{
*** a/src/include/catalog/pg_proc.h
--- b/src/include/catalog/pg_proc.h
***************
*** 2756,2762 **** DATA(insert OID = 3057 ( pg_stat_get_autoanalyze_count PGNSP PGUID 12 1 0 0 0 f
  DESCR("statistics: number of auto analyzes for a table");
  DATA(insert OID = 1936 (  pg_stat_get_backend_idset		PGNSP PGUID 12 1 100 0 0 f f f f t t s 0 0 23 "" _null_ _null_ _null_ _null_ pg_stat_get_backend_idset _null_ _null_ _null_ ));
  DESCR("statistics: currently active backend IDs");
! DATA(insert OID = 2022 (  pg_stat_get_activity			PGNSP PGUID 12 1 100 0 0 f f f f f t s 1 0 2249 "23" "{23,26,23,26,25,25,25,16,1184,1184,1184,1184,869,25,23,28,28}" "{i,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o}" "{pid,datid,pid,usesysid,application_name,state,query,waiting,xact_start,query_start,backend_start,state_change,client_addr,client_hostname,client_port,backend_xid,backend_xmin}" _null_ pg_stat_get_activity _null_ _null_ _null_ ));
  DESCR("statistics: information about currently active backends");
  DATA(insert OID = 3099 (  pg_stat_get_wal_senders	PGNSP PGUID 12 1 10 0 0 f f f f f t s 0 0 2249 "" "{23,25,3220,3220,3220,3220,23,25}" "{o,o,o,o,o,o,o,o}" "{pid,state,sent_location,write_location,flush_location,replay_location,sync_priority,sync_state}" _null_ pg_stat_get_wal_senders _null_ _null_ _null_ ));
  DESCR("statistics: information about currently active replication");
--- 2756,2762 ----
  DESCR("statistics: number of auto analyzes for a table");
  DATA(insert OID = 1936 (  pg_stat_get_backend_idset		PGNSP PGUID 12 1 100 0 0 f f f f t t s 0 0 23 "" _null_ _null_ _null_ _null_ pg_stat_get_backend_idset _null_ _null_ _null_ ));
  DESCR("statistics: currently active backend IDs");
! DATA(insert OID = 2022 (  pg_stat_get_activity			PGNSP PGUID 12 1 100 0 0 f f f f f t s 1 0 2249 "23" "{23,26,23,26,25,25,25,16,1184,1184,1184,1184,869,25,23,28,28,16,25,25,23,16,25}" "{i,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o}" "{pid,datid,pid,usesysid,application_name,state,query,waiting,xact_start,query_start,backend_start,state_change,client_addr,client_hostname,client_port,backend_xid,backend_xmin,ssl,sslversion,sslcipher,sslbits,sslcompression,sslclientdn}" _null_ pg_stat_get_activity _null_ _null_ _null_ ));
  DESCR("statistics: information about currently active backends");
  DATA(insert OID = 3099 (  pg_stat_get_wal_senders	PGNSP PGUID 12 1 10 0 0 f f f f f t s 0 0 2249 "" "{23,25,3220,3220,3220,3220,23,25}" "{o,o,o,o,o,o,o,o}" "{pid,state,sent_location,write_location,flush_location,replay_location,sync_priority,sync_state}" _null_ pg_stat_get_wal_senders _null_ _null_ _null_ ));
  DESCR("statistics: information about currently active replication");
*** a/src/include/libpq/libpq-be.h
--- b/src/include/libpq/libpq-be.h
***************
*** 212,217 **** extern void be_tls_close(Port *port);
--- 212,222 ----
  extern ssize_t be_tls_read(Port *port, void *ptr, size_t len, int *waitfor);
  extern ssize_t be_tls_write(Port *port, void *ptr, size_t len, int *waitfor);
  
+ extern int be_tls_get_cipher_bits(Port *port);
+ extern bool be_tls_get_compression(Port *port);
+ extern void be_tls_get_version(Port *port, char *ptr, size_t len);
+ extern void be_tls_get_cipher(Port *port, char *ptr, size_t len);
+ extern void be_tls_get_peerdn_name(Port *port, char *ptr, size_t len);
  #endif
  
  extern ProtocolVersion FrontendProtocol;
*** a/src/include/pgstat.h
--- b/src/include/pgstat.h
***************
*** 701,706 **** typedef enum BackendState
--- 701,723 ----
   */
  
  
+ /*
+  * PgBackendSSLStatus
+  *
+  * For each backend, we keep the SSL status in a separate struct, that
+  * is only filled in if SSL is enabled.
+  */
+ typedef struct PgBackendSSLStatus
+ {
+         /* Information about SSL connection */
+         int             ssl_bits;
+         bool            ssl_compression;
+         char            ssl_version[NAMEDATALEN];  /* MUST be null-terminated */
+         char            ssl_cipher[NAMEDATALEN];   /* MUST be null-terminated */
+         char            ssl_clientdn[NAMEDATALEN]; /* MUST be null-terminated */
+ } PgBackendSSLStatus;
+ 
+ 
  /* ----------
   * PgBackendStatus
   *
***************
*** 744,749 **** typedef struct PgBackendStatus
--- 761,770 ----
  	SockAddr	st_clientaddr;
  	char	   *st_clienthostname;		/* MUST be null-terminated */
  
+ 	/* Information about SSL connection */
+ 	bool		st_ssl;
+ 	PgBackendSSLStatus *st_sslstatus;
+ 
  	/* Is backend currently waiting on an lmgr lock? */
  	bool		st_waiting;
  
*** a/src/test/regress/expected/rules.out
--- b/src/test/regress/expected/rules.out
***************
*** 1743,1748 **** pg_stat_replication| SELECT s.pid,
--- 1743,1756 ----
      pg_authid u,
      pg_stat_get_wal_senders() w(pid, state, sent_location, write_location, flush_location, replay_location, sync_priority, sync_state)
    WHERE ((s.usesysid = u.oid) AND (s.pid = w.pid));
+ pg_stat_ssl| SELECT i.pid,
+     i.ssl,
+     i.bits,
+     i.compression,
+     i.version,
+     i.cipher,
+     i.clientdn
+    FROM pg_stat_get_sslstatus() i(pid, ssl, bits, compression, version, cipher, clientdn);
  pg_stat_sys_indexes| SELECT pg_stat_all_indexes.relid,
      pg_stat_all_indexes.indexrelid,
      pg_stat_all_indexes.schemaname,
#26Michael Paquier
michael.paquier@gmail.com
In reply to: Magnus Hagander (#25)
Re: SSL information view

On Fri, Apr 10, 2015 at 4:09 AM, Magnus Hagander wrote:

Attached is a patch which does this, at least the way I think you meant. And
also fixes a nasty crash bug in the previous one that for some reason my
testing missed (can't set a pointer to null and then expect to use it later,
no... When it's in shared memory, it survives into a new backend.)

Good to see that you are back on cleaning up your shame list. Here are
some comments.

This patch has an unused variable when compiled without SSL:
pgstat.c:2482:28: warning: unused variable 'BackendSslStatusBuffer'
[-Wunused-variable]
static PgBackendSSLStatus *BackendSslStatusBuffer = NULL;

+       localsslstatus = (PgBackendSSLStatus *)
+               MemoryContextAlloc(pgStatLocalContext,
+
sizeof(PgBackendSSLStatus) * MaxBackends);
I don't think that it is necessary to do this allocation if !USE_SSL.
I would just set it to NULL.

Instead of having both st_ssl and st_sslstatus, I think that you could
set st_sslstatus = NULL if !USE_SSL and put the ssl status flag
showing if ssl is enabled or disabled directly in st_sslstatus. This
will minimize the number of fields related to SSL in PgBackendStatus
to 1. This mat be a matter of coding style though..

+typedef struct PgBackendSSLStatus
+{
+        /* Information about SSL connection */
+        int             ssl_bits;
+        bool            ssl_compression;
+        char            ssl_version[NAMEDATALEN];  /* MUST be
null-terminated */
+        char            ssl_cipher[NAMEDATALEN];   /* MUST be
null-terminated */
+        char            ssl_clientdn[NAMEDATALEN]; /* MUST be
null-terminated */
+} PgBackendSSLStatus;
git diff is showing in red here, spaces should be replaced with a tab.

make check is broken, rules.out complaining since you merged the SSL
fields into pg_stat_get_activity().

(Note that, contrary to what Andres suggested, I would have separated
the fields of SSL into a separate function and then do a join on PID
to not bloat pg_stat_activity for users who do not use SSL,
particularly when the DB is embedded).

Regards,
--
Michael

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#27Magnus Hagander
magnus@hagander.net
In reply to: Michael Paquier (#26)
1 attachment(s)
Re: SSL information view

On Fri, Apr 10, 2015 at 7:55 AM, Michael Paquier <michael.paquier@gmail.com>
wrote:

On Fri, Apr 10, 2015 at 4:09 AM, Magnus Hagander wrote:

Attached is a patch which does this, at least the way I think you meant.

And

also fixes a nasty crash bug in the previous one that for some reason my
testing missed (can't set a pointer to null and then expect to use it

later,

no... When it's in shared memory, it survives into a new backend.)

Good to see that you are back on cleaning up your shame list. Here are
some comments.

:)

This patch has an unused variable when compiled without SSL:

pgstat.c:2482:28: warning: unused variable 'BackendSslStatusBuffer'
[-Wunused-variable]
static PgBackendSSLStatus *BackendSslStatusBuffer = NULL;

Hmm. Yeah, clearly I never build without SSL. Added #ifdef protection.

+       localsslstatus = (PgBackendSSLStatus *)
+               MemoryContextAlloc(pgStatLocalContext,
+
sizeof(PgBackendSSLStatus) * MaxBackends);
I don't think that it is necessary to do this allocation if !USE_SSL.
I would just set it to NULL.

Well, actually, we don't even *need* localsslstatus if we're not building
with USE_SSL. As the st_ssl value will always be false then we're never
going to look at the buffer, so we might as well skip setting it.

Instead of having both st_ssl and st_sslstatus, I think that you could
set st_sslstatus = NULL if !USE_SSL and put the ssl status flag
showing if ssl is enabled or disabled directly in st_sslstatus. This
will minimize the number of fields related to SSL in PgBackendStatus
to 1. This mat be a matter of coding style though..

Yeah, does it actually matter which struct that field is in? I think the
code becomes more clear this way - as we can now just directly test against
the st_ssl value, and not have to do an "if (x->st_ssl status != NULL &&
x->sslstatus->ssl)" (maybe not exactly that way, but you get what I mean).

+typedef struct PgBackendSSLStatus
+{
+        /* Information about SSL connection */
+        int             ssl_bits;
+        bool            ssl_compression;
+        char            ssl_version[NAMEDATALEN];  /* MUST be
null-terminated */
+        char            ssl_cipher[NAMEDATALEN];   /* MUST be
null-terminated */
+        char            ssl_clientdn[NAMEDATALEN]; /* MUST be
null-terminated */
+} PgBackendSSLStatus;
git diff is showing in red here, spaces should be replaced with a tab.

Ugh. Fixed. One too many copy/pastes I think.

make check is broken, rules.out complaining since you merged the SSL

fields into pg_stat_get_activity().

Good point. I updated it once, but not after the latest change.

New version with those things fixed attached.

(Note that, contrary to what Andres suggested, I would have separated

the fields of SSL into a separate function and then do a join on PID
to not bloat pg_stat_activity for users who do not use SSL,
particularly when the DB is embedded).

The latest version *doesn't* bloat pg_stat_activity - it only changes the
resultset of pg_stat_get_activity(), doesn't change the actual view at all.
Or did I get that wrong?

--
Magnus Hagander
Me: http://www.hagander.net/
Work: http://www.redpill-linpro.com/

Attachments:

pg_stat_ssl_v4.patchtext/x-patch; charset=US-ASCII; name=pg_stat_ssl_v4.patchDownload
*** a/doc/src/sgml/monitoring.sgml
--- b/doc/src/sgml/monitoring.sgml
***************
*** 300,305 **** postgres   27093  0.0  0.0  30096  2752 ?        Ss   11:34   0:00 postgres: ser
--- 300,313 ----
        </entry>
       </row>
  
+      <row>
+       <entry><structname>pg_stat_ssl</><indexterm><primary>pg_stat_ssl</primary></indexterm></entry>
+       <entry>One row per connection (regular and replication), showing information about
+        SSL used on this connection.
+        See <xref linkend="pg-stat-ssl-view"> for details.
+       </entry>
+      </row>
+ 
      </tbody>
     </tgroup>
    </table>
***************
*** 825,830 **** postgres   27093  0.0  0.0  30096  2752 ?        Ss   11:34   0:00 postgres: ser
--- 833,907 ----
     listed; no information is available about downstream standby servers.
    </para>
  
+   <table id="pg-stat-ssl-view" xreflabel="pg_stat_ssl">
+    <title><structname>pg_stat_ssl</structname> View</title>
+    <tgroup cols="3">
+     <thead>
+     <row>
+       <entry>Column</entry>
+       <entry>Type</entry>
+       <entry>Description</entry>
+      </row>
+     </thead>
+ 
+    <tbody>
+     <row>
+      <entry><structfield>pid</></entry>
+      <entry><type>integer</></entry>
+      <entry>Process ID of a backend or WAL sender process</entry>
+     </row>
+     <row>
+      <entry><structfield>ssl</></entry>
+      <entry><type>boolean</></entry>
+      <entry>True if SSL is used on this connection</entry>
+     </row>
+     <row>
+      <entry><structfield>version</></entry>
+      <entry><type>text</></entry>
+      <entry>Version of SSL in use, or NULL if SSL is not in use
+       on this connection</entry>
+     </row>
+     <row>
+      <entry><structfield>cipher</></entry>
+      <entry><type>text</></entry>
+      <entry>Name of SSL cipher in use, or NULL if SSL is not in use
+       on this connection</entry>
+     </row>
+     <row>
+      <entry><structfield>bits</></entry>
+      <entry><type>integer</></entry>
+      <entry>Number of bits in the encryption algorithm used, or NULL
+      if SSL is not used on this connection</entry>
+     </row>
+     <row>
+      <entry><structfield>compression</></entry>
+      <entry><type>boolean</></entry>
+      <entry>True if SSL compression is in use, false if not,
+       or NULL if SSL is not in use on this connection</entry>
+     </row>
+     <row>
+      <entry><structfield>clientdn</></entry>
+      <entry><type>text</></entry>
+      <entry>Distinguished Name (DN) field from the client certificate
+       used, or NULL if no client certificate was supplied or if SSL
+       is not in use on this connection. This field is truncated if the
+       DN field is longer than <symbol>NAMEDATALEN</symbol> (64 characters
+       in a standard build)
+      </entry>
+     </row>
+    </tbody>
+    </tgroup>
+   </table>
+ 
+   <para>
+    The <structname>pg_stat_ssl</structname> view will contain one row per
+    backend or WAL sender process, showing statistics about SSL usage on
+    this connection. It can be joined to <structname>pg_stat_activity</structname>
+    or <structname>pg_stat_replication</structname> on the
+    <structfield>pid</structfield> column to get more details about the
+    connection.
+   </para>
+ 
  
    <table id="pg-stat-archiver-view" xreflabel="pg_stat_archiver">
     <title><structname>pg_stat_archiver</structname> View</title>
*** a/src/backend/catalog/system_views.sql
--- b/src/backend/catalog/system_views.sql
***************
*** 646,651 **** CREATE VIEW pg_stat_replication AS
--- 646,662 ----
      WHERE S.usesysid = U.oid AND
              S.pid = W.pid;
  
+ CREATE VIEW pg_stat_ssl AS
+     SELECT
+             S.pid,
+             S.ssl,
+             S.sslversion AS version,
+             S.sslcipher AS cipher,
+             S.sslbits AS bits,
+             S.sslcompression AS compression,
+             S.sslclientdn AS clientdn
+     FROM pg_stat_get_activity(NULL) AS S;
+ 
  CREATE VIEW pg_replication_slots AS
      SELECT
              L.slot_name,
*** a/src/backend/libpq/be-secure-openssl.c
--- b/src/backend/libpq/be-secure-openssl.c
***************
*** 90,95 **** static void info_cb(const SSL *ssl, int type, int args);
--- 90,97 ----
  static void initialize_ecdh(void);
  static const char *SSLerrmessage(void);
  
+ static char *X509_NAME_to_cstring(X509_NAME *name);
+ 
  /* are we in the middle of a renegotiation? */
  static bool in_ssl_renegotiation = false;
  
***************
*** 1040,1042 **** SSLerrmessage(void)
--- 1042,1146 ----
  	snprintf(errbuf, sizeof(errbuf), _("SSL error code %lu"), errcode);
  	return errbuf;
  }
+ 
+ /*
+  * Return information about the SSL connection
+  */
+ int
+ be_tls_get_cipher_bits(Port *port)
+ {
+ 	int bits;
+ 
+ 	if (port->ssl)
+ 	{
+ 		SSL_get_cipher_bits(port->ssl, &bits);
+ 		return bits;
+ 	}
+ 	else
+ 		return 0;
+ }
+ 
+ bool
+ be_tls_get_compression(Port *port)
+ {
+ 	if (port->ssl)
+ 		return (SSL_get_current_compression(port->ssl) != NULL);
+ 	else
+ 		return false;
+ }
+ 
+ void
+ be_tls_get_version(Port *port, char *ptr, size_t len)
+ {
+ 	if (port->ssl)
+ 		strlcpy(ptr, SSL_get_version(port->ssl), len);
+ 	else
+ 		ptr[0] = '\0';
+ }
+ 
+ void
+ be_tls_get_cipher(Port *port, char *ptr, size_t len)
+ {
+ 	if (port->ssl)
+ 		strlcpy(ptr, SSL_get_cipher(port->ssl), len);
+ 	else
+ 		ptr[0] = '\0';
+ }
+ 
+ void
+ be_tls_get_peerdn_name(Port *port, char *ptr, size_t len)
+ {
+ 	if (port->peer)
+ 		strlcpy(ptr, X509_NAME_to_cstring(X509_get_subject_name(port->peer)), len);
+ 	else
+ 		ptr[0] = '\0';
+ }
+ 
+ /*
+  * Convert an X509 subject name to a cstring.
+  *
+  */
+ static char *
+ X509_NAME_to_cstring(X509_NAME *name)
+ {
+ 	BIO		   *membuf = BIO_new(BIO_s_mem());
+ 	int			i,
+ 				nid,
+ 				count = X509_NAME_entry_count(name);
+ 	X509_NAME_ENTRY *e;
+ 	ASN1_STRING *v;
+ 	const char *field_name;
+ 	size_t		size;
+ 	char		nullterm;
+ 	char	   *sp;
+ 	char	   *dp;
+ 	char	   *result;
+ 
+ 	(void) BIO_set_close(membuf, BIO_CLOSE);
+ 	for (i = 0; i < count; i++)
+ 	{
+ 		e = X509_NAME_get_entry(name, i);
+ 		nid = OBJ_obj2nid(X509_NAME_ENTRY_get_object(e));
+ 		v = X509_NAME_ENTRY_get_data(e);
+ 		field_name = OBJ_nid2sn(nid);
+ 		if (!field_name)
+ 			field_name = OBJ_nid2ln(nid);
+ 		BIO_printf(membuf, "/%s=", field_name);
+ 		ASN1_STRING_print_ex(membuf, v,
+ 							 ((ASN1_STRFLGS_RFC2253 & ~ASN1_STRFLGS_ESC_MSB)
+ 							  | ASN1_STRFLGS_UTF8_CONVERT));
+ 	}
+ 
+ 	/* ensure null termination of the BIO's content */
+ 	nullterm = '\0';
+ 	BIO_write(membuf, &nullterm, 1);
+ 	size = BIO_get_mem_data(membuf, &sp);
+ 	dp = pg_any_to_server(sp, size - 1, PG_UTF8);
+ 
+ 	result = pstrdup(dp);
+ 	if (dp != sp)
+ 		pfree(dp);
+ 	BIO_free(membuf);
+ 
+ 	return result;
+ }
*** a/src/backend/postmaster/pgstat.c
--- b/src/backend/postmaster/pgstat.c
***************
*** 2482,2487 **** static char *BackendClientHostnameBuffer = NULL;
--- 2482,2490 ----
  static char *BackendAppnameBuffer = NULL;
  static char *BackendActivityBuffer = NULL;
  static Size BackendActivityBufferSize = 0;
+ #ifdef USE_SSL
+ static PgBackendSSLStatus *BackendSslStatusBuffer = NULL;
+ #endif
  
  
  /*
***************
*** 2563,2568 **** CreateSharedBackendStatus(void)
--- 2566,2591 ----
  		}
  	}
  
+ #ifdef USE_SSL
+ 	/* Create or attach to the shared SSL status buffer */
+ 	size = mul_size(sizeof(PgBackendSSLStatus), MaxBackends);
+ 	BackendSslStatusBuffer = (PgBackendSSLStatus *)
+ 		ShmemInitStruct("Backend SSL Status Buffer", size, &found);
+ 
+ 	if (!found)
+ 	{
+ 		MemSet(BackendSslStatusBuffer, 0, size);
+ 
+ 		/* Initialize st_sslstatus pointers. */
+ 		buffer = (char *) BackendSslStatusBuffer;
+ 		for (i = 0; i < MaxBackends; i++)
+ 		{
+ 			BackendStatusArray[i].st_sslstatus = (PgBackendSSLStatus *)buffer;
+ 			buffer += sizeof(PgBackendSSLStatus);
+ 		}
+ 	}
+ #endif
+ 
  	/* Create or attach to the shared activity buffer */
  	BackendActivityBufferSize = mul_size(pgstat_track_activity_query_size,
  										 MaxBackends);
***************
*** 2672,2677 **** pgstat_bestart(void)
--- 2695,2717 ----
  				NAMEDATALEN);
  	else
  		beentry->st_clienthostname[0] = '\0';
+ #ifdef USE_SSL
+ 	if (MyProcPort && MyProcPort->ssl != NULL)
+ 	{
+ 		beentry->st_ssl = true;
+ 		beentry->st_sslstatus->ssl_bits = be_tls_get_cipher_bits(MyProcPort);
+ 		beentry->st_sslstatus->ssl_compression = be_tls_get_compression(MyProcPort);
+ 		be_tls_get_version(MyProcPort, beentry->st_sslstatus->ssl_version, NAMEDATALEN);
+ 		be_tls_get_cipher(MyProcPort, beentry->st_sslstatus->ssl_cipher, NAMEDATALEN);
+ 		be_tls_get_peerdn_name(MyProcPort, beentry->st_sslstatus->ssl_clientdn, NAMEDATALEN);
+ 	}
+ 	else
+ 	{
+ 		beentry->st_ssl = false;
+ 	}
+ #else
+ 	beentry->st_ssl = false;
+ #endif
  	beentry->st_waiting = false;
  	beentry->st_state = STATE_UNDEFINED;
  	beentry->st_appname[0] = '\0';
***************
*** 2892,2897 **** pgstat_read_current_status(void)
--- 2932,2940 ----
  	volatile PgBackendStatus *beentry;
  	LocalPgBackendStatus *localtable;
  	LocalPgBackendStatus *localentry;
+ #ifdef USE_SSL
+ 	PgBackendSSLStatus *localsslstatus;
+ #endif
  	char	   *localappname,
  			   *localactivity;
  	int			i;
***************
*** 2908,2913 **** pgstat_read_current_status(void)
--- 2951,2962 ----
  	localappname = (char *)
  		MemoryContextAlloc(pgStatLocalContext,
  						   NAMEDATALEN * MaxBackends);
+ #ifdef USE_SSL
+ 	localsslstatus = (PgBackendSSLStatus *)
+ 		MemoryContextAlloc(pgStatLocalContext,
+ 						   sizeof(PgBackendSSLStatus) * MaxBackends);
+ #endif
+ 
  	localactivity = (char *)
  		MemoryContextAlloc(pgStatLocalContext,
  						   pgstat_track_activity_query_size * MaxBackends);
***************
*** 2944,2949 **** pgstat_read_current_status(void)
--- 2993,3006 ----
  				localentry->backendStatus.st_appname = localappname;
  				strcpy(localactivity, (char *) beentry->st_activity);
  				localentry->backendStatus.st_activity = localactivity;
+ 				localentry->backendStatus.st_ssl = beentry->st_ssl;
+ #ifdef USE_SSL
+ 				if (beentry->st_ssl)
+ 				{
+ 					memcpy(localsslstatus, beentry->st_sslstatus, sizeof(PgBackendSSLStatus));
+ 					localentry->backendStatus.st_sslstatus = localsslstatus;
+ 				}
+ #endif
  			}
  
  			pgstat_save_changecount_after(beentry, after_changecount);
***************
*** 2966,2971 **** pgstat_read_current_status(void)
--- 3023,3031 ----
  			localentry++;
  			localappname += NAMEDATALEN;
  			localactivity += pgstat_track_activity_query_size;
+ #ifdef USE_SSL
+ 			localsslstatus += sizeof(PgBackendSSLStatus);
+ #endif
  			localNumBackends++;
  		}
  	}
*** a/src/backend/utils/adt/pgstatfuncs.c
--- b/src/backend/utils/adt/pgstatfuncs.c
***************
*** 538,544 **** pg_stat_get_activity(PG_FUNCTION_ARGS)
  
  		oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
  
! 		tupdesc = CreateTemplateTupleDesc(16, false);
  		TupleDescInitEntry(tupdesc, (AttrNumber) 1, "datid",
  						   OIDOID, -1, 0);
  		TupleDescInitEntry(tupdesc, (AttrNumber) 2, "pid",
--- 538,544 ----
  
  		oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
  
! 		tupdesc = CreateTemplateTupleDesc(22, false);
  		TupleDescInitEntry(tupdesc, (AttrNumber) 1, "datid",
  						   OIDOID, -1, 0);
  		TupleDescInitEntry(tupdesc, (AttrNumber) 2, "pid",
***************
*** 571,576 **** pg_stat_get_activity(PG_FUNCTION_ARGS)
--- 571,588 ----
  						   XIDOID, -1, 0);
  		TupleDescInitEntry(tupdesc, (AttrNumber) 16, "backend_xmin",
  						   XIDOID, -1, 0);
+ 		TupleDescInitEntry(tupdesc, (AttrNumber) 17, "ssl",
+ 						   BOOLOID, -1, 0);
+ 		TupleDescInitEntry(tupdesc, (AttrNumber) 18, "sslversion",
+ 						   TEXTOID, -1, 0);
+ 		TupleDescInitEntry(tupdesc, (AttrNumber) 19, "sslcipher",
+ 						   TEXTOID, -1, 0);
+ 		TupleDescInitEntry(tupdesc, (AttrNumber) 20, "sslbits",
+ 						   INT4OID, -1, 0);
+ 		TupleDescInitEntry(tupdesc, (AttrNumber) 21, "sslcompression",
+ 						   BOOLOID, -1, 0);
+ 		TupleDescInitEntry(tupdesc, (AttrNumber) 22, "sslclientdn",
+ 						   TEXTOID, -1, 0);
  
  		funcctx->tuple_desc = BlessTupleDesc(tupdesc);
  
***************
*** 622,629 **** pg_stat_get_activity(PG_FUNCTION_ARGS)
  	if (funcctx->call_cntr < funcctx->max_calls)
  	{
  		/* for each row */
! 		Datum		values[16];
! 		bool		nulls[16];
  		HeapTuple	tuple;
  		LocalPgBackendStatus *local_beentry;
  		PgBackendStatus *beentry;
--- 634,641 ----
  	if (funcctx->call_cntr < funcctx->max_calls)
  	{
  		/* for each row */
! 		Datum		values[22];
! 		bool		nulls[22];
  		HeapTuple	tuple;
  		LocalPgBackendStatus *local_beentry;
  		PgBackendStatus *beentry;
***************
*** 676,681 **** pg_stat_get_activity(PG_FUNCTION_ARGS)
--- 688,708 ----
  		else
  			nulls[15] = true;
  
+ 		if (beentry->st_ssl)
+ 		{
+ 			values[16] = BoolGetDatum(true); /* ssl */
+ 			values[17] = CStringGetTextDatum(beentry->st_sslstatus->ssl_version);
+ 			values[18] = CStringGetTextDatum(beentry->st_sslstatus->ssl_cipher);
+ 			values[19] = Int32GetDatum(beentry->st_sslstatus->ssl_bits);
+ 			values[20] = BoolGetDatum(beentry->st_sslstatus->ssl_compression);
+ 			values[21] = CStringGetTextDatum(beentry->st_sslstatus->ssl_clientdn);
+ 		}
+ 		else
+ 		{
+ 			values[16] = BoolGetDatum(false); /* ssl */
+ 			nulls[17] = nulls[18] = nulls[19] = nulls[20] = nulls[21] = true;
+ 		}
+ 
  		/* Values only available to role member */
  		if (has_privs_of_role(GetUserId(), beentry->st_userid))
  		{
*** a/src/include/catalog/pg_proc.h
--- b/src/include/catalog/pg_proc.h
***************
*** 2756,2762 **** DATA(insert OID = 3057 ( pg_stat_get_autoanalyze_count PGNSP PGUID 12 1 0 0 0 f
  DESCR("statistics: number of auto analyzes for a table");
  DATA(insert OID = 1936 (  pg_stat_get_backend_idset		PGNSP PGUID 12 1 100 0 0 f f f f t t s 0 0 23 "" _null_ _null_ _null_ _null_ pg_stat_get_backend_idset _null_ _null_ _null_ ));
  DESCR("statistics: currently active backend IDs");
! DATA(insert OID = 2022 (  pg_stat_get_activity			PGNSP PGUID 12 1 100 0 0 f f f f f t s 1 0 2249 "23" "{23,26,23,26,25,25,25,16,1184,1184,1184,1184,869,25,23,28,28}" "{i,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o}" "{pid,datid,pid,usesysid,application_name,state,query,waiting,xact_start,query_start,backend_start,state_change,client_addr,client_hostname,client_port,backend_xid,backend_xmin}" _null_ pg_stat_get_activity _null_ _null_ _null_ ));
  DESCR("statistics: information about currently active backends");
  DATA(insert OID = 3099 (  pg_stat_get_wal_senders	PGNSP PGUID 12 1 10 0 0 f f f f f t s 0 0 2249 "" "{23,25,3220,3220,3220,3220,23,25}" "{o,o,o,o,o,o,o,o}" "{pid,state,sent_location,write_location,flush_location,replay_location,sync_priority,sync_state}" _null_ pg_stat_get_wal_senders _null_ _null_ _null_ ));
  DESCR("statistics: information about currently active replication");
--- 2756,2762 ----
  DESCR("statistics: number of auto analyzes for a table");
  DATA(insert OID = 1936 (  pg_stat_get_backend_idset		PGNSP PGUID 12 1 100 0 0 f f f f t t s 0 0 23 "" _null_ _null_ _null_ _null_ pg_stat_get_backend_idset _null_ _null_ _null_ ));
  DESCR("statistics: currently active backend IDs");
! DATA(insert OID = 2022 (  pg_stat_get_activity			PGNSP PGUID 12 1 100 0 0 f f f f f t s 1 0 2249 "23" "{23,26,23,26,25,25,25,16,1184,1184,1184,1184,869,25,23,28,28,16,25,25,23,16,25}" "{i,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o}" "{pid,datid,pid,usesysid,application_name,state,query,waiting,xact_start,query_start,backend_start,state_change,client_addr,client_hostname,client_port,backend_xid,backend_xmin,ssl,sslversion,sslcipher,sslbits,sslcompression,sslclientdn}" _null_ pg_stat_get_activity _null_ _null_ _null_ ));
  DESCR("statistics: information about currently active backends");
  DATA(insert OID = 3099 (  pg_stat_get_wal_senders	PGNSP PGUID 12 1 10 0 0 f f f f f t s 0 0 2249 "" "{23,25,3220,3220,3220,3220,23,25}" "{o,o,o,o,o,o,o,o}" "{pid,state,sent_location,write_location,flush_location,replay_location,sync_priority,sync_state}" _null_ pg_stat_get_wal_senders _null_ _null_ _null_ ));
  DESCR("statistics: information about currently active replication");
*** a/src/include/libpq/libpq-be.h
--- b/src/include/libpq/libpq-be.h
***************
*** 212,217 **** extern void be_tls_close(Port *port);
--- 212,222 ----
  extern ssize_t be_tls_read(Port *port, void *ptr, size_t len, int *waitfor);
  extern ssize_t be_tls_write(Port *port, void *ptr, size_t len, int *waitfor);
  
+ extern int be_tls_get_cipher_bits(Port *port);
+ extern bool be_tls_get_compression(Port *port);
+ extern void be_tls_get_version(Port *port, char *ptr, size_t len);
+ extern void be_tls_get_cipher(Port *port, char *ptr, size_t len);
+ extern void be_tls_get_peerdn_name(Port *port, char *ptr, size_t len);
  #endif
  
  extern ProtocolVersion FrontendProtocol;
*** a/src/include/pgstat.h
--- b/src/include/pgstat.h
***************
*** 701,706 **** typedef enum BackendState
--- 701,723 ----
   */
  
  
+ /*
+  * PgBackendSSLStatus
+  *
+  * For each backend, we keep the SSL status in a separate struct, that
+  * is only filled in if SSL is enabled.
+  */
+ typedef struct PgBackendSSLStatus
+ {
+         /* Information about SSL connection */
+ 	int		ssl_bits;
+ 	bool	ssl_compression;
+ 	char	ssl_version[NAMEDATALEN];  /* MUST be null-terminated */
+ 	char	ssl_cipher[NAMEDATALEN];   /* MUST be null-terminated */
+ 	char	ssl_clientdn[NAMEDATALEN]; /* MUST be null-terminated */
+ } PgBackendSSLStatus;
+ 
+ 
  /* ----------
   * PgBackendStatus
   *
***************
*** 744,749 **** typedef struct PgBackendStatus
--- 761,770 ----
  	SockAddr	st_clientaddr;
  	char	   *st_clienthostname;		/* MUST be null-terminated */
  
+ 	/* Information about SSL connection */
+ 	bool		st_ssl;
+ 	PgBackendSSLStatus *st_sslstatus;
+ 
  	/* Is backend currently waiting on an lmgr lock? */
  	bool		st_waiting;
  
*** a/src/test/regress/expected/rules.out
--- b/src/test/regress/expected/rules.out
***************
*** 1633,1639 **** pg_stat_activity| SELECT s.datid,
      s.backend_xmin,
      s.query
     FROM pg_database d,
!     pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, waiting, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin),
      pg_authid u
    WHERE ((s.datid = d.oid) AND (s.usesysid = u.oid));
  pg_stat_all_indexes| SELECT c.oid AS relid,
--- 1633,1639 ----
      s.backend_xmin,
      s.query
     FROM pg_database d,
!     pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, waiting, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, ssl, sslversion, sslcipher, sslbits, sslcompression, sslclientdn),
      pg_authid u
    WHERE ((s.datid = d.oid) AND (s.usesysid = u.oid));
  pg_stat_all_indexes| SELECT c.oid AS relid,
***************
*** 1739,1748 **** pg_stat_replication| SELECT s.pid,
      w.replay_location,
      w.sync_priority,
      w.sync_state
!    FROM pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, waiting, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin),
      pg_authid u,
      pg_stat_get_wal_senders() w(pid, state, sent_location, write_location, flush_location, replay_location, sync_priority, sync_state)
    WHERE ((s.usesysid = u.oid) AND (s.pid = w.pid));
  pg_stat_sys_indexes| SELECT pg_stat_all_indexes.relid,
      pg_stat_all_indexes.indexrelid,
      pg_stat_all_indexes.schemaname,
--- 1739,1756 ----
      w.replay_location,
      w.sync_priority,
      w.sync_state
!    FROM pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, waiting, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, ssl, sslversion, sslcipher, sslbits, sslcompression, sslclientdn),
      pg_authid u,
      pg_stat_get_wal_senders() w(pid, state, sent_location, write_location, flush_location, replay_location, sync_priority, sync_state)
    WHERE ((s.usesysid = u.oid) AND (s.pid = w.pid));
+ pg_stat_ssl| SELECT s.pid,
+     s.ssl,
+     s.sslversion AS version,
+     s.sslcipher AS cipher,
+     s.sslbits AS bits,
+     s.sslcompression AS compression,
+     s.sslclientdn AS clientdn
+    FROM pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, waiting, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, ssl, sslversion, sslcipher, sslbits, sslcompression, sslclientdn);
  pg_stat_sys_indexes| SELECT pg_stat_all_indexes.relid,
      pg_stat_all_indexes.indexrelid,
      pg_stat_all_indexes.schemaname,
#28Michael Paquier
michael.paquier@gmail.com
In reply to: Magnus Hagander (#27)
Re: SSL information view

On Fri, Apr 10, 2015 at 6:39 PM, Magnus Hagander wrote:

On Fri, Apr 10, 2015 at 7:55 AM, Michael Paquier wrote:

Instead of having both st_ssl and st_sslstatus, I think that you could
set st_sslstatus = NULL if !USE_SSL and put the ssl status flag
showing if ssl is enabled or disabled directly in st_sslstatus. This
will minimize the number of fields related to SSL in PgBackendStatus
to 1. This mat be a matter of coding style though..

Yeah, does it actually matter which struct that field is in? I think the
code becomes more clear this way - as we can now just directly test against
the st_ssl value, and not have to do an "if (x->st_ssl status != NULL &&
x->sslstatus->ssl)" (maybe not exactly that way, but you get what I mean).

That's purely a matter of taste :) I would have done without two
fields in PgBackendStatus with the more complicated if condition...
But well, it doesn't really matter.

+typedef struct PgBackendSSLStatus
+{
+        /* Information about SSL connection */
+        int             ssl_bits;
+        bool            ssl_compression;
+        char            ssl_version[NAMEDATALEN];  /* MUST be
null-terminated */
+        char            ssl_cipher[NAMEDATALEN];   /* MUST be
null-terminated */
+        char            ssl_clientdn[NAMEDATALEN]; /* MUST be
null-terminated */
+} PgBackendSSLStatus;
git diff is showing in red here, spaces should be replaced with a tab.

Ugh. Fixed. One too many copy/pastes I think.

You forgot one here:
+ /* Information about SSL connection */

make check is broken, rules.out complaining since you merged the SSL
fields into pg_stat_get_activity().

Good point. I updated it once, but not after the latest change.

New version with those things fixed attached.

(Note that, contrary to what Andres suggested, I would have separated
the fields of SSL into a separate function and then do a join on PID
to not bloat pg_stat_activity for users who do not use SSL,
particularly when the DB is embedded).

The latest version *doesn't* bloat pg_stat_activity - it only changes the
resultset of pg_stat_get_activity(), doesn't change the actual view at all.
Or did I get that wrong?

My words were visibly incorrect: any callers of pg_stat_get_activity()
would get a bunch of NULL fields for a server built without SSL.

Except for those style comments (feel free to ignore them), I tested
the patch and it is doing what it claims. As I don't have more
comments, let's switch that to "Ready for Committer" then...
Regards,
--
Michael

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#29Magnus Hagander
magnus@hagander.net
In reply to: Michael Paquier (#28)
Re: SSL information view

On Fri, Apr 10, 2015 at 2:14 PM, Michael Paquier <michael.paquier@gmail.com>
wrote:

On Fri, Apr 10, 2015 at 6:39 PM, Magnus Hagander wrote:

+typedef struct PgBackendSSLStatus
+{
+        /* Information about SSL connection */
+        int             ssl_bits;
+        bool            ssl_compression;
+        char            ssl_version[NAMEDATALEN];  /* MUST be
null-terminated */
+        char            ssl_cipher[NAMEDATALEN];   /* MUST be
null-terminated */
+        char            ssl_clientdn[NAMEDATALEN]; /* MUST be
null-terminated */
+} PgBackendSSLStatus;
git diff is showing in red here, spaces should be replaced with a tab.

Ugh. Fixed. One too many copy/pastes I think.

You forgot one here:
+ /* Information about SSL connection */

In other news, I have now fixed my git to show these things to be again. It
used to do that, but I broke it :)

Thanks!

Except for those style comments (feel free to ignore them), I tested

the patch and it is doing what it claims. As I don't have more
comments, let's switch that to "Ready for Committer" then...

Ok. Thanks - and patch applied!

--
Magnus Hagander
Me: http://www.hagander.net/
Work: http://www.redpill-linpro.com/