diff options
Diffstat (limited to 'src/interfaces/libpq/fe-secure.c')
-rw-r--r-- | src/interfaces/libpq/fe-secure.c | 899 |
1 files changed, 0 insertions, 899 deletions
diff --git a/src/interfaces/libpq/fe-secure.c b/src/interfaces/libpq/fe-secure.c deleted file mode 100644 index 64cce0c6f5d..00000000000 --- a/src/interfaces/libpq/fe-secure.c +++ /dev/null @@ -1,899 +0,0 @@ -/*------------------------------------------------------------------------- - * - * fe-secure.c - * functions related to setting up a secure connection to the backend. - * Secure connections are expected to provide confidentiality, - * message integrity and endpoint authentication. - * - * - * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group - * Portions Copyright (c) 1994, Regents of the University of California - * - * - * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-secure.c,v 1.7 2002/06/20 20:29:54 momjian Exp $ - * - * NOTES - * The client *requires* a valid server certificate. Since - * SSH tunnels provide anonymous confidentiality, the presumption - * is that sites that want endpoint authentication will use the - * direct SSL support, while sites that are comfortable with - * anonymous connections will use SSH tunnels. - * - * This code verifies the server certificate, to detect simple - * "man-in-the-middle" and "impersonation" attacks. The - * server certificate, or better yet the CA certificate used - * to sign the server certificate, should be present in the - * "$HOME/.postgresql/root.crt" file. If this file isn't - * readable, or the server certificate can't be validated, - * pqsecure_open_client() will return an error code. - * - * Additionally, the server certificate's "common name" must - * resolve to the other end of the socket. This makes it - * substantially harder to pull off a "man-in-the-middle" or - * "impersonation" attack even if the server's private key - * has been stolen. This check limits acceptable network - * layers to Unix sockets (weird, but legal), TCPv4 and TCPv6. - * - * Unfortunately neither the current front- or back-end handle - * failure gracefully, resulting in the backend hiccupping. - * This points out problems in each (the frontend shouldn't even - * try to do SSL if pqsecure_initialize() fails, and the backend - * shouldn't crash/recover if an SSH negotiation fails. The - * backend definitely needs to be fixed, to prevent a "denial - * of service" attack, but I don't know enough about how the - * backend works (especially that pre-SSL negotiation) to identify - * a fix. - * - * ... - * - * Unlike the server's static private key, the client's - * static private key ($HOME/.postgresql/postgresql.key) - * should normally be stored encrypted. However we still - * support EPH since it's useful for other reasons. - * - * ... - * - * Client certificates are supported, if the server requests - * or requires them. Client certificates can be used for - * authentication, to prevent sessions from being hijacked, - * or to allow "road warriors" to access the database while - * keeping it closed to everyone else. - * - * The user's certificate and private key are located in - * $HOME/.postgresql/postgresql.crt - * and - * $HOME/.postgresql/postgresql.key - * respectively. - * - * ... - * - * We don't provide informational callbacks here (like - * info_cb() in be-secure.c), since there's mechanism to - * display that information to the client. - * - * OS DEPENDENCIES - * The code currently assumes a POSIX password entry. How should - * Windows and Mac users be handled? - * - *------------------------------------------------------------------------- - */ - -#include "postgres_fe.h" - -#include <sys/types.h> -#include <signal.h> -#include <fcntl.h> -#include <errno.h> -#include <ctype.h> -#include <string.h> - -#include "libpq-fe.h" -#include "libpq-int.h" -#include "fe-auth.h" -#include "pqsignal.h" - -#ifdef WIN32 -#include "win32.h" -#else -#include <sys/socket.h> -#include <unistd.h> -#include <netdb.h> -#include <netinet/in.h> -#ifdef HAVE_NETINET_TCP_H -#include <netinet/tcp.h> -#endif -#include <arpa/inet.h> -#endif - -#ifndef HAVE_STRDUP -#include "strdup.h" -#endif - -#include <pwd.h> -#include <sys/stat.h> - -#ifdef USE_SSL -#include <openssl/ssl.h> -#include <openssl/e_os.h> -#endif /* USE_SSL */ - - -#ifdef USE_SSL -static int verify_cb(int ok, X509_STORE_CTX *ctx); -static int verify_peer(PGconn *); -static DH *load_dh_file(int keylength); -static DH *load_dh_buffer(const char *, size_t); -static DH *tmp_dh_cb(SSL *s, int is_export, int keylength); -static int client_cert_cb(SSL *, X509 **, EVP_PKEY **); -static int initialize_SSL(PGconn *); -static void destroy_SSL(void); -static int open_client_SSL(PGconn *); -static void close_SSL(PGconn *); -static const char *SSLerrmessage(void); -#endif - -#ifdef USE_SSL -static SSL_CTX *SSL_context = NULL; -#endif - -/* ------------------------------------------------------------ */ -/* Hardcoded values */ -/* ------------------------------------------------------------ */ - -/* - * Hardcoded DH parameters, used in empheral DH keying. - * As discussed above, EDH protects the confidentiality of - * sessions even if the static private key is compromised, - * so we are *highly* motivated to ensure that we can use - * EDH even if the user... or an attacker... deletes the - * $HOME/.postgresql/dh*.pem files. - * - * It's not critical that users have EPH keys, but it doesn't - * hurt and if it's missing someone will demand it, so.... - */ -static const char file_dh512[] = -"-----BEGIN DH PARAMETERS-----\n\ -MEYCQQD1Kv884bEpQBgRjXyEpwpy1obEAxnIByl6ypUM2Zafq9AKUJsCRtMIPWak\n\ -XUGfnHy9iUsiGSa6q6Jew1XpKgVfAgEC\n\ ------END DH PARAMETERS-----\n"; - -static const char file_dh1024[] = -"-----BEGIN DH PARAMETERS-----\n\ -MIGHAoGBAPSI/VhOSdvNILSd5JEHNmszbDgNRR0PfIizHHxbLY7288kjwEPwpVsY\n\ -jY67VYy4XTjTNP18F1dDox0YbN4zISy1Kv884bEpQBgRjXyEpwpy1obEAxnIByl6\n\ -ypUM2Zafq9AKUJsCRtMIPWakXUGfnHy9iUsiGSa6q6Jew1XpL3jHAgEC\n\ ------END DH PARAMETERS-----\n"; - -static const char file_dh2048[] = -"-----BEGIN DH PARAMETERS-----\n\ -MIIBCAKCAQEA9kJXtwh/CBdyorrWqULzBej5UxE5T7bxbrlLOCDaAadWoxTpj0BV\n\ -89AHxstDqZSt90xkhkn4DIO9ZekX1KHTUPj1WV/cdlJPPT2N286Z4VeSWc39uK50\n\ -T8X8dryDxUcwYc58yWb/Ffm7/ZFexwGq01uejaClcjrUGvC/RgBYK+X0iP1YTknb\n\ -zSC0neSRBzZrM2w4DUUdD3yIsxx8Wy2O9vPJI8BD8KVbGI2Ou1WMuF040zT9fBdX\n\ -Q6MdGGzeMyEstSr/POGxKUAYEY18hKcKctaGxAMZyAcpesqVDNmWn6vQClCbAkbT\n\ -CD1mpF1Bn5x8vYlLIhkmuquiXsNV6TILOwIBAg==\n\ ------END DH PARAMETERS-----\n"; - -static const char file_dh4096[] = -"-----BEGIN DH PARAMETERS-----\n\ -MIICCAKCAgEA+hRyUsFN4VpJ1O8JLcCo/VWr19k3BCgJ4uk+d+KhehjdRqNDNyOQ\n\ -l/MOyQNQfWXPeGKmOmIig6Ev/nm6Nf9Z2B1h3R4hExf+zTiHnvVPeRBhjdQi81rt\n\ -Xeoh6TNrSBIKIHfUJWBh3va0TxxjQIs6IZOLeVNRLMqzeylWqMf49HsIXqbcokUS\n\ -Vt1BkvLdW48j8PPv5DsKRN3tloTxqDJGo9tKvj1Fuk74A+Xda1kNhB7KFlqMyN98\n\ -VETEJ6c7KpfOo30mnK30wqw3S8OtaIR/maYX72tGOno2ehFDkq3pnPtEbD2CScxc\n\ -alJC+EL7RPk5c/tgeTvCngvc1KZn92Y//EI7G9tPZtylj2b56sHtMftIoYJ9+ODM\n\ -sccD5Piz/rejE3Ome8EOOceUSCYAhXn8b3qvxVI1ddd1pED6FHRhFvLrZxFvBEM9\n\ -ERRMp5QqOaHJkM+Dxv8Cj6MqrCbfC4u+ZErxodzuusgDgvZiLF22uxMZbobFWyte\n\ -OvOzKGtwcTqO/1wV5gKkzu1ZVswVUQd5Gg8lJicwqRWyyNRczDDoG9jVDxmogKTH\n\ -AaqLulO7R8Ifa1SwF2DteSGVtgWEN8gDpN3RBmmPTDngyF2DHb5qmpnznwtFKdTL\n\ -KWbuHn491xNO25CQWMtem80uKw+pTnisBRF/454n1Jnhub144YRBoN8CAQI=\n\ ------END DH PARAMETERS-----\n"; - -/* ------------------------------------------------------------ */ -/* Procedures common to all secure sessions */ -/* ------------------------------------------------------------ */ - -/* - * Initialize global context - */ -int -pqsecure_initialize (PGconn *conn) -{ - int r = 0; - -#ifdef USE_SSL - r = initialize_SSL(conn); -#endif - - return r; -} - -/* - * Destroy global context - */ -void -pqsecure_destroy (void) -{ -#ifdef USE_SSL - destroy_SSL(); -#endif -} - -/* - * Attempt to negotiate secure session. - */ -int -pqsecure_open_client (PGconn *conn) -{ - int r = 0; - -#ifdef USE_SSL - r = open_client_SSL(conn); -#endif - - return r; -} - -/* - * Close secure session. - */ -void -pqsecure_close (PGconn *conn) -{ -#ifdef USE_SSL - if (conn->ssl) - close_SSL(conn); -#endif -} - -/* - * Read data from a secure connection. - */ -ssize_t -pqsecure_read (PGconn *conn, void *ptr, size_t len) -{ - ssize_t n; - -#ifdef USE_SSL - if (conn->ssl) - { - n = SSL_read(conn->ssl, ptr, len); - switch (SSL_get_error(conn->ssl, n)) - { - case SSL_ERROR_NONE: - break; - case SSL_ERROR_WANT_READ: - break; - case SSL_ERROR_SYSCALL: - SOCK_ERRNO = get_last_socket_error(); - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("SSL SYSCALL error: %s\n"), - SOCK_STRERROR(SOCK_ERRNO)); - break; - case SSL_ERROR_SSL: - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("SSL error: %s\n"), SSLerrmessage()); - /* fall through */ - case SSL_ERROR_ZERO_RETURN: - pqsecure_close(conn); - SOCK_ERRNO = ECONNRESET; - n = -1; - break; - } - } - else -#endif - n = recv(conn->sock, ptr, len, 0); - - return n; -} - -/* - * Write data to a secure connection. - */ -ssize_t -pqsecure_write (PGconn *conn, const void *ptr, size_t len) -{ - ssize_t n; - -#ifndef WIN32 - pqsigfunc oldsighandler = pqsignal(SIGPIPE, SIG_IGN); -#endif - -#ifdef USE_SSL - if (conn->ssl) - { - n = SSL_write(conn->ssl, ptr, len); - switch (SSL_get_error(conn->ssl, n)) - { - case SSL_ERROR_NONE: - break; - case SSL_ERROR_WANT_WRITE: - break; - case SSL_ERROR_SYSCALL: - SOCK_ERRNO = get_last_socket_error(); - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("SSL SYSCALL error: %s\n"), - SOCK_STRERROR(SOCK_ERRNO)); - break; - case SSL_ERROR_SSL: - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("SSL error: %s\n"), SSLerrmessage()); - /* fall through */ - case SSL_ERROR_ZERO_RETURN: - pqsecure_close(conn); - SOCK_ERRNO = ECONNRESET; - n = -1; - break; - } - } - else -#endif - n = send(conn->sock, ptr, len, 0); - -#ifndef WIN32 - pqsignal(SIGPIPE, oldsighandler); -#endif - - return n; -} - -/* ------------------------------------------------------------ */ -/* SSL specific code */ -/* ------------------------------------------------------------ */ -#ifdef USE_SSL -/* - * Certificate verification callback - * - * This callback allows us to log intermediate problems during - * verification, but there doesn't seem to be a clean way to get - * our PGconn * structure. So we can't log anything! - * - * This callback also allows us to override the default acceptance - * criteria (e.g., accepting self-signed or expired certs), but - * for now we accept the default checks. - */ -static int -verify_cb (int ok, X509_STORE_CTX *ctx) -{ - return ok; -} - -/* - * Verify that common name resolves to peer. - * This function is not thread-safe due to gethostbyname2(). - */ -static int -verify_peer (PGconn *conn) -{ - struct hostent *h = NULL; - struct sockaddr addr; - struct sockaddr_in *sin; - struct sockaddr_in6 *sin6; - socklen_t len; - char **s; - unsigned long l; - - /* get the address on the other side of the socket */ - len = sizeof(addr); - if (getpeername(conn->sock, &addr, &len) == -1) - { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("error querying socket: %s\n"), - SOCK_STRERROR(SOCK_ERRNO)); - return -1; - } - - /* weird, but legal case */ - if (addr.sa_family == AF_UNIX) - return 0; - - /* what do we know about the peer's common name? */ - if ((h = gethostbyname2(conn->peer_cn, addr.sa_family)) == NULL) - { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("error getting information about host (%s): %s\n"), - conn->peer_cn, hstrerror(h_errno)); - return -1; - } - - /* does the address match? */ - switch (addr.sa_family) - { - case AF_INET: - sin = (struct sockaddr_in *) &addr; - for (s = h->h_addr_list; *s != NULL; s++) - { - if (!memcmp(&sin->sin_addr.s_addr, *s, h->h_length)) - return 0; - } - break; - - case AF_INET6: - sin6 = (struct sockaddr_in6 *) &addr; - for (s = h->h_addr_list; *s != NULL; s++) - { - if (!memcmp(sin6->sin6_addr.s6_addr8, *s, h->h_length)) - return 0; - } - break; - - default: - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("sorry, this protocol not yet supported\n")); - return -1; - } - - /* the prior test should be definitive, but in practice - * it sometimes fails. So we also check the aliases. */ - for (s = h->h_aliases; *s != NULL; s++) - { - if (strcasecmp(conn->peer_cn, *s) == 0) - return 0; - } - - /* generate protocol-aware error message */ - switch (addr.sa_family) - { - case AF_INET: - sin = (struct sockaddr_in *) &addr; - l = ntohl(sin->sin_addr.s_addr); - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext( - "server common name '%s' does not resolve to %ld.%ld.%ld.%ld\n"), - conn->peer_cn, (l >> 24) % 0x100, (l >> 16) % 0x100, - (l >> 8) % 0x100, l % 0x100); - break; - default: - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext( - "server common name '%s' does not resolve to peer address\n"), - conn->peer_cn); - } - - return -1; -} - -/* - * Load precomputed DH parameters. - * - * To prevent "downgrade" attacks, we perform a number of checks - * to verify that the DBA-generated DH parameters file contains - * what we expect it to contain. - */ -static DH * -load_dh_file (int keylength) -{ - struct passwd *pwd; - FILE *fp; - char fnbuf[2048]; - DH *dh = NULL; - int codes; - - if ((pwd = getpwuid(getuid())) == NULL) - return NULL; - - /* attempt to open file. It's not an error if it doesn't exist. */ - snprintf(fnbuf, sizeof fnbuf, "%s/.postgresql/dh%d.pem", - pwd->pw_dir, keylength); - if ((fp = fopen(fnbuf, "r")) == NULL) - return NULL; - -/* flock(fileno(fp), LOCK_SH); */ - dh = PEM_read_DHparams(fp, NULL, NULL, NULL); -/* flock(fileno(fp), LOCK_UN); */ - fclose(fp); - - /* is the prime the correct size? */ - if (dh != NULL && 8*DH_size(dh) < keylength) - { - dh = NULL; - } - - /* make sure the DH parameters are usable */ - if (dh != NULL) - { - if (DH_check(dh, &codes)) - { - return NULL; - } - if (codes & DH_CHECK_P_NOT_PRIME) - { - return NULL; - } - if ((codes & DH_NOT_SUITABLE_GENERATOR) && - (codes & DH_CHECK_P_NOT_SAFE_PRIME)) - { - return NULL; - } - } - - return dh; -} - -/* - * Load hardcoded DH parameters. - * - * To prevent problems if the DH parameters files don't even - * exist, we can load DH parameters hardcoded into this file. - */ -static DH * -load_dh_buffer (const char *buffer, size_t len) -{ - BIO *bio; - DH *dh = NULL; - - bio = BIO_new_mem_buf((char *) buffer, len); - if (bio == NULL) - return NULL; - dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL); - BIO_free(bio); - - return dh; -} - -/* - * Generate an empheral DH key. Because this can take a long - * time to compute, we can use precomputed parameters of the - * common key sizes. - * - * Since few sites will bother to precompute these parameter - * files, we also provide a fallback to the parameters provided - * by the OpenSSL project. - * - * These values can be static (once loaded or computed) since - * the OpenSSL library can efficiently generate random keys from - * the information provided. - */ -static DH * -tmp_dh_cb (SSL *s, int is_export, int keylength) -{ - DH *r = NULL; - static DH *dh = NULL; - static DH *dh512 = NULL; - static DH *dh1024 = NULL; - static DH *dh2048 = NULL; - static DH *dh4096 = NULL; - - switch (keylength) - { - case 512: - if (dh512 == NULL) - dh512 = load_dh_file(keylength); - if (dh512 == NULL) - dh512 = load_dh_buffer(file_dh512, sizeof file_dh512); - r = dh512; - break; - - case 1024: - if (dh1024 == NULL) - dh1024 = load_dh_file(keylength); - if (dh1024 == NULL) - dh1024 = load_dh_buffer(file_dh1024, sizeof file_dh1024); - r = dh1024; - break; - - case 2048: - if (dh2048 == NULL) - dh2048 = load_dh_file(keylength); - if (dh2048 == NULL) - dh2048 = load_dh_buffer(file_dh2048, sizeof file_dh2048); - r = dh2048; - break; - - case 4096: - if (dh4096 == NULL) - dh4096 = load_dh_file(keylength); - if (dh4096 == NULL) - dh4096 = load_dh_buffer(file_dh4096, sizeof file_dh4096); - r = dh4096; - break; - - default: - if (dh == NULL) - dh = load_dh_file(keylength); - r = dh; - } - - /* this may take a long time, but it may be necessary... */ - if (r == NULL || 8*DH_size(r) < keylength) - { - r = DH_generate_parameters(keylength, DH_GENERATOR_2, NULL, NULL); - } - - return r; -} - -/* - * Callback used by SSL to load client cert and key. - * This callback is only called when the server wants a - * client cert. - * - * Returns 1 on success, 0 on no data, -1 on error. - */ -static int -client_cert_cb (SSL *ssl, X509 **x509, EVP_PKEY **pkey) -{ - struct passwd *pwd; - struct stat buf, buf2; - char fnbuf[2048]; - FILE *fp; - PGconn *conn = (PGconn *) SSL_get_app_data(ssl); - int (*cb)() = NULL; /* how to read user password */ - - if ((pwd = getpwuid(getuid())) == NULL) - { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("unable to get user information\n")); - return -1; - } - - /* read the user certificate */ - snprintf(fnbuf, sizeof fnbuf, "%s/.postgresql/postgresql.crt", - pwd->pw_dir); - if (stat(fnbuf, &buf) == -1) - return 0; - if ((fp = fopen(fnbuf, "r")) == NULL) - { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("unable to open certificate (%s): %s\n"), - fnbuf, strerror(errno)); - return -1; - } - if (PEM_read_X509(fp, x509, NULL, NULL) == NULL) - { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("unable to read certificate (%s): %s\n"), - fnbuf, SSLerrmessage()); - fclose(fp); - return -1; - } - fclose(fp); - - /* read the user key */ - snprintf(fnbuf, sizeof fnbuf, "%s/.postgresql/postgresql.key", - pwd->pw_dir); - if (stat(fnbuf, &buf) == -1) - { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("certificate present, but not private key (%s)\n"), - fnbuf); - X509_free(*x509); - return 0; - } - if (!S_ISREG(buf.st_mode) || (buf.st_mode & 0077) || - buf.st_uid != getuid()) - { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("private key has bad permissions (%s)\n"), fnbuf); - X509_free(*x509); - return -1; - } - if ((fp = fopen(fnbuf, "r")) == NULL) - { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("unable to open private key file (%s): %s\n"), - fnbuf, strerror(errno)); - X509_free(*x509); - return -1; - } - if (fstat(fileno(fp), &buf2) == -1 || - buf.st_dev != buf2.st_dev || buf.st_ino != buf2.st_ino) - { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("private key changed under us (%s)\n"), fnbuf); - X509_free(*x509); - return -1; - } - if (PEM_read_PrivateKey(fp, pkey, cb, NULL) == NULL) - { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("unable to read private key (%s): %s\n"), - fnbuf, SSLerrmessage()); - X509_free(*x509); - fclose(fp); - return -1; - } - fclose(fp); - - /* verify that the cert and key go together */ - if (!X509_check_private_key(*x509, *pkey)) - { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("certificate/private key mismatch (%s): %s\n"), - fnbuf, SSLerrmessage()); - X509_free(*x509); - EVP_PKEY_free(*pkey); - return -1; - } - - return 1; -} - -/* - * Initialize global SSL context. - */ -static int -initialize_SSL (PGconn *conn) -{ - struct stat buf; - struct passwd *pwd; - char fnbuf[2048]; - - if (!SSL_context) - { - SSL_library_init(); - SSL_load_error_strings(); - SSL_context = SSL_CTX_new(TLSv1_method()); - if (!SSL_context) - { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("could not create SSL context: %s\n"), - SSLerrmessage()); - return -1; - } - } - - if ((pwd = getpwuid(getuid())) != NULL) - { - snprintf(fnbuf, sizeof fnbuf, "%s/.postgresql/root.crt", - pwd->pw_dir); - if (stat(fnbuf, &buf) == -1) - { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("could not read root cert list(%s): %s"), - fnbuf, strerror(errno)); - return -1; - } - if (!SSL_CTX_load_verify_locations(SSL_context, fnbuf, 0)) - { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("could not read root cert list (%s): %s"), - fnbuf, SSLerrmessage()); - return -1; - } - } - - SSL_CTX_set_verify(SSL_context, - SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, verify_cb); - SSL_CTX_set_verify_depth(SSL_context, 1); - - /* set up empheral DH keys */ - SSL_CTX_set_tmp_dh_callback(SSL_context, tmp_dh_cb); - SSL_CTX_set_options(SSL_context, SSL_OP_SINGLE_DH_USE); - - /* set up mechanism to provide client certificate, if available */ - SSL_CTX_set_client_cert_cb(SSL_context, client_cert_cb); - - return 0; -} - -/* - * Destroy global SSL context. - */ -static void -destroy_SSL (void) -{ - if (SSL_context) - { - SSL_CTX_free(SSL_context); - SSL_context = NULL; - } -} - -/* - * Attempt to negotiate SSL connection. - */ -static int -open_client_SSL (PGconn *conn) -{ - int r; - - if (!(conn->ssl = SSL_new(SSL_context)) || - !SSL_set_app_data(conn->ssl, conn) || - !SSL_set_fd(conn->ssl, conn->sock) || - SSL_connect(conn->ssl) <= 0) - { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("could not establish SSL connection: %s\n"), - SSLerrmessage()); - close_SSL(conn); - return -1; - } - - /* check the certificate chain of the server */ - /* this eliminates simple man-in-the-middle attacks and - * simple impersonations */ - r = SSL_get_verify_result(conn->ssl); - if (r != X509_V_OK) - { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("certificate could not be validated: %s\n"), - X509_verify_cert_error_string(r)); - close_SSL(conn); - return -1; - } - - /* pull out server distinguished and common names */ - conn->peer = SSL_get_peer_certificate(conn->ssl); - if (conn->peer == NULL) - { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("certificate could not be obtained: %s\n"), - SSLerrmessage()); - close_SSL(conn); - return -1; - } - - X509_NAME_oneline(X509_get_subject_name(conn->peer), - conn->peer_dn, sizeof(conn->peer_dn)); - conn->peer_dn[sizeof(conn->peer_dn)-1] = '\0'; - - X509_NAME_get_text_by_NID(X509_get_subject_name(conn->peer), - NID_commonName, conn->peer_cn, SM_USER); - conn->peer_cn[SM_USER] = '\0'; - - /* verify that the common name resolves to peer */ - /* this is necessary to eliminate man-in-the-middle attacks - * and impersonations where the attacker somehow learned - * the server's private key */ - if (verify_peer(conn) == -1) - { - close_SSL(conn); - return -1; - } - - return 0; -} - -/* - * Close SSL connection. - */ -static void -close_SSL (PGconn *conn) -{ - if (conn->ssl) - { - SSL_shutdown(conn->ssl); - SSL_free(conn->ssl); - conn->ssl = NULL; - } -} - -/* - * Obtain reason string for last SSL error - * - * Some caution is needed here since ERR_reason_error_string will - * return NULL if it doesn't recognize the error code. We don't - * want to return NULL ever. - */ -static const char * -SSLerrmessage(void) -{ - unsigned long errcode; - const char *errreason; - static char errbuf[32]; - - errcode = ERR_get_error(); - if (errcode == 0) - return "No SSL error reported"; - errreason = ERR_reason_error_string(errcode); - if (errreason != NULL) - return errreason; - snprintf(errbuf, sizeof(errbuf), "SSL error code %lu", errcode); - return errbuf; -} - -/* - * Return pointer to SSL object. - */ -SSL * -PQgetssl(PGconn *conn) -{ - if (!conn) - return NULL; - return conn->ssl; -} - -#endif /* USE_SSL */ |