📄 fe-secure.c
字号:
/*------------------------------------------------------------------------- * * 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-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION * $PostgreSQL: pgsql/src/interfaces/libpq/fe-secure.c,v 1.73.2.1 2006/01/24 16:38:50 tgl Exp $ * * NOTES * [ Most of these notes are wrong/obsolete, but perhaps not all ] * * 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 * "~/.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 (~/.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 * ~/.postgresql/postgresql.crt * and * ~/.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. * *------------------------------------------------------------------------- */#include "postgres_fe.h"#include <signal.h>#include <fcntl.h>#include <ctype.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#include <sys/stat.h>#ifdef ENABLE_THREAD_SAFETY#ifdef WIN32#include "pthread-win32.h"#else#include <pthread.h>#endif#endif#ifndef HAVE_STRDUP#include "strdup.h"#endif#ifdef USE_SSL#include <openssl/ssl.h>#include <openssl/dh.h>#endif /* USE_SSL */#ifdef USE_SSL#ifndef WIN32#define USERCERTFILE ".postgresql/postgresql.crt"#define USERKEYFILE ".postgresql/postgresql.key"#define ROOTCERTFILE ".postgresql/root.crt"#define DHFILEPATTERN "%s/.postgresql/dh%d.pem"#else/* On Windows, the "home" directory is already PostgreSQL-specific */#define USERCERTFILE "postgresql.crt"#define USERKEYFILE "postgresql.key"#define ROOTCERTFILE "root.crt"#define DHFILEPATTERN "%s/dh%d.pem"#endif#ifdef NOT_USEDstatic int verify_peer(PGconn *);#endifstatic int verify_cb(int ok, X509_STORE_CTX *ctx);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 init_ssl_system(PGconn *conn);static int initialize_SSL(PGconn *);static void destroy_SSL(void);static PostgresPollingStatusType open_client_SSL(PGconn *);static void close_SSL(PGconn *);static char *SSLerrmessage(void);static void SSLerrfree(char *buf);#endif#ifdef USE_SSLstatic bool pq_initssllib = true;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 * ~/.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.... */#ifdef USE_SSLstatic 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";#endif/* ------------------------------------------------------------ *//* Procedures common to all secure sessions *//* ------------------------------------------------------------ *//* * Exported function to allow application to tell us it's already * initialized OpenSSL. */voidPQinitSSL(int do_init){#ifdef USE_SSL pq_initssllib = do_init;#endif}/* * Initialize global context */intpqsecure_initialize(PGconn *conn){ int r = 0;#ifdef USE_SSL r = initialize_SSL(conn);#endif return r;}/* * Destroy global context */voidpqsecure_destroy(void){#ifdef USE_SSL destroy_SSL();#endif}/* * Attempt to negotiate secure session. */PostgresPollingStatusTypepqsecure_open_client(PGconn *conn){#ifdef USE_SSL /* First time through? */ if (conn->ssl == NULL) { if (!(conn->ssl = SSL_new(SSL_context)) || !SSL_set_app_data(conn->ssl, conn) || !SSL_set_fd(conn->ssl, conn->sock)) { char *err = SSLerrmessage(); printfPQExpBuffer(&conn->errorMessage, libpq_gettext("could not establish SSL connection: %s\n"), err); SSLerrfree(err); close_SSL(conn); return PGRES_POLLING_FAILED; } /* * Initialize errorMessage to empty. This allows open_client_SSL() to * detect whether client_cert_cb() has stored a message. */ resetPQExpBuffer(&conn->errorMessage); } /* Begin or continue the actual handshake */ return open_client_SSL(conn);#else /* shouldn't get here */ return PGRES_POLLING_FAILED;#endif}/* * Close secure session. */voidpqsecure_close(PGconn *conn){#ifdef USE_SSL if (conn->ssl) close_SSL(conn);#endif}/* * Read data from a secure connection. */ssize_tpqsecure_read(PGconn *conn, void *ptr, size_t len){ ssize_t n;#ifdef USE_SSL if (conn->ssl) { int err;rloop: n = SSL_read(conn->ssl, ptr, len); err = SSL_get_error(conn->ssl, n); switch (err) { case SSL_ERROR_NONE: break; case SSL_ERROR_WANT_READ: n = 0; break; case SSL_ERROR_WANT_WRITE: /* * Returning 0 here would cause caller to wait for read-ready, * which is not correct since what SSL wants is wait for * write-ready. The former could get us stuck in an infinite * wait, so don't risk it; busy-loop instead. */ goto rloop; case SSL_ERROR_SYSCALL: { char sebuf[256]; if (n == -1) printfPQExpBuffer(&conn->errorMessage, libpq_gettext("SSL SYSCALL error: %s\n"), SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf))); else { printfPQExpBuffer(&conn->errorMessage, libpq_gettext("SSL SYSCALL error: EOF detected\n")); SOCK_ERRNO_SET(ECONNRESET); n = -1; } break; } case SSL_ERROR_SSL: { char *err = SSLerrmessage(); printfPQExpBuffer(&conn->errorMessage, libpq_gettext("SSL error: %s\n"), err); SSLerrfree(err); } /* fall through */ case SSL_ERROR_ZERO_RETURN: SOCK_ERRNO_SET(ECONNRESET); n = -1; break; default: printfPQExpBuffer(&conn->errorMessage, libpq_gettext("unrecognized SSL error code: %d\n"), err); n = -1; break; } } else#endif n = recv(conn->sock, ptr, len, 0); return n;}/* * Write data to a secure connection. */ssize_tpqsecure_write(PGconn *conn, const void *ptr, size_t len){ ssize_t n;#ifndef WIN32#ifdef ENABLE_THREAD_SAFETY sigset_t osigmask; bool sigpipe_pending; bool got_epipe = false; if (pq_block_sigpipe(&osigmask, &sigpipe_pending) < 0) return -1;#else pqsigfunc oldsighandler = pqsignal(SIGPIPE, SIG_IGN);#endif /* ENABLE_THREAD_SAFETY */#endif /* WIN32 */#ifdef USE_SSL if (conn->ssl) { int err; n = SSL_write(conn->ssl, ptr, len); err = SSL_get_error(conn->ssl, n); switch (err) { case SSL_ERROR_NONE: break; case SSL_ERROR_WANT_READ: /* * Returning 0 here causes caller to wait for write-ready, * which is not really the right thing, but it's the best we * can do. */ n = 0; break; case SSL_ERROR_WANT_WRITE: n = 0; break; case SSL_ERROR_SYSCALL: { char sebuf[256]; if (n == -1) {#if defined(ENABLE_THREAD_SAFETY) && !defined(WIN32) if (SOCK_ERRNO == EPIPE) got_epipe = true;#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -