Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtp (Exim 4.80) (envelope-from ) id 1YGUKC-0002bR-Tl for pgsql-hackers@arkaria.postgresql.org; Wed, 28 Jan 2015 15:14:37 +0000 Received: from localhost ([127.0.0.1] helo=postgresql.org) by malur.postgresql.org with smtp (Exim 4.80) (envelope-from ) id 1YGUKC-0003le-8X for pgsql-hackers@arkaria.postgresql.org; Wed, 28 Jan 2015 15:14:36 +0000 Received: from makus.postgresql.org ([2001:4800:1501:1::229]) by malur.postgresql.org with esmtps (TLS1.2:DHE_RSA_AES_256_CBC_SHA256:256) (Exim 4.80) (envelope-from ) id 1YGUKA-0003lR-9E for pgsql-hackers@postgresql.org; Wed, 28 Jan 2015 15:14:34 +0000 Received: from smtp-outbound-1.vmware.com ([208.91.2.12]) by makus.postgresql.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:256) (Exim 4.80) (envelope-from ) id 1YGUJx-0004HW-2e for pgsql-hackers@postgresql.org; Wed, 28 Jan 2015 15:14:32 +0000 Received: from sc9-mailhost1.vmware.com (sc9-mailhost1.vmware.com [10.113.161.71]) by smtp-outbound-1.vmware.com (Postfix) with ESMTP id 2EFED28FF9; Wed, 28 Jan 2015 07:14:18 -0800 (PST) Received: from EX13-CAS-004.vmware.com (EX13-CAS-004.vmware.com [10.113.191.54]) by sc9-mailhost1.vmware.com (Postfix) with ESMTP id 2A75E189B8; Wed, 28 Jan 2015 07:14:18 -0800 (PST) Received: from [192.168.1.90] (10.113.160.246) by EX13-MBX-024.vmware.com (10.113.191.44) with Microsoft SMTP Server (TLS) id 15.0.913.22; Wed, 28 Jan 2015 07:14:16 -0800 Message-ID: <54C8FCB0.4050304@vmware.com> Date: Wed, 28 Jan 2015 17:13:52 +0200 From: Heikki Linnakangas Organization: VMware User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:31.0) Gecko/20100101 Icedove/31.3.0 MIME-Version: 1.0 To: Robert Haas , Magnus Hagander CC: Tom Lane , PostgreSQL-development Subject: Re: PQgetssl() and alternative SSL implementations References: <53F1E989.8020609@vmware.com> <10419.1408475397@sss.pgh.pa.us> <53F3C87E.2080401@vmware.com> In-Reply-To: <53F3C87E.2080401@vmware.com> Content-Type: multipart/mixed; boundary="------------090503030902040600010401" X-Originating-IP: [10.113.160.246] X-ClientProxiedBy: EX13-CAS-013.vmware.com (10.113.191.65) To EX13-MBX-024.vmware.com (10.113.191.44) X-Pg-Spam-Score: -6.9 (------) List-Archive: List-Help: List-ID: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: X-Mailing-List: pgsql-hackers Precedence: bulk Sender: pgsql-hackers-owner@postgresql.org --------------090503030902040600010401 Content-Type: text/plain; charset="utf-8"; format=flowed Content-Transfer-Encoding: 7bit On 08/20/2014 12:58 AM, Heikki Linnakangas wrote: > On 08/19/2014 10:31 PM, Robert Haas wrote: >> On Tue, Aug 19, 2014 at 3:16 PM, Magnus Hagander wrote: >>> On Tue, Aug 19, 2014 at 9:09 PM, Tom Lane wrote: >>>> Robert's got a point though: there is always going to be somebody who >>>> wants something we fail to expose. It's better to be able to say "well, >>>> you can do PQgetssl and then munge it for yourself" than to have to say >>>> "sorry, you're screwed". So if we're going to define PQgetssl as >>>> returning NULL when you're not using OpenSSL, I don't see why we >>>> shouldn't expose a similarly-defined PQgetXXX for each other underlying >>>> implementation we support. There will not be that many of 'em, and >>>> I suspect the people with very specific needs will not care about more >>>> than one underlying library anyway. >>>> >>>> This does not say that we shouldn't also try to have some >>>> library-independent functionality for interrogating certificate state >>>> etc. Just that having an escape hatch isn't a bad thing. > > Yeah, wouldn't hurt I guess. > >>> I do agree tha thaving both would be useful. We could have something like >>> int PQgetSSLstruct(void **sslstruct) >> >> I think it's likely smarter to have totally separate functions. >> First, to make it less likely that users will try to use a pointer to >> one type of object as a pointer to some other kind of object. And >> second, because you might, for example, someday have an SSL >> implementation that wants to return two pointers. May as well make >> that kind of thing easy. > > The struct it returns is totally SSL-implementation specific anyway, so > for an implementation that would like to return two structs, you could > well define it to return a struct like: > > struct { > CoolStructA *a; > CoolStructB *b; > } CoolSSLStruct; > > I don't much like adding a separate function for every SSL > implementation, but you've got a point that it would be nice to make it > difficult to call PQgetSSLstruct() and just assume that the returned > struct is e.g an OpenSSL struct, while it's actually something else. > Perhaps: > > int PQgetSSLstruct(void **sslstruct, char *structname) > > You'd call it like PQgetSSLStruct(&mystruct, "openssl"), and it checks > that the argument matches the library actually been used, otherwise it > returns an error. And if you need to return two structs, you'd call it > twice: PQgetSSLStruct(&a, "cool_a") and PQgetSSLStruct(&b, "cool_b"). Here's a patch to implement the above scheme. It adds four functions to libpq, to interrogate the SSL status: int PQsslInUse(const PGconn *conn) Returns true (1) if the connection uses SSL, false (0) if not. const char *PQsslAttribute(const PGconn *conn, const char *attribute_name) Returns a piece of information. The list of attributes depends on the implementation, but there are a few that are expected to be supported by all of them. See docs for details. const char **PQsslAttributes(const PGconn *conn); Return an array of SSL attribute names available. void *PQsslStruct(const PGconn *conn, const char *struct_name) Return a pointer to an SSL-implementation specific object describing the connection. PQsslStruct(conn, "OpenSSL SSL") is equivalent to PQgetssl(conn). I think this is expandable enough, because you can easily add attributes later on, and different implementations can support different attributes. It contains the escape hatch for applications that need to do more, and have intimate knowledge of OpenSSL structs. It's also pretty easy to use. Thoughts? - Heikki --------------090503030902040600010401 Content-Type: text/x-diff; name="0001-Add-API-functions-to-libpq-to-interrogate-SSL-relate.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename*0="0001-Add-API-functions-to-libpq-to-interrogate-SSL-relate.pa"; filename*1="tch" From 0e0f2ceb86cf99611c00e57efdbc84615347542e Mon Sep 17 00:00:00 2001 From: Heikki Linnakangas 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); + + - - PQgetsslPQgetssl + + The following functions return information related to SSL. This information + usually doesn't change after a connection is established. + + + + PQsslInUsePQsslInUse - SSLin libpq - 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. -void *PQgetssl(const PGconn *conn); +int PQsslInUse(const PGconn *conn); + + + + + + + + PQsslAttributePQsslAttribute + + + Returns SSL-related information about the connection. + + +const char *PQsslAttribute(const PGconn *conn, const char *attribute_name); - This structure can be used to verify encryption levels, check server - certificates, and more. Refer to the 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. - The actual return value is of type SSL *, - where SSL is a type defined by - the OpenSSL library, but it is not declared - this way to avoid requiring the OpenSSL - header files. To use this function, code along the following lines - could be used: + The following attributes are commonly available: + + + library + + + Name of the SSL implementation in use. (Currently, only + "OpenSSL" is implemented) + + + + + protocol + + + 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. + + + + + key_bits + + + Number of key bits used by the encryption algorithm. + + + + + cipher + + + A short name of the ciphersuite used, e.g. + "DHE-RSA-DES-CBC3-SHA". The names are specific + to each SSL implementation. + + + + + compression + + + 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". + + + + + + + + + + PQsslAttributesPQsslAttributes + + + Return an array of SSL attribute names available. The array is terminated by a NULL pointer. + +const char **PQsslAttributes(const PGconn *conn); + + + + + + + PQsslStructPQsslStruct + + + Return a pointer to an SSL-implementation specific object describing + the connection. + +void *PQsslStruct(const PGconn *conn, const char *struct_name); + + + + The structs available depends on the SSL implementation in use. + + + + For OpenSSL, there is one struct, under the name "OpenSSL SSL", + and it returns a pointer to the OpenSSL SSL struct. + To use this function, code along the following lines could be used: #include @@ -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 */ } ]]> + + This structure can be used to verify encryption levels, check server + certificates, and more. Refer to the OpenSSL + documentation for information about this structure. + + + + + + PQgetsslPQgetssl + + + SSLin libpq + Returns the SSL structure used in the connection, or null + if SSL is not in use. + + +void *PQgetssl(const PGconn *conn); + + + + + This function is equivalent to PQsslStruct(conn, "OpenSSL SSL"). It should + not be used in new code. + 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 /* for umask() */ #include /* for stat() */ #endif -#ifdef USE_OPENSSL -#include -#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 --------------090503030902040600010401 Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers --------------090503030902040600010401--