⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 fe-secure.c

📁 PostgreSQL7.4.6 for Linux
💻 C
📖 第 1 页 / 共 2 页
字号:
/*------------------------------------------------------------------------- * * 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-2003, 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.32.2.1 2003/12/18 22:49:34 tgl 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#ifndef WIN32#include <pwd.h>#endif#include <sys/stat.h>#ifdef USE_SSL#include <openssl/ssl.h>#include <openssl/dh.h>#endif   /* USE_SSL */#ifdef USE_SSLstatic int	verify_cb(int ok, X509_STORE_CTX *ctx);#ifdef NOT_USEDstatic int	verify_peer(PGconn *);#endifstatic 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 PostgresPollingStatusType open_client_SSL(PGconn *);static void close_SSL(PGconn *);static const char *SSLerrmessage(void);#endif#ifdef USE_SSLstatic 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.... */#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			*//* ------------------------------------------------------------ *//* *	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))		{			printfPQExpBuffer(&conn->errorMessage,			   libpq_gettext("could not establish SSL connection: %s\n"),							  SSLerrmessage());			close_SSL(conn);			return PGRES_POLLING_FAILED;		}	}	/* 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)	{rloop:		n = SSL_read(conn->ssl, ptr, len);		switch (SSL_get_error(conn->ssl, n))		{			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:				printfPQExpBuffer(&conn->errorMessage,					  libpq_gettext("SSL error: %s\n"), SSLerrmessage());				/* fall through */			case SSL_ERROR_ZERO_RETURN:				SOCK_ERRNO_SET(ECONNRESET);				n = -1;				break;			default:				printfPQExpBuffer(&conn->errorMessage,							  libpq_gettext("unrecognized SSL error code\n"));				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	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_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)						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:				printfPQExpBuffer(&conn->errorMessage,					  libpq_gettext("SSL error: %s\n"), SSLerrmessage());				/* fall through */			case SSL_ERROR_ZERO_RETURN:				SOCK_ERRNO_SET(ECONNRESET);				n = -1;				break;			default:				printfPQExpBuffer(&conn->errorMessage,							  libpq_gettext("unrecognized SSL error code\n"));				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 intverify_cb(int ok, X509_STORE_CTX *ctx){	return ok;}#ifdef NOT_USED/* *	Verify that common name resolves to peer. */static intverify_peer(PGconn *conn){	struct hostent *h = NULL;	struct sockaddr addr;	struct sockaddr_in *sin;	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)	{		char		sebuf[256];		printfPQExpBuffer(&conn->errorMessage,						  libpq_gettext("error querying socket: %s\n"),						SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));		return -1;	}	/* weird, but legal case */	if (addr.sa_family == AF_UNIX)		return 0;	{		struct hostent hpstr;		char		buf[BUFSIZ];		int			herrno = 0;				/*		 *	Currently, pqGethostbyname() is used only on platforms that		 *	don't have getaddrinfo().  If you enable this function,		 *	you should convert the pqGethostbyname() function call to		 *	use getaddrinfo().		 */		pqGethostbyname(conn->peer_cn, &hpstr, buf, sizeof(buf),						&h, &herrno);	}	/* what do we know about the peer's common name? */	if (h == NULL)	{		printfPQExpBuffer(&conn->errorMessage,		libpq_gettext("could not get 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;		default:			printfPQExpBuffer(&conn->errorMessage,							  libpq_gettext("unsupported protocol\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,

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -