From 91fa7b4719ac583420d9143132ba4ccddefbc5b2 Mon Sep 17 00:00:00 2001 From: Heikki Linnakangas Date: Tue, 3 Feb 2015 19:57:52 +0200 Subject: [PATCH] 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. PQgetssl() still works, but it should be considered as deprecated as it only works with OpenSSL. In particular, PQgetSslInUse() should be used to check if a connection uses SSL, because as soon as we have another implementation, PQgetssl() will return NULL even if SSL is in use. --- doc/src/sgml/libpq.sgml | 181 +++++++++++++++++++---- 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, 264 insertions(+), 50 deletions(-) diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml index 75a4d518f2..39aede4b7b 100644 --- a/doc/src/sgml/libpq.sgml +++ b/doc/src/sgml/libpq.sgml @@ -1848,6 +1848,152 @@ int PQconnectionUsedPassword(const PGconn *conn); + + + + + The following functions return information related to SSL. This information + usually doesn't change after a connection is established. + + + + PQsslInUsePQsslInUse + + + Returns true (1) if the connection uses SSL, false (0) if not. + + +int PQsslInUse(const PGconn *conn); + + + + + + + + PQsslAttributePQsslAttribute + + + Returns SSL-related information about the connection. + + +const char *PQsslAttribute(const PGconn *conn, const char *attribute_name); + + + + + 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 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", + and it returns a pointer to the OpenSSL SSL struct. + To use this function, code along the following lines could be used: + +#include + +... + + SSL *ssl; + + dbconn = PQconnectdb(...); + ... + + ssl = PQsslStruct(dbconn, "OpenSSL"); + 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 @@ -1863,35 +2009,12 @@ void *PQgetssl(const PGconn *conn); - 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 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: - -#include - -... - - SSL *ssl; - - dbconn = PQconnectdb(...); - ... - - ssl = PQgetssl(dbconn); - if (ssl) - { - /* use OpenSSL functions to access ssl */ - } -]]> + This function is equivalent to PQsslStruct(conn, "OpenSSL"). It should + not be used in new applications, because the returned struct is + specific to OpenSSL and will not be available if another SSL + implementation is used. To check if a connection uses SSL, call + PQsslInUse instead, and for more details about the + connection, use PQsslAttribute. diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c index 4ac21f20bf..7c9f28dee0 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 93da50df31..4a21bf1d2c 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 8cdf53ec7f..a32af343a5 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") == 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 3b79c6bbe7..b43f9fe155 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 info 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 c402119fd4..a73eae2087 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);