示例#1
0
/*
 *	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(char *filename, bool isServerStart)
{
	FILE	   *fp;
	DH		   *dh = NULL;
	int			codes;

	/* attempt to open file.  It's not an error if it doesn't exist. */
	if ((fp = AllocateFile(filename, "r")) == NULL)
	{
		ereport(isServerStart ? FATAL : LOG,
				(errcode_for_file_access(),
				 errmsg("could not open DH parameters file \"%s\": %m",
						filename)));
		return NULL;
	}

	dh = PEM_read_DHparams(fp, NULL, NULL, NULL);
	FreeFile(fp);

	if (dh == NULL)
	{
		ereport(isServerStart ? FATAL : LOG,
				(errcode(ERRCODE_CONFIG_FILE_ERROR),
				 errmsg("could not load DH parameters file: %s",
						SSLerrmessage(ERR_get_error()))));
		return NULL;
	}

	/* make sure the DH parameters are usable */
	if (DH_check(dh, &codes) == 0)
	{
		ereport(isServerStart ? FATAL : LOG,
				(errcode(ERRCODE_CONFIG_FILE_ERROR),
				 errmsg("invalid DH parameters: %s",
						SSLerrmessage(ERR_get_error()))));
		return NULL;
	}
	if (codes & DH_CHECK_P_NOT_PRIME)
	{
		ereport(isServerStart ? FATAL : LOG,
				(errcode(ERRCODE_CONFIG_FILE_ERROR),
				 errmsg("invalid DH parameters: p is not prime")));
		return NULL;
	}
	if ((codes & DH_NOT_SUITABLE_GENERATOR) &&
		(codes & DH_CHECK_P_NOT_SAFE_PRIME))
	{
		ereport(isServerStart ? FATAL : LOG,
				(errcode(ERRCODE_CONFIG_FILE_ERROR),
				 errmsg("invalid DH parameters: neither suitable generator or safe prime")));
		return NULL;
	}

	return dh;
}
示例#2
0
/*
 *	Attempt to negotiate secure session.
 */
PostgresPollingStatusType
pqsecure_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
}
示例#3
0
/*
 * Set DH parameters for generating ephemeral DH keys.  The
 * DH parameters can take a long time to compute, so they must be
 * precomputed.
 *
 * Since few sites will bother to create a parameter file, 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 bool
initialize_dh(SSL_CTX *context, bool isServerStart)
{
	DH		   *dh = NULL;

	SSL_CTX_set_options(context, SSL_OP_SINGLE_DH_USE);

	if (ssl_dh_params_file[0])
		dh = load_dh_file(ssl_dh_params_file, isServerStart);
	if (!dh)
		dh = load_dh_buffer(FILE_DH2048, sizeof(FILE_DH2048));
	if (!dh)
	{
		ereport(isServerStart ? FATAL : LOG,
				(errcode(ERRCODE_CONFIG_FILE_ERROR),
				 (errmsg("DH: could not load DH parameters"))));
		return false;
	}

	if (SSL_CTX_set_tmp_dh(context, dh) != 1)
	{
		ereport(isServerStart ? FATAL : LOG,
				(errcode(ERRCODE_CONFIG_FILE_ERROR),
				 (errmsg("DH: could not set DH parameters: %s",
						 SSLerrmessage(ERR_get_error())))));
		return false;
	}
	return true;
}
示例#4
0
ssize_t
be_tls_read(Port *port, void *ptr, size_t len, int *waitfor)
{
	ssize_t		n;
	int			err;
	unsigned long ecode;

	errno = 0;
	ERR_clear_error();
	n = SSL_read(port->ssl, ptr, len);
	err = SSL_get_error(port->ssl, n);
	ecode = (err != SSL_ERROR_NONE || n < 0) ? ERR_get_error() : 0;
	switch (err)
	{
		case SSL_ERROR_NONE:
			/* a-ok */
			break;
		case SSL_ERROR_WANT_READ:
			*waitfor = WL_SOCKET_READABLE;
			errno = EWOULDBLOCK;
			n = -1;
			break;
		case SSL_ERROR_WANT_WRITE:
			*waitfor = WL_SOCKET_WRITEABLE;
			errno = EWOULDBLOCK;
			n = -1;
			break;
		case SSL_ERROR_SYSCALL:
			/* leave it to caller to ereport the value of errno */
			if (n != -1)
			{
				errno = ECONNRESET;
				n = -1;
			}
			break;
		case SSL_ERROR_SSL:
			ereport(COMMERROR,
					(errcode(ERRCODE_PROTOCOL_VIOLATION),
					 errmsg("SSL error: %s", SSLerrmessage(ecode))));
			errno = ECONNRESET;
			n = -1;
			break;
		case SSL_ERROR_ZERO_RETURN:
			/* connection was cleanly shut down by peer */
			n = 0;
			break;
		default:
			ereport(COMMERROR,
					(errcode(ERRCODE_PROTOCOL_VIOLATION),
					 errmsg("unrecognized SSL error code: %d",
							err)));
			errno = ECONNRESET;
			n = -1;
			break;
	}

	return n;
}
/*
 *	Read data from a secure connection.
 */
ssize_t
be_tls_read(Port *port, void *ptr, size_t len, int *waitfor)
{
	ssize_t		n;
	int			err;

	errno = 0;
	n = SSL_read(port->ssl, ptr, len);
	err = SSL_get_error(port->ssl, n);
	switch (err)
	{
		case SSL_ERROR_NONE:
			port->count += n;
			break;
		case SSL_ERROR_WANT_READ:
			*waitfor = WL_SOCKET_READABLE;
			errno = EWOULDBLOCK;
			n = -1;
			break;
		case SSL_ERROR_WANT_WRITE:
			*waitfor = WL_SOCKET_WRITEABLE;
			errno = EWOULDBLOCK;
			n = -1;
			break;
		case SSL_ERROR_SYSCALL:
			/* leave it to caller to ereport the value of errno */
			if (n != -1)
			{
				errno = ECONNRESET;
				n = -1;
			}
			break;
		case SSL_ERROR_SSL:
			ereport(COMMERROR,
					(errcode(ERRCODE_PROTOCOL_VIOLATION),
					 errmsg("SSL error: %s", SSLerrmessage())));
			/* fall through */
		case SSL_ERROR_ZERO_RETURN:
			errno = ECONNRESET;
			n = -1;
			break;
		default:
			ereport(COMMERROR,
					(errcode(ERRCODE_PROTOCOL_VIOLATION),
					 errmsg("unrecognized SSL error code: %d",
							err)));
			errno = ECONNRESET;
			n = -1;
			break;
	}

	return n;
}
/*
 *	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)
{
	FILE	   *fp;
	char		fnbuf[MAXPGPATH];
	DH		   *dh = NULL;
	int			codes;

	/* attempt to open file.  It's not an error if it doesn't exist. */
	snprintf(fnbuf, sizeof(fnbuf), "dh%d.pem", 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)
	{
		elog(LOG, "DH errors (%s): %d bits expected, %d bits found",
			 fnbuf, keylength, 8 * DH_size(dh));
		dh = NULL;
	}

	/* make sure the DH parameters are usable */
	if (dh != NULL)
	{
		if (DH_check(dh, &codes) == 0)
		{
			elog(LOG, "DH_check error (%s): %s", fnbuf,
				 SSLerrmessage(ERR_get_error()));
			return NULL;
		}
		if (codes & DH_CHECK_P_NOT_PRIME)
		{
			elog(LOG, "DH error (%s): p is not prime", fnbuf);
			return NULL;
		}
		if ((codes & DH_NOT_SUITABLE_GENERATOR) &&
			(codes & DH_CHECK_P_NOT_SAFE_PRIME))
		{
			elog(LOG,
				 "DH error (%s): neither suitable generator or safe prime",
				 fnbuf);
			return NULL;
		}
	}

	return dh;
}
示例#7
0
/*
 *	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);
	if (dh == NULL)
		ereport(DEBUG2,
				(errmsg_internal("DH load buffer: %s",
								 SSLerrmessage())));
	BIO_free(bio);

	return dh;
}
示例#8
0
/*
 *	Begin or continue negotiating a secure session.
 */
PostgresPollingStatusType
pqsecure_open_client(PGconn *conn)
{
#ifdef USE_SSL
	/* First time through? */
	if (conn->ssl == NULL)
	{
		/* We cannot use MSG_NOSIGNAL to block SIGPIPE when using SSL */
		conn->sigpipe_flag = false;

		/* Create a connection-specific SSL object */
		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;
		}

		/*
		 * Load client certificate, private key, and trusted CA certs.
		 */
		if (initialize_SSL(conn) != 0)
		{
			/* initialize_SSL already put a message in conn->errorMessage */
			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
}
示例#9
0
/*
 *	Attempt to negotiate SSL connection.
 */
static int
open_server_SSL(Port *port)
{
	Assert(!port->ssl);
	Assert(!port->peer);

	if (!(port->ssl = SSL_new(SSL_context)) ||
		!my_SSL_set_fd(port->ssl, port->sock) ||
		SSL_accept(port->ssl) <= 0)
	{
		ereport(COMMERROR,
				(errcode(ERRCODE_PROTOCOL_VIOLATION),
				 errmsg("could not initialize SSL connection: %s",
						SSLerrmessage())));
		close_SSL(port);
		return -1;
	}
	port->count = 0;

	/* get client certificate, if available. */
	port->peer = SSL_get_peer_certificate(port->ssl);
	if (port->peer == NULL)
	{
		strncpy(port->peer_dn, "(anonymous)", sizeof(port->peer_dn));
		strncpy(port->peer_cn, "(anonymous)", sizeof(port->peer_cn));
	}
	else
	{
		X509_NAME_oneline(X509_get_subject_name(port->peer),
						  port->peer_dn, sizeof(port->peer_dn));
		port->peer_dn[sizeof(port->peer_dn) - 1] = '\0';
		X509_NAME_get_text_by_NID(X509_get_subject_name(port->peer),
				   NID_commonName, port->peer_cn, sizeof(port->peer_cn));
		port->peer_cn[sizeof(port->peer_cn) - 1] = '\0';
	}
	ereport(DEBUG2,
			(errmsg("SSL connection from \"%s\"", port->peer_cn)));

	/* set up debugging/info callback */
	SSL_CTX_set_info_callback(SSL_context, info_cb);

	return 0;
}
示例#10
0
/*
 *	Initialize global SSL context.
 */
static int
initialize_SSL(PGconn *conn)
{
	struct stat buf;
	char		homedir[MAXPGPATH];
	char		fnbuf[MAXPGPATH];

	if (init_ssl_system(conn))
		return -1;

	/* Set up to verify server cert, if root.crt is present */
	if (pqGetHomeDirectory(homedir, sizeof(homedir)))
	{
		snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, ROOTCERTFILE);
		if (stat(fnbuf, &buf) == 0)
		{
			if (!SSL_CTX_load_verify_locations(SSL_context, fnbuf, NULL))
			{
				char	   *err = SSLerrmessage();

				printfPQExpBuffer(&conn->errorMessage,
								  libpq_gettext("could not read root certificate file \"%s\": %s\n"),
								  fnbuf, err);
				SSLerrfree(err);
				return -1;
			}

			SSL_CTX_set_verify(SSL_context, SSL_VERIFY_PEER, verify_cb);
		}
	}

	/* 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;
}
示例#11
0
static int
init_ssl_system(PGconn *conn)
{
#ifdef ENABLE_THREAD_SAFETY
#ifndef WIN32
	static pthread_mutex_t init_mutex = PTHREAD_MUTEX_INITIALIZER;
#else
	static pthread_mutex_t init_mutex = NULL;
	static long mutex_initlock = 0;

	if (init_mutex == NULL)
	{
		while (InterlockedExchange(&mutex_initlock, 1) == 1)
			 /* loop, another thread own the lock */ ;
		if (init_mutex == NULL)
			pthread_mutex_init(&init_mutex, NULL);
		InterlockedExchange(&mutex_initlock, 0);
	}
#endif
	pthread_mutex_lock(&init_mutex);

	if (pq_initssllib && pq_lockarray == NULL)
	{
		int			i;

		CRYPTO_set_id_callback(pq_threadidcallback);

		pq_lockarray = malloc(sizeof(pthread_mutex_t) * CRYPTO_num_locks());
		if (!pq_lockarray)
		{
			pthread_mutex_unlock(&init_mutex);
			return -1;
		}
		for (i = 0; i < CRYPTO_num_locks(); i++)
			pthread_mutex_init(&pq_lockarray[i], NULL);

		CRYPTO_set_locking_callback(pq_lockingcallback);
	}
#endif
	if (!SSL_context)
	{
		if (pq_initssllib)
		{
			SSL_library_init();
			SSL_load_error_strings();
		}
		SSL_context = SSL_CTX_new(TLSv1_method());
		if (!SSL_context)
		{
			char	   *err = SSLerrmessage();

			printfPQExpBuffer(&conn->errorMessage,
						 libpq_gettext("could not create SSL context: %s\n"),
							  err);
			SSLerrfree(err);
#ifdef ENABLE_THREAD_SAFETY
			pthread_mutex_unlock(&init_mutex);
#endif
			return -1;
		}
	}
#ifdef ENABLE_THREAD_SAFETY
	pthread_mutex_unlock(&init_mutex);
#endif
	return 0;
}
示例#12
0
/*
 *	Write data to a secure connection.
 */
ssize_t
secure_write(Port *port, void *ptr, size_t len)
{
	ssize_t		n;

#ifdef USE_SSL
	if (port->ssl)
	{
		int			err;

		if (ssl_renegotiation_limit && port->count > ssl_renegotiation_limit * 1024L)
		{
			SSL_set_session_id_context(port->ssl, (void *) &SSL_context,
									   sizeof(SSL_context));
			if (SSL_renegotiate(port->ssl) <= 0)
				ereport(COMMERROR,
						(errcode(ERRCODE_PROTOCOL_VIOLATION),
						 errmsg("SSL renegotiation failure")));
			if (SSL_do_handshake(port->ssl) <= 0)
				ereport(COMMERROR,
						(errcode(ERRCODE_PROTOCOL_VIOLATION),
						 errmsg("SSL renegotiation failure")));
			if (port->ssl->state != SSL_ST_OK)
				ereport(COMMERROR,
						(errcode(ERRCODE_PROTOCOL_VIOLATION),
						 errmsg("SSL failed to send renegotiation request")));
			port->ssl->state |= SSL_ST_ACCEPT;
			SSL_do_handshake(port->ssl);
			if (port->ssl->state != SSL_ST_OK)
				ereport(COMMERROR,
						(errcode(ERRCODE_PROTOCOL_VIOLATION),
						 errmsg("SSL renegotiation failure")));
			port->count = 0;
		}

wloop:
		errno = 0;
		n = SSL_write(port->ssl, ptr, len);
		err = SSL_get_error(port->ssl, n);
		switch (err)
		{
			case SSL_ERROR_NONE:
				port->count += n;
				break;
			case SSL_ERROR_WANT_READ:
			case SSL_ERROR_WANT_WRITE:
#ifdef WIN32
				pgwin32_waitforsinglesocket(SSL_get_fd(port->ssl),
											(err == SSL_ERROR_WANT_READ) ?
									FD_READ | FD_CLOSE : FD_WRITE | FD_CLOSE,
											INFINITE);
#endif
				goto wloop;
			case SSL_ERROR_SYSCALL:
				/* leave it to caller to ereport the value of errno */
				if (n != -1)
				{
					errno = ECONNRESET;
					n = -1;
				}
				break;
			case SSL_ERROR_SSL:
				ereport(COMMERROR,
						(errcode(ERRCODE_PROTOCOL_VIOLATION),
						 errmsg("SSL error: %s", SSLerrmessage())));
				/* fall through */
			case SSL_ERROR_ZERO_RETURN:
				errno = ECONNRESET;
				n = -1;
				break;
			default:
				ereport(COMMERROR,
						(errcode(ERRCODE_PROTOCOL_VIOLATION),
						 errmsg("unrecognized SSL error code: %d",
								err)));
				n = -1;
				break;
		}
	}
	else
#endif
		n = send(port->sock, ptr, len, 0);

	return n;
}
示例#13
0
/*
 *	Read data from a secure connection.
 */
ssize_t
secure_read(Port *port, void *ptr, size_t len)
{
	ssize_t		n;

#ifdef USE_SSL
	if (port->ssl)
	{
		int			err;

rloop:
		errno = 0;
		n = SSL_read(port->ssl, ptr, len);
		err = SSL_get_error(port->ssl, n);
		switch (err)
		{
			case SSL_ERROR_NONE:
				port->count += n;
				break;
			case SSL_ERROR_WANT_READ:
			case SSL_ERROR_WANT_WRITE:
				if (port->noblock)
				{
					errno = EWOULDBLOCK;
					n = -1;
					break;
				}
#ifdef WIN32
				pgwin32_waitforsinglesocket(SSL_get_fd(port->ssl),
											(err == SSL_ERROR_WANT_READ) ?
									FD_READ | FD_CLOSE : FD_WRITE | FD_CLOSE,
											INFINITE);
#endif
				goto rloop;
			case SSL_ERROR_SYSCALL:
				/* leave it to caller to ereport the value of errno */
				if (n != -1)
				{
					errno = ECONNRESET;
					n = -1;
				}
				break;
			case SSL_ERROR_SSL:
				ereport(COMMERROR,
						(errcode(ERRCODE_PROTOCOL_VIOLATION),
						 errmsg("SSL error: %s", SSLerrmessage())));
				/* fall through */
			case SSL_ERROR_ZERO_RETURN:
				errno = ECONNRESET;
				n = -1;
				break;
			default:
				ereport(COMMERROR,
						(errcode(ERRCODE_PROTOCOL_VIOLATION),
						 errmsg("unrecognized SSL error code: %d",
								err)));
				n = -1;
				break;
		}
	}
	else
#endif
	{
		prepare_for_client_read();

		n = recv(port->sock, ptr, len, 0);

		client_read_ended();
	}

	return n;
}
示例#14
0
/*
 *	Attempt to negotiate SSL connection.
 */
static int
open_server_SSL(Port *port)
{
	int			r;
	int			err;

	Assert(!port->ssl);
	Assert(!port->peer);

	if (!(port->ssl = SSL_new(SSL_context)))
	{
		ereport(COMMERROR,
				(errcode(ERRCODE_PROTOCOL_VIOLATION),
				 errmsg("could not initialize SSL connection: %s",
						SSLerrmessage())));
		close_SSL(port);
		return -1;
	}
	if (!my_SSL_set_fd(port->ssl, port->sock))
	{
		ereport(COMMERROR,
				(errcode(ERRCODE_PROTOCOL_VIOLATION),
				 errmsg("could not set SSL socket: %s",
						SSLerrmessage())));
		close_SSL(port);
		return -1;
	}

aloop:
	r = SSL_accept(port->ssl);
	if (r <= 0)
	{
		err = SSL_get_error(port->ssl, r);
		switch (err)
		{
			case SSL_ERROR_WANT_READ:
			case SSL_ERROR_WANT_WRITE:
#ifdef WIN32
				pgwin32_waitforsinglesocket(SSL_get_fd(port->ssl),
											(err == SSL_ERROR_WANT_READ) ?
					   FD_READ | FD_CLOSE | FD_ACCEPT : FD_WRITE | FD_CLOSE,
											INFINITE);
#endif
				goto aloop;
			case SSL_ERROR_SYSCALL:
				if (r < 0)
					ereport(COMMERROR,
							(errcode_for_socket_access(),
							 errmsg("could not accept SSL connection: %m")));
				else
					ereport(COMMERROR,
							(errcode(ERRCODE_PROTOCOL_VIOLATION),
					errmsg("could not accept SSL connection: EOF detected")));
				break;
			case SSL_ERROR_SSL:
				ereport(COMMERROR,
						(errcode(ERRCODE_PROTOCOL_VIOLATION),
						 errmsg("could not accept SSL connection: %s",
								SSLerrmessage())));
				break;
			case SSL_ERROR_ZERO_RETURN:
				ereport(COMMERROR,
						(errcode(ERRCODE_PROTOCOL_VIOLATION),
				   errmsg("could not accept SSL connection: EOF detected")));
				break;
			default:
				ereport(COMMERROR,
						(errcode(ERRCODE_PROTOCOL_VIOLATION),
						 errmsg("unrecognized SSL error code: %d",
								err)));
				break;
		}
		close_SSL(port);
		return -1;
	}

	port->count = 0;

	/* Get client certificate, if available. */
	port->peer = SSL_get_peer_certificate(port->ssl);

	/* and extract the Common Name from it. */
	port->peer_cn = NULL;
	if (port->peer != NULL)
	{
		int len;

		len = X509_NAME_get_text_by_NID(X509_get_subject_name(port->peer),
						NID_commonName, NULL, 0);

		if (len != -1)
		{
			char *peer_cn;

			peer_cn = MemoryContextAlloc(TopMemoryContext, len + 1);
			r = X509_NAME_get_text_by_NID(X509_get_subject_name(port->peer),
						      NID_commonName, peer_cn, len+1);

			peer_cn[len] = '\0';
			if (r != len)
			{
				/* shouldn't happen */
				pfree(peer_cn);
				close_SSL(port);
				return -1; 
			}

			/*
			 * Reject embedded NULLs in certificate common name to prevent
			 * attacks like CVE-2009-4034.
			 */
			if (len != strlen(peer_cn))
			{
				ereport(COMMERROR,
					(errcode(ERRCODE_PROTOCOL_VIOLATION),
					errmsg("SSL certificate's common name contains embedded null")));
				pfree(peer_cn);
				close_SSL(port);
				return -1;
			}

			port->peer_cn = peer_cn;
		}
	}

	ereport(DEBUG2,
			(errmsg("SSL connection from \"%s\"", 
				port->peer_cn ? port->peer_cn : "(anonymous)")));

	/* set up debugging/info callback */
	SSL_CTX_set_info_callback(SSL_context, info_cb);

	return 0;
}
示例#15
0
/*
 *	Write data to a secure connection.
 */
ssize_t
secure_write(Port *port, void *ptr, size_t len)
{
	ssize_t		n;

#ifdef USE_SSL
	if (port->ssl)
	{
		int			err;

		if (ssl_renegotiation_limit && port->count > ssl_renegotiation_limit * 1024L)
		{
			SSL_set_session_id_context(port->ssl, (void *) &SSL_context,
									   sizeof(SSL_context));
			if (SSL_renegotiate(port->ssl) <= 0)
			{
				report_commerror("SSL renegotiation failure");
			}

			if (SSL_do_handshake(port->ssl) <= 0)
			{
				report_commerror("SSL renegotiation failure");
			}

			if (port->ssl->state != SSL_ST_OK)
			{
				report_commerror("SSL failed to send renegotiation request");
			}

			port->ssl->state |= SSL_ST_ACCEPT;
			SSL_do_handshake(port->ssl);
			if (port->ssl->state != SSL_ST_OK)
			{
				report_commerror("SSL renegotiation failure");
			}

			port->count = 0;
		}

wloop:
		errno = 0;
		n = SSL_write(port->ssl, ptr, len);
		err = SSL_get_error(port->ssl, n);

		const int ERR_MSG_LEN = ERROR_BUF_SIZE + 20;
		char err_msg[ERR_MSG_LEN];

		switch (err)
		{
			case SSL_ERROR_NONE:
				port->count += n;
				break;
			case SSL_ERROR_WANT_READ:
			case SSL_ERROR_WANT_WRITE:
#ifdef WIN32
				pgwin32_waitforsinglesocket(SSL_get_fd(port->ssl),
											(err == SSL_ERROR_WANT_READ) ?
								   FD_READ | FD_CLOSE : FD_WRITE | FD_CLOSE,
											INFINITE);
#endif
				goto wloop;
			case SSL_ERROR_SYSCALL:
				/* leave it to caller to ereport the value of errno */
				if (n != -1)
				{
					errno = ECONNRESET;
					n = -1;
				}
				break;
			case SSL_ERROR_SSL:
				snprintf((char *)&err_msg, ERR_MSG_LEN, "SSL error: %s", SSLerrmessage());
				report_commerror(err_msg);
				/* fall through */
			case SSL_ERROR_ZERO_RETURN:
				errno = ECONNRESET;
				n = -1;
				break;
			default:
				snprintf((char *)&err_msg, ERR_MSG_LEN, "unrecognized SSL error code: %d", err);
				report_commerror(err_msg);

				n = -1;
				break;
		}
	}
	else
#endif
	{
		prepare_for_client_write();
		n = send(port->sock, ptr, len, 0);
		client_write_ended();
	}

	return n;
}
示例#16
0
/*
 *	Attempt to negotiate SSL connection.
 */
static PostgresPollingStatusType
open_client_SSL(PGconn *conn)
{
	int			r;

	r = SSL_connect(conn->ssl);
	if (r <= 0)
	{
		int			err = SSL_get_error(conn->ssl, r);

		switch (err)
		{
			case SSL_ERROR_WANT_READ:
				return PGRES_POLLING_READING;

			case SSL_ERROR_WANT_WRITE:
				return PGRES_POLLING_WRITING;

			case SSL_ERROR_SYSCALL:
				{
					char		sebuf[256];

					if (r == -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"));
					close_SSL(conn);
					return PGRES_POLLING_FAILED;
				}
			case SSL_ERROR_SSL:
				{
					/*
					 * If there are problems with the local certificate files,
					 * these will be detected by client_cert_cb() which is
					 * called from SSL_connect().  We want to return that
					 * error message and not the rather unhelpful error that
					 * OpenSSL itself returns.	So check to see if an error
					 * message was already stored.
					 */
					if (conn->errorMessage.len == 0)
					{
						char	   *err = SSLerrmessage();

						printfPQExpBuffer(&conn->errorMessage,
										  libpq_gettext("SSL error: %s\n"),
										  err);
						SSLerrfree(err);
					}
					close_SSL(conn);
					return PGRES_POLLING_FAILED;
				}

			default:
				printfPQExpBuffer(&conn->errorMessage,
						  libpq_gettext("unrecognized SSL error code: %d\n"),
								  err);
				close_SSL(conn);
				return PGRES_POLLING_FAILED;
		}
	}

	/* check the certificate chain of the server */

#ifdef NOT_USED
	/* CLIENT CERTIFICATES NOT REQUIRED  bjm 2002-09-26 */

	/*
	 * 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 PGRES_POLLING_FAILED;
	}
#endif

	/* pull out server distinguished and common names */
	conn->peer = SSL_get_peer_certificate(conn->ssl);
	if (conn->peer == NULL)
	{
		char	   *err = SSLerrmessage();

		printfPQExpBuffer(&conn->errorMessage,
					libpq_gettext("certificate could not be obtained: %s\n"),
						  err);
		SSLerrfree(err);
		close_SSL(conn);
		return PGRES_POLLING_FAILED;
	}

	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 */

#ifdef NOT_USED
	/* CLIENT CERTIFICATES NOT REQUIRED  bjm 2002-09-26 */

	/*
	 * 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 PGRES_POLLING_FAILED;
	}
#endif

	/* SSL handshake is complete */
	return PGRES_POLLING_OK;
}
示例#17
0
int
be_tls_init(bool isServerStart)
{
	STACK_OF(X509_NAME) *root_cert_list = NULL;
	SSL_CTX    *context;

	/* This stuff need be done only once. */
	if (!SSL_initialized)
	{
#ifdef HAVE_OPENSSL_INIT_SSL
		OPENSSL_init_ssl(OPENSSL_INIT_LOAD_CONFIG, NULL);
#else
		OPENSSL_config(NULL);
		SSL_library_init();
		SSL_load_error_strings();
#endif
		SSL_initialized = true;
	}

	/*
	 * We use SSLv23_method() because it can negotiate use of the highest
	 * mutually supported protocol version, while alternatives like
	 * TLSv1_2_method() permit only one specific version.  Note that we don't
	 * actually allow SSL v2 or v3, only TLS protocols (see below).
	 */
	context = SSL_CTX_new(SSLv23_method());
	if (!context)
	{
		ereport(isServerStart ? FATAL : LOG,
				(errmsg("could not create SSL context: %s",
						SSLerrmessage(ERR_get_error()))));
		goto error;
	}

	/*
	 * Disable OpenSSL's moving-write-buffer sanity check, because it causes
	 * unnecessary failures in nonblocking send cases.
	 */
	SSL_CTX_set_mode(context, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);

	/*
	 * Set password callback
	 */
	if (isServerStart)
	{
		if (ssl_passphrase_command[0])
			SSL_CTX_set_default_passwd_cb(context, ssl_external_passwd_cb);
	}
	else
	{
		if (ssl_passphrase_command[0] && ssl_passphrase_command_supports_reload)
			SSL_CTX_set_default_passwd_cb(context, ssl_external_passwd_cb);
		else

			/*
			 * If reloading and no external command is configured, override
			 * OpenSSL's default handling of passphrase-protected files,
			 * because we don't want to prompt for a passphrase in an
			 * already-running server.
			 */
			SSL_CTX_set_default_passwd_cb(context, dummy_ssl_passwd_cb);
	}
	/* used by the callback */
	ssl_is_server_start = isServerStart;

	/*
	 * Load and verify server's certificate and private key
	 */
	if (SSL_CTX_use_certificate_chain_file(context, ssl_cert_file) != 1)
	{
		ereport(isServerStart ? FATAL : LOG,
				(errcode(ERRCODE_CONFIG_FILE_ERROR),
				 errmsg("could not load server certificate file \"%s\": %s",
						ssl_cert_file, SSLerrmessage(ERR_get_error()))));
		goto error;
	}

	if (!check_ssl_key_file_permissions(ssl_key_file, isServerStart))
		goto error;

	/*
	 * OK, try to load the private key file.
	 */
	dummy_ssl_passwd_cb_called = false;

	if (SSL_CTX_use_PrivateKey_file(context,
									ssl_key_file,
									SSL_FILETYPE_PEM) != 1)
	{
		if (dummy_ssl_passwd_cb_called)
			ereport(isServerStart ? FATAL : LOG,
					(errcode(ERRCODE_CONFIG_FILE_ERROR),
					 errmsg("private key file \"%s\" cannot be reloaded because it requires a passphrase",
							ssl_key_file)));
		else
			ereport(isServerStart ? FATAL : LOG,
					(errcode(ERRCODE_CONFIG_FILE_ERROR),
					 errmsg("could not load private key file \"%s\": %s",
							ssl_key_file, SSLerrmessage(ERR_get_error()))));
		goto error;
	}

	if (SSL_CTX_check_private_key(context) != 1)
	{
		ereport(isServerStart ? FATAL : LOG,
				(errcode(ERRCODE_CONFIG_FILE_ERROR),
				 errmsg("check of private key failed: %s",
						SSLerrmessage(ERR_get_error()))));
		goto error;
	}

	if (ssl_min_protocol_version)
		SSL_CTX_set_min_proto_version(context,
									  ssl_protocol_version_to_openssl(ssl_min_protocol_version,
																	  "ssl_min_protocol_version"));
	if (ssl_max_protocol_version)
		SSL_CTX_set_max_proto_version(context,
									  ssl_protocol_version_to_openssl(ssl_max_protocol_version,
																	  "ssl_max_protocol_version"));

	/* disallow SSL session tickets */
#ifdef SSL_OP_NO_TICKET			/* added in OpenSSL 0.9.8f */
	SSL_CTX_set_options(context, SSL_OP_NO_TICKET);
#endif

	/* disallow SSL session caching, too */
	SSL_CTX_set_session_cache_mode(context, SSL_SESS_CACHE_OFF);

	/* set up ephemeral DH and ECDH keys */
	if (!initialize_dh(context, isServerStart))
		goto error;
	if (!initialize_ecdh(context, isServerStart))
		goto error;

	/* set up the allowed cipher list */
	if (SSL_CTX_set_cipher_list(context, SSLCipherSuites) != 1)
	{
		ereport(isServerStart ? FATAL : LOG,
				(errcode(ERRCODE_CONFIG_FILE_ERROR),
				 errmsg("could not set the cipher list (no valid ciphers available)")));
		goto error;
	}

	/* Let server choose order */
	if (SSLPreferServerCiphers)
		SSL_CTX_set_options(context, SSL_OP_CIPHER_SERVER_PREFERENCE);

	/*
	 * Load CA store, so we can verify client certificates if needed.
	 */
	if (ssl_ca_file[0])
	{
		if (SSL_CTX_load_verify_locations(context, ssl_ca_file, NULL) != 1 ||
			(root_cert_list = SSL_load_client_CA_file(ssl_ca_file)) == NULL)
		{
			ereport(isServerStart ? FATAL : LOG,
					(errcode(ERRCODE_CONFIG_FILE_ERROR),
					 errmsg("could not load root certificate file \"%s\": %s",
							ssl_ca_file, SSLerrmessage(ERR_get_error()))));
			goto error;
		}
	}

	/*----------
	 * Load the Certificate Revocation List (CRL).
	 * http://searchsecurity.techtarget.com/sDefinition/0,,sid14_gci803160,00.html
	 *----------
	 */
	if (ssl_crl_file[0])
	{
		X509_STORE *cvstore = SSL_CTX_get_cert_store(context);

		if (cvstore)
		{
			/* Set the flags to check against the complete CRL chain */
			if (X509_STORE_load_locations(cvstore, ssl_crl_file, NULL) == 1)
			{
				/* OpenSSL 0.96 does not support X509_V_FLAG_CRL_CHECK */
#ifdef X509_V_FLAG_CRL_CHECK
				X509_STORE_set_flags(cvstore,
									 X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL);
#else
				ereport(LOG,
						(errcode(ERRCODE_CONFIG_FILE_ERROR),
						 errmsg("SSL certificate revocation list file \"%s\" ignored",
								ssl_crl_file),
						 errdetail("SSL library does not support certificate revocation lists.")));
#endif
			}
			else
			{
				ereport(isServerStart ? FATAL : LOG,
						(errcode(ERRCODE_CONFIG_FILE_ERROR),
						 errmsg("could not load SSL certificate revocation list file \"%s\": %s",
								ssl_crl_file, SSLerrmessage(ERR_get_error()))));
				goto error;
			}
		}
	}

	if (ssl_ca_file[0])
	{
		/*
		 * Always ask for SSL client cert, but don't fail if it's not
		 * presented.  We might fail such connections later, depending on what
		 * we find in pg_hba.conf.
		 */
		SSL_CTX_set_verify(context,
						   (SSL_VERIFY_PEER |
							SSL_VERIFY_CLIENT_ONCE),
						   verify_cb);

		/*
		 * Tell OpenSSL to send the list of root certs we trust to clients in
		 * CertificateRequests.  This lets a client with a keystore select the
		 * appropriate client certificate to send to us.
		 */
		SSL_CTX_set_client_CA_list(context, root_cert_list);
	}

	/*
	 * Success!  Replace any existing SSL_context.
	 */
	if (SSL_context)
		SSL_CTX_free(SSL_context);

	SSL_context = context;

	/*
	 * Set flag to remember whether CA store has been loaded into SSL_context.
	 */
	if (ssl_ca_file[0])
		ssl_loaded_verify_locations = true;
	else
		ssl_loaded_verify_locations = false;

	return 0;

error:
	if (context)
		SSL_CTX_free(context);
	return -1;
}
示例#18
0
/*
 *	Write data to a secure connection.
 */
ssize_t
pqsecure_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
						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 = send(conn->sock, ptr, len, 0);
#if defined(ENABLE_THREAD_SAFETY) && !defined(WIN32)
		if (n < 0 && SOCK_ERRNO == EPIPE)
			got_epipe = true;
#endif
	}

#ifndef WIN32
#ifdef ENABLE_THREAD_SAFETY
	pq_reset_sigpipe(&osigmask, sigpipe_pending, got_epipe);
#else
	pqsignal(SIGPIPE, oldsighandler);
#endif   /* ENABLE_THREAD_SAFETY */
#endif   /* WIN32 */

	return n;
}
示例#19
0
/*++
 * Function:	Get_Server_conn
 *
 * Purpose:	When a client login attempt is made, fetch a usable server
 *              connection descriptor.  This means that either we reuse an
 *              existing ICD, or we open a new one.  Hide that abstraction from
 *              the caller...
 *
 * Parameters:	ptr to username string
 *		ptr to password string
 *              const ptr to client hostname or IP string (for logging only)
 *              in_port_t, client port number (for logging only)
 *              unsigned char - flag to indicate that the client sent the
 *                              password as a string literal.
 *
 * Returns:	ICD * on success
 *              NULL on failure
 *
 * Authors:	Dave McMurtrie <*****@*****.**>
 *
 * Credit:      Major SSL additions by Ken Murchison <*****@*****.**>
 *
 *--
 */
extern ICD_Struct *Get_Server_conn( char *Username, 
				    char *Password,
				    const char *ClientAddr,
				    in_port_t sin_port,
				    unsigned char LiteralPasswd )
{
    char *fn = "Get_Server_conn()";
    unsigned int HashIndex;
    ICC_Struct *HashEntry = NULL;
    char SendBuf[BUFSIZE];
    unsigned int BufLen = BUFSIZE - 1;
    char md5pw[MD5_DIGEST_LENGTH];
    char *tokenptr;
    char *endptr;
    char *last;
    ICC_Struct *ICC_Active;
    ICC_Struct *ICC_tptr;
    ITD_Struct Server;
    int rc;
    unsigned int Expiration;

    EVP_MD_CTX mdctx;
    int md_len;

    Expiration = PC_Struct.cache_expiration_time;
    memset( &Server, 0, sizeof Server );
    
    /* need to md5 the passwd regardless, so do that now */
    EVP_DigestInit(&mdctx, EVP_md5());
    EVP_DigestUpdate(&mdctx, Password, strlen(Password));
    EVP_DigestFinal(&mdctx, md5pw, &md_len);
    
    /* see if we have a reusable connection available */
    ICC_Active = NULL;
    HashIndex = Hash( Username, HASH_TABLE_SIZE );
    
    LockMutex( &mp );
        
    /*
     * Now we just iterate through the linked list at this hash index until
     * we either find the string we're looking for or we find a NULL.
     */
    for ( HashEntry = ICC_HashTable[ HashIndex ]; 
	  HashEntry; 
	  HashEntry = HashEntry->next )
    {
	if ( ( strcmp( Username, HashEntry->username ) == 0 ) &&
	     ( HashEntry->logouttime > 1 ) )
	{
	    ICC_Active = HashEntry;
	    /*
	     * we found this username in our hash table.  Need to know if
	     * the password matches.
	     */
	    if ( memcmp( md5pw, ICC_Active->hashedpw, sizeof md5pw ) )
	    {
		syslog( LOG_NOTICE, "%s: Unable to reuse server sd [%d] for user '%s' (%s:%d) because password doesn't match.", fn, ICC_Active->server_conn->sd, Username, ClientAddr, sin_port );
		ICC_Active->logouttime = 1;
	    }
	    else
	    {
		/*
		 * We found a matching password on an inactive server socket.
		 * We can use this guy.  Before we release the mutex, set the
		 * logouttime such that we mark this connection as "active"
		 * again.
		 */
		ICC_Active->logouttime = 0;
	
		/*
		 * The fact that we have this stored in a table as an open
		 * server socket doesn't really mean that it's open.  The
		 * server could've closed it on us.
		 * We need a speedy way to make sure this is still open.
		 * We'll set the fd to non-blocking and try to read from it.
		 * If we get a zero back, the connection is closed.  If we get
		 * EWOULDBLOCK (or some data) we know it's still open.  If we
		 * do read data, make sure we read all the data so we "drain"
		 * any puss that may be left on this socket.
		 */
		fcntl( ICC_Active->server_conn->sd, F_SETFL,
		       fcntl( ICC_Active->server_conn->sd, F_GETFL, 0) | O_NONBLOCK );
		
		while ( ( rc = IMAP_Read( ICC_Active->server_conn, Server.ReadBuf, 
				     sizeof Server.ReadBuf ) ) > 0 );
		
		if ( !rc )
		{
		    syslog(LOG_NOTICE, "%s: Unable to reuse server sd [%d] for user '%s' (%s:%d).  Connection closed by server.", fn, ICC_Active->server_conn->sd, Username, ClientAddr, sin_port );
		    ICC_Active->logouttime = 1;
		    continue;
		}
	    
		if ( errno != EWOULDBLOCK )
		{
		    syslog(LOG_NOTICE, "%s: Unable to reuse server sd [%d] for user '%s' (%s:%d). IMAP_read() error: %s", fn, ICC_Active->server_conn->sd, Username, ClientAddr, sin_port, strerror( errno ) );
		    ICC_Active->logouttime = 1;
		    continue;
		}
		
		fcntl( ICC_Active->server_conn->sd, F_SETFL, 
		       fcntl( ICC_Active->server_conn->sd, F_GETFL, 0) & ~O_NONBLOCK );
		
		/* now release the mutex and return the sd to the caller */
		UnLockMutex( &mp );

		/*
		 * We're reusing an existing server socket.  There are a few
		 * counters we have to deal with.
		 */
		IMAPCount->RetainedServerConnections--;
		IMAPCount->InUseServerConnections++;
		IMAPCount->TotalServerConnectionsReused++;
		
		if ( IMAPCount->InUseServerConnections >
		     IMAPCount->PeakInUseServerConnections )
		    IMAPCount->PeakInUseServerConnections = IMAPCount->InUseServerConnections;
	    
		syslog(LOG_INFO, "LOGIN: '******' (%s:%d) on existing sd [%d]", Username, ClientAddr, sin_port, ICC_Active->server_conn->sd );
		return( ICC_Active->server_conn );
	    }
	}
    }
    
    
    UnLockMutex( &mp );
    
    /*
     * We don't have an active connection for this user, or the password
     * didn't match.
     * Open a connection to the IMAP server so we can attempt to login 
     */
    Server.conn = ( ICD_Struct * ) malloc( sizeof ( ICD_Struct ) );
    memset( Server.conn, 0, sizeof ( ICD_Struct ) );
    Server.conn->sd = socket( PF_INET, SOCK_STREAM, IPPROTO_TCP );
    if ( Server.conn->sd == -1 )
    {
	syslog(LOG_INFO, "LOGIN: '******' (%s:%d) failed: Unable to open server socket: %s", Username, ClientAddr, sin_port, strerror( errno ) );
	goto fail;
    }

    if ( PC_Struct.send_tcp_keepalives )
    {
	int onoff = 1;
	setsockopt( Server.conn->sd, SOL_SOCKET, SO_KEEPALIVE, &onoff, sizeof onoff );
    }
    
    if ( connect( Server.conn->sd, (struct sockaddr *)&ISD.srv, 
		  sizeof(ISD.srv) ) == -1 )
    {
	syslog(LOG_INFO, "LOGIN: '******' (%s:%d) failed: Unable to connect to IMAP server: %s", Username, ClientAddr, sin_port, strerror( errno ) );
	goto fail;
    }
    
    
    /* Read & throw away the banner line from the server */
    
    if ( IMAP_Line_Read( &Server ) == -1 )
    {
	syslog(LOG_INFO, "LOGIN: '******' (%s:%d) failed: No banner line received from IMAP server", Username, ClientAddr, sin_port );
	goto fail;
    }


    /*
     * Do STARTTLS if necessary.
     */
#if HAVE_LIBSSL
    if ( PC_Struct.login_disabled )
    {
	snprintf( SendBuf, BufLen, "S0001 STARTTLS\r\n" );
	if ( IMAP_Write( Server.conn, SendBuf, strlen(SendBuf) ) == -1 )
	{
	    syslog(LOG_INFO, "STARTTLS failed: IMAP_Write() failed attempting to send STARTTLS command to IMAP server: %s", strerror( errno ) );
	    goto fail;
	}

	/*
	 * Read the server response
	 */
	if ( ( rc = IMAP_Line_Read( &Server ) ) == -1 )
	{
	    syslog(LOG_INFO, "STARTTLS failed: No response from IMAP server after sending STARTTLS command" );
	    goto fail;
	}
    
	/*
	 * Try to match up the tag in the server response to the client tag.
	 */
	endptr = Server.ReadBuf + rc;
    
	tokenptr = memtok( Server.ReadBuf, endptr, &last );
    
	if ( !tokenptr )
	{
	    /* 
	     * no tokens found in server response?  Not likely, but we still
	     * have to check.
	     */
	    syslog(LOG_INFO, "STARTTLS failed: server response to STARTTLS command contained no tokens." );
	    goto fail;
	}
    
	if ( memcmp( (const void *)tokenptr, (const void *)"S0001", 
		     strlen( tokenptr ) ) )
	{
	    /* 
	     * non-matching tag read back from the server... Lord knows what this
	     * is, so we'll fail.
	     */
	    syslog(LOG_INFO, "STARTTLS failed: server response to STARTTLS command contained non-matching tag." );
	    goto fail;
	}
    
	/*
	 * Now that we've matched the tags up, see if the response was 'OK'
	 */
	tokenptr = memtok( NULL, endptr, &last );
    
	if ( !tokenptr )
	{
	    /* again, not likely but we still have to check... */
	    syslog(LOG_INFO, "STARTTLS failed: Malformed server response to STARTTLS command" );
	    goto fail;
	}
    
	if ( memcmp( (const void *)tokenptr, "OK", 2 ) )
	{
	    /*
	     * If the server sent back a "NO" or "BAD", we can look at the actual
	     * server logs to figure out why.  We don't have to break our ass here
	     * putting the string back together just for the sake of logging.
	     */
	    syslog(LOG_INFO, "STARTTLS failed: non-OK server response to STARTTLS command" );
	    goto fail;
	}
    
	Server.conn->tls = SSL_new( tls_ctx );
	if ( Server.conn->tls == NULL )
	{
	    syslog(LOG_INFO, "STARTTLS failed: SSL_new() failed" );
	    goto fail;
	}
	    
	SSL_clear( Server.conn->tls );
	rc = SSL_set_fd( Server.conn->tls, Server.conn->sd );
	if ( rc == 0 )
	{
	    syslog(LOG_INFO, "STARTTLS failed: SSL_set_fd() failed: %d",
		   SSL_get_error( Server.conn->tls, rc ) );
	    goto fail;
	}

	SSL_set_connect_state( Server.conn->tls );
	rc = SSL_connect( Server.conn->tls );
	if ( rc <= 0 )
	{
	    syslog(LOG_INFO, "STARTTLS failed: SSL_connect() failed, %d: %s",
		   SSL_get_error( Server.conn->tls, rc ), SSLerrmessage() );
	    goto fail;
	}

	/* XXX Should we grab the session id for later reuse? */
    }
#endif /* HAVE_LIBSSL */


    /*
     * Send the login command off to the IMAP server.  Have to treat a literal
     * password different.
     */
    if ( LiteralPasswd )
    {
	snprintf( SendBuf, BufLen, "A0001 LOGIN %s {%d}\r\n", 
		  Username, strlen( Password ) );
	if ( IMAP_Write( Server.conn, SendBuf, strlen(SendBuf) ) == -1 )
	{
	    syslog(LOG_INFO, "LOGIN: '******' (%s:%d) failed: IMAP_Write() failed attempting to send LOGIN command to IMAP server: %s", Username, ClientAddr, sin_port, strerror( errno ) );
	    goto fail;
	}
	
	/*
	 * the server response should be a go ahead
	 */
	if ( ( rc = IMAP_Line_Read( &Server ) ) == -1 )
	{
	    syslog(LOG_INFO, "LOGIN: '******' (%s:%d) failed: Failed to receive go-ahead from IMAP server after sending LOGIN command", Username, ClientAddr, sin_port );
	    goto fail;
	}
	
	if ( Server.ReadBuf[0] != '+' )
	{
	    syslog( LOG_INFO, "LOGIN: '******' (%s:%d) failed: bad response from server after sending string literal specifier", Username, ClientAddr, sin_port );
	    goto fail;
	}
	
	/* 
	 * now send the password
	 */
	snprintf( SendBuf, BufLen, "%s\r\n", Password );
	
	if ( IMAP_Write( Server.conn, SendBuf, strlen( SendBuf ) ) == -1 )
	{
	    syslog(LOG_INFO, "LOGIN: '******' (%s:%d) failed: IMAP_Write() failed attempting to send literal password to IMAP server: %s", Username, ClientAddr, sin_port, strerror( errno ) );
	    goto fail;
	}
    }
    else
    {
	/*
	 * just send the login command via normal means.
	 */
	snprintf( SendBuf, BufLen, "A0001 LOGIN %s %s\r\n", 
		  Username, Password );
	
	if ( IMAP_Write( Server.conn, SendBuf, strlen(SendBuf) ) == -1 )
	{
	    syslog(LOG_INFO, "LOGIN: '******' (%s:%d) failed: IMAP_Write() failed attempting to send LOGIN command to IMAP server: %s", Username, ClientAddr, sin_port, strerror( errno ) );
	    goto fail;
	}
    }
    
	
    /*
     * Read the server response.  From RFC 3501:
     *
     * A server MAY include a CAPABILITY response code in the tagged OK
     * response to a successful LOGIN command in order to send
     * capabilities automatically.  It is unnecessary for a client to
     * send a separate CAPABILITY command if it recognizes these
     * automatic capabilities.
     *
     * We have to be ready for the possibility that this might be an 
     * untagged response...  In an ideal world, we'd want to pass the
     * untagged stuff back to the client.  For now, since the RFC doesn't
     * mandate that behaviour, we're not going to since we don't have a client
     * socket descriptor to send it to.
     */
    for ( ;; )
    {
	if ( ( rc = IMAP_Line_Read( &Server ) ) == -1 )
	{
	    syslog(LOG_INFO, "LOGIN: '******' (%s:%d) failed: No response from IMAP server after sending LOGIN command", Username, ClientAddr, sin_port );
	    goto fail;
	}
    
	if ( Server.ReadBuf[0] != '*' )
	    break;
    }
    
    
    /*
     * Try to match up the tag in the server response to the client tag.
     */
    endptr = Server.ReadBuf + rc;
    
    tokenptr = memtok( Server.ReadBuf, endptr, &last );
    
    if ( !tokenptr )
    {
	/* 
	 * no tokens found in server response?  Not likely, but we still
	 * have to check.
	 */
	syslog(LOG_INFO, "LOGIN: '******' (%s:%d) failed: server response to LOGIN command contained no tokens.", Username, ClientAddr, sin_port );
	goto fail;
    }
    
    if ( memcmp( (const void *)tokenptr, (const void *)"A0001", 
		 strlen( tokenptr ) ) )
    {
	/* 
	 * non-matching tag read back from the server... Lord knows what this
	 * is, so we'll fail.
	 */
	syslog(LOG_INFO, "LOGIN: '******' (%s:%d) failed: server response to LOGIN command contained non-matching tag.", Username, ClientAddr, sin_port );
	goto fail;
    }
    
    
    /*
     * Now that we've matched the tags up, see if the response was 'OK'
     */
    tokenptr = memtok( NULL, endptr, &last );
    
    if ( !tokenptr )
    {
	/* again, not likely but we still have to check... */
	syslog(LOG_INFO, "LOGIN: '******' (%s:%d) failed: Malformed server response to LOGIN command", Username, ClientAddr, sin_port );
	goto fail;
    }
    
    if ( memcmp( (const void *)tokenptr, "OK", 2 ) )
    {
	/*
	 * If the server sent back a "NO" or "BAD", we can look at the actual
	 * server logs to figure out why.  We don't have to break our ass here
	 * putting the string back together just for the sake of logging.
	 */
	syslog(LOG_INFO, "LOGIN: '******' (%s:%d) failed: non-OK server response to LOGIN command", Username, ClientAddr, sin_port );
	goto fail;
    }
    
    /*
     * put this in our used list and remove it from the free list
     */
    for( ; ; )
    {
	LockMutex( &mp );
	
	if ( ICC_free->next )
	{
	    /* generate the hash index */
	    HashIndex = Hash( Username, HASH_TABLE_SIZE );
	    
	    /* temporarily store the address of the next free structure */
	    ICC_tptr = ICC_free->next;
	    
	    /*
	     * We want to add the newest "used" structure at the front of
	     * the list at the hash index.
	     */
	    ICC_free->next = ICC_HashTable[ HashIndex ];
	    ICC_HashTable[ HashIndex ] = ICC_free;
	    
	    /* 
	     * less typing and more readability, set an "active" pointer.
	     */
	    ICC_Active = ICC_free;
	    
	    /* now point the free listhead to the next available free struct */
	    ICC_free = ICC_tptr;
	    
	    /* fill in the newest used (oxymoron?) structure */
	    strncpy( ICC_Active->username, Username, 
		     sizeof ICC_Active->username );
	    memcpy( ICC_Active->hashedpw, md5pw, sizeof ICC_Active->hashedpw );
	    ICC_Active->logouttime = 0;    /* zero means, "it's active". */
	    ICC_Active->server_conn = Server.conn;
	    
	    UnLockMutex( &mp );
	    
	    IMAPCount->InUseServerConnections++;
	    IMAPCount->TotalServerConnectionsCreated++;

	    if ( IMAPCount->InUseServerConnections >
		 IMAPCount->PeakInUseServerConnections )
		IMAPCount->PeakInUseServerConnections = IMAPCount->InUseServerConnections;
	    syslog(LOG_INFO, "LOGIN: '******' (%s:%d) on new sd [%d]", Username, ClientAddr, sin_port, Server.conn->sd );
	    return( Server.conn );
	}
	
	/*
	 * There weren't any free ICC structs.  Try to free one.  Make sure
	 * we unlock the mutex, since ICC_Recycle needs to obtain it.
	 */
	UnLockMutex( &mp );
	
	Expiration = abs( Expiration / 2 );
	
	/*
	 * Eventually, we have to fail
	 */
	if ( Expiration <= 2 )
	{
	    syslog(LOG_INFO, "LOGIN: '******' (%s:%d) failed: Out of free ICC structs.", Username, ClientAddr, sin_port );
	    goto fail;
	}
	
	ICC_Recycle( Expiration );
	
    }
    
  fail:
#if HAVE_LIBSSL
    if ( Server.conn->tls )
    {
	SSL_shutdown( Server.conn->tls );
	SSL_free( Server.conn->tls );
    }
#endif
    close( Server.conn->sd );
    free( Server.conn );
    return( NULL );
}
示例#20
0
/*
 *	Initialize global SSL context.
 */
static void
initialize_SSL(void)
{
	struct stat buf;

	STACK_OF(X509_NAME) *root_cert_list = NULL;

	if (!SSL_context)
	{
#if SSLEAY_VERSION_NUMBER >= 0x0907000L
		OPENSSL_config(NULL);
#endif
		SSL_library_init();
		SSL_load_error_strings();
		SSL_context = SSL_CTX_new(SSLv23_method());
		if (!SSL_context)
			ereport(FATAL,
					(errmsg("could not create SSL context: %s",
							SSLerrmessage())));

		/*
		 * Disable OpenSSL's moving-write-buffer sanity check, because it
		 * causes unnecessary failures in nonblocking send cases.
		 */
		SSL_CTX_set_mode(SSL_context, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);

		/*
		 * Load and verify server's certificate and private key
		 */
		if (SSL_CTX_use_certificate_chain_file(SSL_context,
											   SERVER_CERT_FILE) != 1)
			ereport(FATAL,
					(errcode(ERRCODE_CONFIG_FILE_ERROR),
				  errmsg("could not load server certificate file \"%s\": %s",
						 SERVER_CERT_FILE, SSLerrmessage())));

		if (stat(SERVER_PRIVATE_KEY_FILE, &buf) != 0)
			ereport(FATAL,
					(errcode_for_file_access(),
					 errmsg("could not access private key file \"%s\": %m",
							SERVER_PRIVATE_KEY_FILE)));

		/*
		 * Require no public access to key file.
		 *
		 * XXX temporarily suppress check when on Windows, because there may
		 * not be proper support for Unix-y file permissions.  Need to think
		 * of a reasonable check to apply on Windows.  (See also the data
		 * directory permission check in postmaster.c)
		 */
#if !defined(WIN32) && !defined(__CYGWIN__)
		if (!S_ISREG(buf.st_mode) || buf.st_mode & (S_IRWXG | S_IRWXO))
			ereport(FATAL,
					(errcode(ERRCODE_CONFIG_FILE_ERROR),
				  errmsg("private key file \"%s\" has group or world access",
						 SERVER_PRIVATE_KEY_FILE),
				   errdetail("Permissions should be u=rw (0600) or less.")));
#endif

		if (SSL_CTX_use_PrivateKey_file(SSL_context,
										SERVER_PRIVATE_KEY_FILE,
										SSL_FILETYPE_PEM) != 1)
			ereport(FATAL,
					(errmsg("could not load private key file \"%s\": %s",
							SERVER_PRIVATE_KEY_FILE, SSLerrmessage())));

		if (SSL_CTX_check_private_key(SSL_context) != 1)
			ereport(FATAL,
					(errmsg("check of private key failed: %s",
							SSLerrmessage())));
	}

	/* set up ephemeral DH keys, and disallow SSL v2 while at it */
	SSL_CTX_set_tmp_dh_callback(SSL_context, tmp_dh_cb);
	SSL_CTX_set_options(SSL_context, SSL_OP_SINGLE_DH_USE | SSL_OP_NO_SSLv2);

	/* set up the allowed cipher list */
	if (SSL_CTX_set_cipher_list(SSL_context, SSLCipherSuites) != 1)
		elog(FATAL, "could not set the cipher list (no valid ciphers available)");

	/*
	 * Attempt to load CA store, so we can verify client certificates if
	 * needed.
	 */
	ssl_loaded_verify_locations = false;

	if (access(ROOT_CERT_FILE, R_OK) != 0)
	{
		/*
		 * If root certificate file simply not found, don't log an error here,
		 * because it's quite likely the user isn't planning on using client
		 * certificates. If we can't access it for other reasons, it is an
		 * error.
		 */
		if (errno != ENOENT)
			ereport(FATAL,
				 (errmsg("could not access root certificate file \"%s\": %m",
						 ROOT_CERT_FILE)));
	}
	else if (SSL_CTX_load_verify_locations(SSL_context, ROOT_CERT_FILE, NULL) != 1 ||
		  (root_cert_list = SSL_load_client_CA_file(ROOT_CERT_FILE)) == NULL)
	{
		/*
		 * File was there, but we could not load it. This means the file is
		 * somehow broken, and we cannot do verification at all - so fail.
		 */
		ereport(FATAL,
				(errmsg("could not load root certificate file \"%s\": %s",
						ROOT_CERT_FILE, SSLerrmessage())));
	}
	else
	{
		/*----------
		 * Load the Certificate Revocation List (CRL) if file exists.
		 * http://searchsecurity.techtarget.com/sDefinition/0,,sid14_gci803160,00.html
		 *----------
		 */
		X509_STORE *cvstore = SSL_CTX_get_cert_store(SSL_context);

		if (cvstore)
		{
			/* Set the flags to check against the complete CRL chain */
			if (X509_STORE_load_locations(cvstore, ROOT_CRL_FILE, NULL) == 1)
			{
				/* OpenSSL 0.96 does not support X509_V_FLAG_CRL_CHECK */
#ifdef X509_V_FLAG_CRL_CHECK
				X509_STORE_set_flags(cvstore,
						  X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL);
#else
				ereport(LOG,
				(errmsg("SSL certificate revocation list file \"%s\" ignored",
						ROOT_CRL_FILE),
				 errdetail("SSL library does not support certificate revocation lists.")));
#endif
			}
			else
			{
				/* Not fatal - we do not require CRL */
				ereport(LOG,
						(errmsg("SSL certificate revocation list file \"%s\" not found, skipping: %s",
								ROOT_CRL_FILE, SSLerrmessage()),
						 errdetail("Certificates will not be checked against revocation list.")));
			}

			/*
			 * Always ask for SSL client cert, but don't fail if it's not
			 * presented.  We might fail such connections later, depending on
			 * what we find in pg_hba.conf.
			 */
			SSL_CTX_set_verify(SSL_context,
							   (SSL_VERIFY_PEER |
								SSL_VERIFY_CLIENT_ONCE),
							   verify_cb);

			/* Set flag to remember CA store is successfully loaded */
			ssl_loaded_verify_locations = true;
		}

		/*
		 * Tell OpenSSL to send the list of root certs we trust to clients in
		 * CertificateRequests.  This lets a client with a keystore select the
		 * appropriate client certificate to send to us.
		 */
		SSL_CTX_set_client_CA_list(SSL_context, root_cert_list);
	}
}
示例#21
0
/*
 *	Attempt to negotiate SSL connection.
 */
int
be_tls_open_server(Port *port)
{
	int			r;
	int			err;
	int			waitfor;

	Assert(!port->ssl);
	Assert(!port->peer);

	if (!(port->ssl = SSL_new(SSL_context)))
	{
		ereport(COMMERROR,
				(errcode(ERRCODE_PROTOCOL_VIOLATION),
				 errmsg("could not initialize SSL connection: %s",
						SSLerrmessage())));
		be_tls_close(port);
		return -1;
	}
	if (!my_SSL_set_fd(port, port->sock))
	{
		ereport(COMMERROR,
				(errcode(ERRCODE_PROTOCOL_VIOLATION),
				 errmsg("could not set SSL socket: %s",
						SSLerrmessage())));
		be_tls_close(port);
		return -1;
	}
	port->ssl_in_use = true;

aloop:
	r = SSL_accept(port->ssl);
	if (r <= 0)
	{
		err = SSL_get_error(port->ssl, r);
		switch (err)
		{
			case SSL_ERROR_WANT_READ:
			case SSL_ERROR_WANT_WRITE:
				/* not allowed during connection establishment */
				Assert(!port->noblock);

				/*
				 * No need to care about timeouts/interrupts here. At this
				 * point authentication_timeout still employs
				 * StartupPacketTimeoutHandler() which directly exits.
				 */
				if (err == SSL_ERROR_WANT_READ)
					waitfor = WL_SOCKET_READABLE;
				else
					waitfor = WL_SOCKET_WRITEABLE;

				WaitLatchOrSocket(MyLatch, waitfor, port->sock, 0);
				goto aloop;
			case SSL_ERROR_SYSCALL:
				if (r < 0)
					ereport(COMMERROR,
							(errcode_for_socket_access(),
							 errmsg("could not accept SSL connection: %m")));
				else
					ereport(COMMERROR,
							(errcode(ERRCODE_PROTOCOL_VIOLATION),
					errmsg("could not accept SSL connection: EOF detected")));
				break;
			case SSL_ERROR_SSL:
				ereport(COMMERROR,
						(errcode(ERRCODE_PROTOCOL_VIOLATION),
						 errmsg("could not accept SSL connection: %s",
								SSLerrmessage())));
				break;
			case SSL_ERROR_ZERO_RETURN:
				ereport(COMMERROR,
						(errcode(ERRCODE_PROTOCOL_VIOLATION),
				   errmsg("could not accept SSL connection: EOF detected")));
				break;
			default:
				ereport(COMMERROR,
						(errcode(ERRCODE_PROTOCOL_VIOLATION),
						 errmsg("unrecognized SSL error code: %d",
								err)));
				break;
		}
		be_tls_close(port);
		return -1;
	}

	port->count = 0;

	/* Get client certificate, if available. */
	port->peer = SSL_get_peer_certificate(port->ssl);

	/* and extract the Common Name from it. */
	port->peer_cn = NULL;
	port->peer_cert_valid = false;
	if (port->peer != NULL)
	{
		int			len;

		len = X509_NAME_get_text_by_NID(X509_get_subject_name(port->peer),
										NID_commonName, NULL, 0);
		if (len != -1)
		{
			char	   *peer_cn;

			peer_cn = MemoryContextAlloc(TopMemoryContext, len + 1);
			r = X509_NAME_get_text_by_NID(X509_get_subject_name(port->peer),
										  NID_commonName, peer_cn, len + 1);
			peer_cn[len] = '\0';
			if (r != len)
			{
				/* shouldn't happen */
				pfree(peer_cn);
				be_tls_close(port);
				return -1;
			}

			/*
			 * Reject embedded NULLs in certificate common name to prevent
			 * attacks like CVE-2009-4034.
			 */
			if (len != strlen(peer_cn))
			{
				ereport(COMMERROR,
						(errcode(ERRCODE_PROTOCOL_VIOLATION),
						 errmsg("SSL certificate's common name contains embedded null")));
				pfree(peer_cn);
				be_tls_close(port);
				return -1;
			}

			port->peer_cn = peer_cn;
		}
		port->peer_cert_valid = true;
	}

	ereport(DEBUG2,
			(errmsg("SSL connection from \"%s\"",
					port->peer_cn ? port->peer_cn : "(anonymous)")));

	/* set up debugging/info callback */
	SSL_CTX_set_info_callback(SSL_context, info_cb);

	return 0;
}
示例#22
0
/*
 *	Initialize global SSL context.
 */
void
be_tls_init(void)
{
	struct stat buf;

	STACK_OF(X509_NAME) *root_cert_list = NULL;

	if (!SSL_context)
	{
#if SSLEAY_VERSION_NUMBER >= 0x0907000L
		OPENSSL_config(NULL);
#endif
		SSL_library_init();
		SSL_load_error_strings();

		/*
		 * We use SSLv23_method() because it can negotiate use of the highest
		 * mutually supported protocol version, while alternatives like
		 * TLSv1_2_method() permit only one specific version.  Note that we
		 * don't actually allow SSL v2 or v3, only TLS protocols (see below).
		 */
		SSL_context = SSL_CTX_new(SSLv23_method());
		if (!SSL_context)
			ereport(FATAL,
					(errmsg("could not create SSL context: %s",
							SSLerrmessage())));

		/*
		 * Disable OpenSSL's moving-write-buffer sanity check, because it
		 * causes unnecessary failures in nonblocking send cases.
		 */
		SSL_CTX_set_mode(SSL_context, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);

		/*
		 * Load and verify server's certificate and private key
		 */
		if (SSL_CTX_use_certificate_chain_file(SSL_context,
											   ssl_cert_file) != 1)
			ereport(FATAL,
					(errcode(ERRCODE_CONFIG_FILE_ERROR),
				  errmsg("could not load server certificate file \"%s\": %s",
						 ssl_cert_file, SSLerrmessage())));

		if (stat(ssl_key_file, &buf) != 0)
			ereport(FATAL,
					(errcode_for_file_access(),
					 errmsg("could not access private key file \"%s\": %m",
							ssl_key_file)));

		/*
		 * Require no public access to key file.
		 *
		 * XXX temporarily suppress check when on Windows, because there may
		 * not be proper support for Unix-y file permissions.  Need to think
		 * of a reasonable check to apply on Windows.  (See also the data
		 * directory permission check in postmaster.c)
		 */
#if !defined(WIN32) && !defined(__CYGWIN__)
		if (!S_ISREG(buf.st_mode) || buf.st_mode & (S_IRWXG | S_IRWXO))
			ereport(FATAL,
					(errcode(ERRCODE_CONFIG_FILE_ERROR),
				  errmsg("private key file \"%s\" has group or world access",
						 ssl_key_file),
				   errdetail("Permissions should be u=rw (0600) or less.")));
#endif

		if (SSL_CTX_use_PrivateKey_file(SSL_context,
										ssl_key_file,
										SSL_FILETYPE_PEM) != 1)
			ereport(FATAL,
					(errmsg("could not load private key file \"%s\": %s",
							ssl_key_file, SSLerrmessage())));

		if (SSL_CTX_check_private_key(SSL_context) != 1)
			ereport(FATAL,
					(errmsg("check of private key failed: %s",
							SSLerrmessage())));
	}

	/* set up ephemeral DH keys, and disallow SSL v2/v3 while at it */
	SSL_CTX_set_tmp_dh_callback(SSL_context, tmp_dh_cb);
	SSL_CTX_set_options(SSL_context,
						SSL_OP_SINGLE_DH_USE |
						SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);

	/* set up ephemeral ECDH keys */
	initialize_ecdh();

	/* set up the allowed cipher list */
	if (SSL_CTX_set_cipher_list(SSL_context, SSLCipherSuites) != 1)
		elog(FATAL, "could not set the cipher list (no valid ciphers available)");

	/* Let server choose order */
	if (SSLPreferServerCiphers)
		SSL_CTX_set_options(SSL_context, SSL_OP_CIPHER_SERVER_PREFERENCE);

	/*
	 * Load CA store, so we can verify client certificates if needed.
	 */
	if (ssl_ca_file[0])
	{
		if (SSL_CTX_load_verify_locations(SSL_context, ssl_ca_file, NULL) != 1 ||
			(root_cert_list = SSL_load_client_CA_file(ssl_ca_file)) == NULL)
			ereport(FATAL,
					(errmsg("could not load root certificate file \"%s\": %s",
							ssl_ca_file, SSLerrmessage())));
	}

	/*----------
	 * Load the Certificate Revocation List (CRL).
	 * http://searchsecurity.techtarget.com/sDefinition/0,,sid14_gci803160,00.html
	 *----------
	 */
	if (ssl_crl_file[0])
	{
		X509_STORE *cvstore = SSL_CTX_get_cert_store(SSL_context);

		if (cvstore)
		{
			/* Set the flags to check against the complete CRL chain */
			if (X509_STORE_load_locations(cvstore, ssl_crl_file, NULL) == 1)
			{
				/* OpenSSL 0.96 does not support X509_V_FLAG_CRL_CHECK */
#ifdef X509_V_FLAG_CRL_CHECK
				X509_STORE_set_flags(cvstore,
						  X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL);
#else
				ereport(LOG,
				(errmsg("SSL certificate revocation list file \"%s\" ignored",
						ssl_crl_file),
				 errdetail("SSL library does not support certificate revocation lists.")));
#endif
			}
			else
				ereport(FATAL,
						(errmsg("could not load SSL certificate revocation list file \"%s\": %s",
								ssl_crl_file, SSLerrmessage())));
		}
	}

	if (ssl_ca_file[0])
	{
		/*
		 * Always ask for SSL client cert, but don't fail if it's not
		 * presented.  We might fail such connections later, depending on what
		 * we find in pg_hba.conf.
		 */
		SSL_CTX_set_verify(SSL_context,
						   (SSL_VERIFY_PEER |
							SSL_VERIFY_CLIENT_ONCE),
						   verify_cb);

		/* Set flag to remember CA store is successfully loaded */
		ssl_loaded_verify_locations = true;

		/*
		 * Tell OpenSSL to send the list of root certs we trust to clients in
		 * CertificateRequests.  This lets a client with a keystore select the
		 * appropriate client certificate to send to us.
		 */
		SSL_CTX_set_client_CA_list(SSL_context, root_cert_list);
	}
}
示例#23
0
/*
 *	Callback used by SSL to load client cert and key.
 *	This callback is only called when the server wants a
 *	client cert.
 *
 *	Must return 1 on success, 0 on no data or error.
 */
static int
client_cert_cb(SSL *ssl, X509 **x509, EVP_PKEY **pkey)
{
	char		homedir[MAXPGPATH];
	struct stat buf;

#ifndef WIN32
	struct stat buf2;
#endif
	char		fnbuf[MAXPGPATH];
	FILE	   *fp;
	PGconn	   *conn = (PGconn *) SSL_get_app_data(ssl);
	int			(*cb) () = NULL;	/* how to read user password */
	char		sebuf[256];

	if (!pqGetHomeDirectory(homedir, sizeof(homedir)))
	{
		printfPQExpBuffer(&conn->errorMessage,
						  libpq_gettext("could not get user information\n"));
		return 0;
	}

	/* read the user certificate */
	snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, USERCERTFILE);
	if ((fp = fopen(fnbuf, "r")) == NULL)
	{
		printfPQExpBuffer(&conn->errorMessage,
			   libpq_gettext("could not open certificate file \"%s\": %s\n"),
						  fnbuf, pqStrerror(errno, sebuf, sizeof(sebuf)));
		return 0;
	}
	if (PEM_read_X509(fp, x509, NULL, NULL) == NULL)
	{
		char	   *err = SSLerrmessage();

		printfPQExpBuffer(&conn->errorMessage,
			   libpq_gettext("could not read certificate file \"%s\": %s\n"),
						  fnbuf, err);
		SSLerrfree(err);
		fclose(fp);
		return 0;
	}
	fclose(fp);

	/* read the user key */
	snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, USERKEYFILE);
	if (stat(fnbuf, &buf) == -1)
	{
		printfPQExpBuffer(&conn->errorMessage,
						  libpq_gettext("certificate present, but not private key file \"%s\"\n"),
						  fnbuf);
		return 0;
	}
#ifndef WIN32
	if (!S_ISREG(buf.st_mode) || (buf.st_mode & 0077) ||
		buf.st_uid != geteuid())
	{
		printfPQExpBuffer(&conn->errorMessage,
			libpq_gettext("private key file \"%s\" has wrong permissions\n"),
						  fnbuf);
		return 0;
	}
