>From 0e0f2ceb86cf99611c00e57efdbc84615347542e Mon Sep 17 00:00:00 2001
From: Heikki Linnakangas <heikki.linnakangas@iki.fi>
Date: Thu, 27 Nov 2014 20:00:36 +0200
Subject: [PATCH 1/1] Add API functions to libpq to interrogate SSL related
 stuff.

This makes it possible to query for things like the SSL version and cipher
used, without depending on OpenSSL functions or macros. That is a good
thing if we ever get another SSL implementation.
---
 doc/src/sgml/libpq.sgml                  | 154 +++++++++++++++++++++++++++----
 src/bin/psql/command.c                   |  35 +++----
 src/interfaces/libpq/exports.txt         |   4 +
 src/interfaces/libpq/fe-secure-openssl.c |  68 ++++++++++++++
 src/interfaces/libpq/fe-secure.c         |  20 ++++
 src/interfaces/libpq/libpq-fe.h          |   6 ++
 6 files changed, 250 insertions(+), 37 deletions(-)

diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index de272c5..b96686a 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -1848,33 +1848,130 @@ int PQconnectionUsedPassword(const PGconn *conn);
       </para>
      </listitem>
     </varlistentry>
+   </variablelist>
+  </para>
 
-    <varlistentry id="libpq-pqgetssl">
-     <term><function>PQgetssl</function><indexterm><primary>PQgetssl</></></term>
+  <para>
+    The following functions return information related to SSL. This information
+    usually doesn't change after a connection is established.
+
+    <variablelist>
+    <varlistentry id="libpq-pqsslinuse">
+     <term><function>PQsslInUse</function><indexterm><primary>PQsslInUse</></></term>
      <listitem>
       <para>
-       <indexterm><primary>SSL</><secondary sortas="libpq">in libpq</secondary></indexterm>
-       Returns the SSL structure used in the connection, or null
-       if SSL is not in use.
+        Returns true (1) if the connection uses SSL, false (0) if not.
 
 <synopsis>
-void *PQgetssl(const PGconn *conn);
+int PQsslInUse(const PGconn *conn);
+</synopsis>
+      </para>
+
+     </listitem>
+    </varlistentry>
+
+    <varlistentry id="libpq-pqsslAttribute">
+     <term><function>PQsslAttribute</function><indexterm><primary>PQsslAttribute</></></term>
+     <listitem>
+      <para>
+        Returns SSL-related information about the connection.
+
+<synopsis>
+const char *PQsslAttribute(const PGconn *conn, const char *attribute_name);
 </synopsis>
       </para>
 
       <para>
-       This structure can be used to verify encryption levels, check server
-       certificates, and more. Refer to the <productname>OpenSSL</>
-       documentation for information about this structure.
+       The list of available attributes varies depending on the SSL library
+       being used, and the type of connection. If an attribute is not
+       available, returns NULL.
       </para>
 
       <para>
-       The actual return value is of type <type>SSL *</type>,
-       where <type>SSL</type> is a type defined by
-       the <productname>OpenSSL</productname> library, but it is not declared
-       this way to avoid requiring the <productname>OpenSSL</productname>
-       header files.  To use this function, code along the following lines
-       could be used:
+       The following attributes are commonly available:
+       <variablelist>
+        <varlistentry>
+         <term><literal>library</literal></term>
+          <listitem>
+           <para>
+            Name of the SSL implementation in use. (Currently, only
+            <literal>"OpenSSL"</literal> is implemented)
+           </para>
+          </listitem>
+         </varlistentry>
+        <varlistentry>
+         <term><literal>protocol</literal></term>
+          <listitem>
+           <para>
+             SSL/TLS version in use. Common values are "SSLv2", "SSLv3", 
+             "TLSv1", "TLSv1.1" and "TLSv1.2", but an implementation may
+             return other strings if some other protocol is used.
+           </para>
+          </listitem>
+         </varlistentry>
+        <varlistentry>
+         <term><literal>key_bits</literal></term>
+          <listitem>
+           <para>
+            Number of key bits used by the encryption algorithm.
+           </para>
+          </listitem>
+         </varlistentry>
+        <varlistentry>
+         <term><literal>cipher</literal></term>
+          <listitem>
+           <para>
+            A short name of the ciphersuite used, e.g. 
+            <literal>"DHE-RSA-DES-CBC3-SHA"</literal>. The names are specific
+            to each SSL implementation.
+           </para>
+          </listitem>
+         </varlistentry>
+        <varlistentry>
+         <term><literal>compression</literal></term>
+          <listitem>
+           <para>
+            If SSL compression is in use, returns the name of the compression
+            algorithm, or "on" if compression is used but the algorithm is
+            not known. If compression is not in use, returns "off".
+           </para>
+          </listitem>
+         </varlistentry>
+       </variablelist>
+      </para>
+     </listitem>
+    </varlistentry>
+
+    <varlistentry id="libpq-pqsslattributes">
+     <term><function>PQsslAttributes</function><indexterm><primary>PQsslAttributes</></></term>
+     <listitem>
+      <para>
+       Return an array of SSL attribute names available. The array is terminated by a NULL pointer.
+<synopsis>
+const char **PQsslAttributes(const PGconn *conn);
+</synopsis>
+      </para>
+     </listitem>
+    </varlistentry>
+
+    <varlistentry id="libpq-pqsslstruct">
+     <term><function>PQsslStruct</function><indexterm><primary>PQsslStruct</></></term>
+     <listitem>
+      <para>
+       Return a pointer to an SSL-implementation specific object describing
+       the connection.
+<synopsis>
+void *PQsslStruct(const PGconn *conn, const char *struct_name);
+</synopsis>
+      </para>
+      <para>
+       The structs available depends on the SSL implementation in use.
+      </para>
+
+      <para>
+       For OpenSSL, there is one struct, under the name "OpenSSL SSL",
+       and it returns a pointer to the OpenSSL <literal>SSL</literal> struct.
+       To use this function, code along the following lines could be used:
 <programlisting><![CDATA[
 #include <libpq-fe.h>
 #include <openssl/ssl.h>
@@ -1886,13 +1983,38 @@ void *PQgetssl(const PGconn *conn);
     dbconn = PQconnectdb(...);
     ...
 
-    ssl = PQgetssl(dbconn);
+    ssl = PQsslStruct(dbconn, "OpenSSL SSL");
     if (ssl)
     {
         /* use OpenSSL functions to access ssl */
     }
 ]]></programlisting>
       </para>
+      <para>
+       This structure can be used to verify encryption levels, check server
+       certificates, and more. Refer to the <productname>OpenSSL</>
+       documentation for information about this structure.
+      </para>
+     </listitem>
+    </varlistentry>
+
+    <varlistentry id="libpq-pqgetssl">
+     <term><function>PQgetssl</function><indexterm><primary>PQgetssl</></></term>
+     <listitem>
+      <para>
+       <indexterm><primary>SSL</><secondary sortas="libpq">in libpq</secondary></indexterm>
+       Returns the SSL structure used in the connection, or null
+       if SSL is not in use.
+
+<synopsis>
+void *PQgetssl(const PGconn *conn);
+</synopsis>
+      </para>
+
+      <para>
+       This function is equivalent to PQsslStruct(conn, "OpenSSL SSL"). It should
+       not be used in new code.
+      </para>
      </listitem>
     </varlistentry>
 
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 4ac21f2..7c9f28d 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -30,9 +30,6 @@
 #include <sys/types.h>			/* for umask() */
 #include <sys/stat.h>			/* for stat() */
 #endif
-#ifdef USE_OPENSSL
-#include <openssl/ssl.h>
-#endif
 
 #include "portability/instr_time.h"
 
@@ -1815,28 +1812,24 @@ connection_warnings(bool in_startup)
 static void
 printSSLInfo(void)
 {
-#ifdef USE_OPENSSL
-	int			sslbits = -1;
-	SSL		   *ssl;
+	const char *protocol;
+	const char *cipher;
+	const char *bits;
+	const char *compression;
 
-	ssl = PQgetssl(pset.db);
-	if (!ssl)
+	if (!PQsslInUse(pset.db))
 		return;					/* no SSL */
 
-	SSL_get_cipher_bits(ssl, &sslbits);
-	printf(_("SSL connection (protocol: %s, cipher: %s, bits: %d, compression: %s)\n"),
-		   SSL_get_version(ssl), SSL_get_cipher(ssl), sslbits,
-		   SSL_get_current_compression(ssl) ? _("on") : _("off"));
-#else
+	protocol = PQsslAttribute(pset.db, "protocol");
+	cipher = PQsslAttribute(pset.db, "cipher");
+	bits = PQsslAttribute(pset.db, "key_bits");
+	compression = PQsslAttribute(pset.db, "compression");
 
-	/*
-	 * If psql is compiled without SSL but is using a libpq with SSL, we
-	 * cannot figure out the specifics about the connection. But we know it's
-	 * SSL secured.
-	 */
-	if (PQgetssl(pset.db))
-		printf(_("SSL connection (unknown cipher)\n"));
-#endif
+	printf(_("SSL connection (protocol: %s, cipher: %s, bits: %s, compression: %s)\n"),
+		   protocol ? protocol : _("unknown"),
+		   cipher ? cipher : _("unknown"),
+		   bits ? bits : _("unknown"),
+		   (compression && strcmp(compression, "off") != 0) ? _("on") : _("off"));
 }
 
 
diff --git a/src/interfaces/libpq/exports.txt b/src/interfaces/libpq/exports.txt
index 93da50d..4a21bf1 100644
--- a/src/interfaces/libpq/exports.txt
+++ b/src/interfaces/libpq/exports.txt
@@ -165,3 +165,7 @@ lo_lseek64                162
 lo_tell64                 163
 lo_truncate64             164
 PQconninfo                165
+PQsslInUse                166
+PQsslStruct               167
+PQsslAttributes           168
+PQsslAttribute            169
diff --git a/src/interfaces/libpq/fe-secure-openssl.c b/src/interfaces/libpq/fe-secure-openssl.c
index d8b5995..0dee00c 100644
--- a/src/interfaces/libpq/fe-secure-openssl.c
+++ b/src/interfaces/libpq/fe-secure-openssl.c
@@ -1488,6 +1488,18 @@ SSLerrfree(char *buf)
 		free(buf);
 }
 
+/* ------------------------------------------------------------ */
+/*					SSL information functions					*/
+/* ------------------------------------------------------------ */
+
+int
+PQsslInUse(PGconn *conn)
+{
+	if (!conn)
+		return 0;
+	return conn->ssl_in_use;
+}
+
 /*
  *	Return pointer to OpenSSL object.
  */
@@ -1499,6 +1511,62 @@ PQgetssl(PGconn *conn)
 	return conn->ssl;
 }
 
+void *
+PQsslStruct(PGconn *conn, const char *struct_name)
+{
+	if (!conn)
+		return NULL;
+	if (strcmp(struct_name, "OpenSSL SSL") == 0)
+		return conn->ssl;
+	return NULL;
+}
+
+const char **
+PQsslAttributes(PGconn *conn)
+{
+	static const char *result[] = {
+		"library",
+		"key_bits",
+		"cipher",
+		"compression",
+		"protocol",
+		NULL
+	};
+	return result;
+}
+
+const char *
+PQsslAttribute(PGconn *conn, const char *attribute_name)
+{
+	if (!conn)
+		return NULL;
+	if (conn->ssl == NULL)
+		return NULL;
+
+	if (strcmp(attribute_name, "library") == 0)
+		return "OpenSSL";
+
+	if (strcmp(attribute_name, "key_bits") == 0)
+	{
+		static char sslbits_str[10];
+		int		sslbits;
+
+		SSL_get_cipher_bits(conn->ssl, &sslbits);
+		snprintf(sslbits_str, sizeof(sslbits_str), "%d", sslbits);
+		return sslbits_str;
+	}
+
+	if (strcmp(attribute_name, "cipher") == 0)
+		return SSL_get_cipher(conn->ssl);
+
+	if (strcmp(attribute_name, "compression") == 0)
+		return SSL_get_current_compression(conn->ssl) ? "on" : "off";
+
+	if (strcmp(attribute_name, "protocol") == 0)
+		return SSL_get_version(conn->ssl);
+
+	return NULL;		/* unknown attribute */
+}
 
 /*
  * Private substitute BIO: this does the sending and receiving using send() and
diff --git a/src/interfaces/libpq/fe-secure.c b/src/interfaces/libpq/fe-secure.c
index 3b79c6b..1ee9f36 100644
--- a/src/interfaces/libpq/fe-secure.c
+++ b/src/interfaces/libpq/fe-secure.c
@@ -381,12 +381,32 @@ retry_masked:
 	return n;
 }
 
+/* Dummy versions of SSL getter functions, when built without SSL support */
 #ifndef USE_SSL
+
+int
+PQsslInUse(PGconn *conn)
+{
+	return 0;
+}
+
 void *
 PQgetssl(PGconn *conn)
 {
 	return NULL;
 }
+
+void *
+PQsslStruct(PGconn *conn, const char *struct_name)
+{
+	return NULL;
+}
+
+const char *
+PQsslAttribute(PGconn *conn, const char *attribute_name)
+{
+	return NULL;
+}
 #endif   /* USE_SSL */
 
 
diff --git a/src/interfaces/libpq/libpq-fe.h b/src/interfaces/libpq/libpq-fe.h
index c402119..a73eae2 100644
--- a/src/interfaces/libpq/libpq-fe.h
+++ b/src/interfaces/libpq/libpq-fe.h
@@ -318,6 +318,12 @@ extern int	PQconnectionUsedPassword(const PGconn *conn);
 extern int	PQclientEncoding(const PGconn *conn);
 extern int	PQsetClientEncoding(PGconn *conn, const char *encoding);
 
+/* SSL information functions */
+extern int	PQsslInUse(PGconn *conn);
+extern void *PQsslStruct(PGconn *conn, const char *struct_name);
+extern const char *PQsslAttribute(PGconn *conn, const char *attribute_name);
+extern const char **PQsslAttributes(PGconn *conn);
+
 /* Get the OpenSSL structure associated with a connection. Returns NULL for
  * unencrypted connections or if any other TLS library is in use. */
 extern void *PQgetssl(PGconn *conn);
-- 
2.1.4

