From 649755518f9872e9cadd421434396ea999d75229 Mon Sep 17 00:00:00 2001 From: Daniel Gustafsson Date: Tue, 4 Oct 2016 14:41:39 +0200 Subject: [PATCH 1/3] WIP: Add support for macOS Secure Transport SSL library This is a work in progress trying to add full support for Secure Transport to the front- and backend. Currently only the frontend is worked on in an attempt to start small (or rather, somewhere). --- configure.in | 14 + src/Makefile.global.in | 1 + src/include/pg_config.h.in | 3 + src/include/pg_config_manual.h | 2 +- src/interfaces/libpq/Makefile | 5 + src/interfaces/libpq/fe-connect.c | 4 + src/interfaces/libpq/fe-secure-securetransport.c | 1589 ++++++++++++++++++++++ src/interfaces/libpq/libpq-int.h | 17 + 8 files changed, 1634 insertions(+), 1 deletion(-) create mode 100644 src/interfaces/libpq/fe-secure-securetransport.c diff --git a/configure.in b/configure.in index 9850d99..7506bfb 100644 --- a/configure.in +++ b/configure.in @@ -713,6 +713,15 @@ AC_MSG_RESULT([$with_openssl]) AC_SUBST(with_openssl) # +# macOS Secure Transport +# +AC_MSG_CHECKING([whether to build with macOS Secure Transport support]) +PGAC_ARG_BOOL(with, securetransport, no, [build with macOS Secure Transport support], + [AC_DEFINE([USE_SECURETRANSPORT], 1, [Define to build with macOS Secure Transport support. (--with-securetransport)])]) +AC_MSG_RESULT([$with_securetransport]) +AC_SUBST(with_securetransport) + +# # SELinux # AC_MSG_CHECKING([whether to build with SELinux support]) @@ -1283,6 +1292,11 @@ if test "$with_openssl" = yes ; then AC_CHECK_HEADER(openssl/err.h, [], [AC_MSG_ERROR([header file is required for OpenSSL])]) fi +if test "$with_securetransport" = yes ; then + AC_CHECK_HEADER(Security/Security.h, [], [AC_MSG_ERROR([header file is required for Secure Transport])]) + AC_CHECK_HEADER(CoreFoundation/CoreFoundation.h, [], [AC_MSG_ERROR([header file is required for Secure Transport])]) +fi + if test "$with_pam" = yes ; then AC_CHECK_HEADERS(security/pam_appl.h, [], [AC_CHECK_HEADERS(pam/pam_appl.h, [], diff --git a/src/Makefile.global.in b/src/Makefile.global.in index c211a2d..54a4651 100644 --- a/src/Makefile.global.in +++ b/src/Makefile.global.in @@ -183,6 +183,7 @@ with_perl = @with_perl@ with_python = @with_python@ with_tcl = @with_tcl@ with_openssl = @with_openssl@ +with_securetransport = @with_securetransport@ with_selinux = @with_selinux@ with_systemd = @with_systemd@ with_libxml = @with_libxml@ diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in index 7dbfa90..cf4df0d 100644 --- a/src/include/pg_config.h.in +++ b/src/include/pg_config.h.in @@ -842,6 +842,9 @@ /* Define to build with OpenSSL support. (--with-openssl) */ #undef USE_OPENSSL +/* Define to build with Apple Secure Transport support. (--with-securetransport) */ +#undef USE_SECURETRANSPORT + /* Define to 1 to build with PAM support. (--with-pam) */ #undef USE_PAM diff --git a/src/include/pg_config_manual.h b/src/include/pg_config_manual.h index a2b2b61..357ebbb 100644 --- a/src/include/pg_config_manual.h +++ b/src/include/pg_config_manual.h @@ -152,7 +152,7 @@ * implementation. (Currently, only OpenSSL is supported, but we might add * more implementations in the future.) */ -#ifdef USE_OPENSSL +#if defined(USE_OPENSSL) || defined(USE_SECURETRANSPORT) #define USE_SSL #endif diff --git a/src/interfaces/libpq/Makefile b/src/interfaces/libpq/Makefile index b1789eb..b8aed5a 100644 --- a/src/interfaces/libpq/Makefile +++ b/src/interfaces/libpq/Makefile @@ -48,6 +48,11 @@ ifeq ($(with_openssl),yes) OBJS += fe-secure-openssl.o endif +ifeq ($(with_securetransport), yes) +OBJS += fe-secure-securetransport.o +override CFLAGS += -framework Security -framework CoreFoundation -fconstant-cfstrings +endif + ifeq ($(PORTNAME), cygwin) override shlib = cyg$(NAME)$(DLSUFFIX) endif diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c index f3a9e5a..e141786 100644 --- a/src/interfaces/libpq/fe-connect.c +++ b/src/interfaces/libpq/fe-connect.c @@ -254,6 +254,10 @@ static const internalPQconninfoOption PQconninfoOptions[] = { "SSL-Mode", "", 12, /* sizeof("verify-full") == 12 */ offsetof(struct pg_conn, sslmode)}, + /* + * SSL Compression is only available in OpenSSL enabled builds making this + * option a no op when using other SSL libraries. + */ {"sslcompression", "PGSSLCOMPRESSION", "1", NULL, "SSL-Compression", "", 1, offsetof(struct pg_conn, sslcompression)}, diff --git a/src/interfaces/libpq/fe-secure-securetransport.c b/src/interfaces/libpq/fe-secure-securetransport.c new file mode 100644 index 0000000..1b3772e --- /dev/null +++ b/src/interfaces/libpq/fe-secure-securetransport.c @@ -0,0 +1,1589 @@ +/*------------------------------------------------------------------------- + * + * fe-secure-securetransport.c + * Secure Transport support + * + * + * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/interfaces/libpq/fe-secure-securetransport.c + * + * NOTES + * Unlike the OpenSSL support there is no shared state between connections + * so there is no special handling for ENABLE_THREAD_SAFETY. + * + * TODO: + * - CRL support + * - CRL is handled by the Keychain and there is no (obvious) way to + * inject x509 CRL records programatically. Perhaps creating a temp + * keychain for the connection and see if we can manipulate that? + * - Load certificate/key from keychain + * - Currently the certificate/key are expected to be files on disk + * but with a good prefix like keychain:foo we can load directly + * from the keychain the user wants. + * - Supprt pkcs12 certificates + * - Remove use of private API + * - Remove use of deprecated functions + * - pgcrypto + * + *------------------------------------------------------------------------- + */ + +#include "postgres_fe.h" + +#include +#include +#include + +#include "libpq-fe.h" +#include "fe-auth.h" +#include "libpq-int.h" + +#include +#include +#include +#include +#ifdef HAVE_NETINET_TCP_H +#include +#endif +#include + +#include + +#include +#include +#include + +/* + * The number of required key/value pairs in the dictionary used for querying + * the Keychain: Class, Return Reference, MatchLimit, MatchPolicy + */ +#define KEYCHAIN_SEARCH_SIZE 4 + +/* + * Private API call used in the Webkit code for creating an identity from a + * certificate with a key. While stable and used in many open source projects + * it should be replaced with a published API call since private APIs aren't + * subject to the same deprecation rules. Could potentially be replaced by + * using SecIdentityCreateWithCertificate() ? + */ +extern SecIdentityRef SecIdentityCreate(CFAllocatorRef allocator, + SecCertificateRef certificate, + SecKeyRef privateKey); + +static char *SSLerrmessage(OSStatus errcode); +static void SSLerrfree(char *err_buf); +static int SSLsessionstate(PGconn *conn, char *msg, size_t len); +static const char * SSLciphername(SSLCipherSuite cipher); + +static OSStatus SSLSocketRead(SSLConnectionRef conn, void *data, + size_t *len); +static OSStatus SSLSocketWrite(SSLConnectionRef conn, const void *data, + size_t *len); +static OSStatus SSLOpenClient(PGconn *conn); +static OSStatus SSLLoadCertificate(PGconn *conn, CFArrayRef *cert_array, + CFArrayRef *key_array, + CFArrayRef *rootcert_array); + +static OSStatus import_certificate(const char *path, int size, + SecExternalFormat format, char *passphrase, + CFArrayRef *certificate); +static OSStatus import_pem(const char *path, int size, char *passphrase, + CFArrayRef *cert_arr); +static OSStatus import_pkcs12(const char *path, int size, char *passphrase, + CFArrayRef *cert_arr); + +/* ------------------------------------------------------------ */ +/* Public interface */ +/* ------------------------------------------------------------ */ + +/* + * Exported function to allow application to tell us it's already + * initialized Secure Transport and/or libcrypto. + */ +void +pgtls_init_library(bool do_ssl, int do_crypto) +{ +#ifndef __darwin__ + /* + * Secure Transport is only available on Darwin platforms so autoconf + * should protect us from ever reaching here + */ + Assert(false); +#endif +} + +/* + * Begin or continue negotiating a secure session. + */ +PostgresPollingStatusType +pgtls_open_client(PGconn *conn) +{ + OSStatus open_status; + + CFArrayRef certificate; + CFArrayRef key; + CFArrayRef rootcert; + + /* + * If the SSL context hasn't been set up then initiate it, else continue + * with handshake + */ + if (conn->ssl == NULL) + { + conn->ssl_key_bits = 0; + conn->ssl_buffered = 0; + /* + * Create the SSL context using the new API introduced in 10.8 since + * the SSLNewContext() API call was deprecated in 10.9. The standard + * allocator is used since we are client side. + */ + conn->ssl = SSLCreateContext(NULL /* allocator */, + kSSLClientSide, kSSLStreamType); + if (!conn->ssl) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("could not create SSL context\n")); + return PGRES_POLLING_FAILED; + } + /* + * SSLSetProtocolVersionEnabled() is marked as deprecated as of 10.9 + * but the alternative SSLSetSessionConfig() is as of 10.11 not yet + * documented with the kSSLSessionConfig_xxx constants belonging to + * the 10.12 SDK. Rely on the deprecation for now until the dust has + * properly settled around this. + */ + SSLSetProtocolVersionEnabled(conn->ssl, kTLSProtocol12, true); + + open_status = SSLSetConnection(conn->ssl, conn); + if (open_status != noErr) + goto error; + + /* + * Set the low level functions for reading and writing off a socket + */ + open_status = SSLSetIOFuncs(conn->ssl, SSLSocketRead, SSLSocketWrite); + if (open_status != noErr) + goto error; + + /* + * Load client certificate, private key, and trusted CA certs. The + * conn->errorMessage will be populated by the certificate loading + * so we can return without altering it. + */ + if (SSLLoadCertificate(conn, &certificate, &key, &rootcert) != noErr) + { + pgtls_close(conn); + return PGRES_POLLING_FAILED; + } + + conn->st_rootcert = CFRetain(rootcert); + + /* + * If we are asked to verify the peer hostname, set it as a requirement + * on the connection. This must be set before calling SSLHandshake(). + */ + if (strcmp(conn->sslmode, "verify-full") == 0) + { + /* If we are asked to verify a hostname we dont have, error out */ + if (!conn->pghost) + { + pgtls_close(conn); + return PGRES_POLLING_FAILED; + } + + SSLSetPeerDomainName(conn->ssl, conn->pghost, strlen(conn->pghost)); + } + } + + /* + * Perform handshake + */ + open_status = SSLOpenClient(conn); + if (open_status == noErr) + { + conn->ssl_in_use = true; + return PGRES_POLLING_OK; + } + +error: + if (open_status != noErr) + { + char *err_msg = SSLerrmessage(open_status); + if (conn->errorMessage.len > 0) + appendPQExpBuffer(&conn->errorMessage, + libpq_gettext(", ssl error: %s\n"), err_msg); + else + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("could not establish SSL connection: %s\n"), + err_msg); + SSLerrfree(err_msg); + + pgtls_close(conn); + } + + return PGRES_POLLING_FAILED; +} + +/* + * SSLOpenClient + * Validates remote certificate and performs handshake. + * + * If the user has supplied a root certificate we add that to the chain here + * before initiating validation. The caller is responsible for invoking error + * logging in the case of errors returned. + */ +static OSStatus +SSLOpenClient(PGconn *conn) +{ + OSStatus status; + SecTrustRef trust = NULL; + SecTrustResultType trust_eval = 0; + bool trusted = false; + bool only_anchor = true; + + SSLSetSessionOption(conn->ssl, kSSLSessionOptionBreakOnServerAuth, true); + + /* + * TODO: Is there a better way to repeatedly call SSLHandshake until we get + * another response than an errSSLWouldBlock? + */ + do + { + status = SSLHandshake(conn->ssl); + /* busy-wait loop */ + } + while (status == errSSLWouldBlock || status == -1); + + if (status != errSSLServerAuthCompleted) + return status; + + /* + * Get peer server certificate and validate it. SSLCopyPeerTrust() is not + * supposed to return a NULL trust on noErr but have been reported to do + * in the past so add a belts-and-suspenders check + */ + status = SSLCopyPeerTrust(conn->ssl, &trust); + if (status != noErr || trust == NULL) + return status; + + /* + * If we have our own root certificate configured then add it to the chain + * of trust and specify that it should be trusted. + */ + if (conn->st_rootcert) + { + status = SecTrustSetAnchorCertificates(trust, conn->st_rootcert); + if (status != noErr) + return status; + + /* We have a trusted local root cert, trust more than anchor */ + only_anchor = false; + } + + status = SecTrustSetAnchorCertificatesOnly(trust, only_anchor); + if (status != noErr) + return status; + + status = SecTrustEvaluate(trust, &trust_eval); + if (status == errSecSuccess) + { + switch (trust_eval) + { + /* + * If 'Unspecified' then an anchor certificate was reached without + * encountering any explicit user trust. If 'Proceed' then the user + * has chosen to explicitly trust a certificate in the chain by + * clicking "Trust" in the Keychain app. + */ + case kSecTrustResultUnspecified: + case kSecTrustResultProceed: + trusted = true; + break; + + /* + * 'Confirm' indicates that an interactive confirmation from the + * user is requested. This result code was deprecated in 10.9 + * however so treat it as a Deny to avoid having to invoke UI + * elements from the Keychain. + */ + case kSecTrustResultConfirm: + /* + * 'RecoverableTrustFailure' indicates that the certificate was + * rejected but might be trusted with minor changes to the eval + * context (ignoring expired certificate etc). TODO: Opening up to + * changing the eval context here seems dangerous but we can do + * better logging of the error by invoking SecTrustGetTrustResult() + * to get info on exactly what failed; call and extract relevant + * information to the logstring. + */ + case kSecTrustResultRecoverableTrustFailure: + /* + * The below results are all cases where the certificate should be + * rejected without further questioning. + */ + case kSecTrustResultDeny: + case kSecTrustResultFatalTrustFailure: + case kSecTrustResultOtherError: + default: + trusted = false; + break; + } + } + + /* + * TODO: return a better error code than SSLInternalError + */ + if (!trusted) + return errSecInternalError; + + /* + * If we reach here the documentation states we need to run the Handshake + * again after validating the trust */ + return SSLOpenClient(conn); +} + +/* + * Is there unread data waiting in the SSL read buffer? + */ +bool +pgtls_read_pending(PGconn *conn) +{ + OSStatus read_status; + size_t len = 0; + + read_status = SSLGetBufferedReadSize(conn->ssl, &len); + + /* + * Should we get an error back then we assume that subsequent read + * operations will fail as well. + */ + return (read_status == noErr && len > 0); +} + +/* + * pgtls_read + * Read data from a secure connection. + * + * On failure, this function is responsible for putting a suitable message + * into conn->errorMessage. The caller must still inspect errno, but only + * to determine whether to continue/retry after error. + */ +ssize_t +pgtls_read(PGconn *conn, void *ptr, size_t len) +{ + OSStatus read_status; + size_t n = 0; + ssize_t ret = 0; + int read_errno = 0; + char sess_msg[25]; + + /* + * Double-check that we have a connection which is in the correct state + * for reading before attempting to pull any data off the wire. + */ + if (SSLsessionstate(conn, sess_msg, sizeof(sess_msg)) == -1) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("SSL connection is: %s\n"), sess_msg); + read_errno = ECONNRESET; + return -1; + } + + read_status = SSLRead(conn->ssl, ptr, len, &n); + ret = (ssize_t) n; + + switch (read_status) + { + case noErr: + break; + case -1: + case errSSLWouldBlock: + /* If we did perform a read then skip EAGAIN */ + if (n == 0) + read_errno = EINTR; + break; + + /* + * Clean disconnections + */ + case errSSLClosedNoNotify: + /* fall through */ + case errSSLClosedGraceful: + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("SSL connection has been closed unexpectedly\n")); + read_errno = ECONNRESET; + ret = -1; + break; + + default: + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("unrecognized SSL error %d\n"), read_status); + read_errno = ECONNRESET; + ret = -1; + break; + } + + SOCK_ERRNO_SET(read_errno); + return ret; +} + +/* + * Write data to a secure connection. + * + * On failure, this function is responsible for putting a suitable message + * into conn->errorMessage. The caller must still inspect errno, but only + * to determine whether to continue/retry after error. + */ +ssize_t +pgtls_write(PGconn *conn, const void *ptr, size_t len) +{ + OSStatus write_status; + size_t n = 0; + ssize_t ret = 0; + int write_errno = 0; + char sess_msg[25]; + + /* + * Double-check that we have a connection which is in the correct state + * for writing before attempting to push any data on to the wire or the + * local SSL buffer. + */ + if (SSLsessionstate(conn, sess_msg, sizeof(sess_msg)) == -1) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("SSL connection is: %s\n"), sess_msg); + write_errno = ECONNRESET; + return -1; + } + + if (conn->ssl_buffered > 0) + { + write_status = SSLWrite(conn->ssl, NULL, 0, &n); + + if (write_status == noErr) + { + ret = conn->ssl_buffered; + conn->ssl_buffered = 0; + } + else if (write_status == errSSLWouldBlock || write_status == -1) + { + ret = 0; + write_errno = EINTR; + } + else + { + /* TODO: pull error message string, on read too */ + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("unrecognized SSL error: %d\n"), write_status); + ret = -1; + write_errno = ECONNRESET; + } + } + else + { + write_status = SSLWrite(conn->ssl, ptr, len, &n); + ret = n; + + switch (write_status) + { + case noErr: + break; + + case -1: + case errSSLWouldBlock: + conn->ssl_buffered = len; + ret = 0; +#ifdef EAGAIN + write_errno = EAGAIN; +#else + write_errno = EINTR; +#endif + break; + + /* + * Clean disconnections + */ + case errSSLClosedNoNotify: + /* fall through */ + case errSSLClosedGraceful: + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("SSL connection has been closed unexpectedly\n")); + write_errno = ECONNRESET; + ret = -1; + break; + + default: + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("unrecognized SSL error %d\n"), write_status); + write_errno = ECONNRESET; + ret = -1; + break; + } + } + + SOCK_ERRNO_SET(write_errno); + return ret; +} + +/* + * Initialize SSL system, in particular creating the SSL_context object + * that will be shared by all SSL-using connections in this process. + * + * In threadsafe mode, this includes setting up libcrypto callback functions + * to do thread locking. + * + * If the caller has told us (through PQinitOpenSSL) that he's taking care + * of libcrypto, we expect that callbacks are already set, and won't try to + * override it. + * + * The conn parameter is only used to be able to pass back an error + * message - no connection-local setup is made here. + * + * Returns 0 if OK, -1 on failure (with a message in conn->errorMessage). + */ +int +pgtls_init(PGconn *conn) +{ + conn->ssl_buffered = 0; + conn->ssl_in_use = false; + + return 0; +} + +/* + * pgtls_close + * Close SSL connection. + * + * This function must cope with connections in all states of disrepair since + * it will be called from pgtls_open_client to clean up any potentially used + * resources in case it breaks halfway. + */ +void +pgtls_close(PGconn *conn) +{ + if (!conn->ssl) + return; + + CFRelease(conn->st_rootcert); + + SSLClose(conn->ssl); + CFRelease(conn->ssl); + + /* TODO: Release any certificates loaded */ + + conn->ssl = NULL; + conn->ssl_in_use = false; +} + + + +/* + * The amount of read bytes is returned in the len variable + */ +static OSStatus +SSLSocketRead(SSLConnectionRef conn, void *data, size_t *len) +{ + OSStatus status = noErr; + int res; + + res = pqsecure_raw_read((PGconn *) conn, data, *len); + + if (res < 0) + { + /* TODO: Handle more error cases? */ + switch (SOCK_ERRNO) + { + case ENOENT: + status = errSSLClosedGraceful; + break; + +#ifdef EAGAIN + case EAGAIN: +#endif +#if defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN)) + case EWOULDBLOCK: +#endif + case EINTR: + status = errSSLWouldBlock; + break; + } + } + + *len = res; + + return status; +} + +static OSStatus +SSLSocketWrite(SSLConnectionRef conn, const void *data, size_t *len) +{ + OSStatus status = noErr; + int res; + + res = pqsecure_raw_write((PGconn *) conn, data, *len); + + if (res < 0) + { + /* TODO: Handle more error cases? */ + switch (SOCK_ERRNO) + { +#ifdef EAGAIN + case EAGAIN: +#endif +#if defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN)) + case EWOULDBLOCK: +#endif + case EINTR: + status = errSSLWouldBlock; + break; + + default: + break; + } + } + + *len = res; + + return status; +} + +static OSStatus +import_pem(const char *path, int size, char *passphrase, CFArrayRef *cert_arr) +{ + return import_certificate(path, size, kSecFormatPEMSequence, passphrase, cert_arr); +} + +static OSStatus +import_pkcs12(const char *path, int size, char *passphrase, CFArrayRef *cert_arr) +{ + return import_certificate(path, size, kSecFormatPKCS12, passphrase, cert_arr); +} + +/* + * import_certificate_keychain + * + * Queries the local keychain for a certificate with the passed identity. + */ +static OSStatus +import_certificate_keychain(const char *identity, SecIdentityRef *certificate) +{ + OSStatus status = errSecItemNotFound; + CFTypeRef key[KEYCHAIN_SEARCH_SIZE]; + CFTypeRef val[KEYCHAIN_SEARCH_SIZE]; + CFDictionaryRef identity_search; + CFStringRef identity_ref; + + identity_ref = CFStringCreateWithCString(NULL /* allocator */, + identity, kCFStringEncodingUTF8); + key[0] = kSecClass; + val[0] = kSecClassIdentity; + key[1] = kSecReturnRef; + val[1] = kCFBooleanTrue; + key[2] = kSecMatchLimit; + val[2] = kSecMatchLimitOne; + key[3] = kSecMatchPolicy; + val[3] = SecPolicyCreateSSL(false, identity_ref); + + identity_search = CFDictionaryCreate(NULL /* allocator */, + (const void **) key, + (const void **) val, + KEYCHAIN_SEARCH_SIZE, + &kCFCopyStringDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + + status = SecItemCopyMatching(identity_search, (CFTypeRef *) certificate); + + CFRelease(identity_search); + CFRelease(val[3]); + CFRelease(identity_ref); + + return status; +} + +static OSStatus +import_certificate(const char *path, int size, SecExternalFormat format, + char *passphrase, CFArrayRef *certificate) +{ + OSStatus status; + CFDataRef data_ref; + CFStringRef file_type; + SecExternalItemType item_type; + SecItemImportExportKeyParameters params; + + FILE *fp; + UInt8 *certdata; + + Assert(path && strlen(path) > 0); + + fp = fopen(path, "r"); + if (!fp) + return errSecInternalError; + + certdata = malloc(size); + if (!certdata) + { + fclose(fp); + return errSecAllocate; + } + + /* TODO: should we use fopen()/fread() here or other fs abstractions */ + if (fread(certdata, 1, size, fp) != size) + { + fclose(fp); + return errSecInternalError; + } + fclose(fp); + + data_ref = CFDataCreate(NULL /* allocator */, certdata, size); + + memset(¶ms, 0, sizeof(SecItemImportExportKeyParameters)); + params.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION; + /* Set OS default access control on the imported key */ + params.flags = kSecKeyNoAccessControl; + if (passphrase) + params.passphrase = CFStringCreateWithCString(NULL, passphrase, + kCFStringEncodingUTF8); + + /* + * Provide a synthetic file ending for the certificate file to aid the + * parsing of the certificate, the default name of ".crt" isn't terribly + * helpful for figuring out the type + */ + if (format == kSecFormatPKCS12) + file_type = CFSTR(".pkcs12"); + else + file_type = CFSTR(".pem"); + + item_type = kSecItemTypeCertificate; + + status = SecItemImport(data_ref, file_type, &format, &item_type, + 0 /* flags */, ¶ms, NULL /* keychain */, + certificate); + + if (status != errSecSuccess) + return status; + + return status; +} + +/* + * Since failures can come from multiple places, the PGconn errorMessage is + * populated here even for SSL library errors. + */ +static OSStatus +SSLLoadCertificate(PGconn *conn, CFArrayRef *cert_array, CFArrayRef *key_array, + CFArrayRef *rootcert_array) +{ + OSStatus status; + struct stat buf; + char homedir[MAXPGPATH]; + char fnbuf[MAXPGPATH]; + char sebuf[256]; + bool have_homedir; + bool have_cert; + char *ssl_err_msg; + CFMutableArrayRef cert_copy; + SecIdentityRef identity; + SecCertificateRef cert_ref; + SecKeyRef key_ref; + + /* + * We'll need the home directory if any of the relevant parameters are + * defaulted. If pqGetHomeDirectory fails, act as though none of the + * files could be found. + */ + if (!(conn->sslcert && strlen(conn->sslcert) > 0) || + !(conn->sslkey && strlen(conn->sslkey) > 0) || + !(conn->sslrootcert && strlen(conn->sslrootcert) > 0) || + !(conn->sslcrl && strlen(conn->sslcrl) > 0)) + have_homedir = pqGetHomeDirectory(homedir, sizeof(homedir)); + else /* won't need it */ + have_homedir = false; + + /* Read the client certificate file */ + if (conn->sslcert && strlen(conn->sslcert) > 0) + strlcpy(fnbuf, conn->sslcert, sizeof(fnbuf)); + else if (have_homedir) + snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, USER_CERT_FILE); + else + fnbuf[0] = '\0'; + + if (fnbuf[0] == '\0') + { + /* no home directory, proceed without a client cert */ + have_cert = false; + } + else if (stat(fnbuf, &buf) != 0) + { + /* + * If file is not present, just go on without a client cert; server + * might or might not accept the connection. Any other error, + * however, is grounds for complaint. + */ + if (errno != ENOENT && errno != ENOTDIR) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("could not open certificate file \"%s\": %s\n"), + fnbuf, pqStrerror(errno, sebuf, sizeof(sebuf))); + return errSecInternalError; + } + have_cert = false; + } + else + { + status = import_pem(fnbuf, buf.st_size, NULL, cert_array); + if (status != noErr) + { + ssl_err_msg = SSLerrmessage(status); + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("could not load certificate file \"%s\": (%d) %s\n"), + fnbuf, status, ssl_err_msg); + SSLerrfree(ssl_err_msg); + return status; + } + + have_cert = true; + } + + if (have_cert) + { + if (conn->sslkey && strlen(conn->sslkey) > 0) + strlcpy(fnbuf, conn->sslkey, sizeof(fnbuf)); + else if (have_homedir) + snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, USER_KEY_FILE); + else + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("certificate present, but private key file not found\n")); + return errSecInternalError; + } + + if (stat(fnbuf, &buf) != 0) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("certificate present, but not private key file \"%s\"\n"), + fnbuf); + return errSecInternalError; + } + + status = import_pem(fnbuf, buf.st_size, NULL, key_array); + if (status != noErr) + { + ssl_err_msg = SSLerrmessage(status); + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("could not load private key file \"%s\": %s\n"), + fnbuf, ssl_err_msg); + SSLerrfree(ssl_err_msg); + return status; + } + + cert_ref = (SecCertificateRef) CFArrayGetValueAtIndex(*cert_array, 0); + key_ref = (SecKeyRef) CFArrayGetValueAtIndex(*key_array, 0); + + identity = SecIdentityCreate(NULL /* allocator */, cert_ref, key_ref); + + cert_copy = CFArrayCreateMutableCopy(NULL /* allocator */, 0, *cert_array); + CFArraySetValueAtIndex(cert_copy, 0, identity); + + /* FIXME: CFRelease whats not needed */ + + status = SSLSetCertificate(conn->ssl, cert_copy); + + CFRelease(cert_copy); + + if (status != noErr) + { + ssl_err_msg = SSLerrmessage(status); + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("could not set certificate for connection: (%d) %s\n"), + status, ssl_err_msg); + SSLerrfree(ssl_err_msg); + return status; + } + } + + /* Load the root cert */ + if (conn->sslrootcert && strlen(conn->sslrootcert) > 0) + strlcpy(fnbuf, conn->sslrootcert, sizeof(fnbuf)); + else if (have_homedir) + snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, ROOT_CERT_FILE); + else + fnbuf[0] = '\0'; + + if (fnbuf[0] != '\0') + { + if (stat(fnbuf, &buf) != 0) + { + /* + * stat() failed; assume root file doesn't exist. If sslmode is + * verify-ca or verify-full, this is an error. Otherwise, continue + * without performing any server cert verification. + */ + if (conn->sslmode[0] == 'v') /* "verify-ca" or "verify-full" */ + { + /* + * The only way to reach here with an empty filename is if + * pqGetHomeDirectory failed. That's a sufficiently unusual case + * that it seems worth having a specialized error message for it. + */ + if (fnbuf[0] == '\0') + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("could not get home directory to locate root certificate file\n" + "Either provide the file or change sslmode to disable server certificate verification.\n")); + else + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("root certificate file \"%s\" does not exist\n" + "Either provide the file or change sslmode to disable server certificate verification.\n"), fnbuf); + return errSecInternalError; + } + } + else + { + status = import_pem(fnbuf, buf.st_size, NULL, rootcert_array); + if (status != noErr) + { + ssl_err_msg = SSLerrmessage(status); + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("could not load root certificate file \"%s\": %s\n"), + fnbuf, ssl_err_msg); + SSLerrfree(ssl_err_msg); + return status; + } + + /* TODO: Load the CRL */ + } + } + + /* + * Reaching here implies that the certificate and key has been loaded and + * verified, now we can safely set the key size used. + */ + conn->ssl_key_bits = SecKeyGetBlockSize(key_ref); + return noErr; +} + +/* ------------------------------------------------------------ */ +/* SSL information functions */ +/* ------------------------------------------------------------ */ + +int +PQsslInUse(PGconn *conn) +{ + if (!conn) + return 0; + return conn->ssl_in_use; +} + +/* + * Return pointer to the Secure Transport SSL Context object. + */ +void * +PQgetssl(PGconn *conn) +{ + if (!conn) + return NULL; + return conn->ssl; +} + +void * +PQsslStruct(PGconn *conn, const char *struct_name) +{ + if (!conn) + return NULL; + if (strcmp(struct_name, "SecureTransport") == 0) + return conn->ssl; + return NULL; +} + +const char *const * +PQsslAttributeNames(PGconn *conn) +{ + static const char *const result[] = { + "library", + "key_bits", + "cipher", + "protocol", + NULL + }; + + return result; +} + +const char * +PQsslAttribute(PGconn *conn, const char *attribute_name) +{ + SSLCipherSuite cipher; + SSLProtocol protocol; + OSStatus status; + const char *attribute = NULL; + + if (!conn || !conn->ssl) + return NULL; + + if (strcmp(attribute_name, "library") == 0) + attribute = "SecureTransport"; + else if (strcmp(attribute_name, "key_bits") == 0) + { + if (conn->ssl_key_bits > 0) + { + static char sslbits_str[10]; + snprintf(sslbits_str, sizeof(sslbits_str), "%d", conn->ssl_key_bits); + attribute = sslbits_str; + } + } + else if (strcmp(attribute_name, "cipher") == 0) + { + status = SSLGetNegotiatedCipher(conn->ssl, &cipher); + if (status == noErr) + return SSLciphername(cipher); + } + else if (strcmp(attribute_name, "protocol") == 0) + { + status = SSLGetNegotiatedProtocolVersion(conn->ssl, &protocol); + if (status == noErr) + { + switch (protocol) + { + case kTLSProtocol11: + attribute = "TLSv1.1"; + break; + case kTLSProtocol12: + attribute = "TLSv1.2"; + break; + default: + break; + } + } + } + + return attribute; +} + +/* ------------------------------------------------------------ */ +/* Secure Transport Information Functions */ +/* ------------------------------------------------------------ */ + +/* + * Obtain reason string for passed SSL errcode + */ +static char ssl_noerr[] = "no SSL error reported"; +static char ssl_nomem[] = "out of memory allocating error description"; +#define SSL_ERR_LEN 128 + +static char * +SSLerrmessage(OSStatus errcode) +{ + char *err_buf; + const char *tmp; + CFStringRef err_msg; + + if (errcode == noErr || errcode == errSecSuccess) + return ssl_noerr; + + err_buf = malloc(SSL_ERR_LEN); + if (!err_buf) + return ssl_nomem; + + err_msg = SecCopyErrorMessageString(errcode, NULL); + if (err_msg) + { + tmp = CFStringGetCStringPtr(err_msg, kCFStringEncodingUTF8); + strlcpy(err_buf, tmp, SSL_ERR_LEN); + CFRelease(err_msg); + } + else + snprintf(err_buf, sizeof(err_buf), _("SSL error code %d"), errcode); + + return err_buf; +} + +static void +SSLerrfree(char *err_buf) +{ + if (err_buf && err_buf != ssl_nomem && err_buf != ssl_noerr) + free(err_buf); +} + +/* + * SSLsessionstate + * + * Returns 0 if the connection is open and -1 in case the connection is closed + * or its status unknown. If msg is non-NULL the current state is copied with + * at most len characters. + */ +static int +SSLsessionstate(PGconn *conn, char *msg, size_t len) +{ + SSLSessionState state = -1; + OSStatus status = errSecInternalError; + const char *status_msg; + + /* + * If conn->ssl isn't defined we will report "Unknown" which it could be + * argued being correct or not, but since we don't know if there has ever + * been a connection at all it's not more correct to say "Closed" or + * "Aborted". + */ + if (conn->ssl) + status = SSLGetSessionState(conn->ssl, &state); + + switch (state) + { + case kSSLConnected: + status_msg = "Connected"; + status = 0; + break; + case kSSLHandshake: + status_msg = "Handshake"; + status = 0; + break; + case kSSLIdle: + status_msg = "Idle"; + status = 0; + break; + case kSSLClosed: + status_msg = "Closed"; + status = -1; + break; + case kSSLAborted: + status_msg = "Aborted"; + status = -1; + break; + default: + status_msg = "Unknown"; + status = -1; + break; + } + + if (msg) + strlcpy(msg, status_msg, len); + + return (status == noErr ? 0 : -1); +} + +/* + * SSLciphername + * + * Translate an SSLCipherSuite code into a string literal suitable for printing + * in log/informational messages to the user. Since this implementation of the + * Secure Transport lib doesn't support SSLv2/v3 these ciphernames are omitted. + */ +static const char * +SSLciphername(SSLCipherSuite cipher) +{ + switch (cipher) + { + case TLS_NULL_WITH_NULL_NULL: + return "NULL_WITH_NULL_NULL"; + break; + + /* TLS addenda using AES, per RFC 3268 */ + case TLS_RSA_WITH_AES_128_CBC_SHA: + return "RSA_WITH_AES_128_CBC_SHA"; + break; + case TLS_DH_DSS_WITH_AES_128_CBC_SHA: + return "DH_DSS_WITH_AES_128_CBC_SHA"; + break; + case TLS_DH_RSA_WITH_AES_128_CBC_SHA: + return "DH_RSA_WITH_AES_128_CBC_SHA"; + break; + case TLS_DHE_DSS_WITH_AES_128_CBC_SHA: + return "DHE_DSS_WITH_AES_128_CBC_SHA"; + break; + case TLS_DHE_RSA_WITH_AES_128_CBC_SHA: + return "DHE_RSA_WITH_AES_128_CBC_SHA"; + break; + case TLS_DH_anon_WITH_AES_128_CBC_SHA: + return "DH_anon_WITH_AES_128_CBC_SHA"; + break; + case TLS_RSA_WITH_AES_256_CBC_SHA: + return "RSA_WITH_AES_256_CBC_SHA"; + break; + case TLS_DH_DSS_WITH_AES_256_CBC_SHA: + return "DH_DSS_WITH_AES_256_CBC_SHA"; + break; + case TLS_DH_RSA_WITH_AES_256_CBC_SHA: + return "DH_RSA_WITH_AES_256_CBC_SHA"; + break; + case TLS_DHE_DSS_WITH_AES_256_CBC_SHA: + return "DHE_DSS_WITH_AES_256_CBC_SHA"; + break; + case TLS_DHE_RSA_WITH_AES_256_CBC_SHA: + return "DHE_RSA_WITH_AES_256_CBC_SHA"; + break; + case TLS_DH_anon_WITH_AES_256_CBC_SHA: + return "DH_anon_WITH_AES_256_CBC_SHA"; + break; + + /* ECDSA addenda, RFC 4492 */ + case TLS_ECDH_ECDSA_WITH_NULL_SHA: + return "ECDH_ECDSA_WITH_NULL_SHA"; + break; + case TLS_ECDH_ECDSA_WITH_RC4_128_SHA: + return "ECDH_ECDSA_WITH_RC4_128_SHA"; + break; + case TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA: + return "ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA"; + break; + case TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA: + return "ECDH_ECDSA_WITH_AES_128_CBC_SHA"; + break; + case TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA: + return "ECDH_ECDSA_WITH_AES_256_CBC_SHA"; + break; + case TLS_ECDHE_ECDSA_WITH_NULL_SHA: + return "ECDHE_ECDSA_WITH_NULL_SHA"; + break; + case TLS_ECDHE_ECDSA_WITH_RC4_128_SHA: + return "ECDHE_ECDSA_WITH_RC4_128_SHA"; + break; + case TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA: + return "ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA"; + break; + case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA: + return "ECDHE_ECDSA_WITH_AES_128_CBC_SHA"; + break; + case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA: + return "ECDHE_ECDSA_WITH_AES_256_CBC_SHA"; + break; + case TLS_ECDH_RSA_WITH_NULL_SHA: + return "ECDH_RSA_WITH_NULL_SHA"; + break; + case TLS_ECDH_RSA_WITH_RC4_128_SHA: + return "ECDH_RSA_WITH_RC4_128_SHA"; + break; + case TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA: + return "ECDH_RSA_WITH_3DES_EDE_CBC_SHA"; + break; + case TLS_ECDH_RSA_WITH_AES_128_CBC_SHA: + return "ECDH_RSA_WITH_AES_128_CBC_SHA"; + break; + case TLS_ECDH_RSA_WITH_AES_256_CBC_SHA: + return "ECDH_RSA_WITH_AES_256_CBC_SHA"; + break; + case TLS_ECDHE_RSA_WITH_NULL_SHA: + return "ECDHE_RSA_WITH_NULL_SHA"; + break; + case TLS_ECDHE_RSA_WITH_RC4_128_SHA: + return "ECDHE_RSA_WITH_RC4_128_SHA"; + break; + case TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA: + return "ECDHE_RSA_WITH_3DES_EDE_CBC_SHA"; + break; + case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA: + return "ECDHE_RSA_WITH_AES_128_CBC_SHA"; + break; + case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA: + return "ECDHE_RSA_WITH_AES_256_CBC_SHA"; + break; + case TLS_ECDH_anon_WITH_NULL_SHA: + return "ECDH_anon_WITH_NULL_SHA"; + break; + case TLS_ECDH_anon_WITH_RC4_128_SHA: + return "ECDH_anon_WITH_RC4_128_SHA"; + break; + case TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA: + return "ECDH_anon_WITH_3DES_EDE_CBC_SHA"; + break; + case TLS_ECDH_anon_WITH_AES_128_CBC_SHA: + return "ECDH_anon_WITH_AES_128_CBC_SHA"; + break; + case TLS_ECDH_anon_WITH_AES_256_CBC_SHA: + return "ECDH_anon_WITH_AES_256_CBC_SHA"; + break; + + /* Server provided RSA certificate for key exchange. */ + case TLS_RSA_WITH_NULL_MD5: + return "RSA_WITH_NULL_MD5"; + break; + case TLS_RSA_WITH_NULL_SHA: + return "RSA_WITH_NULL_SHA"; + break; + case TLS_RSA_WITH_RC4_128_MD5: + return "RSA_WITH_RC4_128_MD5"; + break; + case TLS_RSA_WITH_RC4_128_SHA: + return "RSA_WITH_RC4_128_SHA"; + break; + case TLS_RSA_WITH_3DES_EDE_CBC_SHA: + return "RSA_WITH_3DES_EDE_CBC_SHA"; + break; + case TLS_RSA_WITH_NULL_SHA256: + return "RSA_WITH_NULL_SHA256"; + break; + case TLS_RSA_WITH_AES_128_CBC_SHA256: + return "RSA_WITH_AES_128_CBC_SHA256"; + break; + case TLS_RSA_WITH_AES_256_CBC_SHA256: + return "RSA_WITH_AES_256_CBC_SHA256"; + break; + + /* + * Server-authenticated (and optionally client-authenticated) + * Diffie-Hellman. + */ + case TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA: + return "DH_DSS_WITH_3DES_EDE_CBC_SHA"; + break; + case TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA: + return "DH_RSA_WITH_3DES_EDE_CBC_SHA"; + break; + case TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA: + return "DHE_DSS_WITH_3DES_EDE_CBC_SHA"; + break; + case TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA: + return "DHE_RSA_WITH_3DES_EDE_CBC_SHA"; + break; + case TLS_DH_DSS_WITH_AES_128_CBC_SHA256: + return "DH_DSS_WITH_AES_128_CBC_SHA256"; + break; + case TLS_DH_RSA_WITH_AES_128_CBC_SHA256: + return "DH_RSA_WITH_AES_128_CBC_SHA256"; + break; + case TLS_DHE_DSS_WITH_AES_128_CBC_SHA256: + return "DHE_DSS_WITH_AES_128_CBC_SHA256"; + break; + case TLS_DHE_RSA_WITH_AES_128_CBC_SHA256: + return "DHE_RSA_WITH_AES_128_CBC_SHA256"; + break; + case TLS_DH_DSS_WITH_AES_256_CBC_SHA256: + return "DH_DSS_WITH_AES_256_CBC_SHA256"; + break; + case TLS_DH_RSA_WITH_AES_256_CBC_SHA256: + return "DH_RSA_WITH_AES_256_CBC_SHA256"; + break; + case TLS_DHE_DSS_WITH_AES_256_CBC_SHA256: + return "DHE_DSS_WITH_AES_256_CBC_SHA256"; + break; + case TLS_DHE_RSA_WITH_AES_256_CBC_SHA256: + return "DHE_RSA_WITH_AES_256_CBC_SHA256"; + break; + + /* Completely anonymous Diffie-Hellman */ + case TLS_DH_anon_WITH_RC4_128_MD5: + return "DH_anon_WITH_RC4_128_MD5"; + break; + case TLS_DH_anon_WITH_3DES_EDE_CBC_SHA: + return "DH_anon_WITH_3DES_EDE_CBC_SHA"; + break; + case TLS_DH_anon_WITH_AES_128_CBC_SHA256: + return "DH_anon_WITH_AES_128_CBC_SHA256"; + break; + case TLS_DH_anon_WITH_AES_256_CBC_SHA256: + return "DH_anon_WITH_AES_256_CBC_SHA256"; + break; + + /* Addendum from RFC 4279, TLS PSK */ + case TLS_PSK_WITH_RC4_128_SHA: + return "PSK_WITH_RC4_128_SHA"; + break; + case TLS_PSK_WITH_3DES_EDE_CBC_SHA: + return "PSK_WITH_3DES_EDE_CBC_SHA"; + break; + case TLS_PSK_WITH_AES_128_CBC_SHA: + return "PSK_WITH_AES_128_CBC_SHA"; + break; + case TLS_PSK_WITH_AES_256_CBC_SHA: + return "PSK_WITH_AES_256_CBC_SHA"; + break; + case TLS_DHE_PSK_WITH_RC4_128_SHA: + return "DHE_PSK_WITH_RC4_128_SHA"; + break; + case TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA: + return "DHE_PSK_WITH_3DES_EDE_CBC_SHA"; + break; + case TLS_DHE_PSK_WITH_AES_128_CBC_SHA: + return "DHE_PSK_WITH_AES_128_CBC_SHA"; + break; + case TLS_DHE_PSK_WITH_AES_256_CBC_SHA: + return "DHE_PSK_WITH_AES_256_CBC_SHA"; + break; + case TLS_RSA_PSK_WITH_RC4_128_SHA: + return "RSA_PSK_WITH_RC4_128_SHA"; + break; + case TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA: + return "RSA_PSK_WITH_3DES_EDE_CBC_SHA"; + break; + case TLS_RSA_PSK_WITH_AES_128_CBC_SHA: + return "RSA_PSK_WITH_AES_128_CBC_SHA"; + break; + case TLS_RSA_PSK_WITH_AES_256_CBC_SHA: + return "RSA_PSK_WITH_AES_256_CBC_SHA"; + break; + + /* RFC 4785 - Pre-Shared Key (PSK) Ciphersuites with NULL Encryption */ + case TLS_PSK_WITH_NULL_SHA: + return "PSK_WITH_NULL_SHA"; + break; + case TLS_DHE_PSK_WITH_NULL_SHA: + return "DHE_PSK_WITH_NULL_SHA"; + break; + case TLS_RSA_PSK_WITH_NULL_SHA: + return "RSA_PSK_WITH_NULL_SHA"; + break; + + /* + * Addenda from rfc 5288 AES Galois Counter Mode (GCM) Cipher Suites + * for TLS. + */ + case TLS_RSA_WITH_AES_128_GCM_SHA256: + return "RSA_WITH_AES_128_GCM_SHA256"; + break; + case TLS_RSA_WITH_AES_256_GCM_SHA384: + return "RSA_WITH_AES_256_GCM_SHA384"; + break; + case TLS_DHE_RSA_WITH_AES_128_GCM_SHA256: + return "DHE_RSA_WITH_AES_128_GCM_SHA256"; + break; + case TLS_DHE_RSA_WITH_AES_256_GCM_SHA384: + return "DHE_RSA_WITH_AES_256_GCM_SHA384"; + break; + case TLS_DH_RSA_WITH_AES_128_GCM_SHA256: + return "DH_RSA_WITH_AES_128_GCM_SHA256"; + break; + case TLS_DH_RSA_WITH_AES_256_GCM_SHA384: + return "DH_RSA_WITH_AES_256_GCM_SHA384"; + break; + case TLS_DHE_DSS_WITH_AES_128_GCM_SHA256: + return "DHE_DSS_WITH_AES_128_GCM_SHA256"; + break; + case TLS_DHE_DSS_WITH_AES_256_GCM_SHA384: + return "DHE_DSS_WITH_AES_256_GCM_SHA384"; + break; + case TLS_DH_DSS_WITH_AES_128_GCM_SHA256: + return "DH_DSS_WITH_AES_128_GCM_SHA256"; + break; + case TLS_DH_DSS_WITH_AES_256_GCM_SHA384: + return "DH_DSS_WITH_AES_256_GCM_SHA384"; + break; + case TLS_DH_anon_WITH_AES_128_GCM_SHA256: + return "DH_anon_WITH_AES_128_GCM_SHA256"; + break; + case TLS_DH_anon_WITH_AES_256_GCM_SHA384: + return "DH_anon_WITH_AES_256_GCM_SHA384"; + break; + + /* RFC 5487 - PSK with SHA-256/384 and AES GCM */ + case TLS_PSK_WITH_AES_128_GCM_SHA256: + return "PSK_WITH_AES_128_GCM_SHA256"; + break; + case TLS_PSK_WITH_AES_256_GCM_SHA384: + return "PSK_WITH_AES_256_GCM_SHA384"; + break; + case TLS_DHE_PSK_WITH_AES_128_GCM_SHA256: + return "DHE_PSK_WITH_AES_128_GCM_SHA256"; + break; + case TLS_DHE_PSK_WITH_AES_256_GCM_SHA384: + return "DHE_PSK_WITH_AES_256_GCM_SHA384"; + break; + case TLS_RSA_PSK_WITH_AES_128_GCM_SHA256: + return "RSA_PSK_WITH_AES_128_GCM_SHA256"; + break; + case TLS_RSA_PSK_WITH_AES_256_GCM_SHA384: + return "RSA_PSK_WITH_AES_256_GCM_SHA384"; + break; + case TLS_PSK_WITH_AES_128_CBC_SHA256: + return "PSK_WITH_AES_128_CBC_SHA256"; + break; + case TLS_PSK_WITH_AES_256_CBC_SHA384: + return "PSK_WITH_AES_256_CBC_SHA384"; + break; + case TLS_PSK_WITH_NULL_SHA256: + return "PSK_WITH_NULL_SHA256"; + break; + case TLS_PSK_WITH_NULL_SHA384: + return "PSK_WITH_NULL_SHA384"; + break; + case TLS_DHE_PSK_WITH_AES_128_CBC_SHA256: + return "DHE_PSK_WITH_AES_128_CBC_SHA256"; + break; + case TLS_DHE_PSK_WITH_AES_256_CBC_SHA384: + return "DHE_PSK_WITH_AES_256_CBC_SHA384"; + break; + case TLS_DHE_PSK_WITH_NULL_SHA256: + return "DHE_PSK_WITH_NULL_SHA256"; + break; + case TLS_DHE_PSK_WITH_NULL_SHA384: + return "DHE_PSK_WITH_NULL_SHA384"; + break; + case TLS_RSA_PSK_WITH_AES_128_CBC_SHA256: + return "RSA_PSK_WITH_AES_128_CBC_SHA256"; + break; + case TLS_RSA_PSK_WITH_AES_256_CBC_SHA384: + return "RSA_PSK_WITH_AES_256_CBC_SHA384"; + break; + case TLS_RSA_PSK_WITH_NULL_SHA256: + return "RSA_PSK_WITH_NULL_SHA256"; + break; + case TLS_RSA_PSK_WITH_NULL_SHA384: + return "RSA_PSK_WITH_NULL_SHA384"; + break; + + /* + * Addenda from rfc 5289 Elliptic Curve Cipher Suites with + * HMAC SHA-256/384. + */ + case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256: + return "ECDHE_ECDSA_WITH_AES_128_CBC_SHA256"; + break; + case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384: + return "ECDHE_ECDSA_WITH_AES_256_CBC_SHA384"; + break; + case TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256: + return "ECDH_ECDSA_WITH_AES_128_CBC_SHA256"; + break; + case TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384: + return "ECDH_ECDSA_WITH_AES_256_CBC_SHA384"; + break; + case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256: + return "ECDHE_RSA_WITH_AES_128_CBC_SHA256"; + break; + case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384: + return "ECDHE_RSA_WITH_AES_256_CBC_SHA384"; + break; + case TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256: + return "ECDH_RSA_WITH_AES_128_CBC_SHA256"; + break; + case TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384: + return "ECDH_RSA_WITH_AES_256_CBC_SHA384"; + break; + + /* + * Addenda from rfc 5289 Elliptic Curve Cipher Suites with + * SHA-256/384 and AES Galois Counter Mode (GCM) + */ + case TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: + return "ECDHE_ECDSA_WITH_AES_128_GCM_SHA256"; + break; + case TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: + return "ECDHE_ECDSA_WITH_AES_256_GCM_SHA384"; + break; + case TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256: + return "ECDH_ECDSA_WITH_AES_128_GCM_SHA256"; + break; + case TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384: + return "ECDH_ECDSA_WITH_AES_256_GCM_SHA384"; + break; + case TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: + return "ECDHE_RSA_WITH_AES_128_GCM_SHA256"; + break; + case TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: + return "ECDHE_RSA_WITH_AES_256_GCM_SHA384"; + break; + case TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256: + return "ECDH_RSA_WITH_AES_128_GCM_SHA256"; + break; + case TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384: + return "ECDH_RSA_WITH_AES_256_GCM_SHA384"; + break; + + default: + break; + } + + return NULL; +} diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h index be6c370..649d22f 100644 --- a/src/interfaces/libpq/libpq-int.h +++ b/src/interfaces/libpq/libpq-int.h @@ -82,6 +82,15 @@ typedef struct #endif #endif /* USE_OPENSSL */ +#ifdef USE_SECURETRANSPORT +#define Size pg_Size +#define uint64 pg_uint64 +#include +#include +#undef Size +#undef uint64 +#endif + /* * POSTGRES backend dependent Constants. */ @@ -439,6 +448,14 @@ struct pg_conn * OpenSSL version changes */ #endif #endif /* USE_OPENSSL */ + +#ifdef USE_SECURETRANSPORT + SSLContextRef ssl; /* SSL context reference */ + CFArrayRef st_rootcert; + ssize_t ssl_buffered; + int ssl_key_bits; +#endif /* USE_SECURETRANSPORT */ + #endif /* USE_SSL */ #ifdef ENABLE_GSS -- 2.6.4 (Apple Git-63)