#endif
	if ((fp = fopen(fnbuf, "r")) == NULL)
	{
		printfPQExpBuffer(&conn->errorMessage,
			   libpq_gettext("could not open private key file \"%s\": %s\n"),
						  fnbuf, pqStrerror(errno, sebuf, sizeof(sebuf)));
		return 0;
	}
#ifndef WIN32
	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 file \"%s\" changed during execution\n"), fnbuf);
		return 0;
	}
#endif
	if (PEM_read_PrivateKey(fp, pkey, cb, NULL) == NULL)
	{
		char	   *err = SSLerrmessage();

		printfPQExpBuffer(&conn->errorMessage,
			   libpq_gettext("could not read private key file \"%s\": %s\n"),
						  fnbuf, err);
		SSLerrfree(err);
		fclose(fp);
		return 0;
	}
	fclose(fp);

	/* verify that the cert and key go together */
	if (!X509_check_private_key(*x509, *pkey))
	{
		char	   *err = SSLerrmessage();

		printfPQExpBuffer(&conn->errorMessage,
						  libpq_gettext("certificate does not match private key file \"%s\": %s\n"),
						  fnbuf, err);
		SSLerrfree(err);
		return 0;
	}

	return 1;
}
示例#24
0
/*
 *	Write data to a secure connection.
 */
ssize_t
secure_write(Port *port, void *ptr, size_t len)
{
	ssize_t		n;

#ifdef USE_SSL
	if (port->ssl)
	{
		int			err;

		/*
		 * If SSL renegotiations are enabled and we're getting close to the
		 * limit, start one now; but avoid it if there's one already in
		 * progress.  Request the renegotiation 1kB before the limit has
		 * actually expired.
		 */
		if (ssl_renegotiation_limit && !in_ssl_renegotiation &&
			port->count > (ssl_renegotiation_limit - 1) * 1024L)
		{
			in_ssl_renegotiation = true;

			/*
			 * The way we determine that a renegotiation has completed is by
			 * observing OpenSSL's internal renegotiation counter.  Make sure
			 * we start out at zero, and assume that the renegotiation is
			 * complete when the counter advances.
			 *
			 * OpenSSL provides SSL_renegotiation_pending(), but this doesn't
			 * seem to work in testing.
			 */
			SSL_clear_num_renegotiations(port->ssl);

			SSL_set_session_id_context(port->ssl, (void *) &SSL_context,
									   sizeof(SSL_context));
			if (SSL_renegotiate(port->ssl) <= 0)
				ereport(COMMERROR,
						(errcode(ERRCODE_PROTOCOL_VIOLATION),
						 errmsg("SSL failure during renegotiation start")));
			else
			{
				int			retries;

				/*
				 * A handshake can fail, so be prepared to retry it, but only
				 * a few times.
				 */
				for (retries = 0; retries++;)
				{
					if (SSL_do_handshake(port->ssl) > 0)
						break;	/* done */
					ereport(COMMERROR,
							(errcode(ERRCODE_PROTOCOL_VIOLATION),
							 errmsg("SSL handshake failure on renegotiation, retrying")));
					if (retries >= 20)
						ereport(FATAL,
								(errcode(ERRCODE_PROTOCOL_VIOLATION),
								 errmsg("unable to complete SSL handshake")));
				}
			}
		}

wloop:
		errno = 0;
		n = SSL_write(port->ssl, ptr, len);
		err = SSL_get_error(port->ssl, n);
		switch (err)
		{
			case SSL_ERROR_NONE:
				port->count += n;
				break;
			case SSL_ERROR_WANT_READ:
			case SSL_ERROR_WANT_WRITE:
#ifdef WIN32
				pgwin32_waitforsinglesocket(SSL_get_fd(port->ssl),
											(err == SSL_ERROR_WANT_READ) ?
									FD_READ | FD_CLOSE : FD_WRITE | FD_CLOSE,
											INFINITE);
#endif
				goto wloop;
			case SSL_ERROR_SYSCALL:
				/* leave it to caller to ereport the value of errno */
				if (n != -1)
				{
					errno = ECONNRESET;
					n = -1;
				}
				break;
			case SSL_ERROR_SSL:
				ereport(COMMERROR,
						(errcode(ERRCODE_PROTOCOL_VIOLATION),
						 errmsg("SSL error: %s", SSLerrmessage())));
				/* fall through */
			case SSL_ERROR_ZERO_RETURN:
				errno = ECONNRESET;
				n = -1;
				break;
			default:
				ereport(COMMERROR,
						(errcode(ERRCODE_PROTOCOL_VIOLATION),
						 errmsg("unrecognized SSL error code: %d",
								err)));
				errno = ECONNRESET;
				n = -1;
				break;
		}

		if (n >= 0)
		{
			/* is renegotiation complete? */
			if (in_ssl_renegotiation &&
				SSL_num_renegotiations(port->ssl) >= 1)
			{
				in_ssl_renegotiation = false;
				port->count = 0;
			}

			/*
			 * if renegotiation is still ongoing, and we've gone beyond the
			 * limit, kill the connection now -- continuing to use it can be
			 * considered a security problem.
			 */
			if (in_ssl_renegotiation &&
				port->count > ssl_renegotiation_limit * 1024L)
				ereport(FATAL,
						(errcode(ERRCODE_PROTOCOL_VIOLATION),
						 errmsg("SSL failed to renegotiate connection before limit expired")));
		}
	}
	else
#endif
		n = send(port->sock, ptr, len, 0);

	return n;
}
示例#25
0
/*
 *	Attempt to negotiate SSL connection.
 */
static int
open_server_SSL(Port *port)
{
	int			r;
	int			err;

	Assert(!port->ssl);
	Assert(!port->peer);

	if (!(port->ssl = SSL_new(SSL_context)))
	{
		ereport(COMMERROR,
				(errcode(ERRCODE_PROTOCOL_VIOLATION),
				 errmsg("could not initialize SSL connection: %s",
						SSLerrmessage())));
		close_SSL(port);
		return -1;
	}
	if (!my_SSL_set_fd(port->ssl, port->sock))
	{
		ereport(COMMERROR,
				(errcode(ERRCODE_PROTOCOL_VIOLATION),
				 errmsg("could not set SSL socket: %s",
						SSLerrmessage())));
		close_SSL(port);
		return -1;
	}

aloop:
	r = SSL_accept(port->ssl);
	if (r <= 0)
	{
		err = SSL_get_error(port->ssl, r);
		switch (err)
		{
			case SSL_ERROR_WANT_READ:
			case SSL_ERROR_WANT_WRITE:
#ifdef WIN32
				pgwin32_waitforsinglesocket(SSL_get_fd(port->ssl),
											(err == SSL_ERROR_WANT_READ) ?
						FD_READ | FD_CLOSE | FD_ACCEPT : FD_WRITE | FD_CLOSE,
											INFINITE);
#endif
				goto aloop;
			case SSL_ERROR_SYSCALL:
				if (r < 0)
					ereport(COMMERROR,
							(errcode_for_socket_access(),
							 errmsg("could not accept SSL connection: %m")));
				else
					ereport(COMMERROR,
							(errcode(ERRCODE_PROTOCOL_VIOLATION),
					errmsg("could not accept SSL connection: EOF detected")));
				break;
			case SSL_ERROR_SSL:
				ereport(COMMERROR,
						(errcode(ERRCODE_PROTOCOL_VIOLATION),
						 errmsg("could not accept SSL connection: %s",
								SSLerrmessage())));
				break;
			case SSL_ERROR_ZERO_RETURN:
				ereport(COMMERROR,
						(errcode(ERRCODE_PROTOCOL_VIOLATION),
				   errmsg("could not accept SSL connection: EOF detected")));
				break;
			default:
				ereport(COMMERROR,
						(errcode(ERRCODE_PROTOCOL_VIOLATION),
						 errmsg("unrecognized SSL error code: %d",
								err)));
				break;
		}
		close_SSL(port);
		return -1;
	}

	port->count = 0;

	/* get client certificate, if available. */
	port->peer = SSL_get_peer_certificate(port->ssl);
	if (port->peer == NULL)
	{
		strlcpy(port->peer_dn, "(anonymous)", sizeof(port->peer_dn));
		strlcpy(port->peer_cn, "(anonymous)", sizeof(port->peer_cn));
	}
	else
	{
		X509_NAME_oneline(X509_get_subject_name(port->peer),
						  port->peer_dn, sizeof(port->peer_dn));
		port->peer_dn[sizeof(port->peer_dn) - 1] = '\0';
		r = X509_NAME_get_text_by_NID(X509_get_subject_name(port->peer),
					   NID_commonName, port->peer_cn, sizeof(port->peer_cn));
		port->peer_cn[sizeof(port->peer_cn) - 1] = '\0';
		if (r == -1)
		{
			/* Unable to get the CN, set it to blank so it can't be used */
			port->peer_cn[0] = '\0';
		}
		else
		{
			/*
			 * Reject embedded NULLs in certificate common name to prevent
			 * attacks like CVE-2009-4034.
			 */
			if (r != strlen(port->peer_cn))
			{
				ereport(COMMERROR,
						(errcode(ERRCODE_PROTOCOL_VIOLATION),
						 errmsg("SSL certificate's common name contains embedded null")));
				close_SSL(port);
				return -1;
			}
		}
	}
	ereport(DEBUG2,
			(errmsg("SSL connection from \"%s\"", port->peer_cn)));

	/* set up debugging/info callback */
	SSL_CTX_set_info_callback(SSL_context, info_cb);

	return 0;
}
示例#26
0
/*
 *	Write data to a secure connection.
 */
ssize_t
be_tls_write(Port *port, void *ptr, size_t len, int *waitfor)
{
	ssize_t		n;
	int			err;

	/*
	 * If SSL renegotiations are enabled and we're getting close to the
	 * limit, start one now; but avoid it if there's one already in
	 * progress.  Request the renegotiation 1kB before the limit has
	 * actually expired.
	 */
	if (ssl_renegotiation_limit && !in_ssl_renegotiation &&
		port->count > (ssl_renegotiation_limit - 1) * 1024L)
	{
		in_ssl_renegotiation = true;

		/*
		 * The way we determine that a renegotiation has completed is by
		 * observing OpenSSL's internal renegotiation counter.  Make sure
		 * we start out at zero, and assume that the renegotiation is
		 * complete when the counter advances.
		 *
		 * OpenSSL provides SSL_renegotiation_pending(), but this doesn't
		 * seem to work in testing.
		 */
		SSL_clear_num_renegotiations(port->ssl);

		/* without this, renegotiation fails when a client cert is used */
		SSL_set_session_id_context(port->ssl, (void *) &SSL_context,
								   sizeof(SSL_context));

		if (SSL_renegotiate(port->ssl) <= 0)
			ereport(COMMERROR,
					(errcode(ERRCODE_PROTOCOL_VIOLATION),
					 errmsg("SSL failure during renegotiation start")));
	}

	errno = 0;
	n = SSL_write(port->ssl, ptr, len);
	err = SSL_get_error(port->ssl, n);
	switch (err)
	{
		case SSL_ERROR_NONE:
			port->count += n;
			break;
		case SSL_ERROR_WANT_READ:
			*waitfor = WL_SOCKET_READABLE;
			errno = EWOULDBLOCK;
			n = -1;
			break;
		case SSL_ERROR_WANT_WRITE:
			*waitfor = WL_SOCKET_WRITEABLE;
			errno = EWOULDBLOCK;
			n = -1;
			break;
		case SSL_ERROR_SYSCALL:
			/* leave it to caller to ereport the value of errno */
			if (n != -1)
			{
				errno = ECONNRESET;
				n = -1;
			}
			break;
		case SSL_ERROR_SSL:
			ereport(COMMERROR,
					(errcode(ERRCODE_PROTOCOL_VIOLATION),
					 errmsg("SSL error: %s", SSLerrmessage())));
			/* fall through */
		case SSL_ERROR_ZERO_RETURN:
			errno = ECONNRESET;
			n = -1;
			break;
		default:
			ereport(COMMERROR,
					(errcode(ERRCODE_PROTOCOL_VIOLATION),
					 errmsg("unrecognized SSL error code: %d",
							err)));
			errno = ECONNRESET;
			n = -1;
			break;
	}

	if (n >= 0)
	{
		/* is renegotiation complete? */
		if (in_ssl_renegotiation &&
			SSL_num_renegotiations(port->ssl) >= 1)
		{
			in_ssl_renegotiation = false;
			port->count = 0;
		}

		/*
		 * if renegotiation is still ongoing, and we've gone beyond the
		 * limit, kill the connection now -- continuing to use it can be
		 * considered a security problem.
		 */
		if (in_ssl_renegotiation &&
			port->count > ssl_renegotiation_limit * 1024L)
			ereport(FATAL,
					(errcode(ERRCODE_PROTOCOL_VIOLATION),
					 errmsg("SSL failed to renegotiate connection before limit expired")));
	}

	return n;
}
示例#27
0
文件: secure.c 项目: colinet/sqlix
/*
 *	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 pqsecure_read(PGconn * conn, void *ptr, size_t len)
{
	ssize_t n;
	int result_errno = 0;
	char sebuf[256];

#ifdef USE_SSL
	if (conn->ssl) {
		int err;

		DECLARE_SIGPIPE_INFO(spinfo);

		/* SSL_read can write to the socket, so we need to disable SIGPIPE */
		DISABLE_SIGPIPE(conn, spinfo, return -1);

rloop:
		SOCK_ERRNO_SET(0);
		n = SSL_read(conn->ssl, ptr, len);
		err = SSL_get_error(conn->ssl, n);
		switch (err) {
		case SSL_ERROR_NONE:
			if (n < 0) {
				/* Not supposed to happen, so we don't translate the msg */
				print_pqbuf(&conn->errorMessage,
					"SSL_read failed but did not provide error information\n");
				/* assume the connection is broken */
				result_errno = ECONNRESET;
			}
			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:
			if (n < 0) {
				result_errno = SOCK_ERRNO;
				REMEMBER_EPIPE(spinfo, result_errno == EPIPE);
				if (result_errno == EPIPE ||
				    result_errno == ECONNRESET)
					print_pqbuf(&conn->errorMessage,
						libpq_gettext
						  ("server closed the connection unexpectedly\n"
						   "\tThis probably means the server terminated abnormally\n"
						   "\tbefore or while processing the request.\n"));
				else
					print_pqbuf(&conn->errorMessage,
						 libpq_gettext("SSL SYSCALL error: %s\n"),
						 SOCK_STRERROR(result_errno, sebuf, sizeof(sebuf)));
			} else {
				print_pqbuf(&conn->errorMessage,
					libpq_gettext("SSL SYSCALL error: EOF detected\n"));
				/* assume the connection is broken */
				result_errno = ECONNRESET;
				n = -1;
			}
			break;
		case SSL_ERROR_SSL: {
				char *errm = SSLerrmessage();

				print_pqbuf(&conn->errorMessage,
					  libpq_gettext("SSL error: %s\n"), errm);
				SSLerrfree(errm);
				/* assume the connection is broken */
				result_errno = ECONNRESET;
				n = -1;
				break;
			}
		case SSL_ERROR_ZERO_RETURN:
			/*
			 * Per OpenSSL documentation, this error code is only returned
			 * for a clean connection closure, so we should not report it
			 * as a server crash.
			 */
			print_pqbuf(&conn->errorMessage,
				libpq_gettext("SSL connection has been closed unexpectedly\n"));
			result_errno = ECONNRESET;
			n = -1;
			break;
		default:
			print_pqbuf(&conn->errorMessage,
				libpq_gettext("unrecognized SSL error code: %d\n"), err);
			/* assume the connection is broken */
			result_errno = ECONNRESET;
			n = -1;
			break;
		}

		RESTORE_SIGPIPE(conn, spinfo);
	} else
示例#28
0
int
be_tls_open_server(Port *port)
{
	int			r;
	int			err;
	int			waitfor;
	unsigned long ecode;

	Assert(!port->ssl);
	Assert(!port->peer);

	if (!SSL_context)
	{
		ereport(COMMERROR,
				(errcode(ERRCODE_PROTOCOL_VIOLATION),
				 errmsg("could not initialize SSL connection: SSL context not set up")));
		return -1;
	}

	if (!(port->ssl = SSL_new(SSL_context)))
	{
		ereport(COMMERROR,
				(errcode(ERRCODE_PROTOCOL_VIOLATION),
				 errmsg("could not initialize SSL connection: %s",
						SSLerrmessage(ERR_get_error()))));
		return -1;
	}
	if (!my_SSL_set_fd(port, port->sock))
	{
		ereport(COMMERROR,
				(errcode(ERRCODE_PROTOCOL_VIOLATION),
				 errmsg("could not set SSL socket: %s",
						SSLerrmessage(ERR_get_error()))));
		return -1;
	}
	port->ssl_in_use = true;

aloop:

	/*
	 * Prepare to call SSL_get_error() by clearing thread's OpenSSL error
	 * queue.  In general, the current thread's error queue must be empty
	 * before the TLS/SSL I/O operation is attempted, or SSL_get_error() will
	 * not work reliably.  An extension may have failed to clear the
	 * per-thread error queue following another call to an OpenSSL I/O
	 * routine.
	 */
	ERR_clear_error();
	r = SSL_accept(port->ssl);
	if (r <= 0)
	{
		err = SSL_get_error(port->ssl, r);

		/*
		 * Other clients of OpenSSL in the backend may fail to call
		 * ERR_get_error(), but we always do, so as to not cause problems for
		 * OpenSSL clients that don't call ERR_clear_error() defensively.  Be
		 * sure that this happens by calling now. SSL_get_error() relies on
		 * the OpenSSL per-thread error queue being intact, so this is the
		 * earliest possible point ERR_get_error() may be called.
		 */
		ecode = ERR_get_error();
		switch (err)
		{
			case SSL_ERROR_WANT_READ:
			case SSL_ERROR_WANT_WRITE:
				/* not allowed during connection establishment */
				Assert(!port->noblock);

				/*
				 * No need to care about timeouts/interrupts here. At this
				 * point authentication_timeout still employs
				 * StartupPacketTimeoutHandler() which directly exits.
				 */
				if (err == SSL_ERROR_WANT_READ)
					waitfor = WL_SOCKET_READABLE | WL_EXIT_ON_PM_DEATH;
				else
					waitfor = WL_SOCKET_WRITEABLE | WL_EXIT_ON_PM_DEATH;

				(void) WaitLatchOrSocket(MyLatch, waitfor, port->sock, 0,
										 WAIT_EVENT_SSL_OPEN_SERVER);
				goto aloop;
			case SSL_ERROR_SYSCALL:
				if (r < 0)
					ereport(COMMERROR,
							(errcode_for_socket_access(),
							 errmsg("could not accept SSL connection: %m")));
				else
					ereport(COMMERROR,
							(errcode(ERRCODE_PROTOCOL_VIOLATION),
							 errmsg("could not accept SSL connection: EOF detected")));
				break;
			case SSL_ERROR_SSL:
				ereport(COMMERROR,
						(errcode(ERRCODE_PROTOCOL_VIOLATION),
						 errmsg("could not accept SSL connection: %s",
								SSLerrmessage(ecode))));
				break;
			case SSL_ERROR_ZERO_RETURN:
				ereport(COMMERROR,
						(errcode(ERRCODE_PROTOCOL_VIOLATION),
						 errmsg("could not accept SSL connection: EOF detected")));
				break;
			default:
				ereport(COMMERROR,
						(errcode(ERRCODE_PROTOCOL_VIOLATION),
						 errmsg("unrecognized SSL error code: %d",
								err)));
				break;
		}
		return -1;
	}

	/* Get client certificate, if available. */
	port->peer = SSL_get_peer_certificate(port->ssl);

	/* and extract the Common Name from it. */
	port->peer_cn = NULL;
	port->peer_cert_valid = false;
	if (port->peer != NULL)
	{
		int			len;

		len = X509_NAME_get_text_by_NID(X509_get_subject_name(port->peer),
										NID_commonName, NULL, 0);
		if (len != -1)
		{
			char	   *peer_cn;

			peer_cn = MemoryContextAlloc(TopMemoryContext, len + 1);
			r = X509_NAME_get_text_by_NID(X509_get_subject_name(port->peer),
										  NID_commonName, peer_cn, len + 1);
			peer_cn[len] = '\0';
			if (r != len)
			{
				/* shouldn't happen */
				pfree(peer_cn);
				return -1;
			}

			/*
			 * Reject embedded NULLs in certificate common name to prevent
			 * attacks like CVE-2009-4034.
			 */
			if (len != strlen(peer_cn))
			{
				ereport(COMMERROR,
						(errcode(ERRCODE_PROTOCOL_VIOLATION),
						 errmsg("SSL certificate's common name contains embedded null")));
				pfree(peer_cn);
				return -1;
			}

			port->peer_cn = peer_cn;
		}
		port->peer_cert_valid = true;
	}

	/* set up debugging/info callback */
	SSL_CTX_set_info_callback(SSL_context, info_cb);

	return 0;
}
示例#29
0
/*
 *	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)
	{
		int			err;

		DECLARE_SIGPIPE_INFO(spinfo);

		/* SSL_read can write to the socket, so we need to disable SIGPIPE */
		DISABLE_SIGPIPE(conn, spinfo, return -1);

rloop:
		SOCK_ERRNO_SET(0);
		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)
					{
						REMEMBER_EPIPE(spinfo, SOCK_ERRNO == EPIPE);
						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;
		}

		RESTORE_SIGPIPE(conn, spinfo);
	}
示例#30
0
/*
 *	Initialize global SSL context.
 */
static int
initialize_SSL(void)
{
	char		fnbuf[MAXPGPATH];
	struct stat buf;

	if (!SSL_context)
	{
		SSL_library_init();
		SSL_load_error_strings();
		SSL_context = SSL_CTX_new(SSLv23_method());
		if (!SSL_context)
			ereport(FATAL,
					(errmsg("could not create SSL context: %s",
							SSLerrmessage())));

		/*
		 * Load and verify certificate and private key
		 */
		snprintf(fnbuf, sizeof(fnbuf), "%s/server.crt", DataDir);
		if (!SSL_CTX_use_certificate_file(SSL_context, fnbuf, SSL_FILETYPE_PEM))
			ereport(FATAL,
					(errcode(ERRCODE_CONFIG_FILE_ERROR),
			  errmsg("could not load server certificate file \"%s\": %s",
					 fnbuf, SSLerrmessage())));

		snprintf(fnbuf, sizeof(fnbuf), "%s/server.key", DataDir);
		if (stat(fnbuf, &buf) == -1)
			ereport(FATAL,
					(errcode_for_file_access(),
				   errmsg("could not access private key file \"%s\": %m",
						  fnbuf)));
		if (!S_ISREG(buf.st_mode) || (buf.st_mode & (S_IRWXG | S_IRWXO)) ||
			buf.st_uid != getuid())
			ereport(FATAL,
					(errcode(ERRCODE_CONFIG_FILE_ERROR),
				  errmsg("unsafe permissions on private key file \"%s\"",
						 fnbuf),
					 errdetail("File must be owned by the database user and must have no permissions for \"group\" or \"other\".")));

		if (!SSL_CTX_use_PrivateKey_file(SSL_context, fnbuf, SSL_FILETYPE_PEM))
			ereport(FATAL,
					(errmsg("could not load private key file \"%s\": %s",
							fnbuf, SSLerrmessage())));

		if (!SSL_CTX_check_private_key(SSL_context))
			ereport(FATAL,
					(errmsg("check of private key failed: %s",
							SSLerrmessage())));
	}

	/* 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 | SSL_OP_NO_SSLv2);

	/* setup the allowed cipher list */
	if (SSL_CTX_set_cipher_list(SSL_context, "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH") != 1)
		elog(FATAL, "could not set the cipher list (no valid ciphers available)");

	/* accept client certificates, but don't require them. */
	snprintf(fnbuf, sizeof(fnbuf), "%s/root.crt", DataDir);
	if (!SSL_CTX_load_verify_locations(SSL_context, fnbuf, CA_PATH))
	{
		/* Not fatal - we do not require client certificates */
		ereport(LOG,
				(errmsg("could not load root certificate file \"%s\": %s",
						fnbuf, SSLerrmessage()),
				 errdetail("Will not verify client certificates.")));
		return 0;
	}
	SSL_CTX_set_verify(SSL_context,
					   SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE,
					   verify_cb);

	return 0;
}