Supporting Windows SChannel as OpenSSL replacement

Started by Heikki Linnakangasover 11 years ago38 messages
#1Heikki Linnakangas
hlinnakangas@vmware.com

Hi,

I've been looking at Windows' native SSL implementatation, the SChannel
API. It would be nice to support that as a replacement for OpenSSL on
Windows. Currently, we bundle the OpenSSL library in the PostgreSQL,
installers, which is annoying because whenever OpenSSL puts out a new
release that fixes vulnerabilities, we need to do a security release of
PostgreSQL on Windows. I was reminded of this recently wrt. psqlODBC,
which bundles libpq and openssl as well. It's particularly annoying for
psqlODBC and other client applications, as people typically update it
less diligently than their servers.

I think that we should keep the user-visible behavior the same, i.e. the
libpq connection options, locations of the certificate files etc. would
all be the same regardless of which SSL implementation is used. Using
Windows SChannel API might make it possible to integrate better with
Windows' own certificate store etc. but I don't really know much about
that stuff, so for starters I'd like to just use it as a drop-in
replacement for OpenSSL.

Thoughts? While we're at it, we'll probably want to refactor things so
that it's easy to support other SSL implementations too, like gnutls.

- Heikki

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#2Magnus Hagander
magnus@hagander.net
In reply to: Heikki Linnakangas (#1)
Re: Supporting Windows SChannel as OpenSSL replacement

On Monday, June 9, 2014, Heikki Linnakangas <hlinnakangas@vmware.com> wrote:

Hi,

I've been looking at Windows' native SSL implementatation, the SChannel
API. It would be nice to support that as a replacement for OpenSSL on
Windows. Currently, we bundle the OpenSSL library in the PostgreSQL,
installers, which is annoying because whenever OpenSSL puts out a new
release that fixes vulnerabilities, we need to do a security release of
PostgreSQL on Windows. I was reminded of this recently wrt. psqlODBC, which
bundles libpq and openssl as well. It's particularly annoying for psqlODBC
and other client applications, as people typically update it less
diligently than their servers.

I think that we should keep the user-visible behavior the same, i.e. the
libpq connection options, locations of the certificate files etc. would all
be the same regardless of which SSL implementation is used. Using Windows
SChannel API might make it possible to integrate better with Windows' own
certificate store etc. but I don't really know much about that stuff, so
for starters I'd like to just use it as a drop-in replacement for OpenSSL.

Thoughts? While we're at it, we'll probably want to refactor things so
that it's easy to support other SSL implementations too, like gnutls.

It's a project that many have started, and nobody has finished :) I'm
definitely interested in working on such a things, but I've been unable to
carve out enough time recently.

One problem is as you say, that we're exposing openssl too much. For one
thing, we *cannot* keep the current interface, because it returns OpenSSL
internal datastructures. Those functions will need to be deprecated and
replaced with something else.

Also, my memory says that SChannel doesn't support the key file format that
we use now, which makes a much bigger break with the supported platforms.
That may have changed of course - have you researched that part?

The main other entries I've been looking at are NSS and gnutls, both of
which can speak our current file formats. I think the right thing is to
start with those and thereby make it more pluggable, and only after that
tackle schannel. But I do think it would be good to have them all.

It's also a question of if we can accept supporting a different set of
libraries on the server vs on the client. It's really on the client that
it's a bigger problem, but in the end I think we want to have "symmetrical
support". But it might be worth doing just the client side initially, and
then move to the server. I think in general, the client side is actually
likely to be *harder* than the server side..

--
Magnus Hagander
Me: http://www.hagander.net/
Work: http://www.redpill-linpro.com/

#3Heikki Linnakangas
hlinnakangas@vmware.com
In reply to: Magnus Hagander (#2)
Re: Supporting Windows SChannel as OpenSSL replacement

On 06/09/2014 02:53 PM, Magnus Hagander wrote:

Also, my memory says that SChannel doesn't support the key file format that
we use now, which makes a much bigger break with the supported platforms.
That may have changed of course - have you researched that part?

A quick web search turned up a few discussion forums threads with a
recipe for this (e.g
https://stackoverflow.com/questions/1231178/load-an-x509-pem-file-into-windows-cryptoapi).
There's no direct "read this file" function, but there are low-level
functions that can decode the file format once it's read into memory. So
it seems possible to make it work.

It's also a question of if we can accept supporting a different set of
libraries on the server vs on the client. It's really on the client that
it's a bigger problem, but in the end I think we want to have "symmetrical
support". But it might be worth doing just the client side initially, and
then move to the server. I think in general, the client side is actually
likely to be *harder* than the server side..

Once we've modified the client to support multiple libraries, it's
probably not much extra effort to do the same to the server. I wouldn't
like to support different libraries in client and server, if only
because it would be more complicated to have separate ./configure
options for client and server.

- Heikki

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#4Andres Freund
andres@2ndquadrant.com
In reply to: Magnus Hagander (#2)
Re: Supporting Windows SChannel as OpenSSL replacement

On 2014-06-09 13:53:15 +0200, Magnus Hagander wrote:

The main other entries I've been looking at are NSS and gnutls, both of
which can speak our current file formats. I think the right thing is to
start with those and thereby make it more pluggable, and only after that
tackle schannel. But I do think it would be good to have them all.

I think NSS makes a great deal of sense - the advantages of supporting
gnutls are much less clear to me. Maybe it's little enough additional
code that that doesn't matter much, but we imo shouldn't focus on it.

Greetings,

Andres Freund

--
Andres Freund http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#5Marko Kreen
markokr@gmail.com
In reply to: Heikki Linnakangas (#1)
Re: Supporting Windows SChannel as OpenSSL replacement

On Mon, Jun 09, 2014 at 02:45:08PM +0300, Heikki Linnakangas wrote:

Thoughts? While we're at it, we'll probably want to refactor things
so that it's easy to support other SSL implementations too, like
gnutls.

One project that is proud to support several SSL implementations
is curl: http://curl.haxx.se/

Git: https://github.com/bagder/curl.git
Implementations: https://github.com/bagder/curl/tree/master/lib/vtls

List from vtls.c:

- OpenSSL
- GnuTLS
- NSS
- QsoSSL
- GSKit
- PolarSSL
- CyaSSL
- Schannel SSPI
- SecureTransport (Darwin)

We cannot reuse the code directly, but seems it's usable for
reference for various gotchas that need to be solved.

--
marko

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#6Andreas Karlsson
andreas@proxel.se
In reply to: Heikki Linnakangas (#1)
Re: Supporting Windows SChannel as OpenSSL replacement

On 06/09/2014 01:45 PM, Heikki Linnakangas wrote:

Thoughts? While we're at it, we'll probably want to refactor things so
that it's easy to support other SSL implementations too, like gnutls.

There was a patch set for this from Martijn van Oosterhout which was
quite complete.

/messages/by-id/20060504134807.GK4752@svana.org

I am interested in dropping the dependency on OpenSSL, if only to fix
the situation with Debian, libreadline and OpenSSL[1].

Notes

1. They now compile against libedit and change to using libreadline at
runtime. This does not work perfectly though since libreadline supports
some features which libedit does not which can only be checked for at
compile time.

Andreas

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#7Magnus Hagander
magnus@hagander.net
In reply to: Andreas Karlsson (#6)
Re: Supporting Windows SChannel as OpenSSL replacement

On Mon, Jun 9, 2014 at 3:19 PM, Andreas Karlsson <andreas@proxel.se> wrote:

On 06/09/2014 01:45 PM, Heikki Linnakangas wrote:

Thoughts? While we're at it, we'll probably want to refactor things so
that it's easy to support other SSL implementations too, like gnutls.

There was a patch set for this from Martijn van Oosterhout which was quite
complete.

/messages/by-id/20060504134807.GK4752@svana.org

A lot has, unfortunately, changed since 2006. It might be a good
startingpoint. But also actively starting from the point of "let's try to
support multiple libraries" rather than "let's try to support gnutls" is
probably also important.

I am interested in dropping the dependency on OpenSSL, if only to fix the

situation with Debian, libreadline and OpenSSL[1].

That's one of the many reasons, yes :)

At some point we should design a new API, so that we can deprecate the old
one. Even if we don't hve the code ready, we need to get rid of PQgetssl(),
and replace it with something else. I'm thinking probably a functoin that
returns both a void pointer and an enum that tells you which library is
actually in use. And a boolean just saying "ssl on/off", because that's
what a lot of clients are interested in and they don't care aobut more than
that.

Obviously, we also have to do something about PQinitOpenSSL().

Unfortunately, I think it's too late to do that for 9.4 - otherwise it
would've been good to have a whole cycle of deprecation on it...

--
Magnus Hagander
Me: http://www.hagander.net/
Work: http://www.redpill-linpro.com/

#8Magnus Hagander
magnus@hagander.net
In reply to: Marko Kreen (#5)
Re: Supporting Windows SChannel as OpenSSL replacement

On Mon, Jun 9, 2014 at 3:02 PM, Marko Kreen <markokr@gmail.com> wrote:

On Mon, Jun 09, 2014 at 02:45:08PM +0300, Heikki Linnakangas wrote:

Thoughts? While we're at it, we'll probably want to refactor things
so that it's easy to support other SSL implementations too, like
gnutls.

One project that is proud to support several SSL implementations
is curl: http://curl.haxx.se/

Git: https://github.com/bagder/curl.git
Implementations: https://github.com/bagder/curl/tree/master/lib/vtls

List from vtls.c:

- OpenSSL
- GnuTLS
- NSS
- QsoSSL
- GSKit
- PolarSSL
- CyaSSL
- Schannel SSPI
- SecureTransport (Darwin)

We cannot reuse the code directly, but seems it's usable for
reference for various gotchas that need to be solved.

I did actually talk to Daniel at some point about turning that into a
generalized library, and/or getting him interested in helping out with it.
I can't remember where that ended up - I'll see if I can poke his interest
:)

--
Magnus Hagander
Me: http://www.hagander.net/
Work: http://www.redpill-linpro.com/

#9Tom Lane
tgl@sss.pgh.pa.us
In reply to: Heikki Linnakangas (#1)
Re: Supporting Windows SChannel as OpenSSL replacement

Heikki Linnakangas <hlinnakangas@vmware.com> writes:

I've been looking at Windows' native SSL implementatation, the SChannel
API. It would be nice to support that as a replacement for OpenSSL on
Windows. Currently, we bundle the OpenSSL library in the PostgreSQL,
installers, which is annoying because whenever OpenSSL puts out a new
release that fixes vulnerabilities, we need to do a security release of
PostgreSQL on Windows.

Does SChannel have a better security track record than OpenSSL? Or is
the point here just that we can define it as not our problem when a
vulnerability surfaces?

I'm doubtful that we can ignore security issues affecting PG just because
somebody else is responsible for shipping the fix, and thus am concerned
that if we support N different SSL libraries, we will need to keep track
of N sets of vulnerabilities instead of just one.

regards, tom lane

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#10Andres Freund
andres@2ndquadrant.com
In reply to: Tom Lane (#9)
Re: Supporting Windows SChannel as OpenSSL replacement

Hi,

On 2014-06-09 10:18:40 -0400, Tom Lane wrote:

Does SChannel have a better security track record than OpenSSL? Or is
the point here just that we can define it as not our problem when a
vulnerability surfaces?

Well, it's patched as part of the OS - so no new PG binaries have to be
released when it's buggy.

I'm doubtful that we can ignore security issues affecting PG just because
somebody else is responsible for shipping the fix, and thus am concerned
that if we support N different SSL libraries, we will need to keep track
of N sets of vulnerabilities instead of just one.

In most of the cases where such a issue exists it'll primarily affect
binary distributions that include the ssl library - and those will only
pick one anyway.

Greetings,

Andres Freund

--
Andres Freund http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#11MauMau
maumau307@gmail.com
In reply to: Heikki Linnakangas (#1)
Re: Supporting Windows SChannel as OpenSSL replacement

From: "Heikki Linnakangas" <hlinnakangas@vmware.com>

Thoughts? While we're at it, we'll probably want to refactor things so
that it's easy to support other SSL implementations too, like gnutls.

That may be good because it provides users with choices. But I wonder if it
is worth the complexity and maintainability of PostgreSQL code.

* Are SChannel and other libraries more secure than OpenSSL? IIRC, recently
I read in the news that GnuTLS had a vulnerability. OpenSSL is probably the
most widely used library, and many people are getting more interested in its
quality. I expect the quality will improve thanks to the help from The
Linux foundation and other organizations/researchers.

* Do other libraries get support from commercial vendor product support?
For example, Safenet Inc., the famous HSM (hardware security module) vendor,
supports OpenSSL to access the private key stored in its HSM product. Intel
offered AES-NI implementation code to OpenSSL community. I guess OpenSSL
will continue to be the most functional and obtain the widest adoption and
support.

Regards
MauMau

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#12Martijn van Oosterhout
kleptog@svana.org
In reply to: Magnus Hagander (#7)
Re: Supporting Windows SChannel as OpenSSL replacement

On Mon, Jun 09, 2014 at 03:35:23PM +0200, Magnus Hagander wrote:

On Mon, Jun 9, 2014 at 3:19 PM, Andreas Karlsson <andreas@proxel.se> wrote:

On 06/09/2014 01:45 PM, Heikki Linnakangas wrote:
There was a patch set for this from Martijn van Oosterhout which was quite
complete.

/messages/by-id/20060504134807.GK4752@svana.org

Wow, blast from the past.

A lot has, unfortunately, changed since 2006. It might be a good
startingpoint. But also actively starting from the point of "let's try to
support multiple libraries" rather than "let's try to support gnutls" is
probably also important.

The patch did provide an API. The idea was that there were a number of
functions which would need to be defined to support an SSL library.
Each library would then have a wrapper which wrapped the library and
based on the results of configure it compiled the right file into the
backend.

These functions were:

extern void pgtls_initialize(void);
extern void pgtls_destroy(void);
extern int pgtls_open_server(Port *);
extern void pgtls_close(Port *);
extern ssize_t pgtls_read(Port *port, void *ptr, size_t len);
extern ssize_t pgtls_write(Port *port, void *ptr, size_t len);

Which should be easy enough to support for any library. These days
you'd need to add support for verifying certificates, but I don't think
that that would be difficult (unless the actual certificate formats are
different).

No switching after compile time, that would just lead to useless
overhead.

At some point we should design a new API, so that we can deprecate the old
one. Even if we don't hve the code ready, we need to get rid of PQgetssl(),
and replace it with something else. I'm thinking probably a functoin that
returns both a void pointer and an enum that tells you which library is
actually in use. And a boolean just saying "ssl on/off", because that's
what a lot of clients are interested in and they don't care aobut more than
that.

Obviously, we also have to do something about PQinitOpenSSL().

Yeah, I think this was one of the more controversial parts. Support in
the backend was primarily moving code around and renaming functions,
fairly straightforward. Even error handling was not so hard (I found
the gnutls handling of errors much easier than openssl).

One tricky part is that programs like to use libpq for the
authentication, and then they hijack the connection using PGgetssl().
The way I dealt with this is defining a new state "passthrough" where
the caller would get a few function pointers to read/write/check the
connection. Then the callers would not need to know what library libpq
was compiled with. And libpq would know the connection was hijacked
and refuse to act anymore. I don't think everyone was pleased with
this, but no real alternative was presented (other than requiring
people hijacking the connection to do the hard work).

For information about which library was in use there was PQgettlsinfo()
which returned a PGresult with information about the library and
connection. I beleive since then new functions have been added to
libpq to retrive info about certificates, so that might need a rethink
also.

Basically, I think these last two points are the hard parts to get
agreement (assuming there's agreement to do anything at all about the
problem) and without nailing those down first whoever picks this up
will be in for a lot of work.

Have a nice day,
--
Martijn van Oosterhout <kleptog@svana.org> http://svana.org/kleptog/

He who writes carelessly confesses thereby at the very outset that he does
not attach much importance to his own thoughts.

-- Arthur Schopenhauer

#13Heikki Linnakangas
hlinnakangas@vmware.com
In reply to: Andres Freund (#10)
Re: Supporting Windows SChannel as OpenSSL replacement

On 06/09/2014 05:22 PM, Andres Freund wrote:

Hi,

On 2014-06-09 10:18:40 -0400, Tom Lane wrote:

Does SChannel have a better security track record than OpenSSL? Or is
the point here just that we can define it as not our problem when a
vulnerability surfaces?

Well, it's patched as part of the OS - so no new PG binaries have to be
released when it's buggy.

Right. I have no idea what SChannel's track record is, but when there's
a vulnerability in the native SSL implementation in Windows, you better
upgrade anyway, regardless of PostgreSQL. So when we rely on that, we
don't put any extra burden on users. And we won't need to release new
binaries just to update the DLL included in it.

- Heikki

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#14Robert Haas
robertmhaas@gmail.com
In reply to: Heikki Linnakangas (#13)
Re: Supporting Windows SChannel as OpenSSL replacement

On Mon, Jun 9, 2014 at 10:40 AM, Heikki Linnakangas
<hlinnakangas@vmware.com> wrote:

Right. I have no idea what SChannel's track record is, but when there's a
vulnerability in the native SSL implementation in Windows, you better
upgrade anyway, regardless of PostgreSQL. So when we rely on that, we don't
put any extra burden on users. And we won't need to release new binaries
just to update the DLL included in it.

Right, heartily agreed. It wouldn't surprise me if there are lots of
Windows machines out there that have 4 or 5 copies of OpenSSL on them,
each provided by a different installer for some other piece of
software that happens to depend on OpenSSL. When OpenSSL then has a
security vulnerability, you're not safe until all of the people who
produce those installers produce new versions and you upgrade to all
of those new versions. In practice, I'm sure that an enormous amount
slips through the cracks here. Relying on something that is part of
the OS and updated by the OS vendor seems like less work for both
packagers (who have to prepare the updates) and users (who have to
apply them). Of course there may be cases where the OS implementation
sucks badly or otherwise can't be relied upon, and then we'll just
have to live with shipping copies of things. But avoiding it sounds
better, if someone's volunteering to do the work....

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#15Magnus Hagander
magnus@hagander.net
In reply to: Martijn van Oosterhout (#12)
Re: Supporting Windows SChannel as OpenSSL replacement

On Mon, Jun 9, 2014 at 4:39 PM, Martijn van Oosterhout <kleptog@svana.org>
wrote:

On Mon, Jun 09, 2014 at 03:35:23PM +0200, Magnus Hagander wrote:

On Mon, Jun 9, 2014 at 3:19 PM, Andreas Karlsson <andreas@proxel.se>

wrote:

On 06/09/2014 01:45 PM, Heikki Linnakangas wrote:
There was a patch set for this from Martijn van Oosterhout which was

quite

complete.

/messages/by-id/20060504134807.GK4752@svana.org

Wow, blast from the past.

That's fun, itsn't it? :)

A lot has, unfortunately, changed since 2006. It might be a good

startingpoint. But also actively starting from the point of "let's try to
support multiple libraries" rather than "let's try to support gnutls" is
probably also important.

The patch did provide an API. The idea was that there were a number of
functions which would need to be defined to support an SSL library.
Each library would then have a wrapper which wrapped the library and
based on the results of configure it compiled the right file into the
backend.

These functions were:

extern void pgtls_initialize(void);
extern void pgtls_destroy(void);
extern int pgtls_open_server(Port *);
extern void pgtls_close(Port *);
extern ssize_t pgtls_read(Port *port, void *ptr, size_t len);
extern ssize_t pgtls_write(Port *port, void *ptr, size_t len);

Which should be easy enough to support for any library. These days
you'd need to add support for verifying certificates, but I don't think
that that would be difficult (unless the actual certificate formats are
different).

The two difficult points I think are the async support (libpq) and the
windows socket emulation support (backend). Do those really work there? In
particular the win32 stuff - though I guess that's less critical since we
can actually do hackish things there, unlike in libpq. But the example
there is that we can't have the library use recv()/send(), instead having
it work through our own functions.

No switching after compile time, that would just lead to useless
overhead.

Yes, I absolutely think we don't need to support >1 library at runtime.

At some point we should design a new API, so that we can deprecate the old

one. Even if we don't hve the code ready, we need to get rid of

PQgetssl(),

and replace it with something else. I'm thinking probably a functoin that
returns both a void pointer and an enum that tells you which library is
actually in use. And a boolean just saying "ssl on/off", because that's
what a lot of clients are interested in and they don't care aobut more

than

that.

Obviously, we also have to do something about PQinitOpenSSL().

Yeah, I think this was one of the more controversial parts. Support in
the backend was primarily moving code around and renaming functions,
fairly straightforward. Even error handling was not so hard (I found
the gnutls handling of errors much easier than openssl).

Yeah, the backend is easier, but also less important from the original
reason. For the patching reason, it's of course just as important.

One tricky part is that programs like to use libpq for the

authentication, and then they hijack the connection using PGgetssl().

Is there *anybody* other than odbc that does that? Do we actually need a
published API for that, or just a hack for pgodbc?

The way I dealt with this is defining a new state "passthrough" where
the caller would get a few function pointers to read/write/check the
connection. Then the callers would not need to know what library libpq
was compiled with. And libpq would know the connection was hijacked
and refuse to act anymore. I don't think everyone was pleased with
this, but no real alternative was presented (other than requiring
people hijacking the connection to do the hard work).

For information about which library was in use there was PQgettlsinfo()
which returned a PGresult with information about the library and
connection. I beleive since then new functions have been added to
libpq to retrive info about certificates, so that might need a rethink
also.

Not really, no. we return the OpenSSL structure and have the client use
that one directly. That's quite horrible already :)

Basically, I think these last two points are the hard parts to get

agreement (assuming there's agreement to do anything at all about the
problem) and without nailing those down first whoever picks this up
will be in for a lot of work.

Agreed.

--
Magnus Hagander
Me: http://www.hagander.net/
Work: http://www.redpill-linpro.com/

#16Martijn van Oosterhout
kleptog@svana.org
In reply to: MauMau (#11)
Re: Supporting Windows SChannel as OpenSSL replacement

On Mon, Jun 09, 2014 at 11:39:17PM +0900, MauMau wrote:

From: "Heikki Linnakangas" <hlinnakangas@vmware.com>

Thoughts? While we're at it, we'll probably want to refactor
things so that it's easy to support other SSL implementations too,
like gnutls.

That may be good because it provides users with choices. But I
wonder if it is worth the complexity and maintainability of
PostgreSQL code.

The complexity is very low. SSL is a standard protocol and so all
libraries offer the same functionality. Were not really doing anything
complex.

* Are SChannel and other libraries more secure than OpenSSL? IIRC,
recently I read in the news that GnuTLS had a vulnerability.
OpenSSL is probably the most widely used library, and many people
are getting more interested in its quality. I expect the quality
will improve thanks to the help from The Linux foundation and other
organizations/researchers.

Does that matter? What's wrong with letting people choose. OpenVPN
these days supports multiple SSL libraries, because PolarSSL (for
example) has been vetted for a higher security level than OpenSSL.

* Do other libraries get support from commercial vendor product
support? For example, Safenet Inc., the famous HSM (hardware
security module) vendor, supports OpenSSL to access the private key
stored in its HSM product. Intel offered AES-NI implementation code
to OpenSSL community. I guess OpenSSL will continue to be the most
functional and obtain the widest adoption and support.

And the crappiest license. I think it's silly for PostgreSQL dictate
what SSL library users must use, when there are so many possibilities.
We also support libedit for, in my opinion, worse reasons.

Have a nice day,
--
Martijn van Oosterhout <kleptog@svana.org> http://svana.org/kleptog/

He who writes carelessly confesses thereby at the very outset that he does
not attach much importance to his own thoughts.

-- Arthur Schopenhauer

#17Heikki Linnakangas
hlinnakangas@vmware.com
In reply to: Magnus Hagander (#15)
Re: Supporting Windows SChannel as OpenSSL replacement

On 06/09/2014 06:03 PM, Magnus Hagander wrote:

One tricky part is that programs like to use libpq for the

authentication, and then they hijack the connection using PGgetssl().

Is there*anybody* other than odbc that does that? Do we actually need a
published API for that, or just a hack for pgodbc?

I wish psqlODBC would stop doing that. It's kind of cool that it
supports compiling without libpq, but it's really quite a mess. I think
we should modify psqlODBC to use the libpq API like most people do.

- Heikki

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#18Magnus Hagander
magnus@hagander.net
In reply to: Heikki Linnakangas (#17)
Re: Supporting Windows SChannel as OpenSSL replacement

On Mon, Jun 9, 2014 at 7:45 PM, Heikki Linnakangas <hlinnakangas@vmware.com>
wrote:

On 06/09/2014 06:03 PM, Magnus Hagander wrote:

One tricky part is that programs like to use libpq for the

authentication, and then they hijack the connection using PGgetssl().

Is there*anybody* other than odbc that does that? Do we actually need a

published API for that, or just a hack for pgodbc?

I wish psqlODBC would stop doing that. It's kind of cool that it supports
compiling without libpq, but it's really quite a mess. I think we should
modify psqlODBC to use the libpq API like most people do.

This was, I believe, done at one point, and then reverted. I think that was
because libpq didn't actually have all the features required either for the
current or for planned featues of the ODBC driver. That situation might be
very different now though, there's more functionality available in libpq..

--
Magnus Hagander
Me: http://www.hagander.net/
Work: http://www.redpill-linpro.com/

#19Heikki Linnakangas
hlinnakangas@vmware.com
In reply to: Martijn van Oosterhout (#12)
2 attachment(s)
Re: Supporting Windows SChannel as OpenSSL replacement

On 06/09/2014 05:39 PM, Martijn van Oosterhout wrote:

On Mon, Jun 09, 2014 at 03:35:23PM +0200, Magnus Hagander wrote:

On Mon, Jun 9, 2014 at 3:19 PM, Andreas Karlsson <andreas@proxel.se> wrote:

On 06/09/2014 01:45 PM, Heikki Linnakangas wrote:
There was a patch set for this from Martijn van Oosterhout which was quite
complete.

/messages/by-id/20060504134807.GK4752@svana.org

Wow, blast from the past.

A lot has, unfortunately, changed since 2006. It might be a good
startingpoint. But also actively starting from the point of "let's try to
support multiple libraries" rather than "let's try to support gnutls" is
probably also important.

The patch did provide an API. The idea was that there were a number of
functions which would need to be defined to support an SSL library.
Each library would then have a wrapper which wrapped the library and
based on the results of configure it compiled the right file into the
backend.

These functions were:

extern void pgtls_initialize(void);
extern void pgtls_destroy(void);
extern int pgtls_open_server(Port *);
extern void pgtls_close(Port *);
extern ssize_t pgtls_read(Port *port, void *ptr, size_t len);
extern ssize_t pgtls_write(Port *port, void *ptr, size_t len);

Which should be easy enough to support for any library. These days
you'd need to add support for verifying certificates, but I don't think
that that would be difficult (unless the actual certificate formats are
different).

No switching after compile time, that would just lead to useless
overhead.

Yeah, that seems like a reasonable design.

I did again the refactoring you did back in 2006, patch attached. One
thing I did differently: I moved the raw, non-encrypted, read/write
functions to separate functions: pqsecure_raw_read and
pqsecure_raw_write. Those functions encapsulate the SIGPIPE handling.
The OpenSSL code implements a custom BIO, which calls to
pqsecure_raw_read/write to do the low-level I/O. Similarly in the
server-side, there are be_tls_raw_read and pg_tls_raw_write functions,
which do the prepare_for_client_read()/client_read_ended() dance, so
that the SSL implementation doesn't need to know about that.

I then implemented a quick proof-of-concept Windows SChannel
implementation of that API. It's client-side only, and there's no
support for validating server certificate or specifying non-default
ciphers or anything yet. But I did implement performing authentication
with a client certificate, as a proof of concept that it's possible to
read the OpenSSL key and certificate files - although in this proof of
concept the key and certificate are hard-coded in the sources, not read
from a file.

The SChannel implementation obviously needs a lot of work, but I'm
fairly confident that it's doable, and the new internal SSL API works
for that. Except for the user-visible PQgetssl() functions and such - I
don't know what to do with those.

At some point we should design a new API, so that we can deprecate the old
one. Even if we don't hve the code ready, we need to get rid of PQgetssl(),
and replace it with something else. I'm thinking probably a functoin that
returns both a void pointer and an enum that tells you which library is
actually in use. And a boolean just saying "ssl on/off", because that's
what a lot of clients are interested in and they don't care aobut more than
that.

Obviously, we also have to do something about PQinitOpenSSL().

Yeah, I think this was one of the more controversial parts. Support in
the backend was primarily moving code around and renaming functions,
fairly straightforward. Even error handling was not so hard (I found
the gnutls handling of errors much easier than openssl).

One tricky part is that programs like to use libpq for the
authentication, and then they hijack the connection using PGgetssl().
The way I dealt with this is defining a new state "passthrough" where
the caller would get a few function pointers to read/write/check the
connection. Then the callers would not need to know what library libpq
was compiled with. And libpq would know the connection was hijacked
and refuse to act anymore. I don't think everyone was pleased with
this, but no real alternative was presented (other than requiring
people hijacking the connection to do the hard work).

Sounds good. If we want to support such hijacking if the first place.

- Heikki

Attachments:

0002-Add-crude-version-of-Windows-SChannel-support.patchtext/x-diff; name=0002-Add-crude-version-of-Windows-SChannel-support.patchDownload
>From 7f8258e911779138e7271f42cf75e2dd414e86be Mon Sep 17 00:00:00 2001
From: Heikki Linnakangas <heikki.linnakangas@iki.fi>
Date: Wed, 11 Jun 2014 00:39:28 +0300
Subject: [PATCH 2/2] Add crude version of Windows SChannel support.

libpq only ATM, no server-side support
---
 configure                                    |  47 +-
 configure.in                                 |  14 +
 src/backend/libpq/Makefile                   |   4 +
 src/backend/libpq/be-secure-winschannel.c    |  64 +++
 src/include/pg_config.h.in                   |   3 +
 src/include/pg_config.h.win32                |   3 +
 src/include/port.h                           |   5 +-
 src/interfaces/libpq/Makefile                |   4 +
 src/interfaces/libpq/fe-secure-winschannel.c | 822 +++++++++++++++++++++++++++
 src/interfaces/libpq/libpq-int.h             |  13 +-
 src/interfaces/libpq/win32.mak               |  12 +
 src/tools/msvc/Mkvcbuild.pm                  |  18 +
 src/tools/msvc/Solution.pm                   |   3 +
 src/tools/msvc/config_default.pl             |   1 +
 14 files changed, 1008 insertions(+), 5 deletions(-)
 create mode 100644 src/backend/libpq/be-secure-winschannel.c
 create mode 100644 src/interfaces/libpq/fe-secure-winschannel.c

diff --git a/configure b/configure
index 0e6b109..6244e38 100755
--- a/configure
+++ b/configure
@@ -708,6 +708,7 @@ XML2_CONFIG
 UUID_EXTRA_OBJS
 with_uuid
 with_selinux
+with_winschannel
 with_openssl
 krb_srvtab
 with_python
@@ -824,6 +825,7 @@ with_pam
 with_ldap
 with_bonjour
 with_openssl
+with_winschannel
 with_selinux
 with_readline
 with_libedit_preferred
@@ -1510,6 +1512,7 @@ Optional Packages:
   --with-ldap             build with LDAP support
   --with-bonjour          build with Bonjour support
   --with-openssl          build with OpenSSL support
+  --with-winschannel      build with Windows SChannel support
   --with-selinux          build with SELinux support
   --without-readline      do not use GNU Readline nor BSD Libedit for editing
   --with-libedit-preferred
@@ -5516,6 +5519,46 @@ $as_echo "$with_openssl" >&6; }
 
 
 #
+# Windows SChannel
+#
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build with native Windows SSL support" >&5
+$as_echo_n "checking whether to build with native Windows SSL support... " >&6; }
+
+
+
+# Check whether --with-winschannel was given.
+if test "${with_winschannel+set}" = set; then :
+  withval=$with_winschannel;
+  case $withval in
+    yes)
+
+$as_echo "#define USE_WINDOWS_SCHANNEL 1" >>confdefs.h
+
+      ;;
+    no)
+      :
+      ;;
+    *)
+      as_fn_error $? "no argument expected for --with-winschannel option" "$LINENO" 5
+      ;;
+  esac
+
+else
+  with_winschannel=no
+
+fi
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_winschannel" >&5
+$as_echo "$with_winschannel" >&6; }
+
+
+if test "$with_openssl" = yes -a "$with_winschannel" = yes ; then
+  as_fn_error $? "
+*** Cannot select both OpenSSL and Windows SChannel." "$LINENO" 5
+fi
+
+#
 # SELinux
 #
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build with SELinux support" >&5
@@ -10940,7 +10983,7 @@ else
     We can't simply define LARGE_OFF_T to be 9223372036854775807,
     since some C++ compilers masquerading as C compilers
     incorrectly reject 9223372036854775807.  */
-#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
+#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
   int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
 		       && LARGE_OFF_T % 2147483647 == 1)
 		      ? 1 : -1];
@@ -10964,7 +11007,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
     We can't simply define LARGE_OFF_T to be 9223372036854775807,
     since some C++ compilers masquerading as C compilers
     incorrectly reject 9223372036854775807.  */
-#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
+#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
   int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
 		       && LARGE_OFF_T % 2147483647 == 1)
 		      ? 1 : -1];
diff --git a/configure.in b/configure.in
index 4bdd0f7..e1452da 100644
--- a/configure.in
+++ b/configure.in
@@ -663,6 +663,20 @@ AC_MSG_RESULT([$with_openssl])
 AC_SUBST(with_openssl)
 
 #
+# Windows SChannel
+#
+AC_MSG_CHECKING([whether to build with native Windows SSL support])
+PGAC_ARG_BOOL(with, winschannel, no, [build with Windows SChannel support],
+              [AC_DEFINE([USE_WINDOWS_SCHANNEL], 1, [Define to build with Windows SChannel support. (--with-winschannel)])])
+AC_MSG_RESULT([$with_winschannel])
+AC_SUBST(with_winschannel)
+
+if test "$with_openssl" = yes -a "$with_winschannel" = yes ; then
+  AC_MSG_ERROR([
+*** Cannot select both OpenSSL and Windows SChannel.])
+fi
+
+#
 # SELinux
 #
 AC_MSG_CHECKING([whether to build with SELinux support])
diff --git a/src/backend/libpq/Makefile b/src/backend/libpq/Makefile
index 8be0572..88d0b8b 100644
--- a/src/backend/libpq/Makefile
+++ b/src/backend/libpq/Makefile
@@ -21,4 +21,8 @@ ifeq ($(with_openssl),yes)
 OBJS += be-secure-openssl.o
 endif
 
+ifeq ($(with_winschannel),yes)
+OBJS += be-secure-winschannel.o
+endif
+
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/libpq/be-secure-winschannel.c b/src/backend/libpq/be-secure-winschannel.c
new file mode 100644
index 0000000..5fee673
--- /dev/null
+++ b/src/backend/libpq/be-secure-winschannel.c
@@ -0,0 +1,64 @@
+/*-------------------------------------------------------------------------
+ *
+ * be-secure-winschannel.c
+ *	  SSL support using Windows SChannel APIs
+ *
+ *
+ * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  src/backend/libpq/be-secure-winschannel.c
+ *
+ *	  TODO: this is just a dummy placeholder
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "libpq/libpq-be.h"
+
+/*
+ *	Write data to a secure connection.
+ */
+ssize_t
+be_tls_write(Port *port, void *ptr, size_t len)
+{
+	elog(ERROR, "be_tls_write not implemented");
+	return -1;
+}
+
+/*
+ *	Initialize global SSL context.
+ */
+void
+be_tls_init(void)
+{
+}
+
+/*
+ *	Attempt to negotiate SSL connection.
+ */
+int
+be_tls_open_server(Port *port)
+{
+	return -1;
+}
+
+/*
+ *	Close SSL connection.
+ */
+void
+be_tls_close(Port *port)
+{
+}
+
+ssize_t
+be_tls_read(Port *port, void *ptr, size_t len)
+{
+	elog(ERROR, "be_tls_read not implemented");
+	return -1;
+}
+
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index 0a0e0d4..a9f7171 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -803,6 +803,9 @@
 /* Define to select Win32-style shared memory. */
 #undef USE_WIN32_SHARED_MEMORY
 
+/* Define to build with Windows SChannel support. (--with-winschannel) */
+#undef USE_WINDOWS_SCHANNEL
+
 /* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most
    significant byte first (like Motorola and SPARC, unlike Intel). */
 #if defined AC_APPLE_UNIVERSAL_BUILD
diff --git a/src/include/pg_config.h.win32 b/src/include/pg_config.h.win32
index 6a6d2f3..c4f8ec4 100644
--- a/src/include/pg_config.h.win32
+++ b/src/include/pg_config.h.win32
@@ -650,6 +650,9 @@
 /* Define to select Win32-style semaphores. */
 #define USE_WIN32_SEMAPHORES 1
 
+/* Define to build with Windows SChannel support. (--with-winschannel) */
+#undef USE_WINDOWS_SCHANNEL
+
 /* Number of bits in a file offset, on hosts where this is settable. */
 /* #undef _FILE_OFFSET_BITS */
 
diff --git a/src/include/port.h b/src/include/port.h
index 7d2e0c4..c8b19d5 100644
--- a/src/include/port.h
+++ b/src/include/port.h
@@ -472,9 +472,10 @@ extern char *escape_single_quotes_ascii(const char *src);
 /* port/wait_error.c */
 extern char *wait_result_to_str(int exit_status);
 
-/* If we're building with OpenSSL, then we have SSL support */
+/* If we're building with OpenSSL or Windows SChannel, then we have SSL
+ * support */
 /* XXX: this doesn't belong here.. I couldn't find a precedence to follow */
-#ifdef USE_OPENSSL
+#if defined (USE_OPENSSL) || defined(USE_WINDOWS_SCHANNEL)
 #define USE_SSL
 #endif
 
diff --git a/src/interfaces/libpq/Makefile b/src/interfaces/libpq/Makefile
index 7827ac0..3b11b5b 100644
--- a/src/interfaces/libpq/Makefile
+++ b/src/interfaces/libpq/Makefile
@@ -48,6 +48,10 @@ ifeq ($(with_openssl),yes)
 OBJS += fe-secure-openssl.o
 endif
 
+ifeq ($(with_winschannel),yes)
+OBJS += fe-secure-winschannel.o
+endif
+
 ifeq ($(PORTNAME), cygwin)
 override shlib = cyg$(NAME)$(DLSUFFIX)
 endif
diff --git a/src/interfaces/libpq/fe-secure-winschannel.c b/src/interfaces/libpq/fe-secure-winschannel.c
new file mode 100644
index 0000000..efdbd98
--- /dev/null
+++ b/src/interfaces/libpq/fe-secure-winschannel.c
@@ -0,0 +1,822 @@
+/*-------------------------------------------------------------------------
+ *
+ * fe-winschannel.c
+ *	  functions for Windows Schannel SSL implementation
+ *
+ * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  src/interfaces/libpq/fe-winschannel.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres_fe.h"
+
+#include <ctype.h>
+#include <fcntl.h>
+
+#include "libpq-fe.h"
+#include "libpq-int.h"
+
+#include "win32.h"
+
+#include <sspi.h>
+#include <schnlsp.h>
+
+struct SchannelContext
+{
+	SecPkgContext_StreamSizes sizes;
+	CtxtHandle  ctxHandle;
+
+	char	   *encryptBuffer;
+	int			encryptBufferSize;
+	int			encryptBufferLen;
+	int			encryptBufferPos;
+	int			plaintextLen;
+	bool		encrypted;
+
+	char	   *decryptBuffer;
+	int			decryptBufferSize;
+	/*
+	 * These are the length and position of the ciphertext left in the buffer,
+	 * that hasn't been decrypted yet.
+	 */
+	int			decryptBufferLen;
+
+	/* decrypted data not returned to the caller yet */
+	char	   *decryptedData;
+	int			decryptedLen;
+
+	/*
+	 * extra data not decrypted yet (this is normally a pointer somewhere
+	 * in decryptBuffer
+	 */
+	char	   *extraData;
+	int			extraLen;
+};
+
+typedef struct SchannelContext SchannelContext;
+
+#define BUFSIZE 10000
+
+static CERT_CONTEXT *getClientCert();
+
+void
+pgtls_init_library(bool do_ssl, int do_crypto)
+{
+}
+
+
+/*
+ * Initialize SSL system, in particular creating the SSL_context object
+ * that will be shared by all SSL-using connections in this process.
+ *
+ * The conn parameter is only used to be able to pass back an error
+ * message - no connection-local setup is made here.
+ *
+ * Returns 0 if OK, -1 on failure (with a message in conn->errorMessage).
+ */
+int
+pgtls_init(PGconn *conn)
+{
+	/* nothing to do here ATM */
+	return 0;
+}
+
+
+/*
+ *	Begin or continue negotiating a secure session.
+ */
+PostgresPollingStatusType
+pgtls_open_client(PGconn *conn)
+{
+	SchannelContext *ctx = conn->windows_schannel;
+	SecBufferDesc outsbufdesc;
+	SecBuffer	outsbufs[1];
+	SecBufferDesc insbufdesc;
+	SecBuffer	insbufs[2];
+	CredHandle credHandle;
+	SECURITY_STATUS rc;
+	DWORD		flags;
+	DWORD		outflags;
+	int			n;
+	bool		first_call = (ctx == NULL);
+
+#ifdef SCHANNEL_DEBUG
+	fprintf(stderr, "pgtls_open_client called: %p\n", conn->windows_schannel);
+	fflush(stderr);
+#endif
+
+	if (first_call)
+	{
+		SCHANNEL_CRED credData;
+		CERT_CONTEXT *cert;
+
+		cert = getClientCert();
+
+		memset(&credData, 0, sizeof(credData));
+		credData.dwVersion = SCHANNEL_CRED_VERSION;
+		credData.dwFlags |= SCH_CRED_NO_SERVERNAME_CHECK |
+			SCH_CRED_MANUAL_CRED_VALIDATION; /* FIXME */
+		credData.cCreds = 1;
+		credData.paCred = &cert;
+
+		ctx = (SchannelContext *) malloc(sizeof(SchannelContext));
+		if (ctx == NULL)
+			goto fail;
+		memset(ctx, 0, sizeof(SchannelContext));
+
+		/* FIXME: check server cert */
+		rc = AcquireCredentialsHandle(NULL,
+									  UNISP_NAME,
+									  SECPKG_CRED_OUTBOUND,
+									  NULL,
+									  &credData,
+									  NULL,
+									  NULL,
+									  &credHandle,
+									  NULL);
+#ifdef SCHANNEL_DEBUG
+		fprintf(stderr, "AcquireCredentialsHandle returned %x\n", rc);
+		fflush(stderr);
+#endif
+		if (rc != SEC_E_OK)
+		{
+			printfPQExpBuffer(&conn->errorMessage,
+							  "AcquireCredentialsHandle failed: %d\n", rc);
+			goto fail;
+		}
+
+		ctx->encryptBufferSize = ctx->decryptBufferSize =
+			BUFSIZE;
+		ctx->encryptBuffer = malloc(ctx->encryptBufferSize);
+		if (ctx->encryptBuffer == NULL)
+			goto fail;
+
+		ctx->decryptBuffer = malloc(ctx->encryptBufferSize);
+		if (ctx->decryptBuffer == NULL)
+			goto fail;
+
+		conn->windows_schannel = ctx;
+		conn->ssl_in_use = true;
+	}
+	else
+	{
+		int			maxsize;
+		int			readlen;
+
+		/* read more data to buffer, and decrypt */
+		maxsize = ctx->decryptBufferSize - ctx->decryptBufferLen;
+#ifdef SCHANNEL_DEBUG
+		fprintf(stderr, "reading maxsize %d buflen %d\n", maxsize, ctx->decryptBufferLen);
+		fflush(stderr);
+#endif
+		readlen = pqsecure_raw_read(conn,
+									&ctx->decryptBuffer[ctx->decryptBufferLen],
+									maxsize);
+		fprintf(stderr, "received %d bytes in handshake\n", readlen);
+		fflush(stderr);
+		if (readlen < 0)
+			return PGRES_POLLING_FAILED; /* FIXME: error msg? */
+
+		ctx->decryptBufferLen += readlen;
+
+		insbufs[0].pvBuffer = ctx->decryptBuffer;
+		insbufs[0].cbBuffer = ctx->decryptBufferLen;
+		insbufs[0].BufferType = SECBUFFER_TOKEN;
+
+		insbufs[1].pvBuffer = NULL;
+		insbufs[1].cbBuffer = 0;
+		insbufs[1].BufferType = SECBUFFER_EMPTY;
+
+		insbufdesc.cBuffers = 2;
+		insbufdesc.pBuffers = insbufs;
+		insbufdesc.ulVersion = SECBUFFER_VERSION;
+
+		/* consume the input */
+		ctx->decryptBufferLen = 0;
+	}
+
+	flags = ISC_REQ_ALLOCATE_MEMORY |
+		ISC_REQ_CONFIDENTIALITY |
+		ISC_RET_EXTENDED_ERROR |
+		ISC_REQ_REPLAY_DETECT |
+		ISC_REQ_SEQUENCE_DETECT |
+		ISC_REQ_STREAM;
+
+	outsbufs[0].pvBuffer   = NULL;
+	outsbufs[0].BufferType = SECBUFFER_TOKEN;
+	outsbufs[0].cbBuffer   = 0;
+
+	outsbufdesc.cBuffers = 1;
+	outsbufdesc.pBuffers = outsbufs;
+	outsbufdesc.ulVersion = SECBUFFER_VERSION;
+
+	rc = InitializeSecurityContext(&credHandle,
+								   first_call ? NULL : &ctx->ctxHandle,
+								   NULL, /* pszTargetName */
+								   flags,
+								   0,
+								   0,
+								   first_call ? NULL : &insbufdesc,
+								   0,
+								   first_call ? &ctx->ctxHandle : NULL,
+								   &outsbufdesc,
+								   &outflags,
+								   NULL);
+#ifdef SCHANNEL_DEBUG
+	fprintf(stderr, "InitializeSecurityContext returned %x\n", (unsigned int) rc);
+	fflush(stderr);
+#endif
+
+	conn->windows_schannel = ctx;
+
+	switch(rc)
+	{
+		case SEC_E_OK:
+			/* SSL handshake is complete */
+			return PGRES_POLLING_OK;
+
+		case SEC_I_COMPLETE_AND_CONTINUE:
+			printfPQExpBuffer(&conn->errorMessage,
+							  "SEC_I_COMPLETE_AND_CONTINUE not implemented\n", rc);
+			return PGRES_POLLING_FAILED;
+
+		case SEC_I_COMPLETE_NEEDED:
+			printfPQExpBuffer(&conn->errorMessage,
+							  "SEC_I_COMPLETE_NEEDED not implemented\n", rc);
+			return PGRES_POLLING_FAILED;
+
+		case SEC_I_CONTINUE_NEEDED:
+			/*
+			 * Must send the token that InitializeSecurityContext put in
+			 * the out buffer.
+			 */
+#ifdef SCHANNEL_DEBUG
+			fprintf(stderr, "got SEC_I_CONTINUE_NEEDED. Sending %d bytes\n",
+					outsbufs[0].cbBuffer);
+			fflush(stderr);
+#endif
+			n = pqsecure_raw_write(conn,
+								   outsbufs[0].pvBuffer,
+								   outsbufs[0].cbBuffer);
+			if (n != outsbufs[0].cbBuffer)
+			{
+				printfPQExpBuffer(&conn->errorMessage,
+								  "short write\n", rc);
+				return PGRES_POLLING_FAILED;
+			}
+			FreeContextBuffer(outsbufs[0].pvBuffer);
+			outsbufs[0].pvBuffer = NULL;
+
+			return PGRES_POLLING_READING;
+
+		case SEC_I_INCOMPLETE_CREDENTIALS:
+			printfPQExpBuffer(&conn->errorMessage,
+							  "SEC_I_INCOMPLETE_CREDENTIALS not implemented\n", rc);
+			return PGRES_POLLING_FAILED;
+
+		case SEC_E_INCOMPLETE_MESSAGE:
+			printfPQExpBuffer(&conn->errorMessage,
+							  "SEC_E_INCOMPLETE_MESSAGE not implemented\n", rc);
+			return PGRES_POLLING_FAILED;
+
+		default:
+			printfPQExpBuffer(&conn->errorMessage,
+							  "InitializeSecurityContext failed: %d\n", rc);
+			return PGRES_POLLING_FAILED;
+	}
+
+	rc = QueryContextAttributes(&ctx->ctxHandle,
+								SECPKG_ATTR_STREAM_SIZES, &ctx->sizes);
+#ifdef SCHANNEL_DEBUG
+	fprintf(stderr, "QueryContextAttributes returned %d\n", rc);
+	fflush(stderr);
+#endif
+	if (rc != SEC_E_OK)
+	{
+		printfPQExpBuffer(&conn->errorMessage,
+						  "QueryContextAttributes failed: %d\n", rc);
+		goto fail; /* FIXME: set errno */
+	}
+
+	/* SSL handshake is complete */
+	return PGRES_POLLING_OK;
+
+fail:
+	if (ctx->decryptBuffer)
+		free(ctx->decryptBuffer);
+	if (ctx->encryptBuffer)
+		free(ctx->encryptBuffer);
+	if (ctx)
+		free(ctx);
+
+	return PGRES_POLLING_FAILED;
+}
+
+
+/*
+ *	Close SSL connection.
+ */
+void
+pgtls_close(PGconn *conn)
+{
+	/* TODO: Not implemented. We just leak everything */
+
+	if (conn->ssl_in_use)
+	{
+		conn->windows_schannel = NULL;
+		conn->ssl_in_use = false;
+	}
+}
+
+/*
+ *	Read data from a secure connection.
+ *
+ * On failure, this function is responsible for putting a suitable message
+ * into conn->errorMessage.  The caller must still inspect errno, but only
+ * to determine whether to continue/retry after error.
+ */
+ssize_t
+pgtls_read(PGconn *conn, char *buffer, size_t len)
+{
+	SchannelContext *ctx = conn->windows_schannel;
+	SecBufferDesc sbufdesc;
+	SecBuffer	sbufs[4];
+	int			readlen;
+	int			maxsize;
+	int			rc;
+	int			i;
+	int			totalreadlen = 0;
+	int			origlen = len;
+
+#ifdef SCHANNEL_DEBUG
+	fprintf(stderr, "pgtls_read called\n");
+	fflush(stderr);
+#endif
+
+	if (ctx->decryptedLen == 0)
+	{
+		/* read more data to buffer, and decrypt */
+		maxsize = ctx->decryptBufferSize - ctx->decryptBufferLen;
+		readlen = pqsecure_raw_read(conn,
+									&ctx->decryptBuffer[ctx->decryptBufferLen],
+									maxsize);
+		if (readlen < 0)
+			return readlen;
+
+#ifdef SCHANNEL_DEBUG
+		fprintf(stderr, "pgtls_read read %d bytes\n", readlen);
+		fflush(stderr);
+#endif
+
+		ctx->decryptBufferLen += readlen;
+	}
+
+retry:
+	if (ctx->decryptedLen == 0)
+	{
+		/* try to decrypt what we got */
+
+		sbufs[0].pvBuffer = ctx->decryptBuffer;
+		sbufs[0].cbBuffer = ctx->decryptBufferLen;
+		sbufs[0].BufferType = SECBUFFER_DATA;
+		sbufs[1].pvBuffer = NULL;
+		sbufs[1].cbBuffer = 0;
+		sbufs[1].BufferType   = SECBUFFER_EMPTY;
+		sbufs[2].pvBuffer = NULL;
+		sbufs[2].cbBuffer = 0;
+		sbufs[2].BufferType   = SECBUFFER_EMPTY;
+		sbufs[3].pvBuffer = NULL;
+		sbufs[3].cbBuffer = 0;
+		sbufs[3].BufferType   = SECBUFFER_EMPTY;
+
+		sbufdesc.ulVersion = SECBUFFER_VERSION;
+		sbufdesc.cBuffers = 4;
+		sbufdesc.pBuffers = sbufs;
+
+		rc = DecryptMessage(&ctx->ctxHandle, &sbufdesc, 0, NULL);
+
+#ifdef SCHANNEL_DEBUG
+		fprintf(stderr, "DecryptMessage returned %d\n", rc);
+		fflush(stderr);
+#endif
+
+		if (rc == SEC_E_INCOMPLETE_MESSAGE)
+		{
+			/* We don't have the full message yet. Need more data. */
+			return totalreadlen;
+		}
+		if (rc != SEC_E_OK && rc != SEC_I_RENEGOTIATE)
+		{
+			/* FIXME: set errno */
+			printfPQExpBuffer(&conn->errorMessage,
+							  "DecryptMessage failed: %d\n", rc);
+			return -1;
+		}
+
+		/* Did we not decrypt everything? */
+		ctx->extraData = NULL;
+		ctx->extraLen = 0;
+		ctx->decryptedData = NULL;
+		ctx->decryptedLen = 0;
+		for (i = 0; i < 4; i++)
+		{
+			if (sbufs[i].BufferType == SECBUFFER_EXTRA)
+			{
+				ctx->extraData = sbufs[i].pvBuffer;
+				ctx->extraLen = sbufs[i].cbBuffer;
+#ifdef SCHANNEL_DEBUG
+				fprintf(stderr, "found %d extra bytes\n", sbufs[i].cbBuffer);
+				fflush(stderr);
+#endif
+			}
+			if (sbufs[i].BufferType == SECBUFFER_DATA)
+			{
+				ctx->decryptedData = sbufs[i].pvBuffer;
+				ctx->decryptedLen = sbufs[i].cbBuffer;
+#ifdef SCHANNEL_DEBUG
+				fprintf(stderr, "found %d data bytes\n", sbufs[i].cbBuffer);
+				fflush(stderr);
+#endif
+			}
+		}
+	}
+
+	/* we should now have some decrypted data in the buffer. Return it */
+	readlen = len;
+	if (readlen > ctx->decryptedLen)
+		readlen = ctx->decryptedLen;
+
+	memcpy(buffer, ctx->decryptedData, readlen);
+	ctx->decryptedData += readlen;
+	ctx->decryptedLen -= readlen;
+
+#ifdef SCHANNEL_DEBUG
+	fprintf(stderr, "returning %d decrypted bytes (%d in total, %d max)\n", readlen, totalreadlen, origlen);
+	fflush(stderr);
+#endif
+
+	if (ctx->decryptedLen == 0)
+	{
+		/*
+		 * Finished returning this message. If there's some extra data in the
+		 * buffer, move it to beginning of buffer for next call.
+		 */
+		if (ctx->extraData > 0)
+		{
+			memmove(ctx->decryptBuffer,
+					ctx->extraData,
+					ctx->extraLen);
+#ifdef SCHANNEL_DEBUG
+			fprintf(stderr, "shifted %d extra bytes\n", ctx->extraLen);
+			fflush(stderr);
+#endif
+		}
+		ctx->decryptBufferLen = ctx->extraLen;
+		ctx->extraData = NULL;
+		ctx->extraLen = 0;
+	}
+
+	/*
+	 * If we decrypted some data, but it yielded 0 plaintext bytes (i.e. it
+	 * was all SSL's internal protocol stuff), try again.
+	 */
+	if (readlen == 0)
+	{
+#ifdef SCHANNEL_DEBUG
+		fprintf(stderr, "schannel_read got 0 bytes, retrying\n");
+		fflush(stderr);
+#endif
+		goto retry;
+	}
+
+	totalreadlen += readlen;
+
+	/*
+	 * If the target buffer has some room left, and we have more data in the
+	 * buffer that we could decrypt and yield more output, try that.
+	 */
+	if (readlen < len && ctx->decryptBufferLen > 0)
+	{
+		buffer += readlen;
+		len -= readlen;
+		goto retry;
+	}
+
+	return totalreadlen;
+}
+
+/*
+ *	Write data to a secure connection.
+ *
+ * On failure, this function is responsible for putting a suitable message
+ * into conn->errorMessage.  The caller must still inspect errno, but only
+ * to determine whether to continue/retry after error.
+ */
+ssize_t
+pgtls_write(PGconn *conn, const void *ptr, size_t len)
+{
+	SchannelContext *ctx = conn->windows_schannel;
+	SecBufferDesc sbufdesc;
+	SecBuffer	sbufs[4];
+	int			sendlen;
+	SECURITY_STATUS rc;
+
+#ifdef SCHANNEL_DEBUG
+	fprintf(stderr, "pgtls_write called %d\n", len);
+	fflush(stderr);
+#endif
+	if (!ctx->encrypted)
+	{
+		sendlen = len;
+
+#ifdef SCHANNEL_DEBUG
+		fprintf(stderr, "pgtls_write sizes before %d + %d + %d\n",
+			ctx->sizes.cbHeader + ctx->sizes.cbMaximumMessage + ctx->sizes.cbTrailer);
+		fflush(stderr);
+#endif
+
+		rc = QueryContextAttributes(&ctx->ctxHandle,
+									SECPKG_ATTR_STREAM_SIZES, &ctx->sizes);
+#ifdef SCHANNEL_DEBUG
+		fprintf(stderr, "QueryContextAttributes returned %d\n", rc);
+		fflush(stderr);
+#endif
+		if (rc != SEC_E_OK)
+		{
+			printfPQExpBuffer(&conn->errorMessage,
+							  "QueryContextAttributes failed: %d\n", rc);
+			return -1; /* FIXME: set errno */
+		}
+#ifdef SCHANNEL_DEBUG
+		fprintf(stderr, "pgtls_write sizes after %d + %d + %d\n",
+			ctx->sizes.cbHeader + ctx->sizes.cbMaximumMessage + ctx->sizes.cbTrailer);
+		fflush(stderr);
+#endif
+
+		if (ctx->sizes.cbHeader + ctx->sizes.cbTrailer + sendlen > ctx->encryptBufferSize)
+			sendlen = ctx->encryptBufferSize - (ctx->sizes.cbHeader + ctx->sizes.cbTrailer);
+
+		memcpy(&ctx->encryptBuffer[ctx->sizes.cbHeader], ptr, sendlen);
+
+#ifdef SCHANNEL_DEBUG
+		fprintf(stderr, "pgtls_write sendlen %d\n", sendlen);
+		fflush(stderr);
+#endif
+
+		sbufs[0].pvBuffer = ctx->encryptBuffer;
+		sbufs[0].cbBuffer = ctx->sizes.cbHeader;
+		sbufs[0].BufferType = SECBUFFER_STREAM_HEADER;
+
+		sbufs[1].pvBuffer = &ctx->encryptBuffer[ctx->sizes.cbHeader];
+		sbufs[1].cbBuffer = sendlen;
+		sbufs[1].BufferType = SECBUFFER_DATA;
+
+		sbufs[2].pvBuffer = &ctx->encryptBuffer[ctx->sizes.cbHeader + sendlen];
+		sbufs[2].cbBuffer = ctx->sizes.cbTrailer;
+		sbufs[2].BufferType = SECBUFFER_STREAM_TRAILER;
+
+		sbufs[3].pvBuffer = NULL;
+		sbufs[3].cbBuffer = 0;
+		sbufs[3].BufferType = SECBUFFER_EMPTY;
+
+		sbufdesc.ulVersion = SECBUFFER_VERSION;
+		sbufdesc.cBuffers = 4;
+		sbufdesc.pBuffers = sbufs;
+
+#ifdef SCHANNEL_DEBUG
+		fprintf(stderr, "encrypting %d + %d + %d bytes\n",
+				sbufs[0].cbBuffer, sbufs[1].cbBuffer, sbufs[2].cbBuffer);
+		fflush(stderr);
+#endif
+		rc = EncryptMessage(&ctx->ctxHandle, 0, &sbufdesc, 0);
+#ifdef SCHANNEL_DEBUG
+		fprintf(stderr, "returned %d\n", rc),
+		fflush(stderr);
+#endif
+		if (rc != SEC_E_OK)
+		{
+			printfPQExpBuffer(&conn->errorMessage,
+							  "EncryptMessage failed: %d\n", rc);
+			return -1;
+		}
+
+		ctx->encryptBufferLen = sbufs[0].cbBuffer + sbufs[1].cbBuffer + sbufs[2].cbBuffer;
+		ctx->encryptBufferPos = 0;
+		ctx->plaintextLen = sendlen;
+		ctx->encrypted = true;
+	}
+
+	/* we now have some encrypted data in the buffer. Send it. */
+	sendlen = pqsecure_raw_write(conn,
+								 &ctx->encryptBuffer[ctx->encryptBufferPos],
+								 ctx->encryptBufferLen - ctx->encryptBufferPos);
+	if (sendlen <= 0)
+		return sendlen;
+
+	ctx->encryptBufferPos += sendlen;
+
+	if (ctx->encryptBufferPos == ctx->encryptBufferLen)
+	{
+		/* sent all the data in the buffer. */
+		ctx->encrypted = false;
+		return ctx->plaintextLen;
+	}
+	else
+		return 0;
+}
+
+static const char *keyStr =
+"-----BEGIN RSA PRIVATE KEY-----\n"
+"MIICXQIBAAKBgQC8K4U6m1APskR3VY277O3fp1/aEMC2UbgohFL2y/jo1Ij2tqr6\n"
+"dxIzwOD/KqjQ2/4DSEoGINDx8RObiLF4HWK6JhN135zn4gs++gN4DDYcZbWV107q\n"
+"zcq3G4FgH8obuUUl7BIWT2NuzfPw9nOJjGkE2ySCW27Iv0/yGG3Jv7am1QIDAQAB\n"
+"AoGAeP4S4KhVRoJ9+62LQTcLjS/2NiVnhNPu8j6DiiWFm1Bt8RVrwzkk/xoakng/\n"
+"rGyIXFvzHRWkxK0aynuOgYmDnMoQhil5aMvemnR7ANJP5JXq3J5sl1ghB1HdBtxd\n"
+"UhbpyoE3NMjHDBi6XDPiIut6w4wusc7EQxCa/L0yNZJ4R9ECQQD2b9/VyEaAwvib\n"
+"Y9UhHf8/KoUt4eC5MUDwDYPqmhyZZniq9D5ucgGQNHdPEK+yJQKcq+j/bo1vNzwu\n"
+"P49/cdrPAkEAw3jRGXVDeitLk0wJYhxr8b4nCMWHcIYfR6NvYlXwLbi2LbDF311W\n"
+"Zo6T+zsm8k/4YnhR1fMlrj8ScqfOxKr9GwJBAN/fIeiD2AiBFneTabp8FbS8W+Ai\n"
+"opOaOvPYU058Uh7JCDXFTDHpP6JRB1G8Rt/+3zCUu0XQmIvAEduxwhp0w+ECQQCq\n"
+"WKqY7glM+VgWWIhA2RX1CUqJKWMb27Z8vuA9qTjzD2qsLMZ0HqpCG/S4V1dcifaj\n"
+"Ecn3krr+u/Z+tuDJoV2LAkB9BHZbcdrYAOpWNfKJ3YyIOmb5ZMabDgav3QTGUXWN\n"
+"hLgVhgidvb6xa3zT2Ps2C8PvsIGpD5d3UzdJvUwcCZfP\n"
+	"-----END RSA PRIVATE KEY-----\n";
+
+static const char	*crtStr =
+"-----BEGIN CERTIFICATE-----\n"
+"MIICMjCCAZsCCQCyIo/FfT60NzANBgkqhkiG9w0BAQsFADB2MQswCQYDVQQGEwJG\n"
+"STETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0\n"
+"cyBQdHkgTHRkMQ8wDQYDVQQDDAZIZWlra2kxHjAcBgkqhkiG9w0BCQEWD2hsaW5u\n"
+"YWthQGlraS5maTAeFw0xNDA2MDkxMzM0MzJaFw0xNDA3MDkxMzM0MzJaMEUxCzAJ\n"
+"BgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5l\n"
+"dCBXaWRnaXRzIFB0eSBMdGQwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBALwr\n"
+"hTqbUA+yRHdVjbvs7d+nX9oQwLZRuCiEUvbL+OjUiPa2qvp3EjPA4P8qqNDb/gNI\n"
+"SgYg0PHxE5uIsXgdYromE3XfnOfiCz76A3gMNhxltZXXTurNyrcbgWAfyhu5RSXs\n"
+"EhZPY27N8/D2c4mMaQTbJIJbbsi/T/IYbcm/tqbVAgMBAAEwDQYJKoZIhvcNAQEL\n"
+"BQADgYEAjvK0hCMzGrAawUmSlf7SnCGv3fwjCVDka9NusHX16TQaFP4E1pYBQYkY\n"
+"vKwkSj+bZTrw7fWDHKpGXghS1S8JdqSMOG0uLeqTa2dasnj1SnhbTafK6VgUPB3U\n"
+"J/30HV9WioBqX+4tUzUEvLNl3JYbO4Rd4BX5Kzq2QkrtMX+yPaM=\n"
+	"-----END CERTIFICATE-----\n";
+
+/*
+ * Loads the certificate and associated private key into a Certificate
+ * Context.
+ */
+static CERT_CONTEXT *
+getClientCert()
+{
+	CERT_CONTEXT *cert_cxt;
+	char		crtBuf[50000]; /* TODO: enlarge as needed */
+	DWORD		crtBufLen = 50000;
+	char		keyBuf[50000]; /* TODO: enlarge as needed */
+	DWORD		keyBufLen = 50000;
+	char		privKeyBlob[50000];
+	int			privKeyBlobLen = 50000;
+	CERT_KEY_CONTEXT keyCtx;
+	HCRYPTPROV	cryptProvHandle;
+	HCRYPTKEY	keyHandle;
+	PUBLICKEYSTRUC *blob;
+
+	/* TODO: read cert from file */
+
+
+	/* convert from PEM to DER */
+	if (!CryptStringToBinary(
+			crtStr,
+			strlen(crtStr),
+			CRYPT_STRING_BASE64HEADER,
+			crtBuf,
+			&crtBufLen,
+			NULL,
+			NULL))
+	{
+#ifdef SCHANNEL_DEBUG
+		fprintf(stderr, "CryptStringToBinary failed: %d\n", GetLastError());
+		fflush(stderr);
+#endif
+		return NULL;
+	}
+
+	cert_cxt = CertCreateCertificateContext(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
+											crtBuf,
+											crtBufLen);
+	if (cert_cxt == NULL)
+	{
+#ifdef SCHANNEL_DEBUG
+		fprintf(stderr, "CertCreateCertificateContext failed: %x\n", GetLastError());
+		fflush(stderr);
+#endif
+		return NULL;
+	}
+
+#ifdef SCHANNEL_DEBUG
+	fprintf(stderr, "CertCreateCertificateContext succeeded!\n");
+	fflush(stderr);
+#endif
+
+	/*
+	 * Cool, got the cert. But we also need to get the private key associated
+	 * with it.
+	 */
+
+	/* convert from PEM to DER */
+	if (!CryptStringToBinary(
+			keyStr,
+			strlen(keyStr),
+			CRYPT_STRING_BASE64HEADER,
+			keyBuf,
+			&keyBufLen,
+			NULL,
+			NULL))
+	{
+#ifdef SCHANNEL_DEBUG
+		fprintf(stderr, "CryptStringToBinary failed: %d\n", GetLastError());
+		fflush(stderr);
+#endif
+		return NULL;
+	}
+
+	/*
+	 * Get private key
+	 */
+	if (!CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
+							 PKCS_RSA_PRIVATE_KEY,
+							 keyBuf,
+							 keyBufLen,
+							 0,
+							 NULL,
+							 privKeyBlob,
+							 &privKeyBlobLen))
+	{
+#ifdef SCHANNEL_DEBUG
+		fprintf(stderr, "CryptDecodeObjectEx failed: %x\n", GetLastError());
+		fflush(stderr);
+#endif
+		return NULL;
+	}
+	blob = (PUBLICKEYSTRUC *) privKeyBlob;
+
+#ifdef SCHANNEL_DEBUG
+	fprintf(stderr, "decoded bType %d bVersion %d, alg %d len %d\n",
+			blob->bType, blob->bVersion, blob->aiKeyAlg, privKeyBlobLen);
+	fflush(stderr);
+#endif
+
+	if (!CryptAcquireContext(&cryptProvHandle, NULL, MS_ENHANCED_PROV, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))
+	{
+#ifdef SCHANNEL_DEBUG
+		fprintf(stderr, "CryptAcquireContext failed: %x\n", GetLastError());
+		fflush(stderr);
+#endif
+		return NULL;
+	}
+
+	if (!CryptImportKey(cryptProvHandle, privKeyBlob, privKeyBlobLen,
+						(HCRYPTKEY) NULL, 0, &keyHandle))
+	{
+#ifdef SCHANNEL_DEBUG
+        fprintf(stderr, "CryptImportKey failed: %x\n", GetLastError());
+		fflush(stderr);
+#endif
+		return NULL;
+	}
+
+	memset(&keyCtx, 0, sizeof(keyCtx));
+	keyCtx.cbSize = sizeof(keyCtx);
+	keyCtx.hCryptProv = cryptProvHandle;
+	keyCtx.dwKeySpec = AT_KEYEXCHANGE;
+
+	if (!CertSetCertificateContextProperty(cert_cxt,
+										   CERT_KEY_CONTEXT_PROP_ID,
+										   0,
+										   &keyCtx))
+	{
+#ifdef SCHANNEL_DEBUG
+		fprintf(stderr, "CertSetCertificateContextProperty failed: %x\n", GetLastError());
+		fflush(stderr);
+#endif
+		return NULL;
+	}
+
+	return cert_cxt;
+}
+
+
+
+/*
+ *	TODO: need a new API for this.
+ */
+void *
+PQgetssl(PGconn *conn)
+{
+	/*
+	 * return non-zero, so that psql reports that SSL is used. Of course, it's
+	 * totally bogus for anyone who actually tries to get the OpenSSL
+	 * SSL struct.
+	 */
+	return (void *) 1;
+}
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index 6032904..4a1b413 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -82,6 +82,10 @@ typedef struct
 #endif
 #endif   /* USE_OPENSSL */
 
+#ifdef USE_WINDOWS_SCHANNEL
+struct SchannelContext;
+#endif
+
 /*
  * POSTGRES backend dependent Constants.
  */
@@ -428,6 +432,7 @@ struct pg_conn
 	bool		wait_ssl_try;	/* Delay SSL negotiation until after
 								 * attempting normal connection */
 	bool		ssl_in_use;
+
 #ifdef USE_OPENSSL
 	SSL		   *ssl;			/* SSL status, if have SSL connection */
 	X509	   *peer;			/* X509 cert of server */
@@ -438,6 +443,11 @@ struct pg_conn
 								 * OpenSSL version changes */
 #endif
 #endif   /* USE_OPENSSL */
+
+#ifdef USE_WINDOWS_SCHANNEL
+
+#endif	/* USE_WINDOWS_SCHANNEL */
+	struct SchannelContext *windows_schannel;
 #endif   /* USE_SSL */
 
 #ifdef ENABLE_GSS
@@ -634,7 +644,8 @@ extern void pq_reset_sigpipe(sigset_t *osigset, bool sigpipe_pending,
 #endif
 
 /*
- * The SSL implementatation provides these functions (fe-secure-openssl.c)
+ * The SSL implementation provides these functions (i.e. fe-secure-openssl.c
+ * or fe-secure-winschannel.c)
  */
 extern void pgtls_init_library(bool do_ssl, int do_crypto);
 extern int pgtls_init(PGconn *conn);
diff --git a/src/interfaces/libpq/win32.mak b/src/interfaces/libpq/win32.mak
index 79b6d49..6a78917 100644
--- a/src/interfaces/libpq/win32.mak
+++ b/src/interfaces/libpq/win32.mak
@@ -101,6 +101,12 @@ CLEAN :
 	-@erase "$(INTDIR)\fe-misc.obj"
 	-@erase "$(INTDIR)\fe-print.obj"
 	-@erase "$(INTDIR)\fe-secure.obj"
+!IFDEF USE_OPENSSL
+	-@erase "$(INTDIR)\fe-secure-openssl.obj"
+!ENDIF
+!IFDEF USE_WINDOWS_SCHANNEL
+	-@erase "$(INTDIR)\fe-secure-winschannel.obj"
+!ENDIF
 	-@erase "$(INTDIR)\libpq-events.obj"
 	-@erase "$(INTDIR)\pqexpbuffer.obj"
 	-@erase "$(INTDIR)\win32.obj"
@@ -171,6 +177,12 @@ LIB32_OBJS= \
 	LIB32_OBJS=$(LIB32_OBJS) "$(INTDIR)\fe-secure-openssl.obj"
 !ENDIF
 
+!IFDEF USE_OPENSSL
+	LIB32_OBJS=$(LIB32_OBJS) "$(INTDIR)\fe-secure-openssl.obj"
+!ENDIF
+!IFDEF USE_WINDOWS_SCHANNEL
+	LIB32_OBJS=$(LIB32_OBJS) "$(INTDIR)\fe-secure-winschannel.obj"
+!ENDIF
 
 config: ..\..\include\pg_config.h ..\..\include\pg_config_ext.h pg_config_paths.h  ..\..\include\pg_config_os.h
 
diff --git a/src/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pm
index af231ba..d29f450 100644
--- a/src/tools/msvc/Mkvcbuild.pm
+++ b/src/tools/msvc/Mkvcbuild.pm
@@ -116,6 +116,10 @@ sub mkvcbuild
 	$postgres->AddLibrary('wsock32.lib');
 	$postgres->AddLibrary('ws2_32.lib');
 	$postgres->AddLibrary('secur32.lib');
+	if ($solution->{options}->{winschannel})
+	{
+		$postgres->AddLibrary('crypt32.lib');
+	}
 	$postgres->AddLibrary('wldap32.lib') if ($solution->{options}->{ldap});
 	$postgres->FullExportDLL('postgres.lib');
 	# The OBJS scraper doesn't know about ifdefs, so remove fe-secure-openssl.c
@@ -124,6 +128,11 @@ sub mkvcbuild
 	{
 		$postgres->RemoveFile('src\backend\libpq\be-secure-openssl.c');
 	}
+	# and same for winschannel
+	if (!$solution->{options}->{winschannel})
+	{
+		$postgres->RemoveFile('src\backend\libpq\be-secure-winschannel.c');
+	}
 
 	my $snowball = $solution->AddProject('dict_snowball', 'dll', '',
 		'src\backend\snowball');
@@ -278,6 +287,10 @@ sub mkvcbuild
 	$libpq->AddIncludeDir('src\port');
 	$libpq->AddLibrary('wsock32.lib');
 	$libpq->AddLibrary('secur32.lib');
+	if ($solution->{options}->{winschannel})
+	{
+		$libpq->AddLibrary('crypt32.lib');
+	}
 	$libpq->AddLibrary('ws2_32.lib');
 	$libpq->AddLibrary('wldap32.lib') if ($solution->{options}->{ldap});
 	$libpq->UseDef('src\interfaces\libpq\libpqdll.def');
@@ -290,6 +303,11 @@ sub mkvcbuild
 	{
 		$libpq->RemoveFile('src\interfaces\libpq\fe-secure-openssl.c');
 	}
+	# and same for winschannel
+	if (!$solution->{options}->{winschannel})
+	{
+		$libpq->RemoveFile('src\interfaces\libpq\fe-secure-winschannel.c');
+	}
 
 	my $libpqwalreceiver =
 	  $solution->AddProject('libpqwalreceiver', 'dll', '',
diff --git a/src/tools/msvc/Solution.pm b/src/tools/msvc/Solution.pm
index f66d1f6..8031acb 100644
--- a/src/tools/msvc/Solution.pm
+++ b/src/tools/msvc/Solution.pm
@@ -181,6 +181,8 @@ s{PG_VERSION_STR "[^"]+"}{__STRINGIFY(x) #x\n#define __STRINGIFY2(z) __STRINGIFY
 		print O "#define USE_LDAP 1\n"   if ($self->{options}->{ldap});
 		print O "#define HAVE_LIBZ 1\n"  if ($self->{options}->{zlib});
 		print O "#define USE_OPENSSL 1\n" if ($self->{options}->{openssl});
+		print O "#define USE_WINDOWS_SCHANNEL 1\n"
+		  if ($self->{options}->{winschannel});
 		print O "#define ENABLE_NLS 1\n" if ($self->{options}->{nls});
 
 		print O "#define BLCKSZ ", 1024 * $self->{options}->{blocksize}, "\n";
@@ -625,6 +627,7 @@ sub GetFakeConfigure
 	$cfg .= ' --with-ldap'  if ($self->{options}->{ldap});
 	$cfg .= ' --without-zlib' unless ($self->{options}->{zlib});
 	$cfg .= ' --with-openssl'   if ($self->{options}->{openssl});
+	$cfg .= ' --with-winschannel' if ($self->{options}->{winschannel});
 	$cfg .= ' --with-ossp-uuid' if ($self->{options}->{uuid});
 	$cfg .= ' --with-libxml'    if ($self->{options}->{xml});
 	$cfg .= ' --with-libxslt'   if ($self->{options}->{xslt});
diff --git a/src/tools/msvc/config_default.pl b/src/tools/msvc/config_default.pl
index 7c04de0..62bc9ab 100644
--- a/src/tools/msvc/config_default.pl
+++ b/src/tools/msvc/config_default.pl
@@ -16,6 +16,7 @@ our $config = {
 	perl    => undef,    # --with-perl
 	python  => undef,    # --with-python=<path>
 	openssl => undef,    # --with-openssl=<path>
+	winschannel => 1,    # --with-winschannel
 	uuid    => undef,    # --with-ossp-uuid
 	xml     => undef,    # --with-libxml=<path>
 	xslt    => undef,    # --with-libxslt=<path>
-- 
2.0.0

0001-Invent-a-new-internal-API-for-interfacing-with-SSL.patchtext/x-diff; name=0001-Invent-a-new-internal-API-for-interfacing-with-SSL.patchDownload
>From 057457369cacce009ca29fd4dc5cf2e59f3d3351 Mon Sep 17 00:00:00 2001
From: Heikki Linnakangas <heikki.linnakangas@iki.fi>
Date: Tue, 10 Jun 2014 23:30:51 +0300
Subject: [PATCH 1/2] Invent a new internal API for interfacing with SSL.

Refactor OpenSSL specific code to separate files, implementing the new API.
---
 configure                                |    2 +-
 configure.in                             |    2 +-
 src/backend/libpq/Makefile               |    4 +
 src/backend/libpq/auth.c                 |   14 +-
 src/backend/libpq/be-secure-openssl.c    | 1052 +++++++++++++++++++++
 src/backend/libpq/be-secure.c            |  927 +------------------
 src/backend/libpq/hba.c                  |    2 +-
 src/backend/postmaster/fork_process.c    |    4 +-
 src/backend/utils/init/postinit.c        |    8 +-
 src/backend/utils/misc/guc.c             |    3 -
 src/bin/psql/command.c                   |    4 +-
 src/include/libpq/libpq-be.h             |   23 +-
 src/include/libpq/libpq.h                |    9 +
 src/include/pg_config.h.in               |    6 +-
 src/include/pg_config.h.win32            |    6 +-
 src/include/port.h                       |    6 +
 src/interfaces/libpq/Makefile            |    4 +
 src/interfaces/libpq/fe-connect.c        |    7 +-
 src/interfaces/libpq/fe-misc.c           |    6 +-
 src/interfaces/libpq/fe-secure-openssl.c | 1471 ++++++++++++++++++++++++++++++
 src/interfaces/libpq/fe-secure.c         | 1467 ++---------------------------
 src/interfaces/libpq/libpq-int.h         |   37 +-
 src/interfaces/libpq/win32.mak           |   12 +-
 src/tools/msvc/Mkvcbuild.pm              |   12 +
 src/tools/msvc/Solution.pm               |    4 +-
 src/tools/msvc/config_default.pl         |    2 +-
 26 files changed, 2785 insertions(+), 2309 deletions(-)
 create mode 100644 src/backend/libpq/be-secure-openssl.c
 create mode 100644 src/interfaces/libpq/fe-secure-openssl.c

diff --git a/configure b/configure
index ed1ff0a..0e6b109 100755
--- a/configure
+++ b/configure
@@ -5494,7 +5494,7 @@ if test "${with_openssl+set}" = set; then :
   case $withval in
     yes)
 
-$as_echo "#define USE_SSL 1" >>confdefs.h
+$as_echo "#define USE_OPENSSL 1" >>confdefs.h
 
       ;;
     no)
diff --git a/configure.in b/configure.in
index 80df1d7..4bdd0f7 100644
--- a/configure.in
+++ b/configure.in
@@ -658,7 +658,7 @@ AC_MSG_RESULT([$with_bonjour])
 #
 AC_MSG_CHECKING([whether to build with OpenSSL support])
 PGAC_ARG_BOOL(with, openssl, no, [build with OpenSSL support],
-              [AC_DEFINE([USE_SSL], 1, [Define to build with (Open)SSL support. (--with-openssl)])])
+              [AC_DEFINE([USE_OPENSSL], 1, [Define to build with OpenSSL support. (--with-openssl)])])
 AC_MSG_RESULT([$with_openssl])
 AC_SUBST(with_openssl)
 
diff --git a/src/backend/libpq/Makefile b/src/backend/libpq/Makefile
index e929864..8be0572 100644
--- a/src/backend/libpq/Makefile
+++ b/src/backend/libpq/Makefile
@@ -17,4 +17,8 @@ include $(top_builddir)/src/Makefile.global
 OBJS = be-fsstubs.o be-secure.o auth.o crypt.o hba.o ip.o md5.o pqcomm.o \
        pqformat.o pqsignal.o
 
+ifeq ($(with_openssl),yes)
+OBJS += be-secure-openssl.o
+endif
+
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c
index 70b0b93..b1974d1 100644
--- a/src/backend/libpq/auth.c
+++ b/src/backend/libpq/auth.c
@@ -161,7 +161,7 @@ static int	pg_SSPI_recvauth(Port *port);
  * RADIUS Authentication
  *----------------------------------------------------------------
  */
-#ifdef USE_SSL
+#ifdef USE_OPENSSL
 #include <openssl/rand.h>
 #endif
 static int	CheckRADIUSAuth(Port *port);
@@ -330,7 +330,7 @@ ClientAuthentication(Port *port)
 		 * already if it didn't verify ok.
 		 */
 #ifdef USE_SSL
-		if (!port->peer)
+		if (!port->peer_cert_valid)
 		{
 			ereport(FATAL,
 					(errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
@@ -378,7 +378,7 @@ ClientAuthentication(Port *port)
 					   (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
 						errmsg("pg_hba.conf rejects replication connection for host \"%s\", user \"%s\", %s",
 							   hostinfo, port->user_name,
-							   port->ssl ? _("SSL on") : _("SSL off"))));
+							   port->ssl_in_use ? _("SSL on") : _("SSL off"))));
 #else
 					ereport(FATAL,
 					   (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
@@ -394,7 +394,7 @@ ClientAuthentication(Port *port)
 						errmsg("pg_hba.conf rejects connection for host \"%s\", user \"%s\", database \"%s\", %s",
 							   hostinfo, port->user_name,
 							   port->database_name,
-							   port->ssl ? _("SSL on") : _("SSL off"))));
+							   port->ssl_in_use ? _("SSL on") : _("SSL off"))));
 #else
 					ereport(FATAL,
 					   (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
@@ -452,7 +452,7 @@ ClientAuthentication(Port *port)
 					   (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
 						errmsg("no pg_hba.conf entry for replication connection from host \"%s\", user \"%s\", %s",
 							   hostinfo, port->user_name,
-							   port->ssl ? _("SSL on") : _("SSL off")),
+							   port->ssl_in_use ? _("SSL on") : _("SSL off")),
 						HOSTNAME_LOOKUP_DETAIL(port)));
 #else
 					ereport(FATAL,
@@ -470,7 +470,7 @@ ClientAuthentication(Port *port)
 						errmsg("no pg_hba.conf entry for host \"%s\", user \"%s\", database \"%s\", %s",
 							   hostinfo, port->user_name,
 							   port->database_name,
-							   port->ssl ? _("SSL on") : _("SSL off")),
+							   port->ssl_in_use ? _("SSL on") : _("SSL off")),
 						HOSTNAME_LOOKUP_DETAIL(port)));
 #else
 					ereport(FATAL,
@@ -2315,7 +2315,7 @@ CheckRADIUSAuth(Port *port)
 	/* Construct RADIUS packet */
 	packet->code = RADIUS_ACCESS_REQUEST;
 	packet->length = RADIUS_HEADER_LENGTH;
-#ifdef USE_SSL
+#ifdef USE_OPENSSL
 	if (RAND_bytes(packet->vector, RADIUS_VECTOR_LENGTH) != 1)
 	{
 		ereport(LOG,
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
new file mode 100644
index 0000000..dca1bde
--- /dev/null
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -0,0 +1,1052 @@
+/*-------------------------------------------------------------------------
+ *
+ * be-secure-openssl.c
+ *	  functions related OpenSSL
+ *
+ *
+ * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  src/backend/libpq/be-secure-openssl.c
+ *
+ *	  Since the server static private key ($DataDir/server.key)
+ *	  will normally be stored unencrypted so that the database
+ *	  backend can restart automatically, it is important that
+ *	  we select an algorithm that continues to provide confidentiality
+ *	  even if the attacker has the server's private key.  Ephemeral
+ *	  DH (EDH) keys provide this, and in fact provide Perfect Forward
+ *	  Secrecy (PFS) except for situations where the session can
+ *	  be hijacked during a periodic handshake/renegotiation.
+ *	  Even that backdoor can be closed if client certificates
+ *	  are used (since the imposter will be unable to successfully
+ *	  complete renegotiation).
+ *
+ *	  N.B., the static private key should still be protected to
+ *	  the largest extent possible, to minimize the risk of
+ *	  impersonations.
+ *
+ *	  Another benefit of EDH is that it allows the backend and
+ *	  clients to use DSA keys.  DSA keys can only provide digital
+ *	  signatures, not encryption, and are often acceptable in
+ *	  jurisdictions where RSA keys are unacceptable.
+ *
+ *	  The downside to EDH is that it makes it impossible to
+ *	  use ssldump(1) if there's a problem establishing an SSL
+ *	  session.  In this case you'll need to temporarily disable
+ *	  EDH by commenting out the callback.
+ *
+ *	  ...
+ *
+ *	  Because the risk of cryptanalysis increases as large
+ *	  amounts of data are sent with the same session key, the
+ *	  session keys are periodically renegotiated.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include <sys/stat.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#ifdef HAVE_NETINET_TCP_H
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+#endif
+
+#include <openssl/ssl.h>
+#include <openssl/dh.h>
+#if SSLEAY_VERSION_NUMBER >= 0x0907000L
+#include <openssl/conf.h>
+#endif
+#if (OPENSSL_VERSION_NUMBER >= 0x0090800fL) && !defined(OPENSSL_NO_ECDH)
+#include <openssl/ec.h>
+#endif
+
+#include "libpq/libpq.h"
+#include "tcop/tcopprot.h"
+#include "utils/memutils.h"
+
+
+
+static DH  *load_dh_file(int keylength);
+static DH  *load_dh_buffer(const char *, size_t);
+static DH  *tmp_dh_cb(SSL *s, int is_export, int keylength);
+static int	verify_cb(int, X509_STORE_CTX *);
+static void info_cb(const SSL *ssl, int type, int args);
+static const char *SSLerrmessage(void);
+
+/*
+ *	How much data can be sent across a secure connection
+ *	(total in both directions) before we require renegotiation.
+ *	Set to 0 to disable renegotiation completely.
+ */
+int			ssl_renegotiation_limit;
+
+/* are we in the middle of a renegotiation? */
+static bool in_ssl_renegotiation = false;
+
+static SSL_CTX *SSL_context = NULL;
+
+/* ------------------------------------------------------------ */
+/*						 Hardcoded values						*/
+/* ------------------------------------------------------------ */
+
+/*
+ *	Hardcoded DH parameters, used in ephemeral 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 DBA... or an attacker... deletes the
+ *	$DataDir/dh*.pem files.
+ *
+ *	We could refuse SSL connections unless a good DH parameter
+ *	file exists, but some clients may quietly renegotiate an
+ *	unsecured connection without fully informing the user.
+ *	Very uncool.
+ *
+ *	Alternatively, the backend could attempt to load these files
+ *	on startup if SSL is enabled - and refuse to start if any
+ *	do not exist - but this would tend to piss off DBAs.
+ *
+ *	If you want to create your own hardcoded DH parameters
+ *	for fun and profit, review "Assigned Number for SKIP
+ *	Protocols" (http://www.skip-vpn.org/spec/numbers.html)
+ *	for suggestions.
+ */
+
+static const char file_dh512[] =
+"-----BEGIN DH PARAMETERS-----\n\
+MEYCQQD1Kv884bEpQBgRjXyEpwpy1obEAxnIByl6ypUM2Zafq9AKUJsCRtMIPWak\n\
+XUGfnHy9iUsiGSa6q6Jew1XpKgVfAgEC\n\
+-----END DH PARAMETERS-----\n";
+
+static const char file_dh1024[] =
+"-----BEGIN DH PARAMETERS-----\n\
+MIGHAoGBAPSI/VhOSdvNILSd5JEHNmszbDgNRR0PfIizHHxbLY7288kjwEPwpVsY\n\
+jY67VYy4XTjTNP18F1dDox0YbN4zISy1Kv884bEpQBgRjXyEpwpy1obEAxnIByl6\n\
+ypUM2Zafq9AKUJsCRtMIPWakXUGfnHy9iUsiGSa6q6Jew1XpL3jHAgEC\n\
+-----END DH PARAMETERS-----\n";
+
+static const char file_dh2048[] =
+"-----BEGIN DH PARAMETERS-----\n\
+MIIBCAKCAQEA9kJXtwh/CBdyorrWqULzBej5UxE5T7bxbrlLOCDaAadWoxTpj0BV\n\
+89AHxstDqZSt90xkhkn4DIO9ZekX1KHTUPj1WV/cdlJPPT2N286Z4VeSWc39uK50\n\
+T8X8dryDxUcwYc58yWb/Ffm7/ZFexwGq01uejaClcjrUGvC/RgBYK+X0iP1YTknb\n\
+zSC0neSRBzZrM2w4DUUdD3yIsxx8Wy2O9vPJI8BD8KVbGI2Ou1WMuF040zT9fBdX\n\
+Q6MdGGzeMyEstSr/POGxKUAYEY18hKcKctaGxAMZyAcpesqVDNmWn6vQClCbAkbT\n\
+CD1mpF1Bn5x8vYlLIhkmuquiXsNV6TILOwIBAg==\n\
+-----END DH PARAMETERS-----\n";
+
+static const char file_dh4096[] =
+"-----BEGIN DH PARAMETERS-----\n\
+MIICCAKCAgEA+hRyUsFN4VpJ1O8JLcCo/VWr19k3BCgJ4uk+d+KhehjdRqNDNyOQ\n\
+l/MOyQNQfWXPeGKmOmIig6Ev/nm6Nf9Z2B1h3R4hExf+zTiHnvVPeRBhjdQi81rt\n\
+Xeoh6TNrSBIKIHfUJWBh3va0TxxjQIs6IZOLeVNRLMqzeylWqMf49HsIXqbcokUS\n\
+Vt1BkvLdW48j8PPv5DsKRN3tloTxqDJGo9tKvj1Fuk74A+Xda1kNhB7KFlqMyN98\n\
+VETEJ6c7KpfOo30mnK30wqw3S8OtaIR/maYX72tGOno2ehFDkq3pnPtEbD2CScxc\n\
+alJC+EL7RPk5c/tgeTvCngvc1KZn92Y//EI7G9tPZtylj2b56sHtMftIoYJ9+ODM\n\
+sccD5Piz/rejE3Ome8EOOceUSCYAhXn8b3qvxVI1ddd1pED6FHRhFvLrZxFvBEM9\n\
+ERRMp5QqOaHJkM+Dxv8Cj6MqrCbfC4u+ZErxodzuusgDgvZiLF22uxMZbobFWyte\n\
+OvOzKGtwcTqO/1wV5gKkzu1ZVswVUQd5Gg8lJicwqRWyyNRczDDoG9jVDxmogKTH\n\
+AaqLulO7R8Ifa1SwF2DteSGVtgWEN8gDpN3RBmmPTDngyF2DHb5qmpnznwtFKdTL\n\
+KWbuHn491xNO25CQWMtem80uKw+pTnisBRF/454n1Jnhub144YRBoN8CAQI=\n\
+-----END DH PARAMETERS-----\n";
+
+/*
+ *	Write data to a secure connection.
+ */
+ssize_t
+be_tls_write(Port *port, void *ptr, size_t len)
+{
+	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);
+
+		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")));
+	}
+
+	return n;
+}
+
+/* ------------------------------------------------------------ */
+/*						OpenSSL specific code					*/
+/* ------------------------------------------------------------ */
+
+/*
+ * Private substitute BIO: this does the sending and receiving using send() and
+ * recv() instead. This is so that we can enable and disable interrupts
+ * just while calling recv(). We cannot have interrupts occurring while
+ * the bulk of openssl runs, because it uses malloc() and possibly other
+ * non-reentrant libc facilities. We also need to call send() and recv()
+ * directly so it gets passed through the socket/signals layer on Win32.
+ *
+ * These functions are closely modelled on the standard socket BIO in OpenSSL;
+ * see sock_read() and sock_write() in OpenSSL's crypto/bio/bss_sock.c.
+ * XXX OpenSSL 1.0.1e considers many more errcodes than just EINTR as reasons
+ * to retry; do we need to adopt their logic for that?
+ */
+
+static bool my_bio_initialized = false;
+static BIO_METHOD my_bio_methods;
+
+static int
+my_sock_read(BIO *h, char *buf, int size)
+{
+	int			res = 0;
+
+	if (buf != NULL)
+	{
+		res = secure_raw_read(((Port *)h->ptr), buf, size);
+		BIO_clear_retry_flags(h);
+		if (res <= 0)
+		{
+			/* If we were interrupted, tell caller to retry */
+			if (errno == EINTR)
+			{
+				BIO_set_retry_read(h);
+			}
+		}
+	}
+
+	return res;
+}
+
+static int
+my_sock_write(BIO *h, const char *buf, int size)
+{
+	int			res = 0;
+
+	res = secure_raw_write(((Port *) h->ptr), buf, size);
+	BIO_clear_retry_flags(h);
+	if (res <= 0)
+	{
+		if (errno == EINTR)
+		{
+			BIO_set_retry_write(h);
+		}
+	}
+
+	return res;
+}
+
+static BIO_METHOD *
+my_BIO_s_socket(void)
+{
+	if (!my_bio_initialized)
+	{
+		memcpy(&my_bio_methods, BIO_s_socket(), sizeof(BIO_METHOD));
+		my_bio_methods.bread = my_sock_read;
+		my_bio_methods.bwrite = my_sock_write;
+		my_bio_initialized = true;
+	}
+	return &my_bio_methods;
+}
+
+/* This should exactly match openssl's SSL_set_fd except for using my BIO */
+static int
+my_SSL_set_fd(Port *port, int fd)
+{
+	int			ret = 0;
+	BIO		   *bio = NULL;
+
+	bio = BIO_new(my_BIO_s_socket());
+
+	if (bio == NULL)
+	{
+		SSLerr(SSL_F_SSL_SET_FD, ERR_R_BUF_LIB);
+		goto err;
+	}
+	/* Use 'ptr' to store pointer to PGconn */
+	bio->ptr = port;
+
+	BIO_set_fd(bio, fd, BIO_NOCLOSE);
+	SSL_set_bio(port->ssl, bio, bio);
+	ret = 1;
+err:
+	return ret;
+}
+
+/*
+ *	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());
+			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;
+}
+
+/*
+ *	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;
+}
+
+/*
+ *	Generate an ephemeral DH key.  Because this can take a long
+ *	time to compute, we can use precomputed parameters of the
+ *	common key sizes.
+ *
+ *	Since few sites will bother to precompute these parameter
+ *	files, we also provide a fallback to the parameters provided
+ *	by the OpenSSL project.
+ *
+ *	These values can be static (once loaded or computed) since
+ *	the OpenSSL library can efficiently generate random keys from
+ *	the information provided.
+ */
+static DH  *
+tmp_dh_cb(SSL *s, int is_export, int keylength)
+{
+	DH		   *r = NULL;
+	static DH  *dh = NULL;
+	static DH  *dh512 = NULL;
+	static DH  *dh1024 = NULL;
+	static DH  *dh2048 = NULL;
+	static DH  *dh4096 = NULL;
+
+	switch (keylength)
+	{
+		case 512:
+			if (dh512 == NULL)
+				dh512 = load_dh_file(keylength);
+			if (dh512 == NULL)
+				dh512 = load_dh_buffer(file_dh512, sizeof file_dh512);
+			r = dh512;
+			break;
+
+		case 1024:
+			if (dh1024 == NULL)
+				dh1024 = load_dh_file(keylength);
+			if (dh1024 == NULL)
+				dh1024 = load_dh_buffer(file_dh1024, sizeof file_dh1024);
+			r = dh1024;
+			break;
+
+		case 2048:
+			if (dh2048 == NULL)
+				dh2048 = load_dh_file(keylength);
+			if (dh2048 == NULL)
+				dh2048 = load_dh_buffer(file_dh2048, sizeof file_dh2048);
+			r = dh2048;
+			break;
+
+		case 4096:
+			if (dh4096 == NULL)
+				dh4096 = load_dh_file(keylength);
+			if (dh4096 == NULL)
+				dh4096 = load_dh_buffer(file_dh4096, sizeof file_dh4096);
+			r = dh4096;
+			break;
+
+		default:
+			if (dh == NULL)
+				dh = load_dh_file(keylength);
+			r = dh;
+	}
+
+	/* this may take a long time, but it may be necessary... */
+	if (r == NULL || 8 * DH_size(r) < keylength)
+	{
+		ereport(DEBUG2,
+				(errmsg_internal("DH: generating parameters (%d bits)",
+								 keylength)));
+		r = DH_generate_parameters(keylength, DH_GENERATOR_2, NULL, NULL);
+	}
+
+	return r;
+}
+
+/*
+ *	Certificate verification callback
+ *
+ *	This callback allows us to log intermediate problems during
+ *	verification, but for now we'll see if the final error message
+ *	contains enough information.
+ *
+ *	This callback also allows us to override the default acceptance
+ *	criteria (e.g., accepting self-signed or expired certs), but
+ *	for now we accept the default checks.
+ */
+static int
+verify_cb(int ok, X509_STORE_CTX *ctx)
+{
+	return ok;
+}
+
+/*
+ *	This callback is used to copy SSL information messages
+ *	into the PostgreSQL log.
+ */
+static void
+info_cb(const SSL *ssl, int type, int args)
+{
+	switch (type)
+	{
+		case SSL_CB_HANDSHAKE_START:
+			ereport(DEBUG4,
+					(errmsg_internal("SSL: handshake start")));
+			break;
+		case SSL_CB_HANDSHAKE_DONE:
+			ereport(DEBUG4,
+					(errmsg_internal("SSL: handshake done")));
+			break;
+		case SSL_CB_ACCEPT_LOOP:
+			ereport(DEBUG4,
+					(errmsg_internal("SSL: accept loop")));
+			break;
+		case SSL_CB_ACCEPT_EXIT:
+			ereport(DEBUG4,
+					(errmsg_internal("SSL: accept exit (%d)", args)));
+			break;
+		case SSL_CB_CONNECT_LOOP:
+			ereport(DEBUG4,
+					(errmsg_internal("SSL: connect loop")));
+			break;
+		case SSL_CB_CONNECT_EXIT:
+			ereport(DEBUG4,
+					(errmsg_internal("SSL: connect exit (%d)", args)));
+			break;
+		case SSL_CB_READ_ALERT:
+			ereport(DEBUG4,
+					(errmsg_internal("SSL: read alert (0x%04x)", args)));
+			break;
+		case SSL_CB_WRITE_ALERT:
+			ereport(DEBUG4,
+					(errmsg_internal("SSL: write alert (0x%04x)", args)));
+			break;
+	}
+}
+
+#if (OPENSSL_VERSION_NUMBER >= 0x0090800fL) && !defined(OPENSSL_NO_ECDH)
+static void
+initialize_ecdh(void)
+{
+	EC_KEY	   *ecdh;
+	int			nid;
+
+	nid = OBJ_sn2nid(SSLECDHCurve);
+	if (!nid)
+		ereport(FATAL,
+				(errmsg("ECDH: unrecognized curve name: %s", SSLECDHCurve)));
+
+	ecdh = EC_KEY_new_by_curve_name(nid);
+	if (!ecdh)
+		ereport(FATAL,
+				(errmsg("ECDH: could not create key")));
+
+	SSL_CTX_set_options(SSL_context, SSL_OP_SINGLE_ECDH_USE);
+	SSL_CTX_set_tmp_ecdh(SSL_context, ecdh);
+	EC_KEY_free(ecdh);
+}
+#else
+#define initialize_ecdh()
+#endif
+
+/*
+ *	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);
+	}
+}
+
+/*
+ *	Attempt to negotiate SSL connection.
+ */
+int
+be_tls_open_server(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())));
+		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:
+#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;
+		}
+		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;
+}
+
+/*
+ *	Close SSL connection.
+ */
+void
+be_tls_close(Port *port)
+{
+	if (port->ssl)
+	{
+		SSL_shutdown(port->ssl);
+		SSL_free(port->ssl);
+		port->ssl = NULL;
+		port->ssl_in_use = false;
+	}
+
+	if (port->peer)
+	{
+		X509_free(port->peer);
+		port->peer = NULL;
+	}
+
+	if (port->peer_cn)
+	{
+		pfree(port->peer_cn);
+		port->peer_cn = NULL;
+	}
+}
+
+ssize_t
+be_tls_read(Port *port, void *ptr, size_t len)
+{
+	ssize_t		n;
+	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)));
+			errno = ECONNRESET;
+			n = -1;
+			break;
+	}
+
+	return n;
+}
+
+/*
+ * Obtain reason string for last SSL error
+ *
+ * Some caution is needed here since ERR_reason_error_string will
+ * return NULL if it doesn't recognize the error code.  We don't
+ * want to return NULL ever.
+ */
+static const char *
+SSLerrmessage(void)
+{
+	unsigned long errcode;
+	const char *errreason;
+	static char errbuf[32];
+
+	errcode = ERR_get_error();
+	if (errcode == 0)
+		return _("no SSL error reported");
+	errreason = ERR_reason_error_string(errcode);
+	if (errreason != NULL)
+		return errreason;
+	snprintf(errbuf, sizeof(errbuf), _("SSL error code %lu"), errcode);
+	return errbuf;
+}
+
diff --git a/src/backend/libpq/be-secure.c b/src/backend/libpq/be-secure.c
index 59204cf..6d943cf 100644
--- a/src/backend/libpq/be-secure.c
+++ b/src/backend/libpq/be-secure.c
@@ -63,7 +63,7 @@
 #include <arpa/inet.h>
 #endif
 
-#ifdef USE_SSL
+#ifdef USE_OPENSSL
 #include <openssl/ssl.h>
 #include <openssl/dh.h>
 #if SSLEAY_VERSION_NUMBER >= 0x0907000L
@@ -72,26 +72,13 @@
 #if (OPENSSL_VERSION_NUMBER >= 0x0090800fL) && !defined(OPENSSL_NO_ECDH)
 #include <openssl/ec.h>
 #endif
-#endif   /* USE_SSL */
+#endif   /* USE_OPENSSL */
 
 #include "libpq/libpq.h"
 #include "tcop/tcopprot.h"
 #include "utils/memutils.h"
 
 
-#ifdef USE_SSL
-
-static DH  *load_dh_file(int keylength);
-static DH  *load_dh_buffer(const char *, size_t);
-static DH  *tmp_dh_cb(SSL *s, int is_export, int keylength);
-static int	verify_cb(int, X509_STORE_CTX *);
-static void info_cb(const SSL *ssl, int type, int args);
-static void initialize_SSL(void);
-static int	open_server_SSL(Port *);
-static void close_SSL(Port *);
-static const char *SSLerrmessage(void);
-#endif
-
 char	   *ssl_cert_file;
 char	   *ssl_key_file;
 char	   *ssl_ca_file;
@@ -105,11 +92,7 @@ char	   *ssl_crl_file;
 int			ssl_renegotiation_limit;
 
 #ifdef USE_SSL
-/* are we in the middle of a renegotiation? */
-static bool in_ssl_renegotiation = false;
-
-static SSL_CTX *SSL_context = NULL;
-static bool ssl_loaded_verify_locations = false;
+bool ssl_loaded_verify_locations = false;
 #endif
 
 /* GUC variable controlling SSL cipher list */
@@ -147,7 +130,7 @@ bool		SSLPreferServerCiphers;
  *	Protocols" (http://www.skip-vpn.org/spec/numbers.html)
  *	for suggestions.
  */
-#ifdef USE_SSL
+#ifdef USE_OPENSSL
 
 static const char file_dh512[] =
 "-----BEGIN DH PARAMETERS-----\n\
@@ -198,8 +181,8 @@ KWbuHn491xNO25CQWMtem80uKw+pTnisBRF/454n1Jnhub144YRBoN8CAQI=\n\
 int
 secure_initialize(void)
 {
-#ifdef USE_SSL
-	initialize_SSL();
+#ifdef USE_OPENSSL
+	be_tls_init();
 #endif
 
 	return 0;
@@ -211,7 +194,7 @@ secure_initialize(void)
 bool
 secure_loaded_verify_locations(void)
 {
-#ifdef USE_SSL
+#ifdef USE_OPENSSL
 	return ssl_loaded_verify_locations;
 #else
 	return false;
@@ -227,7 +210,7 @@ secure_open_server(Port *port)
 	int			r = 0;
 
 #ifdef USE_SSL
-	r = open_server_SSL(port);
+	r = be_tls_open_server(port);
 #endif
 
 	return r;
@@ -240,8 +223,8 @@ void
 secure_close(Port *port)
 {
 #ifdef USE_SSL
-	if (port->ssl)
-		close_SSL(port);
+	if (port->ssl_in_use)
+		be_tls_close(port);
 #endif
 }
 
@@ -254,908 +237,56 @@ secure_read(Port *port, void *ptr, size_t len)
 	ssize_t		n;
 
 #ifdef USE_SSL
-	if (port->ssl)
+	if (port->ssl_in_use)
 	{
-		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)));
-				errno = ECONNRESET;
-				n = -1;
-				break;
-		}
+		n = be_tls_read(port, ptr, len);
 	}
 	else
 #endif
 	{
-		prepare_for_client_read();
-
-		n = recv(port->sock, ptr, len, 0);
-
-		client_read_ended();
+		n = secure_raw_read(port, ptr, len);
 	}
 
 	return n;
 }
 
-/*
- *	Write data to a secure connection.
- */
 ssize_t
-secure_write(Port *port, void *ptr, size_t len)
+secure_raw_read(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;
-}
-
-/* ------------------------------------------------------------ */
-/*						  SSL specific code						*/
-/* ------------------------------------------------------------ */
-#ifdef USE_SSL
-
-/*
- * Private substitute BIO: this does the sending and receiving using send() and
- * recv() instead. This is so that we can enable and disable interrupts
- * just while calling recv(). We cannot have interrupts occurring while
- * the bulk of openssl runs, because it uses malloc() and possibly other
- * non-reentrant libc facilities. We also need to call send() and recv()
- * directly so it gets passed through the socket/signals layer on Win32.
- *
- * These functions are closely modelled on the standard socket BIO in OpenSSL;
- * see sock_read() and sock_write() in OpenSSL's crypto/bio/bss_sock.c.
- * XXX OpenSSL 1.0.1e considers many more errcodes than just EINTR as reasons
- * to retry; do we need to adopt their logic for that?
- */
-
-static bool my_bio_initialized = false;
-static BIO_METHOD my_bio_methods;
-
-static int
-my_sock_read(BIO *h, char *buf, int size)
-{
-	int			res = 0;
-
 	prepare_for_client_read();
 
-	if (buf != NULL)
-	{
-		res = recv(h->num, buf, size, 0);
-		BIO_clear_retry_flags(h);
-		if (res <= 0)
-		{
-			/* If we were interrupted, tell caller to retry */
-			if (errno == EINTR)
-			{
-				BIO_set_retry_read(h);
-			}
-		}
-	}
+	n = recv(port->sock, ptr, len, 0);
 
 	client_read_ended();
 
-	return res;
-}
-
-static int
-my_sock_write(BIO *h, const char *buf, int size)
-{
-	int			res = 0;
-
-	res = send(h->num, buf, size, 0);
-	BIO_clear_retry_flags(h);
-	if (res <= 0)
-	{
-		if (errno == EINTR)
-		{
-			BIO_set_retry_write(h);
-		}
-	}
-
-	return res;
-}
-
-static BIO_METHOD *
-my_BIO_s_socket(void)
-{
-	if (!my_bio_initialized)
-	{
-		memcpy(&my_bio_methods, BIO_s_socket(), sizeof(BIO_METHOD));
-		my_bio_methods.bread = my_sock_read;
-		my_bio_methods.bwrite = my_sock_write;
-		my_bio_initialized = true;
-	}
-	return &my_bio_methods;
-}
-
-/* This should exactly match openssl's SSL_set_fd except for using my BIO */
-static int
-my_SSL_set_fd(SSL *s, int fd)
-{
-	int			ret = 0;
-	BIO		   *bio = NULL;
-
-	bio = BIO_new(my_BIO_s_socket());
-
-	if (bio == NULL)
-	{
-		SSLerr(SSL_F_SSL_SET_FD, ERR_R_BUF_LIB);
-		goto err;
-	}
-	BIO_set_fd(bio, fd, BIO_NOCLOSE);
-	SSL_set_bio(s, bio, bio);
-	ret = 1;
-err:
-	return ret;
-}
-
-/*
- *	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());
-			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;
-}
-
-/*
- *	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;
-}
-
-/*
- *	Generate an ephemeral DH key.  Because this can take a long
- *	time to compute, we can use precomputed parameters of the
- *	common key sizes.
- *
- *	Since few sites will bother to precompute these parameter
- *	files, we also provide a fallback to the parameters provided
- *	by the OpenSSL project.
- *
- *	These values can be static (once loaded or computed) since
- *	the OpenSSL library can efficiently generate random keys from
- *	the information provided.
- */
-static DH  *
-tmp_dh_cb(SSL *s, int is_export, int keylength)
-{
-	DH		   *r = NULL;
-	static DH  *dh = NULL;
-	static DH  *dh512 = NULL;
-	static DH  *dh1024 = NULL;
-	static DH  *dh2048 = NULL;
-	static DH  *dh4096 = NULL;
-
-	switch (keylength)
-	{
-		case 512:
-			if (dh512 == NULL)
-				dh512 = load_dh_file(keylength);
-			if (dh512 == NULL)
-				dh512 = load_dh_buffer(file_dh512, sizeof file_dh512);
-			r = dh512;
-			break;
-
-		case 1024:
-			if (dh1024 == NULL)
-				dh1024 = load_dh_file(keylength);
-			if (dh1024 == NULL)
-				dh1024 = load_dh_buffer(file_dh1024, sizeof file_dh1024);
-			r = dh1024;
-			break;
-
-		case 2048:
-			if (dh2048 == NULL)
-				dh2048 = load_dh_file(keylength);
-			if (dh2048 == NULL)
-				dh2048 = load_dh_buffer(file_dh2048, sizeof file_dh2048);
-			r = dh2048;
-			break;
-
-		case 4096:
-			if (dh4096 == NULL)
-				dh4096 = load_dh_file(keylength);
-			if (dh4096 == NULL)
-				dh4096 = load_dh_buffer(file_dh4096, sizeof file_dh4096);
-			r = dh4096;
-			break;
-
-		default:
-			if (dh == NULL)
-				dh = load_dh_file(keylength);
-			r = dh;
-	}
-
-	/* this may take a long time, but it may be necessary... */
-	if (r == NULL || 8 * DH_size(r) < keylength)
-	{
-		ereport(DEBUG2,
-				(errmsg_internal("DH: generating parameters (%d bits)",
-								 keylength)));
-		r = DH_generate_parameters(keylength, DH_GENERATOR_2, NULL, NULL);
-	}
-
-	return r;
-}
-
-/*
- *	Certificate verification callback
- *
- *	This callback allows us to log intermediate problems during
- *	verification, but for now we'll see if the final error message
- *	contains enough information.
- *
- *	This callback also allows us to override the default acceptance
- *	criteria (e.g., accepting self-signed or expired certs), but
- *	for now we accept the default checks.
- */
-static int
-verify_cb(int ok, X509_STORE_CTX *ctx)
-{
-	return ok;
-}
-
-/*
- *	This callback is used to copy SSL information messages
- *	into the PostgreSQL log.
- */
-static void
-info_cb(const SSL *ssl, int type, int args)
-{
-	switch (type)
-	{
-		case SSL_CB_HANDSHAKE_START:
-			ereport(DEBUG4,
-					(errmsg_internal("SSL: handshake start")));
-			break;
-		case SSL_CB_HANDSHAKE_DONE:
-			ereport(DEBUG4,
-					(errmsg_internal("SSL: handshake done")));
-			break;
-		case SSL_CB_ACCEPT_LOOP:
-			ereport(DEBUG4,
-					(errmsg_internal("SSL: accept loop")));
-			break;
-		case SSL_CB_ACCEPT_EXIT:
-			ereport(DEBUG4,
-					(errmsg_internal("SSL: accept exit (%d)", args)));
-			break;
-		case SSL_CB_CONNECT_LOOP:
-			ereport(DEBUG4,
-					(errmsg_internal("SSL: connect loop")));
-			break;
-		case SSL_CB_CONNECT_EXIT:
-			ereport(DEBUG4,
-					(errmsg_internal("SSL: connect exit (%d)", args)));
-			break;
-		case SSL_CB_READ_ALERT:
-			ereport(DEBUG4,
-					(errmsg_internal("SSL: read alert (0x%04x)", args)));
-			break;
-		case SSL_CB_WRITE_ALERT:
-			ereport(DEBUG4,
-					(errmsg_internal("SSL: write alert (0x%04x)", args)));
-			break;
-	}
-}
-
-#if (OPENSSL_VERSION_NUMBER >= 0x0090800fL) && !defined(OPENSSL_NO_ECDH)
-static void
-initialize_ecdh(void)
-{
-	EC_KEY	   *ecdh;
-	int			nid;
-
-	nid = OBJ_sn2nid(SSLECDHCurve);
-	if (!nid)
-		ereport(FATAL,
-				(errmsg("ECDH: unrecognized curve name: %s", SSLECDHCurve)));
-
-	ecdh = EC_KEY_new_by_curve_name(nid);
-	if (!ecdh)
-		ereport(FATAL,
-				(errmsg("ECDH: could not create key")));
-
-	SSL_CTX_set_options(SSL_context, SSL_OP_SINGLE_ECDH_USE);
-	SSL_CTX_set_tmp_ecdh(SSL_context, ecdh);
-	EC_KEY_free(ecdh);
+	return n;
 }
-#else
-#define initialize_ecdh()
-#endif
-
-/*
- *	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();
-
-		/*
-		 * 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);
-	}
-}
 
 /*
- *	Attempt to negotiate SSL connection.
+ *	Write data to a secure connection.
  */
-static int
-open_server_SSL(Port *port)
+ssize_t
+secure_write(Port *port, void *ptr, size_t len)
 {
-	int			r;
-	int			err;
-
-	Assert(!port->ssl);
-	Assert(!port->peer);
+	ssize_t		n;
 
-	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))
+#ifdef USE_SSL
+	if (port->ssl_in_use)
 	{
-		ereport(COMMERROR,
-				(errcode(ERRCODE_PROTOCOL_VIOLATION),
-				 errmsg("could not set SSL socket: %s",
-						SSLerrmessage())));
-		close_SSL(port);
-		return -1;
+		n = be_tls_write(port, ptr, len);
 	}
-
-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);
+	else
 #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;
-			}
+		n = secure_raw_read(port, ptr, len);
 
-			/*
-			 * 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;
-}
-
-/*
- *	Close SSL connection.
- */
-static void
-close_SSL(Port *port)
-{
-	if (port->ssl)
-	{
-		SSL_shutdown(port->ssl);
-		SSL_free(port->ssl);
-		port->ssl = NULL;
-	}
-
-	if (port->peer)
-	{
-		X509_free(port->peer);
-		port->peer = NULL;
-	}
-
-	if (port->peer_cn)
-	{
-		pfree(port->peer_cn);
-		port->peer_cn = NULL;
-	}
+	return n;
 }
 
-/*
- * Obtain reason string for last SSL error
- *
- * Some caution is needed here since ERR_reason_error_string will
- * return NULL if it doesn't recognize the error code.  We don't
- * want to return NULL ever.
- */
-static const char *
-SSLerrmessage(void)
+ssize_t
+secure_raw_write(Port *port, const void *ptr, size_t len)
 {
-	unsigned long errcode;
-	const char *errreason;
-	static char errbuf[32];
-
-	errcode = ERR_get_error();
-	if (errcode == 0)
-		return _("no SSL error reported");
-	errreason = ERR_reason_error_string(errcode);
-	if (errreason != NULL)
-		return errreason;
-	snprintf(errbuf, sizeof(errbuf), _("SSL error code %lu"), errcode);
-	return errbuf;
+	return send(port->sock, ptr, len, 0);
 }
-
-#endif   /* USE_SSL */
diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c
index fd98c60..84da823 100644
--- a/src/backend/libpq/hba.c
+++ b/src/backend/libpq/hba.c
@@ -1685,7 +1685,7 @@ check_hba(hbaPort *port)
 
 			/* Check SSL state */
 #ifdef USE_SSL
-			if (port->ssl)
+			if (port->ssl_in_use)
 			{
 				/* Connection is SSL, match both "host" and "hostssl" */
 				if (hba->conntype == ctHostNoSSL)
diff --git a/src/backend/postmaster/fork_process.c b/src/backend/postmaster/fork_process.c
index f6df2de..1d6cc9d 100644
--- a/src/backend/postmaster/fork_process.c
+++ b/src/backend/postmaster/fork_process.c
@@ -17,7 +17,7 @@
 #include <sys/stat.h>
 #include <sys/time.h>
 #include <unistd.h>
-#ifdef USE_SSL
+#ifdef USE_OPENSSL
 #include <openssl/rand.h>
 #endif
 
@@ -131,7 +131,7 @@ fork_process(void)
 		/*
 		 * Make sure processes do not share OpenSSL randomness state.
 		 */
-#ifdef USE_SSL
+#ifdef USE_OPENSSL
 		RAND_cleanup();
 #endif
 	}
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
index ed936d7..d5cf605 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -231,8 +231,8 @@ PerformAuthentication(Port *port)
 	{
 		if (am_walsender)
 		{
-#ifdef USE_SSL
-			if (port->ssl)
+#ifdef USE_OPENSSL
+			if (port->ssl_in_use)
 				ereport(LOG,
 						(errmsg("replication connection authorized: user=%s SSL enabled (protocol=%s, cipher=%s)",
 								port->user_name, SSL_get_version(port->ssl), SSL_get_cipher(port->ssl))));
@@ -244,8 +244,8 @@ PerformAuthentication(Port *port)
 		}
 		else
 		{
-#ifdef USE_SSL
-			if (port->ssl)
+#ifdef USE_OPENSSL
+			if (port->ssl_in_use)
 				ereport(LOG,
 						(errmsg("connection authorized: user=%s database=%s SSL enabled (protocol=%s, cipher=%s)",
 								port->user_name, port->database_name, SSL_get_version(port->ssl), SSL_get_cipher(port->ssl))));
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 1d094f0..a36d11a 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -125,9 +125,6 @@ extern char *default_tablespace;
 extern char *temp_tablespaces;
 extern bool ignore_checksum_failure;
 extern bool synchronize_seqscans;
-extern char *SSLCipherSuites;
-extern char *SSLECDHCurve;
-extern bool SSLPreferServerCiphers;
 
 #ifdef TRACE_SORT
 extern bool trace_sort;
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index cede72a..7613aed 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -30,7 +30,7 @@
 #include <sys/types.h>			/* for umask() */
 #include <sys/stat.h>			/* for stat() */
 #endif
-#ifdef USE_SSL
+#ifdef USE_OPENSSL
 #include <openssl/ssl.h>
 #endif
 
@@ -1791,7 +1791,7 @@ connection_warnings(bool in_startup)
 static void
 printSSLInfo(void)
 {
-#ifdef USE_SSL
+#ifdef USE_OPENSSL
 	int			sslbits = -1;
 	SSL		   *ssl;
 
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index e78c565..1682623 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -21,7 +21,7 @@
 #ifdef HAVE_SYS_TIME_H
 #include <sys/time.h>
 #endif
-#ifdef USE_SSL
+#ifdef USE_OPENSSL
 #include <openssl/ssl.h>
 #include <openssl/err.h>
 #endif
@@ -184,17 +184,34 @@ typedef struct Port
 #endif
 
 	/*
-	 * SSL structures (keep these last so that USE_SSL doesn't affect
+	 * SSL structures (keep these last so that USE_OPENSSL doesn't affect
 	 * locations of other fields)
 	 */
 #ifdef USE_SSL
+	bool		ssl_in_use;
+	char	   *peer_cn;
+	bool		peer_cert_valid;
+#endif
+#ifdef USE_OPENSSL
 	SSL		   *ssl;
 	X509	   *peer;
-	char	   *peer_cn;
 	unsigned long count;
 #endif
+
 } Port;
 
+#ifdef USE_SSL
+/*
+ * These functions are implemented by the glue code specific to each
+ * SSL implementation (e.g. be-secure-openssl.c)
+ */
+extern void be_tls_init(void);
+extern int be_tls_open_server(Port *port);
+extern void be_tls_close(Port *port);
+extern ssize_t be_tls_read(Port *port, void *ptr, size_t len);
+extern ssize_t be_tls_write(Port *port, void *ptr, size_t len);
+
+#endif
 
 extern ProtocolVersion FrontendProtocol;
 
diff --git a/src/include/libpq/libpq.h b/src/include/libpq/libpq.h
index e4e354d..5da9d8d 100644
--- a/src/include/libpq/libpq.h
+++ b/src/include/libpq/libpq.h
@@ -82,5 +82,14 @@ extern int	secure_open_server(Port *port);
 extern void secure_close(Port *port);
 extern ssize_t secure_read(Port *port, void *ptr, size_t len);
 extern ssize_t secure_write(Port *port, void *ptr, size_t len);
+extern ssize_t secure_raw_read(Port *port, void *ptr, size_t len);
+extern ssize_t secure_raw_write(Port *port, const void *ptr, size_t len);
+
+extern bool ssl_loaded_verify_locations;
+
+/* GUCs */
+extern char *SSLCipherSuites;
+extern char *SSLECDHCurve;
+extern bool SSLPreferServerCiphers;
 
 #endif   /* LIBPQ_H */
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index 5ff9e41..0a0e0d4 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -779,15 +779,15 @@
 /* Define to select named POSIX semaphores. */
 #undef USE_NAMED_POSIX_SEMAPHORES
 
+/* Define to build with OpenSSL support. (--with-openssl) */
+#undef USE_OPENSSL
+
 /* Define to 1 to build with PAM support. (--with-pam) */
 #undef USE_PAM
 
 /* Use replacement snprintf() functions. */
 #undef USE_REPL_SNPRINTF
 
-/* Define to build with (Open)SSL support. (--with-openssl) */
-#undef USE_SSL
-
 /* Define to select SysV-style semaphores. */
 #undef USE_SYSV_SEMAPHORES
 
diff --git a/src/include/pg_config.h.win32 b/src/include/pg_config.h.win32
index e6e3c8d..6a6d2f3 100644
--- a/src/include/pg_config.h.win32
+++ b/src/include/pg_config.h.win32
@@ -629,15 +629,15 @@
 /* Define to select named POSIX semaphores. */
 /* #undef USE_NAMED_POSIX_SEMAPHORES */
 
+/* Define to build with OpenSSL support. (--with-openssl) */
+/* #undef USE_OPENSSL */
+
 /* Define to 1 to build with PAM support. (--with-pam) */
 /* #undef USE_PAM */
 
 /* Use replacement snprintf() functions. */
 #define USE_REPL_SNPRINTF 1
 
-/* Define to build with (Open)SSL support. (--with-openssl) */
-/* #undef USE_SSL */
-
 /* Define to select SysV-style semaphores. */
 /* #undef USE_SYSV_SEMAPHORES */
 
diff --git a/src/include/port.h b/src/include/port.h
index c9226f3..7d2e0c4 100644
--- a/src/include/port.h
+++ b/src/include/port.h
@@ -472,4 +472,10 @@ extern char *escape_single_quotes_ascii(const char *src);
 /* port/wait_error.c */
 extern char *wait_result_to_str(int exit_status);
 
+/* If we're building with OpenSSL, then we have SSL support */
+/* XXX: this doesn't belong here.. I couldn't find a precedence to follow */
+#ifdef USE_OPENSSL
+#define USE_SSL
+#endif
+
 #endif   /* PG_PORT_H */
diff --git a/src/interfaces/libpq/Makefile b/src/interfaces/libpq/Makefile
index 2b770d0..7827ac0 100644
--- a/src/interfaces/libpq/Makefile
+++ b/src/interfaces/libpq/Makefile
@@ -44,6 +44,10 @@ OBJS += ip.o md5.o
 # utils/mb
 OBJS += encnames.o wchar.o
 
+ifeq ($(with_openssl),yes)
+OBJS += fe-secure-openssl.o
+endif
+
 ifeq ($(PORTNAME), cygwin)
 override shlib = cyg$(NAME)$(DLSUFFIX)
 endif
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index 540426c..b0b0e1a 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -1961,7 +1961,7 @@ keep_going:						/* We will come back to here until there is
 					conn->allow_ssl_try = false;
 				}
 				if (conn->allow_ssl_try && !conn->wait_ssl_try &&
-					conn->ssl == NULL)
+					!conn->ssl_in_use)
 				{
 					ProtocolVersion pv;
 
@@ -2040,7 +2040,7 @@ keep_going:						/* We will come back to here until there is
 				 * On first time through, get the postmaster's response to our
 				 * SSL negotiation packet.
 				 */
-				if (conn->ssl == NULL)
+				if (!conn->ssl_in_use)
 				{
 					/*
 					 * We use pqReadData here since it has the logic to
@@ -2310,7 +2310,7 @@ keep_going:						/* We will come back to here until there is
 					 * connection already, then retry with an SSL connection
 					 */
 					if (conn->sslmode[0] == 'a' /* "allow" */
-						&& conn->ssl == NULL
+						&& !conn->ssl_in_use
 						&& conn->allow_ssl_try
 						&& conn->wait_ssl_try)
 					{
@@ -2709,6 +2709,7 @@ makeEmptyPGconn(void)
 #ifdef USE_SSL
 	conn->allow_ssl_try = true;
 	conn->wait_ssl_try = false;
+	conn->ssl_in_use = false;
 #endif
 
 	/*
diff --git a/src/interfaces/libpq/fe-misc.c b/src/interfaces/libpq/fe-misc.c
index 7a213bf..d561aa5 100644
--- a/src/interfaces/libpq/fe-misc.c
+++ b/src/interfaces/libpq/fe-misc.c
@@ -743,13 +743,13 @@ retry3:
 	 * since in normal practice we should not be trying to read data unless
 	 * the file selected for reading already.
 	 *
-	 * In SSL mode it's even worse: SSL_read() could say WANT_READ and then
+	 * With OpenSSL it's even worse: SSL_read() could say WANT_READ and then
 	 * data could arrive before we make the pqReadReady() test.  So we must
 	 * play dumb and assume there is more data, relying on the SSL layer to
 	 * detect true EOF.
 	 */
 
-#ifdef USE_SSL
+#ifdef USE_OPENSSL
 	if (conn->ssl)
 		return 0;
 #endif
@@ -1050,7 +1050,7 @@ pqSocketCheck(PGconn *conn, int forRead, int forWrite, time_t end_time)
 		return -1;
 	}
 
-#ifdef USE_SSL
+#ifdef USE_OPENSSL
 	/* Check for SSL library buffering read bytes */
 	if (forRead && conn->ssl && SSL_pending(conn->ssl) > 0)
 	{
diff --git a/src/interfaces/libpq/fe-secure-openssl.c b/src/interfaces/libpq/fe-secure-openssl.c
new file mode 100644
index 0000000..559fe11
--- /dev/null
+++ b/src/interfaces/libpq/fe-secure-openssl.c
@@ -0,0 +1,1471 @@
+/*-------------------------------------------------------------------------
+ *
+ * fe-secure-openssl.c
+ *	  OpenSSL support
+ *
+ *
+ * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  src/interfaces/libpq/fe-secure-openssl.c
+ *
+ * NOTES
+ *
+ *	  We don't provide informational callbacks here (like
+ *	  info_cb() in be-secure.c), since there's no good mechanism to
+ *	  display such information to the user.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres_fe.h"
+
+#include <signal.h>
+#include <fcntl.h>
+#include <ctype.h>
+
+#include "libpq-fe.h"
+#include "fe-auth.h"
+#include "libpq-int.h"
+
+#ifdef WIN32
+#include "win32.h"
+#else
+#include <sys/socket.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#ifdef HAVE_NETINET_TCP_H
+#include <netinet/tcp.h>
+#endif
+#include <arpa/inet.h>
+#endif
+
+#include <sys/stat.h>
+
+#ifdef ENABLE_THREAD_SAFETY
+#ifdef WIN32
+#include "pthread-win32.h"
+#else
+#include <pthread.h>
+#endif
+#endif
+
+#include <openssl/ssl.h>
+#if (SSLEAY_VERSION_NUMBER >= 0x00907000L)
+#include <openssl/conf.h>
+#endif
+#ifdef USE_SSL_ENGINE
+#include <openssl/engine.h>
+#endif
+
+static bool verify_peer_name_matches_certificate(PGconn *);
+static int	verify_cb(int ok, X509_STORE_CTX *ctx);
+static void destroy_ssl_system(void);
+static int	initialize_SSL(PGconn *conn);
+static PostgresPollingStatusType open_client_SSL(PGconn *);
+static char *SSLerrmessage(void);
+static void SSLerrfree(char *buf);
+
+static int my_sock_read(BIO *h, char *buf, int size);
+static int my_sock_write(BIO *h, const char *buf, int size);
+static BIO_METHOD *my_BIO_s_socket(void);
+static int my_SSL_set_fd(PGconn *conn, int fd);
+
+
+static bool pq_init_ssl_lib = true;
+static bool pq_init_crypto_lib = true;
+
+/*
+ * SSL_context is currently shared between threads and therefore we need to be
+ * careful to lock around any usage of it when providing thread safety.
+ * ssl_config_mutex is the mutex that we use to protect it.
+ */
+static SSL_CTX *SSL_context = NULL;
+
+#ifdef ENABLE_THREAD_SAFETY
+static long ssl_open_connections = 0;
+
+#ifndef WIN32
+static pthread_mutex_t ssl_config_mutex = PTHREAD_MUTEX_INITIALIZER;
+#else
+static pthread_mutex_t ssl_config_mutex = NULL;
+static long win32_ssl_create_mutex = 0;
+#endif
+#endif   /* ENABLE_THREAD_SAFETY */
+
+
+/* ------------------------------------------------------------ */
+/*			 Procedures common to all secure sessions			*/
+/* ------------------------------------------------------------ */
+
+/*
+ *	Exported function to allow application to tell us it's already
+ *	initialized OpenSSL and/or libcrypto.
+ */
+void
+pgtls_init_library(bool do_ssl, int do_crypto)
+{
+#ifdef ENABLE_THREAD_SAFETY
+
+	/*
+	 * Disallow changing the flags while we have open connections, else we'd
+	 * get completely confused.
+	 */
+	if (ssl_open_connections != 0)
+		return;
+#endif
+
+	pq_init_ssl_lib = do_ssl;
+	pq_init_crypto_lib = do_crypto;
+}
+
+/*
+ *	Begin or continue negotiating a secure session.
+ */
+PostgresPollingStatusType
+pgtls_open_client(PGconn *conn)
+{
+	/* First time through? */
+	if (conn->ssl == NULL)
+	{
+#ifdef ENABLE_THREAD_SAFETY
+		int			rc;
+#endif
+
+		/* We cannot use MSG_NOSIGNAL to block SIGPIPE when using SSL */
+		conn->sigpipe_flag = false;
+
+#ifdef ENABLE_THREAD_SAFETY
+		if ((rc = pthread_mutex_lock(&ssl_config_mutex)))
+		{
+			printfPQExpBuffer(&conn->errorMessage,
+			   libpq_gettext("could not acquire mutex: %s\n"), strerror(rc));
+			return PGRES_POLLING_FAILED;
+		}
+#endif
+		/* Create a connection-specific SSL object */
+		if (!(conn->ssl = SSL_new(SSL_context)) ||
+			!SSL_set_app_data(conn->ssl, conn) ||
+			!my_SSL_set_fd(conn, conn->sock))
+		{
+			char	   *err = SSLerrmessage();
+
+			printfPQExpBuffer(&conn->errorMessage,
+				   libpq_gettext("could not establish SSL connection: %s\n"),
+							  err);
+			SSLerrfree(err);
+#ifdef ENABLE_THREAD_SAFETY
+			pthread_mutex_unlock(&ssl_config_mutex);
+#endif
+			pgtls_close(conn);
+
+			return PGRES_POLLING_FAILED;
+		}
+		conn->ssl_in_use = true;
+
+#ifdef ENABLE_THREAD_SAFETY
+		pthread_mutex_unlock(&ssl_config_mutex);
+#endif
+
+		/*
+		 * Load client certificate, private key, and trusted CA certs.
+		 */
+		if (initialize_SSL(conn) != 0)
+		{
+			/* initialize_SSL already put a message in conn->errorMessage */
+			pgtls_close(conn);
+			return PGRES_POLLING_FAILED;
+		}
+	}
+
+	/* Begin or continue the actual handshake */
+	return open_client_SSL(conn);
+}
+
+/*
+ *	Read data from a secure connection.
+ *
+ * On failure, this function is responsible for putting a suitable message
+ * into conn->errorMessage.  The caller must still inspect errno, but only
+ * to determine whether to continue/retry after error.
+ */
+ssize_t
+pgtls_read(PGconn *conn, void *ptr, size_t len)
+{
+	ssize_t		n;
+	int			result_errno = 0;
+	char		sebuf[256];
+	int			err;
+
+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 */
+				printfPQExpBuffer(&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;
+				if (result_errno == EPIPE ||
+					result_errno == ECONNRESET)
+					printfPQExpBuffer(&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
+					printfPQExpBuffer(&conn->errorMessage,
+									libpq_gettext("SSL SYSCALL error: %s\n"),
+									  SOCK_STRERROR(result_errno,
+													sebuf, sizeof(sebuf)));
+			}
+			else
+			{
+				printfPQExpBuffer(&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();
+
+				printfPQExpBuffer(&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.
+			 */
+			printfPQExpBuffer(&conn->errorMessage,
+							  libpq_gettext("SSL connection has been closed unexpectedly\n"));
+			result_errno = ECONNRESET;
+			n = -1;
+			break;
+		default:
+			printfPQExpBuffer(&conn->errorMessage,
+						  libpq_gettext("unrecognized SSL error code: %d\n"),
+							  err);
+			/* assume the connection is broken */
+			result_errno = ECONNRESET;
+			n = -1;
+			break;
+	}
+
+	/* ensure we return the intended errno to caller */
+	SOCK_ERRNO_SET(result_errno);
+
+	return n;
+}
+
+/*
+ *	Write data to a secure connection.
+ *
+ * On failure, this function is responsible for putting a suitable message
+ * into conn->errorMessage.  The caller must still inspect errno, but only
+ * to determine whether to continue/retry after error.
+ */
+ssize_t
+pgtls_write(PGconn *conn, const void *ptr, size_t len)
+{
+	ssize_t		n;
+	int			result_errno = 0;
+	char		sebuf[256];
+	int			err;
+
+	SOCK_ERRNO_SET(0);
+	n = SSL_write(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 */
+				printfPQExpBuffer(&conn->errorMessage,
+								  "SSL_write failed but did not provide error information\n");
+				/* assume the connection is broken */
+				result_errno = ECONNRESET;
+			}
+			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:
+			if (n < 0)
+			{
+				result_errno = SOCK_ERRNO;
+				if (result_errno == EPIPE || result_errno == ECONNRESET)
+					printfPQExpBuffer(&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
+					printfPQExpBuffer(&conn->errorMessage,
+									libpq_gettext("SSL SYSCALL error: %s\n"),
+									  SOCK_STRERROR(result_errno,
+													sebuf, sizeof(sebuf)));
+			}
+			else
+			{
+				printfPQExpBuffer(&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();
+
+				printfPQExpBuffer(&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.
+			 */
+			printfPQExpBuffer(&conn->errorMessage,
+							  libpq_gettext("SSL connection has been closed unexpectedly\n"));
+			result_errno = ECONNRESET;
+			n = -1;
+			break;
+		default:
+			printfPQExpBuffer(&conn->errorMessage,
+						  libpq_gettext("unrecognized SSL error code: %d\n"),
+							  err);
+			/* assume the connection is broken */
+			result_errno = ECONNRESET;
+			n = -1;
+			break;
+	}
+
+	/* ensure we return the intended errno to caller */
+	SOCK_ERRNO_SET(result_errno);
+
+	return n;
+}
+
+/* ------------------------------------------------------------ */
+/*						OpenSSL specific code					*/
+/* ------------------------------------------------------------ */
+
+/*
+ *	Certificate verification callback
+ *
+ *	This callback allows us to log intermediate problems during
+ *	verification, but there doesn't seem to be a clean way to get
+ *	our PGconn * structure.  So we can't log anything!
+ *
+ *	This callback also allows us to override the default acceptance
+ *	criteria (e.g., accepting self-signed or expired certs), but
+ *	for now we accept the default checks.
+ */
+static int
+verify_cb(int ok, X509_STORE_CTX *ctx)
+{
+	return ok;
+}
+
+
+/*
+ * Check if a wildcard certificate matches the server hostname.
+ *
+ * The rule for this is:
+ *	1. We only match the '*' character as wildcard
+ *	2. We match only wildcards at the start of the string
+ *	3. The '*' character does *not* match '.', meaning that we match only
+ *	   a single pathname component.
+ *	4. We don't support more than one '*' in a single pattern.
+ *
+ * This is roughly in line with RFC2818, but contrary to what most browsers
+ * appear to be implementing (point 3 being the difference)
+ *
+ * Matching is always case-insensitive, since DNS is case insensitive.
+ */
+static int
+wildcard_certificate_match(const char *pattern, const char *string)
+{
+	int			lenpat = strlen(pattern);
+	int			lenstr = strlen(string);
+
+	/* If we don't start with a wildcard, it's not a match (rule 1 & 2) */
+	if (lenpat < 3 ||
+		pattern[0] != '*' ||
+		pattern[1] != '.')
+		return 0;
+
+	if (lenpat > lenstr)
+		/* If pattern is longer than the string, we can never match */
+		return 0;
+
+	if (pg_strcasecmp(pattern + 1, string + lenstr - lenpat + 1) != 0)
+
+		/*
+		 * If string does not end in pattern (minus the wildcard), we don't
+		 * match
+		 */
+		return 0;
+
+	if (strchr(string, '.') < string + lenstr - lenpat)
+
+		/*
+		 * If there is a dot left of where the pattern started to match, we
+		 * don't match (rule 3)
+		 */
+		return 0;
+
+	/* String ended with pattern, and didn't have a dot before, so we match */
+	return 1;
+}
+
+
+/*
+ *	Verify that common name resolves to peer.
+ */
+static bool
+verify_peer_name_matches_certificate(PGconn *conn)
+{
+	char	   *peer_cn;
+	int			r;
+	int			len;
+	bool		result;
+
+	/*
+	 * If told not to verify the peer name, don't do it. Return true
+	 * indicating that the verification was successful.
+	 */
+	if (strcmp(conn->sslmode, "verify-full") != 0)
+		return true;
+
+	/*
+	 * Extract the common name from the certificate.
+	 *
+	 * XXX: Should support alternate names here
+	 */
+	/* First find out the name's length and allocate a buffer for it. */
+	len = X509_NAME_get_text_by_NID(X509_get_subject_name(conn->peer),
+									NID_commonName, NULL, 0);
+	if (len == -1)
+	{
+		printfPQExpBuffer(&conn->errorMessage,
+						  libpq_gettext("could not get server common name from server certificate\n"));
+		return false;
+	}
+	peer_cn = malloc(len + 1);
+	if (peer_cn == NULL)
+	{
+		printfPQExpBuffer(&conn->errorMessage,
+						  libpq_gettext("out of memory\n"));
+		return false;
+	}
+
+	r = X509_NAME_get_text_by_NID(X509_get_subject_name(conn->peer),
+								  NID_commonName, peer_cn, len + 1);
+	if (r != len)
+	{
+		/* Got different length than on the first call. Shouldn't happen. */
+		printfPQExpBuffer(&conn->errorMessage,
+						  libpq_gettext("could not get server common name from server certificate\n"));
+		free(peer_cn);
+		return false;
+	}
+	peer_cn[len] = '\0';
+
+	/*
+	 * Reject embedded NULLs in certificate common name to prevent attacks
+	 * like CVE-2009-4034.
+	 */
+	if (len != strlen(peer_cn))
+	{
+		printfPQExpBuffer(&conn->errorMessage,
+						  libpq_gettext("SSL certificate's common name contains embedded null\n"));
+		free(peer_cn);
+		return false;
+	}
+
+	/*
+	 * We got the peer's common name. Now compare it against the originally
+	 * given hostname.
+	 */
+	if (!(conn->pghost && conn->pghost[0] != '\0'))
+	{
+		printfPQExpBuffer(&conn->errorMessage,
+						  libpq_gettext("host name must be specified for a verified SSL connection\n"));
+		result = false;
+	}
+	else
+	{
+		if (pg_strcasecmp(peer_cn, conn->pghost) == 0)
+			/* Exact name match */
+			result = true;
+		else if (wildcard_certificate_match(peer_cn, conn->pghost))
+			/* Matched wildcard certificate */
+			result = true;
+		else
+		{
+			printfPQExpBuffer(&conn->errorMessage,
+							  libpq_gettext("server common name \"%s\" does not match host name \"%s\"\n"),
+							  peer_cn, conn->pghost);
+			result = false;
+		}
+	}
+
+	free(peer_cn);
+	return result;
+}
+
+#ifdef ENABLE_THREAD_SAFETY
+/*
+ *	Callback functions for OpenSSL internal locking
+ */
+
+static unsigned long
+pq_threadidcallback(void)
+{
+	/*
+	 * This is not standards-compliant.  pthread_self() returns pthread_t, and
+	 * shouldn't be cast to unsigned long, but CRYPTO_set_id_callback requires
+	 * it, so we have to do it.
+	 */
+	return (unsigned long) pthread_self();
+}
+
+static pthread_mutex_t *pq_lockarray;
+
+static void
+pq_lockingcallback(int mode, int n, const char *file, int line)
+{
+	if (mode & CRYPTO_LOCK)
+	{
+		if (pthread_mutex_lock(&pq_lockarray[n]))
+			PGTHREAD_ERROR("failed to lock mutex");
+	}
+	else
+	{
+		if (pthread_mutex_unlock(&pq_lockarray[n]))
+			PGTHREAD_ERROR("failed to unlock mutex");
+	}
+}
+#endif   /* ENABLE_THREAD_SAFETY */
+
+/*
+ * Initialize SSL system, in particular creating the SSL_context object
+ * that will be shared by all SSL-using connections in this process.
+ *
+ * In threadsafe mode, this includes setting up libcrypto callback functions
+ * to do thread locking.
+ *
+ * If the caller has told us (through PQinitOpenSSL) that he's taking care
+ * of libcrypto, we expect that callbacks are already set, and won't try to
+ * override it.
+ *
+ * The conn parameter is only used to be able to pass back an error
+ * message - no connection-local setup is made here.
+ *
+ * Returns 0 if OK, -1 on failure (with a message in conn->errorMessage).
+ */
+int
+pgtls_init(PGconn *conn)
+{
+#ifdef ENABLE_THREAD_SAFETY
+#ifdef WIN32
+	/* Also see similar code in fe-connect.c, default_threadlock() */
+	if (ssl_config_mutex == NULL)
+	{
+		while (InterlockedExchange(&win32_ssl_create_mutex, 1) == 1)
+			 /* loop, another thread own the lock */ ;
+		if (ssl_config_mutex == NULL)
+		{
+			if (pthread_mutex_init(&ssl_config_mutex, NULL))
+				return -1;
+		}
+		InterlockedExchange(&win32_ssl_create_mutex, 0);
+	}
+#endif
+	if (pthread_mutex_lock(&ssl_config_mutex))
+		return -1;
+
+	if (pq_init_crypto_lib)
+	{
+		/*
+		 * If necessary, set up an array to hold locks for libcrypto.
+		 * libcrypto will tell us how big to make this array.
+		 */
+		if (pq_lockarray == NULL)
+		{
+			int			i;
+
+			pq_lockarray = malloc(sizeof(pthread_mutex_t) * CRYPTO_num_locks());
+			if (!pq_lockarray)
+			{
+				pthread_mutex_unlock(&ssl_config_mutex);
+				return -1;
+			}
+			for (i = 0; i < CRYPTO_num_locks(); i++)
+			{
+				if (pthread_mutex_init(&pq_lockarray[i], NULL))
+				{
+					free(pq_lockarray);
+					pq_lockarray = NULL;
+					pthread_mutex_unlock(&ssl_config_mutex);
+					return -1;
+				}
+			}
+		}
+
+		if (ssl_open_connections++ == 0)
+		{
+			/* These are only required for threaded libcrypto applications */
+			CRYPTO_set_id_callback(pq_threadidcallback);
+			CRYPTO_set_locking_callback(pq_lockingcallback);
+		}
+	}
+#endif   /* ENABLE_THREAD_SAFETY */
+
+	if (!SSL_context)
+	{
+		if (pq_init_ssl_lib)
+		{
+#if SSLEAY_VERSION_NUMBER >= 0x00907000L
+			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)
+		{
+			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(&ssl_config_mutex);
+#endif
+			return -1;
+		}
+
+		/* Disable old protocol versions */
+		SSL_CTX_set_options(SSL_context, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
+
+		/*
+		 * 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);
+	}
+
+#ifdef ENABLE_THREAD_SAFETY
+	pthread_mutex_unlock(&ssl_config_mutex);
+#endif
+	return 0;
+}
+
+/*
+ *	This function is needed because if the libpq library is unloaded
+ *	from the application, the callback functions will no longer exist when
+ *	libcrypto is used by other parts of the system.  For this reason,
+ *	we unregister the callback functions when the last libpq
+ *	connection is closed.  (The same would apply for OpenSSL callbacks
+ *	if we had any.)
+ *
+ *	Callbacks are only set when we're compiled in threadsafe mode, so
+ *	we only need to remove them in this case.
+ */
+static void
+destroy_ssl_system(void)
+{
+#ifdef ENABLE_THREAD_SAFETY
+	/* Mutex is created in initialize_ssl_system() */
+	if (pthread_mutex_lock(&ssl_config_mutex))
+		return;
+
+	if (pq_init_crypto_lib && ssl_open_connections > 0)
+		--ssl_open_connections;
+
+	if (pq_init_crypto_lib && ssl_open_connections == 0)
+	{
+		/* No connections left, unregister libcrypto callbacks */
+		CRYPTO_set_locking_callback(NULL);
+		CRYPTO_set_id_callback(NULL);
+
+		/*
+		 * We don't free the lock array or the SSL_context. If we get another
+		 * connection in this process, we will just re-use them with the
+		 * existing mutexes.
+		 *
+		 * This means we leak a little memory on repeated load/unload of the
+		 * library.
+		 */
+	}
+
+	pthread_mutex_unlock(&ssl_config_mutex);
+#endif
+}
+
+/*
+ *	Initialize (potentially) per-connection SSL data, namely the
+ *	client certificate, private key, and trusted CA certs.
+ *
+ *	conn->ssl must already be created.  It receives the connection's client
+ *	certificate and private key.  Note however that certificates also get
+ *	loaded into the SSL_context object, and are therefore accessible to all
+ *	connections in this process.  This should be OK as long as there aren't
+ *	any hash collisions among the certs.
+ *
+ *	Returns 0 if OK, -1 on failure (with a message in conn->errorMessage).
+ */
+static int
+initialize_SSL(PGconn *conn)
+{
+	struct stat buf;
+	char		homedir[MAXPGPATH];
+	char		fnbuf[MAXPGPATH];
+	char		sebuf[256];
+	bool		have_homedir;
+	bool		have_cert;
+	EVP_PKEY   *pkey = NULL;
+
+	/*
+	 * We'll need the home directory if any of the relevant parameters are
+	 * defaulted.  If pqGetHomeDirectory fails, act as though none of the
+	 * files could be found.
+	 */
+	if (!(conn->sslcert && strlen(conn->sslcert) > 0) ||
+		!(conn->sslkey && strlen(conn->sslkey) > 0) ||
+		!(conn->sslrootcert && strlen(conn->sslrootcert) > 0) ||
+		!(conn->sslcrl && strlen(conn->sslcrl) > 0))
+		have_homedir = pqGetHomeDirectory(homedir, sizeof(homedir));
+	else	/* won't need it */
+		have_homedir = false;
+
+	/* Read the client certificate file */
+	if (conn->sslcert && strlen(conn->sslcert) > 0)
+		strncpy(fnbuf, conn->sslcert, sizeof(fnbuf));
+	else if (have_homedir)
+		snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, USER_CERT_FILE);
+	else
+		fnbuf[0] = '\0';
+
+	if (fnbuf[0] == '\0')
+	{
+		/* no home directory, proceed without a client cert */
+		have_cert = false;
+	}
+	else if (stat(fnbuf, &buf) != 0)
+	{
+		/*
+		 * If file is not present, just go on without a client cert; server
+		 * might or might not accept the connection.  Any other error,
+		 * however, is grounds for complaint.
+		 */
+		if (errno != ENOENT && errno != ENOTDIR)
+		{
+			printfPQExpBuffer(&conn->errorMessage,
+			   libpq_gettext("could not open certificate file \"%s\": %s\n"),
+							  fnbuf, pqStrerror(errno, sebuf, sizeof(sebuf)));
+			return -1;
+		}
+		have_cert = false;
+	}
+	else
+	{
+		/*
+		 * Cert file exists, so load it.  Since OpenSSL doesn't provide the
+		 * equivalent of "SSL_use_certificate_chain_file", we actually have to
+		 * load the file twice.  The first call loads any extra certs after
+		 * the first one into chain-cert storage associated with the
+		 * SSL_context.  The second call loads the first cert (only) into the
+		 * SSL object, where it will be correctly paired with the private key
+		 * we load below.  We do it this way so that each connection
+		 * understands which subject cert to present, in case different
+		 * sslcert settings are used for different connections in the same
+		 * process.
+		 *
+		 * NOTE: This function may also modify our SSL_context and therefore
+		 * we have to lock around this call and any places where we use the
+		 * SSL_context struct.
+		 */
+#ifdef ENABLE_THREAD_SAFETY
+		int			rc;
+
+		if ((rc = pthread_mutex_lock(&ssl_config_mutex)))
+		{
+			printfPQExpBuffer(&conn->errorMessage,
+			   libpq_gettext("could not acquire mutex: %s\n"), strerror(rc));
+			return -1;
+		}
+#endif
+		if (SSL_CTX_use_certificate_chain_file(SSL_context, fnbuf) != 1)
+		{
+			char	   *err = SSLerrmessage();
+
+			printfPQExpBuffer(&conn->errorMessage,
+			   libpq_gettext("could not read certificate file \"%s\": %s\n"),
+							  fnbuf, err);
+			SSLerrfree(err);
+
+#ifdef ENABLE_THREAD_SAFETY
+			pthread_mutex_unlock(&ssl_config_mutex);
+#endif
+			return -1;
+		}
+
+		if (SSL_use_certificate_file(conn->ssl, fnbuf, SSL_FILETYPE_PEM) != 1)
+		{
+			char	   *err = SSLerrmessage();
+
+			printfPQExpBuffer(&conn->errorMessage,
+			   libpq_gettext("could not read certificate file \"%s\": %s\n"),
+							  fnbuf, err);
+			SSLerrfree(err);
+#ifdef ENABLE_THREAD_SAFETY
+			pthread_mutex_unlock(&ssl_config_mutex);
+#endif
+			return -1;
+		}
+
+		/* need to load the associated private key, too */
+		have_cert = true;
+
+#ifdef ENABLE_THREAD_SAFETY
+		pthread_mutex_unlock(&ssl_config_mutex);
+#endif
+	}
+
+	/*
+	 * Read the SSL key. If a key is specified, treat it as an engine:key
+	 * combination if there is colon present - we don't support files with
+	 * colon in the name. The exception is if the second character is a colon,
+	 * in which case it can be a Windows filename with drive specification.
+	 */
+	if (have_cert && conn->sslkey && strlen(conn->sslkey) > 0)
+	{
+#ifdef USE_SSL_ENGINE
+		if (strchr(conn->sslkey, ':')
+#ifdef WIN32
+			&& conn->sslkey[1] != ':'
+#endif
+			)
+		{
+			/* Colon, but not in second character, treat as engine:key */
+			char	   *engine_str = strdup(conn->sslkey);
+			char	   *engine_colon;
+
+			if (engine_str == NULL)
+			{
+				printfPQExpBuffer(&conn->errorMessage,
+								  libpq_gettext("out of memory\n"));
+				return -1;
+			}
+
+			/* cannot return NULL because we already checked before strdup */
+			engine_colon = strchr(engine_str, ':');
+
+			*engine_colon = '\0';		/* engine_str now has engine name */
+			engine_colon++;		/* engine_colon now has key name */
+
+			conn->engine = ENGINE_by_id(engine_str);
+			if (conn->engine == NULL)
+			{
+				char	   *err = SSLerrmessage();
+
+				printfPQExpBuffer(&conn->errorMessage,
+					 libpq_gettext("could not load SSL engine \"%s\": %s\n"),
+								  engine_str, err);
+				SSLerrfree(err);
+				free(engine_str);
+				return -1;
+			}
+
+			if (ENGINE_init(conn->engine) == 0)
+			{
+				char	   *err = SSLerrmessage();
+
+				printfPQExpBuffer(&conn->errorMessage,
+				libpq_gettext("could not initialize SSL engine \"%s\": %s\n"),
+								  engine_str, err);
+				SSLerrfree(err);
+				ENGINE_free(conn->engine);
+				conn->engine = NULL;
+				free(engine_str);
+				return -1;
+			}
+
+			pkey = ENGINE_load_private_key(conn->engine, engine_colon,
+										   NULL, NULL);
+			if (pkey == NULL)
+			{
+				char	   *err = SSLerrmessage();
+
+				printfPQExpBuffer(&conn->errorMessage,
+								  libpq_gettext("could not read private SSL key \"%s\" from engine \"%s\": %s\n"),
+								  engine_colon, engine_str, err);
+				SSLerrfree(err);
+				ENGINE_finish(conn->engine);
+				ENGINE_free(conn->engine);
+				conn->engine = NULL;
+				free(engine_str);
+				return -1;
+			}
+			if (SSL_use_PrivateKey(conn->ssl, pkey) != 1)
+			{
+				char	   *err = SSLerrmessage();
+
+				printfPQExpBuffer(&conn->errorMessage,
+								  libpq_gettext("could not load private SSL key \"%s\" from engine \"%s\": %s\n"),
+								  engine_colon, engine_str, err);
+				SSLerrfree(err);
+				ENGINE_finish(conn->engine);
+				ENGINE_free(conn->engine);
+				conn->engine = NULL;
+				free(engine_str);
+				return -1;
+			}
+
+			free(engine_str);
+
+			fnbuf[0] = '\0';	/* indicate we're not going to load from a
+								 * file */
+		}
+		else
+#endif   /* USE_SSL_ENGINE */
+		{
+			/* PGSSLKEY is not an engine, treat it as a filename */
+			strncpy(fnbuf, conn->sslkey, sizeof(fnbuf));
+		}
+	}
+	else if (have_homedir)
+	{
+		/* No PGSSLKEY specified, load default file */
+		snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, USER_KEY_FILE);
+	}
+	else
+		fnbuf[0] = '\0';
+
+	if (have_cert && fnbuf[0] != '\0')
+	{
+		/* read the client key from file */
+
+		if (stat(fnbuf, &buf) != 0)
+		{
+			printfPQExpBuffer(&conn->errorMessage,
+							  libpq_gettext("certificate present, but not private key file \"%s\"\n"),
+							  fnbuf);
+			return -1;
+		}
+#ifndef WIN32
+		if (!S_ISREG(buf.st_mode) || buf.st_mode & (S_IRWXG | S_IRWXO))
+		{
+			printfPQExpBuffer(&conn->errorMessage,
+							  libpq_gettext("private key file \"%s\" has group or world access; permissions should be u=rw (0600) or less\n"),
+							  fnbuf);
+			return -1;
+		}
+#endif
+
+		if (SSL_use_PrivateKey_file(conn->ssl, fnbuf, SSL_FILETYPE_PEM) != 1)
+		{
+			char	   *err = SSLerrmessage();
+
+			printfPQExpBuffer(&conn->errorMessage,
+			   libpq_gettext("could not load private key file \"%s\": %s\n"),
+							  fnbuf, err);
+			SSLerrfree(err);
+			return -1;
+		}
+	}
+
+	/* verify that the cert and key go together */
+	if (have_cert &&
+		SSL_check_private_key(conn->ssl) != 1)
+	{
+		char	   *err = SSLerrmessage();
+
+		printfPQExpBuffer(&conn->errorMessage,
+						  libpq_gettext("certificate does not match private key file \"%s\": %s\n"),
+						  fnbuf, err);
+		SSLerrfree(err);
+		return -1;
+	}
+
+	/*
+	 * If the root cert file exists, load it so we can perform certificate
+	 * verification. If sslmode is "verify-full" we will also do further
+	 * verification after the connection has been completed.
+	 */
+	if (conn->sslrootcert && strlen(conn->sslrootcert) > 0)
+		strncpy(fnbuf, conn->sslrootcert, sizeof(fnbuf));
+	else if (have_homedir)
+		snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, ROOT_CERT_FILE);
+	else
+		fnbuf[0] = '\0';
+
+	if (fnbuf[0] != '\0' &&
+		stat(fnbuf, &buf) == 0)
+	{
+		X509_STORE *cvstore;
+
+#ifdef ENABLE_THREAD_SAFETY
+		int			rc;
+
+		if ((rc = pthread_mutex_lock(&ssl_config_mutex)))
+		{
+			printfPQExpBuffer(&conn->errorMessage,
+			   libpq_gettext("could not acquire mutex: %s\n"), strerror(rc));
+			return -1;
+		}
+#endif
+		if (SSL_CTX_load_verify_locations(SSL_context, fnbuf, NULL) != 1)
+		{
+			char	   *err = SSLerrmessage();
+
+			printfPQExpBuffer(&conn->errorMessage,
+							  libpq_gettext("could not read root certificate file \"%s\": %s\n"),
+							  fnbuf, err);
+			SSLerrfree(err);
+#ifdef ENABLE_THREAD_SAFETY
+			pthread_mutex_unlock(&ssl_config_mutex);
+#endif
+			return -1;
+		}
+
+		if ((cvstore = SSL_CTX_get_cert_store(SSL_context)) != NULL)
+		{
+			if (conn->sslcrl && strlen(conn->sslcrl) > 0)
+				strncpy(fnbuf, conn->sslcrl, sizeof(fnbuf));
+			else if (have_homedir)
+				snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, ROOT_CRL_FILE);
+			else
+				fnbuf[0] = '\0';
+
+			/* Set the flags to check against the complete CRL chain */
+			if (fnbuf[0] != '\0' &&
+				X509_STORE_load_locations(cvstore, fnbuf, 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
+				char	   *err = SSLerrmessage();
+
+				printfPQExpBuffer(&conn->errorMessage,
+								  libpq_gettext("SSL library does not support CRL certificates (file \"%s\")\n"),
+								  fnbuf);
+				SSLerrfree(err);
+#ifdef ENABLE_THREAD_SAFETY
+				pthread_mutex_unlock(&ssl_config_mutex);
+#endif
+				return -1;
+#endif
+			}
+			/* if not found, silently ignore;  we do not require CRL */
+		}
+#ifdef ENABLE_THREAD_SAFETY
+		pthread_mutex_unlock(&ssl_config_mutex);
+#endif
+
+		SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, verify_cb);
+	}
+	else
+	{
+		/*
+		 * stat() failed; assume root file doesn't exist.  If sslmode is
+		 * verify-ca or verify-full, this is an error.  Otherwise, continue
+		 * without performing any server cert verification.
+		 */
+		if (conn->sslmode[0] == 'v')	/* "verify-ca" or "verify-full" */
+		{
+			/*
+			 * The only way to reach here with an empty filename is if
+			 * pqGetHomeDirectory failed.  That's a sufficiently unusual case
+			 * that it seems worth having a specialized error message for it.
+			 */
+			if (fnbuf[0] == '\0')
+				printfPQExpBuffer(&conn->errorMessage,
+								  libpq_gettext("could not get home directory to locate root certificate file\n"
+												"Either provide the file or change sslmode to disable server certificate verification.\n"));
+			else
+				printfPQExpBuffer(&conn->errorMessage,
+				libpq_gettext("root certificate file \"%s\" does not exist\n"
+							  "Either provide the file or change sslmode to disable server certificate verification.\n"), fnbuf);
+			return -1;
+		}
+	}
+
+	/*
+	 * If the OpenSSL version used supports it (from 1.0.0 on) and the user
+	 * requested it, disable SSL compression.
+	 */
+#ifdef SSL_OP_NO_COMPRESSION
+	if (conn->sslcompression && conn->sslcompression[0] == '0')
+	{
+		SSL_set_options(conn->ssl, SSL_OP_NO_COMPRESSION);
+	}
+#endif
+
+	return 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"));
+					pgtls_close(conn);
+					return PGRES_POLLING_FAILED;
+				}
+			case SSL_ERROR_SSL:
+				{
+					char	   *err = SSLerrmessage();
+
+					printfPQExpBuffer(&conn->errorMessage,
+									  libpq_gettext("SSL error: %s\n"),
+									  err);
+					SSLerrfree(err);
+					pgtls_close(conn);
+					return PGRES_POLLING_FAILED;
+				}
+
+			default:
+				printfPQExpBuffer(&conn->errorMessage,
+						  libpq_gettext("unrecognized SSL error code: %d\n"),
+								  err);
+				pgtls_close(conn);
+				return PGRES_POLLING_FAILED;
+		}
+	}
+
+	/*
+	 * We already checked the server certificate in initialize_SSL() using
+	 * SSL_CTX_set_verify(), if root.crt exists.
+	 */
+
+	/* get server certificate */
+	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);
+		pgtls_close(conn);
+		return PGRES_POLLING_FAILED;
+	}
+
+	if (!verify_peer_name_matches_certificate(conn))
+	{
+		pgtls_close(conn);
+		return PGRES_POLLING_FAILED;
+	}
+
+	/* SSL handshake is complete */
+	return PGRES_POLLING_OK;
+}
+
+/*
+ *	Close SSL connection.
+ */
+void
+pgtls_close(PGconn *conn)
+{
+	bool		destroy_needed = false;
+
+	if (conn->ssl)
+	{
+		/*
+		 * We can't destroy everything SSL-related here due to the possible
+		 * later calls to OpenSSL routines which may need our thread
+		 * callbacks, so set a flag here and check at the end.
+		 */
+		destroy_needed = true;
+
+		SSL_shutdown(conn->ssl);
+		SSL_free(conn->ssl);
+		conn->ssl = NULL;
+		conn->ssl_in_use = false;
+	}
+
+	if (conn->peer)
+	{
+		X509_free(conn->peer);
+		conn->peer = NULL;
+	}
+
+#ifdef USE_SSL_ENGINE
+	if (conn->engine)
+	{
+		ENGINE_finish(conn->engine);
+		ENGINE_free(conn->engine);
+		conn->engine = NULL;
+	}
+#endif
+
+	/*
+	 * This will remove our SSL locking hooks, if this is the last SSL
+	 * connection, which means we must wait to call it until after all SSL
+	 * calls have been made, otherwise we can end up with a race condition and
+	 * possible deadlocks.
+	 *
+	 * See comments above destroy_ssl_system().
+	 */
+	if (destroy_needed)
+		destroy_ssl_system();
+}
+
+
+/*
+ * Obtain reason string for last SSL error
+ *
+ * Some caution is needed here since ERR_reason_error_string will
+ * return NULL if it doesn't recognize the error code.  We don't
+ * want to return NULL ever.
+ */
+static char ssl_nomem[] = "out of memory allocating error description";
+
+#define SSL_ERR_LEN 128
+
+static char *
+SSLerrmessage(void)
+{
+	unsigned long errcode;
+	const char *errreason;
+	char	   *errbuf;
+
+	errbuf = malloc(SSL_ERR_LEN);
+	if (!errbuf)
+		return ssl_nomem;
+	errcode = ERR_get_error();
+	if (errcode == 0)
+	{
+		snprintf(errbuf, SSL_ERR_LEN, libpq_gettext("no SSL error reported"));
+		return errbuf;
+	}
+	errreason = ERR_reason_error_string(errcode);
+	if (errreason != NULL)
+	{
+		strlcpy(errbuf, errreason, SSL_ERR_LEN);
+		return errbuf;
+	}
+	snprintf(errbuf, SSL_ERR_LEN, libpq_gettext("SSL error code %lu"), errcode);
+	return errbuf;
+}
+
+static void
+SSLerrfree(char *buf)
+{
+	if (buf != ssl_nomem)
+		free(buf);
+}
+
+/*
+ *	Return pointer to OpenSSL object.
+ */
+void *
+PQgetssl(PGconn *conn)
+{
+	if (!conn)
+		return NULL;
+	return conn->ssl;
+}
+
+
+/*
+ * Private substitute BIO: this does the sending and receiving using send() and
+ * recv() instead. This is so that we can enable and disable interrupts
+ * just while calling recv(). We cannot have interrupts occurring while
+ * the bulk of openssl runs, because it uses malloc() and possibly other
+ * non-reentrant libc facilities. We also need to call send() and recv()
+ * directly so it gets passed through the socket/signals layer on Win32.
+ *
+ * These functions are closely modelled on the standard socket BIO in OpenSSL;
+ * see sock_read() and sock_write() in OpenSSL's crypto/bio/bss_sock.c.
+ * XXX OpenSSL 1.0.1e considers many more errcodes than just EINTR as reasons
+ * to retry; do we need to adopt their logic for that?
+ */
+
+static bool my_bio_initialized = false;
+static BIO_METHOD my_bio_methods;
+
+static int
+my_sock_read(BIO *h, char *buf, int size)
+{
+	int			res;
+	int			save_errno;
+
+	res = pqsecure_raw_read((PGconn *) h->ptr, buf, size);
+	save_errno = errno;
+	BIO_clear_retry_flags(h);
+	if (res < 0)
+	{
+		switch (save_errno)
+		{
+#ifdef EAGAIN
+			case EAGAIN:
+#endif
+#if defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN))
+			case EWOULDBLOCK:
+#endif
+			case EINTR:
+				BIO_set_retry_read(h);
+				break;
+
+			default:
+				break;
+		}
+	}
+
+	errno = save_errno;
+	return res;
+}
+
+static int
+my_sock_write(BIO *h, const char *buf, int size)
+{
+	int			res;
+	int			save_errno;
+
+	res = pqsecure_raw_write((PGconn *) h->ptr, buf, size);
+	save_errno = errno;
+	BIO_clear_retry_flags(h);
+	if (res <= 0)
+	{
+		if (save_errno == EINTR)
+		{
+			BIO_set_retry_write(h);
+		}
+	}
+
+	return res;
+}
+
+static BIO_METHOD *
+my_BIO_s_socket(void)
+{
+	if (!my_bio_initialized)
+	{
+		memcpy(&my_bio_methods, BIO_s_socket(), sizeof(BIO_METHOD));
+		my_bio_methods.bread = my_sock_read;
+		my_bio_methods.bwrite = my_sock_write;
+		my_bio_initialized = true;
+	}
+	return &my_bio_methods;
+}
+
+/* This should exactly match openssl's SSL_set_fd except for using my BIO */
+static int
+my_SSL_set_fd(PGconn *conn, int fd)
+{
+	int			ret = 0;
+	BIO		   *bio = NULL;
+
+	bio = BIO_new(my_BIO_s_socket());
+	if (bio == NULL)
+	{
+		SSLerr(SSL_F_SSL_SET_FD, ERR_R_BUF_LIB);
+		goto err;
+	}
+	/* Use 'ptr' to store pointer to PGconn */
+	bio->ptr = conn;
+
+	SSL_set_bio(conn->ssl, bio, bio);
+	BIO_set_fd(bio, fd, BIO_NOCLOSE);
+	ret = 1;
+err:
+	return ret;
+}
diff --git a/src/interfaces/libpq/fe-secure.c b/src/interfaces/libpq/fe-secure.c
index 9ba3567..012c718 100644
--- a/src/interfaces/libpq/fe-secure.c
+++ b/src/interfaces/libpq/fe-secure.c
@@ -55,64 +55,6 @@
 #endif
 #endif
 
-#ifdef USE_SSL
-
-#include <openssl/ssl.h>
-#if (SSLEAY_VERSION_NUMBER >= 0x00907000L)
-#include <openssl/conf.h>
-#endif
-#ifdef USE_SSL_ENGINE
-#include <openssl/engine.h>
-#endif
-
-
-#ifndef WIN32
-#define USER_CERT_FILE		".postgresql/postgresql.crt"
-#define USER_KEY_FILE		".postgresql/postgresql.key"
-#define ROOT_CERT_FILE		".postgresql/root.crt"
-#define ROOT_CRL_FILE		".postgresql/root.crl"
-#else
-/* On Windows, the "home" directory is already PostgreSQL-specific */
-#define USER_CERT_FILE		"postgresql.crt"
-#define USER_KEY_FILE		"postgresql.key"
-#define ROOT_CERT_FILE		"root.crt"
-#define ROOT_CRL_FILE		"root.crl"
-#endif
-
-static bool verify_peer_name_matches_certificate(PGconn *);
-static int	verify_cb(int ok, X509_STORE_CTX *ctx);
-static int	init_ssl_system(PGconn *conn);
-static void destroy_ssl_system(void);
-static int	initialize_SSL(PGconn *conn);
-static void destroySSL(void);
-static PostgresPollingStatusType open_client_SSL(PGconn *);
-static void close_SSL(PGconn *);
-static char *SSLerrmessage(void);
-static void SSLerrfree(char *buf);
-
-static bool pq_init_ssl_lib = true;
-static bool pq_init_crypto_lib = true;
-
-/*
- * SSL_context is currently shared between threads and therefore we need to be
- * careful to lock around any usage of it when providing thread safety.
- * ssl_config_mutex is the mutex that we use to protect it.
- */
-static SSL_CTX *SSL_context = NULL;
-
-#ifdef ENABLE_THREAD_SAFETY
-static long ssl_open_connections = 0;
-
-#ifndef WIN32
-static pthread_mutex_t ssl_config_mutex = PTHREAD_MUTEX_INITIALIZER;
-#else
-static pthread_mutex_t ssl_config_mutex = NULL;
-static long win32_ssl_create_mutex = 0;
-#endif
-#endif   /* ENABLE_THREAD_SAFETY */
-#endif   /* SSL */
-
-
 /*
  * Macros to handle disabling and then restoring the state of SIGPIPE handling.
  * On Windows, these are all no-ops since there's no SIGPIPEs.
@@ -194,7 +136,9 @@ struct sigpipe_info
 void
 PQinitSSL(int do_init)
 {
-	PQinitOpenSSL(do_init, do_init);
+#ifdef USE_SSL
+	pgtls_init_library(do_init, do_init);
+#endif
 }
 
 /*
@@ -205,18 +149,7 @@ void
 PQinitOpenSSL(int do_ssl, int do_crypto)
 {
 #ifdef USE_SSL
-#ifdef ENABLE_THREAD_SAFETY
-
-	/*
-	 * Disallow changing the flags while we have open connections, else we'd
-	 * get completely confused.
-	 */
-	if (ssl_open_connections != 0)
-		return;
-#endif
-
-	pq_init_ssl_lib = do_ssl;
-	pq_init_crypto_lib = do_crypto;
+	pgtls_init_library(do_ssl, do_crypto);
 #endif
 }
 
@@ -229,83 +162,20 @@ pqsecure_initialize(PGconn *conn)
 	int			r = 0;
 
 #ifdef USE_SSL
-	r = init_ssl_system(conn);
+	r = pgtls_init(conn);
 #endif
 
 	return r;
 }
 
 /*
- *	Destroy global context
- */
-void
-pqsecure_destroy(void)
-{
-#ifdef USE_SSL
-	destroySSL();
-#endif
-}
-
-/*
  *	Begin or continue negotiating a secure session.
  */
 PostgresPollingStatusType
 pqsecure_open_client(PGconn *conn)
 {
 #ifdef USE_SSL
-	/* First time through? */
-	if (conn->ssl == NULL)
-	{
-#ifdef ENABLE_THREAD_SAFETY
-		int			rc;
-#endif
-
-		/* We cannot use MSG_NOSIGNAL to block SIGPIPE when using SSL */
-		conn->sigpipe_flag = false;
-
-#ifdef ENABLE_THREAD_SAFETY
-		if ((rc = pthread_mutex_lock(&ssl_config_mutex)))
-		{
-			printfPQExpBuffer(&conn->errorMessage,
-			   libpq_gettext("could not acquire mutex: %s\n"), strerror(rc));
-			return PGRES_POLLING_FAILED;
-		}
-#endif
-		/* 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);
-#ifdef ENABLE_THREAD_SAFETY
-			pthread_mutex_unlock(&ssl_config_mutex);
-#endif
-			close_SSL(conn);
-
-			return PGRES_POLLING_FAILED;
-		}
-#ifdef ENABLE_THREAD_SAFETY
-		pthread_mutex_unlock(&ssl_config_mutex);
-#endif
-
-		/*
-		 * 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);
+	return pgtls_open_client(conn);
 #else
 	/* shouldn't get here */
 	return PGRES_POLLING_FAILED;
@@ -319,8 +189,8 @@ void
 pqsecure_close(PGconn *conn)
 {
 #ifdef USE_SSL
-	if (conn->ssl)
-		close_SSL(conn);
+	if (conn->ssl_in_use)
+		pgtls_close(conn);
 #endif
 }
 
@@ -335,149 +205,63 @@ 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)
+	if (conn->ssl_in_use)
+	{
+		n = pgtls_read(conn, ptr, len);
+	}
+	else
+#endif
 	{
-		int			err;
+		n = pqsecure_raw_read(conn, ptr, len);
+	}
 
-		DECLARE_SIGPIPE_INFO(spinfo);
+	return n;
+}
 
-		/* SSL_read can write to the socket, so we need to disable SIGPIPE */
-		DISABLE_SIGPIPE(conn, spinfo, return -1);
+ssize_t
+pqsecure_raw_read(PGconn *conn, void *ptr, size_t len)
+{
+	ssize_t		n;
+	int			result_errno = 0;
+	char		sebuf[256];
 
-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 */
-					printfPQExpBuffer(&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)
-						printfPQExpBuffer(&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
-						printfPQExpBuffer(&conn->errorMessage,
-									libpq_gettext("SSL SYSCALL error: %s\n"),
-										  SOCK_STRERROR(result_errno,
-													  sebuf, sizeof(sebuf)));
-				}
-				else
-				{
-					printfPQExpBuffer(&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();
-
-					printfPQExpBuffer(&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.
-				 */
-				printfPQExpBuffer(&conn->errorMessage,
-								  libpq_gettext("SSL connection has been closed unexpectedly\n"));
-				result_errno = ECONNRESET;
-				n = -1;
-				break;
-			default:
-				printfPQExpBuffer(&conn->errorMessage,
-						  libpq_gettext("unrecognized SSL error code: %d\n"),
-								  err);
-				/* assume the connection is broken */
-				result_errno = ECONNRESET;
-				n = -1;
-				break;
-		}
+	n = recv(conn->sock, ptr, len, 0);
 
-		RESTORE_SIGPIPE(conn, spinfo);
-	}
-	else
-#endif   /* USE_SSL */
+	if (n < 0)
 	{
-		n = recv(conn->sock, ptr, len, 0);
+		result_errno = SOCK_ERRNO;
 
-		if (n < 0)
+		/* Set error message if appropriate */
+		switch (result_errno)
 		{
-			result_errno = SOCK_ERRNO;
-
-			/* Set error message if appropriate */
-			switch (result_errno)
-			{
 #ifdef EAGAIN
-				case EAGAIN:
+			case EAGAIN:
 #endif
 #if defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN))
-				case EWOULDBLOCK:
+			case EWOULDBLOCK:
 #endif
-				case EINTR:
-					/* no error message, caller is expected to retry */
-					break;
+			case EINTR:
+				/* no error message, caller is expected to retry */
+				break;
 
 #ifdef ECONNRESET
-				case ECONNRESET:
-					printfPQExpBuffer(&conn->errorMessage,
-									  libpq_gettext(
+			case ECONNRESET:
+				printfPQExpBuffer(&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"));
-					break;
+				break;
 #endif
 
-				default:
-					printfPQExpBuffer(&conn->errorMessage,
+			default:
+				printfPQExpBuffer(&conn->errorMessage,
 					libpq_gettext("could not receive data from server: %s\n"),
-									  SOCK_STRERROR(result_errno,
-													sebuf, sizeof(sebuf)));
-					break;
-			}
+								  SOCK_STRERROR(result_errno,
+												sebuf, sizeof(sebuf)));
+				break;
 		}
 	}
 
@@ -498,175 +282,98 @@ ssize_t
 pqsecure_write(PGconn *conn, const void *ptr, size_t len)
 {
 	ssize_t		n;
-	int			result_errno = 0;
-	char		sebuf[256];
 
 	DECLARE_SIGPIPE_INFO(spinfo);
 
 #ifdef USE_SSL
-	if (conn->ssl)
+	if (conn->ssl_in_use)
 	{
-		int			err;
-
 		DISABLE_SIGPIPE(conn, spinfo, return -1);
 
-		SOCK_ERRNO_SET(0);
-		n = SSL_write(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 */
-					printfPQExpBuffer(&conn->errorMessage,
-									  "SSL_write failed but did not provide error information\n");
-					/* assume the connection is broken */
-					result_errno = ECONNRESET;
-				}
-				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:
-				if (n < 0)
-				{
-					result_errno = SOCK_ERRNO;
-					REMEMBER_EPIPE(spinfo, result_errno == EPIPE);
-					if (result_errno == EPIPE ||
-						result_errno == ECONNRESET)
-						printfPQExpBuffer(&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
-						printfPQExpBuffer(&conn->errorMessage,
-									libpq_gettext("SSL SYSCALL error: %s\n"),
-										  SOCK_STRERROR(result_errno,
-													  sebuf, sizeof(sebuf)));
-				}
-				else
-				{
-					printfPQExpBuffer(&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();
-
-					printfPQExpBuffer(&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.
-				 */
-				printfPQExpBuffer(&conn->errorMessage,
-								  libpq_gettext("SSL connection has been closed unexpectedly\n"));
-				result_errno = ECONNRESET;
-				n = -1;
-				break;
-			default:
-				printfPQExpBuffer(&conn->errorMessage,
-						  libpq_gettext("unrecognized SSL error code: %d\n"),
-								  err);
-				/* assume the connection is broken */
-				result_errno = ECONNRESET;
-				n = -1;
-				break;
-		}
+		n = pgtls_write(conn, ptr, len);
 	}
-	else
-#endif   /* USE_SSL */
+	else 
+#endif   /* USE_OPENSSL */
 	{
-		int			flags = 0;
+		n = pqsecure_raw_write(conn, ptr, len);
+	}
+
+	return n;
+}
+
+ssize_t
+pqsecure_raw_write(PGconn *conn, const void *ptr, size_t len)
+{
+	ssize_t		n;
+	int			flags = 0;
+	int			result_errno = 0;
+	char		sebuf[256];
+
+	DECLARE_SIGPIPE_INFO(spinfo);
 
 #ifdef MSG_NOSIGNAL
-		if (conn->sigpipe_flag)
-			flags |= MSG_NOSIGNAL;
+	if (conn->sigpipe_flag)
+		flags |= MSG_NOSIGNAL;
 
 retry_masked:
 #endif   /* MSG_NOSIGNAL */
 
-		DISABLE_SIGPIPE(conn, spinfo, return -1);
+	DISABLE_SIGPIPE(conn, spinfo, return -1);
 
-		n = send(conn->sock, ptr, len, flags);
+	n = send(conn->sock, ptr, len, flags);
 
-		if (n < 0)
-		{
-			result_errno = SOCK_ERRNO;
+	if (n < 0)
+	{
+		result_errno = SOCK_ERRNO;
 
-			/*
-			 * If we see an EINVAL, it may be because MSG_NOSIGNAL isn't
-			 * available on this machine.  So, clear sigpipe_flag so we don't
-			 * try the flag again, and retry the send().
-			 */
+		/*
+		 * If we see an EINVAL, it may be because MSG_NOSIGNAL isn't
+		 * available on this machine.  So, clear sigpipe_flag so we don't
+		 * try the flag again, and retry the send().
+		 */
 #ifdef MSG_NOSIGNAL
-			if (flags != 0 && result_errno == EINVAL)
-			{
-				conn->sigpipe_flag = false;
-				flags = 0;
-				goto retry_masked;
-			}
+		if (flags != 0 && result_errno == EINVAL)
+		{
+			conn->sigpipe_flag = false;
+			flags = 0;
+			goto retry_masked;
+		}
 #endif   /* MSG_NOSIGNAL */
 
-			/* Set error message if appropriate */
-			switch (result_errno)
-			{
+		/* Set error message if appropriate */
+		switch (result_errno)
+		{
 #ifdef EAGAIN
-				case EAGAIN:
+			case EAGAIN:
 #endif
 #if defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN))
-				case EWOULDBLOCK:
+			case EWOULDBLOCK:
 #endif
-				case EINTR:
-					/* no error message, caller is expected to retry */
-					break;
+			case EINTR:
+				/* no error message, caller is expected to retry */
+				break;
 
-				case EPIPE:
-					/* Set flag for EPIPE */
-					REMEMBER_EPIPE(spinfo, true);
-					/* FALL THRU */
+			case EPIPE:
+				/* Set flag for EPIPE */
+				REMEMBER_EPIPE(spinfo, true);
+				/* FALL THRU */
 
 #ifdef ECONNRESET
-				case ECONNRESET:
+			case ECONNRESET:
 #endif
-					printfPQExpBuffer(&conn->errorMessage,
-									  libpq_gettext(
+				printfPQExpBuffer(&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"));
-					break;
+				break;
 
-				default:
-					printfPQExpBuffer(&conn->errorMessage,
+			default:
+				printfPQExpBuffer(&conn->errorMessage,
 						libpq_gettext("could not send data to server: %s\n"),
 									  SOCK_STRERROR(result_errno,
 													sebuf, sizeof(sebuf)));
-					break;
-			}
+				break;
 		}
 	}
 
@@ -678,981 +385,7 @@ retry_masked:
 	return n;
 }
 
-/* ------------------------------------------------------------ */
-/*						  SSL specific code						*/
-/* ------------------------------------------------------------ */
-#ifdef USE_SSL
-
-/*
- *	Certificate verification callback
- *
- *	This callback allows us to log intermediate problems during
- *	verification, but there doesn't seem to be a clean way to get
- *	our PGconn * structure.  So we can't log anything!
- *
- *	This callback also allows us to override the default acceptance
- *	criteria (e.g., accepting self-signed or expired certs), but
- *	for now we accept the default checks.
- */
-static int
-verify_cb(int ok, X509_STORE_CTX *ctx)
-{
-	return ok;
-}
-
-
-/*
- * Check if a wildcard certificate matches the server hostname.
- *
- * The rule for this is:
- *	1. We only match the '*' character as wildcard
- *	2. We match only wildcards at the start of the string
- *	3. The '*' character does *not* match '.', meaning that we match only
- *	   a single pathname component.
- *	4. We don't support more than one '*' in a single pattern.
- *
- * This is roughly in line with RFC2818, but contrary to what most browsers
- * appear to be implementing (point 3 being the difference)
- *
- * Matching is always case-insensitive, since DNS is case insensitive.
- */
-static int
-wildcard_certificate_match(const char *pattern, const char *string)
-{
-	int			lenpat = strlen(pattern);
-	int			lenstr = strlen(string);
-
-	/* If we don't start with a wildcard, it's not a match (rule 1 & 2) */
-	if (lenpat < 3 ||
-		pattern[0] != '*' ||
-		pattern[1] != '.')
-		return 0;
-
-	if (lenpat > lenstr)
-		/* If pattern is longer than the string, we can never match */
-		return 0;
-
-	if (pg_strcasecmp(pattern + 1, string + lenstr - lenpat + 1) != 0)
-
-		/*
-		 * If string does not end in pattern (minus the wildcard), we don't
-		 * match
-		 */
-		return 0;
-
-	if (strchr(string, '.') < string + lenstr - lenpat)
-
-		/*
-		 * If there is a dot left of where the pattern started to match, we
-		 * don't match (rule 3)
-		 */
-		return 0;
-
-	/* String ended with pattern, and didn't have a dot before, so we match */
-	return 1;
-}
-
-
-/*
- *	Verify that common name resolves to peer.
- */
-static bool
-verify_peer_name_matches_certificate(PGconn *conn)
-{
-	char	   *peer_cn;
-	int			r;
-	int			len;
-	bool		result;
-
-	/*
-	 * If told not to verify the peer name, don't do it. Return true
-	 * indicating that the verification was successful.
-	 */
-	if (strcmp(conn->sslmode, "verify-full") != 0)
-		return true;
-
-	/*
-	 * Extract the common name from the certificate.
-	 *
-	 * XXX: Should support alternate names here
-	 */
-	/* First find out the name's length and allocate a buffer for it. */
-	len = X509_NAME_get_text_by_NID(X509_get_subject_name(conn->peer),
-									NID_commonName, NULL, 0);
-	if (len == -1)
-	{
-		printfPQExpBuffer(&conn->errorMessage,
-						  libpq_gettext("could not get server common name from server certificate\n"));
-		return false;
-	}
-	peer_cn = malloc(len + 1);
-	if (peer_cn == NULL)
-	{
-		printfPQExpBuffer(&conn->errorMessage,
-						  libpq_gettext("out of memory\n"));
-		return false;
-	}
-
-	r = X509_NAME_get_text_by_NID(X509_get_subject_name(conn->peer),
-								  NID_commonName, peer_cn, len + 1);
-	if (r != len)
-	{
-		/* Got different length than on the first call. Shouldn't happen. */
-		printfPQExpBuffer(&conn->errorMessage,
-						  libpq_gettext("could not get server common name from server certificate\n"));
-		free(peer_cn);
-		return false;
-	}
-	peer_cn[len] = '\0';
-
-	/*
-	 * Reject embedded NULLs in certificate common name to prevent attacks
-	 * like CVE-2009-4034.
-	 */
-	if (len != strlen(peer_cn))
-	{
-		printfPQExpBuffer(&conn->errorMessage,
-						  libpq_gettext("SSL certificate's common name contains embedded null\n"));
-		free(peer_cn);
-		return false;
-	}
-
-	/*
-	 * We got the peer's common name. Now compare it against the originally
-	 * given hostname.
-	 */
-	if (!(conn->pghost && conn->pghost[0] != '\0'))
-	{
-		printfPQExpBuffer(&conn->errorMessage,
-						  libpq_gettext("host name must be specified for a verified SSL connection\n"));
-		result = false;
-	}
-	else
-	{
-		if (pg_strcasecmp(peer_cn, conn->pghost) == 0)
-			/* Exact name match */
-			result = true;
-		else if (wildcard_certificate_match(peer_cn, conn->pghost))
-			/* Matched wildcard certificate */
-			result = true;
-		else
-		{
-			printfPQExpBuffer(&conn->errorMessage,
-							  libpq_gettext("server common name \"%s\" does not match host name \"%s\"\n"),
-							  peer_cn, conn->pghost);
-			result = false;
-		}
-	}
-
-	free(peer_cn);
-	return result;
-}
-
-#ifdef ENABLE_THREAD_SAFETY
-/*
- *	Callback functions for OpenSSL internal locking
- */
-
-static unsigned long
-pq_threadidcallback(void)
-{
-	/*
-	 * This is not standards-compliant.  pthread_self() returns pthread_t, and
-	 * shouldn't be cast to unsigned long, but CRYPTO_set_id_callback requires
-	 * it, so we have to do it.
-	 */
-	return (unsigned long) pthread_self();
-}
-
-static pthread_mutex_t *pq_lockarray;
-
-static void
-pq_lockingcallback(int mode, int n, const char *file, int line)
-{
-	if (mode & CRYPTO_LOCK)
-	{
-		if (pthread_mutex_lock(&pq_lockarray[n]))
-			PGTHREAD_ERROR("failed to lock mutex");
-	}
-	else
-	{
-		if (pthread_mutex_unlock(&pq_lockarray[n]))
-			PGTHREAD_ERROR("failed to unlock mutex");
-	}
-}
-#endif   /* ENABLE_THREAD_SAFETY */
-
-/*
- * Initialize SSL system, in particular creating the SSL_context object
- * that will be shared by all SSL-using connections in this process.
- *
- * In threadsafe mode, this includes setting up libcrypto callback functions
- * to do thread locking.
- *
- * If the caller has told us (through PQinitOpenSSL) that he's taking care
- * of libcrypto, we expect that callbacks are already set, and won't try to
- * override it.
- *
- * The conn parameter is only used to be able to pass back an error
- * message - no connection-local setup is made here.
- *
- * Returns 0 if OK, -1 on failure (with a message in conn->errorMessage).
- */
-static int
-init_ssl_system(PGconn *conn)
-{
-#ifdef ENABLE_THREAD_SAFETY
-#ifdef WIN32
-	/* Also see similar code in fe-connect.c, default_threadlock() */
-	if (ssl_config_mutex == NULL)
-	{
-		while (InterlockedExchange(&win32_ssl_create_mutex, 1) == 1)
-			 /* loop, another thread own the lock */ ;
-		if (ssl_config_mutex == NULL)
-		{
-			if (pthread_mutex_init(&ssl_config_mutex, NULL))
-				return -1;
-		}
-		InterlockedExchange(&win32_ssl_create_mutex, 0);
-	}
-#endif
-	if (pthread_mutex_lock(&ssl_config_mutex))
-		return -1;
-
-	if (pq_init_crypto_lib)
-	{
-		/*
-		 * If necessary, set up an array to hold locks for libcrypto.
-		 * libcrypto will tell us how big to make this array.
-		 */
-		if (pq_lockarray == NULL)
-		{
-			int			i;
-
-			pq_lockarray = malloc(sizeof(pthread_mutex_t) * CRYPTO_num_locks());
-			if (!pq_lockarray)
-			{
-				pthread_mutex_unlock(&ssl_config_mutex);
-				return -1;
-			}
-			for (i = 0; i < CRYPTO_num_locks(); i++)
-			{
-				if (pthread_mutex_init(&pq_lockarray[i], NULL))
-				{
-					free(pq_lockarray);
-					pq_lockarray = NULL;
-					pthread_mutex_unlock(&ssl_config_mutex);
-					return -1;
-				}
-			}
-		}
-
-		if (ssl_open_connections++ == 0)
-		{
-			/* These are only required for threaded libcrypto applications */
-			CRYPTO_set_id_callback(pq_threadidcallback);
-			CRYPTO_set_locking_callback(pq_lockingcallback);
-		}
-	}
-#endif   /* ENABLE_THREAD_SAFETY */
-
-	if (!SSL_context)
-	{
-		if (pq_init_ssl_lib)
-		{
-#if SSLEAY_VERSION_NUMBER >= 0x00907000L
-			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)
-		{
-			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(&ssl_config_mutex);
-#endif
-			return -1;
-		}
-
-		/* Disable old protocol versions */
-		SSL_CTX_set_options(SSL_context, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
-
-		/*
-		 * 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);
-	}
-
-#ifdef ENABLE_THREAD_SAFETY
-	pthread_mutex_unlock(&ssl_config_mutex);
-#endif
-	return 0;
-}
-
-/*
- *	This function is needed because if the libpq library is unloaded
- *	from the application, the callback functions will no longer exist when
- *	libcrypto is used by other parts of the system.  For this reason,
- *	we unregister the callback functions when the last libpq
- *	connection is closed.  (The same would apply for OpenSSL callbacks
- *	if we had any.)
- *
- *	Callbacks are only set when we're compiled in threadsafe mode, so
- *	we only need to remove them in this case.
- */
-static void
-destroy_ssl_system(void)
-{
-#ifdef ENABLE_THREAD_SAFETY
-	/* Mutex is created in initialize_ssl_system() */
-	if (pthread_mutex_lock(&ssl_config_mutex))
-		return;
-
-	if (pq_init_crypto_lib && ssl_open_connections > 0)
-		--ssl_open_connections;
-
-	if (pq_init_crypto_lib && ssl_open_connections == 0)
-	{
-		/* No connections left, unregister libcrypto callbacks */
-		CRYPTO_set_locking_callback(NULL);
-		CRYPTO_set_id_callback(NULL);
-
-		/*
-		 * We don't free the lock array or the SSL_context. If we get another
-		 * connection in this process, we will just re-use them with the
-		 * existing mutexes.
-		 *
-		 * This means we leak a little memory on repeated load/unload of the
-		 * library.
-		 */
-	}
-
-	pthread_mutex_unlock(&ssl_config_mutex);
-#endif
-}
-
-/*
- *	Initialize (potentially) per-connection SSL data, namely the
- *	client certificate, private key, and trusted CA certs.
- *
- *	conn->ssl must already be created.  It receives the connection's client
- *	certificate and private key.  Note however that certificates also get
- *	loaded into the SSL_context object, and are therefore accessible to all
- *	connections in this process.  This should be OK as long as there aren't
- *	any hash collisions among the certs.
- *
- *	Returns 0 if OK, -1 on failure (with a message in conn->errorMessage).
- */
-static int
-initialize_SSL(PGconn *conn)
-{
-	struct stat buf;
-	char		homedir[MAXPGPATH];
-	char		fnbuf[MAXPGPATH];
-	char		sebuf[256];
-	bool		have_homedir;
-	bool		have_cert;
-	EVP_PKEY   *pkey = NULL;
-
-	/*
-	 * We'll need the home directory if any of the relevant parameters are
-	 * defaulted.  If pqGetHomeDirectory fails, act as though none of the
-	 * files could be found.
-	 */
-	if (!(conn->sslcert && strlen(conn->sslcert) > 0) ||
-		!(conn->sslkey && strlen(conn->sslkey) > 0) ||
-		!(conn->sslrootcert && strlen(conn->sslrootcert) > 0) ||
-		!(conn->sslcrl && strlen(conn->sslcrl) > 0))
-		have_homedir = pqGetHomeDirectory(homedir, sizeof(homedir));
-	else	/* won't need it */
-		have_homedir = false;
-
-	/* Read the client certificate file */
-	if (conn->sslcert && strlen(conn->sslcert) > 0)
-		strncpy(fnbuf, conn->sslcert, sizeof(fnbuf));
-	else if (have_homedir)
-		snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, USER_CERT_FILE);
-	else
-		fnbuf[0] = '\0';
-
-	if (fnbuf[0] == '\0')
-	{
-		/* no home directory, proceed without a client cert */
-		have_cert = false;
-	}
-	else if (stat(fnbuf, &buf) != 0)
-	{
-		/*
-		 * If file is not present, just go on without a client cert; server
-		 * might or might not accept the connection.  Any other error,
-		 * however, is grounds for complaint.
-		 */
-		if (errno != ENOENT && errno != ENOTDIR)
-		{
-			printfPQExpBuffer(&conn->errorMessage,
-			   libpq_gettext("could not open certificate file \"%s\": %s\n"),
-							  fnbuf, pqStrerror(errno, sebuf, sizeof(sebuf)));
-			return -1;
-		}
-		have_cert = false;
-	}
-	else
-	{
-		/*
-		 * Cert file exists, so load it.  Since OpenSSL doesn't provide the
-		 * equivalent of "SSL_use_certificate_chain_file", we actually have to
-		 * load the file twice.  The first call loads any extra certs after
-		 * the first one into chain-cert storage associated with the
-		 * SSL_context.  The second call loads the first cert (only) into the
-		 * SSL object, where it will be correctly paired with the private key
-		 * we load below.  We do it this way so that each connection
-		 * understands which subject cert to present, in case different
-		 * sslcert settings are used for different connections in the same
-		 * process.
-		 *
-		 * NOTE: This function may also modify our SSL_context and therefore
-		 * we have to lock around this call and any places where we use the
-		 * SSL_context struct.
-		 */
-#ifdef ENABLE_THREAD_SAFETY
-		int			rc;
-
-		if ((rc = pthread_mutex_lock(&ssl_config_mutex)))
-		{
-			printfPQExpBuffer(&conn->errorMessage,
-			   libpq_gettext("could not acquire mutex: %s\n"), strerror(rc));
-			return -1;
-		}
-#endif
-		if (SSL_CTX_use_certificate_chain_file(SSL_context, fnbuf) != 1)
-		{
-			char	   *err = SSLerrmessage();
-
-			printfPQExpBuffer(&conn->errorMessage,
-			   libpq_gettext("could not read certificate file \"%s\": %s\n"),
-							  fnbuf, err);
-			SSLerrfree(err);
-
-#ifdef ENABLE_THREAD_SAFETY
-			pthread_mutex_unlock(&ssl_config_mutex);
-#endif
-			return -1;
-		}
-
-		if (SSL_use_certificate_file(conn->ssl, fnbuf, SSL_FILETYPE_PEM) != 1)
-		{
-			char	   *err = SSLerrmessage();
-
-			printfPQExpBuffer(&conn->errorMessage,
-			   libpq_gettext("could not read certificate file \"%s\": %s\n"),
-							  fnbuf, err);
-			SSLerrfree(err);
-#ifdef ENABLE_THREAD_SAFETY
-			pthread_mutex_unlock(&ssl_config_mutex);
-#endif
-			return -1;
-		}
-
-		/* need to load the associated private key, too */
-		have_cert = true;
-
-#ifdef ENABLE_THREAD_SAFETY
-		pthread_mutex_unlock(&ssl_config_mutex);
-#endif
-	}
-
-	/*
-	 * Read the SSL key. If a key is specified, treat it as an engine:key
-	 * combination if there is colon present - we don't support files with
-	 * colon in the name. The exception is if the second character is a colon,
-	 * in which case it can be a Windows filename with drive specification.
-	 */
-	if (have_cert && conn->sslkey && strlen(conn->sslkey) > 0)
-	{
-#ifdef USE_SSL_ENGINE
-		if (strchr(conn->sslkey, ':')
-#ifdef WIN32
-			&& conn->sslkey[1] != ':'
-#endif
-			)
-		{
-			/* Colon, but not in second character, treat as engine:key */
-			char	   *engine_str = strdup(conn->sslkey);
-			char	   *engine_colon;
-
-			if (engine_str == NULL)
-			{
-				printfPQExpBuffer(&conn->errorMessage,
-								  libpq_gettext("out of memory\n"));
-				return -1;
-			}
-
-			/* cannot return NULL because we already checked before strdup */
-			engine_colon = strchr(engine_str, ':');
-
-			*engine_colon = '\0';		/* engine_str now has engine name */
-			engine_colon++;		/* engine_colon now has key name */
-
-			conn->engine = ENGINE_by_id(engine_str);
-			if (conn->engine == NULL)
-			{
-				char	   *err = SSLerrmessage();
-
-				printfPQExpBuffer(&conn->errorMessage,
-					 libpq_gettext("could not load SSL engine \"%s\": %s\n"),
-								  engine_str, err);
-				SSLerrfree(err);
-				free(engine_str);
-				return -1;
-			}
-
-			if (ENGINE_init(conn->engine) == 0)
-			{
-				char	   *err = SSLerrmessage();
-
-				printfPQExpBuffer(&conn->errorMessage,
-				libpq_gettext("could not initialize SSL engine \"%s\": %s\n"),
-								  engine_str, err);
-				SSLerrfree(err);
-				ENGINE_free(conn->engine);
-				conn->engine = NULL;
-				free(engine_str);
-				return -1;
-			}
-
-			pkey = ENGINE_load_private_key(conn->engine, engine_colon,
-										   NULL, NULL);
-			if (pkey == NULL)
-			{
-				char	   *err = SSLerrmessage();
-
-				printfPQExpBuffer(&conn->errorMessage,
-								  libpq_gettext("could not read private SSL key \"%s\" from engine \"%s\": %s\n"),
-								  engine_colon, engine_str, err);
-				SSLerrfree(err);
-				ENGINE_finish(conn->engine);
-				ENGINE_free(conn->engine);
-				conn->engine = NULL;
-				free(engine_str);
-				return -1;
-			}
-			if (SSL_use_PrivateKey(conn->ssl, pkey) != 1)
-			{
-				char	   *err = SSLerrmessage();
-
-				printfPQExpBuffer(&conn->errorMessage,
-								  libpq_gettext("could not load private SSL key \"%s\" from engine \"%s\": %s\n"),
-								  engine_colon, engine_str, err);
-				SSLerrfree(err);
-				ENGINE_finish(conn->engine);
-				ENGINE_free(conn->engine);
-				conn->engine = NULL;
-				free(engine_str);
-				return -1;
-			}
-
-			free(engine_str);
-
-			fnbuf[0] = '\0';	/* indicate we're not going to load from a
-								 * file */
-		}
-		else
-#endif   /* USE_SSL_ENGINE */
-		{
-			/* PGSSLKEY is not an engine, treat it as a filename */
-			strncpy(fnbuf, conn->sslkey, sizeof(fnbuf));
-		}
-	}
-	else if (have_homedir)
-	{
-		/* No PGSSLKEY specified, load default file */
-		snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, USER_KEY_FILE);
-	}
-	else
-		fnbuf[0] = '\0';
-
-	if (have_cert && fnbuf[0] != '\0')
-	{
-		/* read the client key from file */
-
-		if (stat(fnbuf, &buf) != 0)
-		{
-			printfPQExpBuffer(&conn->errorMessage,
-							  libpq_gettext("certificate present, but not private key file \"%s\"\n"),
-							  fnbuf);
-			return -1;
-		}
-#ifndef WIN32
-		if (!S_ISREG(buf.st_mode) || buf.st_mode & (S_IRWXG | S_IRWXO))
-		{
-			printfPQExpBuffer(&conn->errorMessage,
-							  libpq_gettext("private key file \"%s\" has group or world access; permissions should be u=rw (0600) or less\n"),
-							  fnbuf);
-			return -1;
-		}
-#endif
-
-		if (SSL_use_PrivateKey_file(conn->ssl, fnbuf, SSL_FILETYPE_PEM) != 1)
-		{
-			char	   *err = SSLerrmessage();
-
-			printfPQExpBuffer(&conn->errorMessage,
-			   libpq_gettext("could not load private key file \"%s\": %s\n"),
-							  fnbuf, err);
-			SSLerrfree(err);
-			return -1;
-		}
-	}
-
-	/* verify that the cert and key go together */
-	if (have_cert &&
-		SSL_check_private_key(conn->ssl) != 1)
-	{
-		char	   *err = SSLerrmessage();
-
-		printfPQExpBuffer(&conn->errorMessage,
-						  libpq_gettext("certificate does not match private key file \"%s\": %s\n"),
-						  fnbuf, err);
-		SSLerrfree(err);
-		return -1;
-	}
-
-	/*
-	 * If the root cert file exists, load it so we can perform certificate
-	 * verification. If sslmode is "verify-full" we will also do further
-	 * verification after the connection has been completed.
-	 */
-	if (conn->sslrootcert && strlen(conn->sslrootcert) > 0)
-		strncpy(fnbuf, conn->sslrootcert, sizeof(fnbuf));
-	else if (have_homedir)
-		snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, ROOT_CERT_FILE);
-	else
-		fnbuf[0] = '\0';
-
-	if (fnbuf[0] != '\0' &&
-		stat(fnbuf, &buf) == 0)
-	{
-		X509_STORE *cvstore;
-
-#ifdef ENABLE_THREAD_SAFETY
-		int			rc;
-
-		if ((rc = pthread_mutex_lock(&ssl_config_mutex)))
-		{
-			printfPQExpBuffer(&conn->errorMessage,
-			   libpq_gettext("could not acquire mutex: %s\n"), strerror(rc));
-			return -1;
-		}
-#endif
-		if (SSL_CTX_load_verify_locations(SSL_context, fnbuf, NULL) != 1)
-		{
-			char	   *err = SSLerrmessage();
-
-			printfPQExpBuffer(&conn->errorMessage,
-							  libpq_gettext("could not read root certificate file \"%s\": %s\n"),
-							  fnbuf, err);
-			SSLerrfree(err);
-#ifdef ENABLE_THREAD_SAFETY
-			pthread_mutex_unlock(&ssl_config_mutex);
-#endif
-			return -1;
-		}
-
-		if ((cvstore = SSL_CTX_get_cert_store(SSL_context)) != NULL)
-		{
-			if (conn->sslcrl && strlen(conn->sslcrl) > 0)
-				strncpy(fnbuf, conn->sslcrl, sizeof(fnbuf));
-			else if (have_homedir)
-				snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, ROOT_CRL_FILE);
-			else
-				fnbuf[0] = '\0';
-
-			/* Set the flags to check against the complete CRL chain */
-			if (fnbuf[0] != '\0' &&
-				X509_STORE_load_locations(cvstore, fnbuf, 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
-				char	   *err = SSLerrmessage();
-
-				printfPQExpBuffer(&conn->errorMessage,
-								  libpq_gettext("SSL library does not support CRL certificates (file \"%s\")\n"),
-								  fnbuf);
-				SSLerrfree(err);
-#ifdef ENABLE_THREAD_SAFETY
-				pthread_mutex_unlock(&ssl_config_mutex);
-#endif
-				return -1;
-#endif
-			}
-			/* if not found, silently ignore;  we do not require CRL */
-		}
-#ifdef ENABLE_THREAD_SAFETY
-		pthread_mutex_unlock(&ssl_config_mutex);
-#endif
-
-		SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, verify_cb);
-	}
-	else
-	{
-		/*
-		 * stat() failed; assume root file doesn't exist.  If sslmode is
-		 * verify-ca or verify-full, this is an error.  Otherwise, continue
-		 * without performing any server cert verification.
-		 */
-		if (conn->sslmode[0] == 'v')	/* "verify-ca" or "verify-full" */
-		{
-			/*
-			 * The only way to reach here with an empty filename is if
-			 * pqGetHomeDirectory failed.  That's a sufficiently unusual case
-			 * that it seems worth having a specialized error message for it.
-			 */
-			if (fnbuf[0] == '\0')
-				printfPQExpBuffer(&conn->errorMessage,
-								  libpq_gettext("could not get home directory to locate root certificate file\n"
-												"Either provide the file or change sslmode to disable server certificate verification.\n"));
-			else
-				printfPQExpBuffer(&conn->errorMessage,
-				libpq_gettext("root certificate file \"%s\" does not exist\n"
-							  "Either provide the file or change sslmode to disable server certificate verification.\n"), fnbuf);
-			return -1;
-		}
-	}
-
-	/*
-	 * If the OpenSSL version used supports it (from 1.0.0 on) and the user
-	 * requested it, disable SSL compression.
-	 */
-#ifdef SSL_OP_NO_COMPRESSION
-	if (conn->sslcompression && conn->sslcompression[0] == '0')
-	{
-		SSL_set_options(conn->ssl, SSL_OP_NO_COMPRESSION);
-	}
-#endif
-
-	return 0;
-}
-
-static void
-destroySSL(void)
-{
-	destroy_ssl_system();
-}
-
-/*
- *	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:
-				{
-					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;
-		}
-	}
-
-	/*
-	 * We already checked the server certificate in initialize_SSL() using
-	 * SSL_CTX_set_verify(), if root.crt exists.
-	 */
-
-	/* get server certificate */
-	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;
-	}
-
-	if (!verify_peer_name_matches_certificate(conn))
-	{
-		close_SSL(conn);
-		return PGRES_POLLING_FAILED;
-	}
-
-	/* SSL handshake is complete */
-	return PGRES_POLLING_OK;
-}
-
-/*
- *	Close SSL connection.
- */
-static void
-close_SSL(PGconn *conn)
-{
-	bool		destroy_needed = false;
-
-	if (conn->ssl)
-	{
-		DECLARE_SIGPIPE_INFO(spinfo);
-
-		/*
-		 * We can't destroy everything SSL-related here due to the possible
-		 * later calls to OpenSSL routines which may need our thread
-		 * callbacks, so set a flag here and check at the end.
-		 */
-		destroy_needed = true;
-
-		DISABLE_SIGPIPE(conn, spinfo, (void) 0);
-		SSL_shutdown(conn->ssl);
-		SSL_free(conn->ssl);
-		conn->ssl = NULL;
-		/* We have to assume we got EPIPE */
-		REMEMBER_EPIPE(spinfo, true);
-		RESTORE_SIGPIPE(conn, spinfo);
-	}
-
-	if (conn->peer)
-	{
-		X509_free(conn->peer);
-		conn->peer = NULL;
-	}
-
-#ifdef USE_SSL_ENGINE
-	if (conn->engine)
-	{
-		ENGINE_finish(conn->engine);
-		ENGINE_free(conn->engine);
-		conn->engine = NULL;
-	}
-#endif
-
-	/*
-	 * This will remove our SSL locking hooks, if this is the last SSL
-	 * connection, which means we must wait to call it until after all SSL
-	 * calls have been made, otherwise we can end up with a race condition and
-	 * possible deadlocks.
-	 *
-	 * See comments above destroy_ssl_system().
-	 */
-	if (destroy_needed)
-		pqsecure_destroy();
-}
-
-/*
- * Obtain reason string for last SSL error
- *
- * Some caution is needed here since ERR_reason_error_string will
- * return NULL if it doesn't recognize the error code.  We don't
- * want to return NULL ever.
- */
-static char ssl_nomem[] = "out of memory allocating error description";
-
-#define SSL_ERR_LEN 128
-
-static char *
-SSLerrmessage(void)
-{
-	unsigned long errcode;
-	const char *errreason;
-	char	   *errbuf;
-
-	errbuf = malloc(SSL_ERR_LEN);
-	if (!errbuf)
-		return ssl_nomem;
-	errcode = ERR_get_error();
-	if (errcode == 0)
-	{
-		snprintf(errbuf, SSL_ERR_LEN, libpq_gettext("no SSL error reported"));
-		return errbuf;
-	}
-	errreason = ERR_reason_error_string(errcode);
-	if (errreason != NULL)
-	{
-		strlcpy(errbuf, errreason, SSL_ERR_LEN);
-		return errbuf;
-	}
-	snprintf(errbuf, SSL_ERR_LEN, libpq_gettext("SSL error code %lu"), errcode);
-	return errbuf;
-}
-
-static void
-SSLerrfree(char *buf)
-{
-	if (buf != ssl_nomem)
-		free(buf);
-}
-
-/*
- *	Return pointer to OpenSSL object.
- */
-void *
-PQgetssl(PGconn *conn)
-{
-	if (!conn)
-		return NULL;
-	return conn->ssl;
-}
-#else							/* !USE_SSL */
-
+#ifndef USE_SSL
 void *
 PQgetssl(PGconn *conn)
 {
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index 4aeb4fa..6032904 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -73,14 +73,14 @@ typedef struct
 #endif
 #endif   /* ENABLE_SSPI */
 
-#ifdef USE_SSL
+#ifdef USE_OPENSSL
 #include <openssl/ssl.h>
 #include <openssl/err.h>
 
 #if (SSLEAY_VERSION_NUMBER >= 0x00907000L) && !defined(OPENSSL_NO_ENGINE)
 #define USE_SSL_ENGINE
 #endif
-#endif   /* USE_SSL */
+#endif   /* USE_OPENSSL */
 
 /*
  * POSTGRES backend dependent Constants.
@@ -427,6 +427,8 @@ struct pg_conn
 	bool		allow_ssl_try;	/* Allowed to try SSL negotiation */
 	bool		wait_ssl_try;	/* Delay SSL negotiation until after
 								 * attempting normal connection */
+	bool		ssl_in_use;
+#ifdef USE_OPENSSL
 	SSL		   *ssl;			/* SSL status, if have SSL connection */
 	X509	   *peer;			/* X509 cert of server */
 #ifdef USE_SSL_ENGINE
@@ -435,6 +437,7 @@ struct pg_conn
 	void	   *engine;			/* dummy field to keep struct the same if
 								 * OpenSSL version changes */
 #endif
+#endif   /* USE_OPENSSL */
 #endif   /* USE_SSL */
 
 #ifdef ENABLE_GSS
@@ -482,6 +485,24 @@ struct pg_cancel
  */
 extern char *const pgresStatus[];
 
+
+#ifdef USE_SSL
+
+#ifndef WIN32
+#define USER_CERT_FILE		".postgresql/postgresql.crt"
+#define USER_KEY_FILE		".postgresql/postgresql.key"
+#define ROOT_CERT_FILE		".postgresql/root.crt"
+#define ROOT_CRL_FILE		".postgresql/root.crl"
+#else
+/* On Windows, the "home" directory is already PostgreSQL-specific */
+#define USER_CERT_FILE		"postgresql.crt"
+#define USER_KEY_FILE		"postgresql.key"
+#define ROOT_CERT_FILE		"root.crt"
+#define ROOT_CRL_FILE		"root.crl"
+#endif
+
+#endif   /* USE_SSL */
+
 /* ----------------
  * Internal functions of libpq
  * Functions declared here need to be visible across files of libpq,
@@ -603,6 +624,8 @@ extern PostgresPollingStatusType pqsecure_open_client(PGconn *);
 extern void pqsecure_close(PGconn *);
 extern ssize_t pqsecure_read(PGconn *, void *ptr, size_t len);
 extern ssize_t pqsecure_write(PGconn *, const void *ptr, size_t len);
+extern ssize_t pqsecure_raw_read(PGconn *, void *ptr, size_t len);
+extern ssize_t pqsecure_raw_write(PGconn *, const void *ptr, size_t len);
 
 #if defined(ENABLE_THREAD_SAFETY) && !defined(WIN32)
 extern int	pq_block_sigpipe(sigset_t *osigset, bool *sigpipe_pending);
@@ -611,6 +634,16 @@ extern void pq_reset_sigpipe(sigset_t *osigset, bool sigpipe_pending,
 #endif
 
 /*
+ * The SSL implementatation provides these functions (fe-secure-openssl.c)
+ */
+extern void pgtls_init_library(bool do_ssl, int do_crypto);
+extern int pgtls_init(PGconn *conn);
+extern PostgresPollingStatusType pgtls_open_client(PGconn *conn);
+extern void pgtls_close(PGconn *conn);
+extern ssize_t pgtls_read(PGconn *conn, void *ptr, size_t len);
+extern ssize_t pgtls_write(PGconn *conn, const void *ptr, size_t len);
+
+/*
  * this is so that we can check if a connection is non-blocking internally
  * without the overhead of a function call
  */
diff --git a/src/interfaces/libpq/win32.mak b/src/interfaces/libpq/win32.mak
index 23e09e9..79b6d49 100644
--- a/src/interfaces/libpq/win32.mak
+++ b/src/interfaces/libpq/win32.mak
@@ -2,7 +2,7 @@
 
 # Will build a static library libpq(d).lib
 #        and a dynamic library libpq(d).dll with import library libpq(d)dll.lib
-# USE_SSL=1 will compile with OpenSSL
+# USE_OPENSSL=1 will compile with OpenSSL
 # USE_KFW=1 will compile with kfw(kerberos for Windows)
 # DEBUG=1 compiles with debugging symbols
 # ENABLE_THREAD_SAFETY=1 compiles with threading enabled
@@ -124,6 +124,9 @@ CLEAN :
 	-@erase "$(OUTDIR)\$(OUTFILENAME).dll.manifest"
 	-@erase "$(OUTDIR)\*.idb"
 	-@erase pg_config_paths.h"
+!IFDEF USE_OPENSSL
+	-@erase "$(INTDIR)\fe-secure-openssl.obj"
+!ENDIF
 
 
 LIB32=link.exe -lib
@@ -164,6 +167,9 @@ LIB32_OBJS= \
 	"$(INTDIR)\win32error.obj" \
 	"$(INTDIR)\win32setlocale.obj" \
 	"$(INTDIR)\pthread-win32.obj"
+!IFDEF USE_OPENSSL
+	LIB32_OBJS=$(LIB32_OBJS) "$(INTDIR)\fe-secure-openssl.obj"
+!ENDIF
 
 
 config: ..\..\include\pg_config.h ..\..\include\pg_config_ext.h pg_config_paths.h  ..\..\include\pg_config_os.h
@@ -189,8 +195,8 @@ CPP_PROJ=/nologo /W3 /EHsc $(OPT) /I "..\..\include" /I "..\..\include\port\win3
  /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /c  \
  /D "_CRT_SECURE_NO_DEPRECATE" $(ADD_DEFINES)
 
-!IFDEF USE_SSL
-CPP_PROJ=$(CPP_PROJ) /D USE_SSL
+!IFDEF USE_OPENSSL
+CPP_PROJ=$(CPP_PROJ) /D USE_OPENSSL
 SSL_LIBS=ssleay32.lib libeay32.lib gdi32.lib
 !ENDIF
 
diff --git a/src/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pm
index 260f630..af231ba 100644
--- a/src/tools/msvc/Mkvcbuild.pm
+++ b/src/tools/msvc/Mkvcbuild.pm
@@ -118,6 +118,12 @@ sub mkvcbuild
 	$postgres->AddLibrary('secur32.lib');
 	$postgres->AddLibrary('wldap32.lib') if ($solution->{options}->{ldap});
 	$postgres->FullExportDLL('postgres.lib');
+	# The OBJS scraper doesn't know about ifdefs, so remove fe-secure-openssl.c
+	# if building without OpenSSL
+	if (!$solution->{options}->{openssl})
+	{
+		$postgres->RemoveFile('src\backend\libpq\be-secure-openssl.c');
+	}
 
 	my $snowball = $solution->AddProject('dict_snowball', 'dll', '',
 		'src\backend\snowball');
@@ -278,6 +284,12 @@ sub mkvcbuild
 	$libpq->ReplaceFile('src\interfaces\libpq\libpqrc.c',
 		'src\interfaces\libpq\libpq.rc');
 	$libpq->AddReference($libpgport);
+	# The OBJS scraper doesn't know about ifdefs, so remove fe-secure-openssl.c
+	# if building without OpenSSL
+	if (!$solution->{options}->{openssl})
+	{
+		$libpq->RemoveFile('src\interfaces\libpq\fe-secure-openssl.c');
+	}
 
 	my $libpqwalreceiver =
 	  $solution->AddProject('libpqwalreceiver', 'dll', '',
diff --git a/src/tools/msvc/Solution.pm b/src/tools/msvc/Solution.pm
index c0d7f38..f66d1f6 100644
--- a/src/tools/msvc/Solution.pm
+++ b/src/tools/msvc/Solution.pm
@@ -180,7 +180,7 @@ s{PG_VERSION_STR "[^"]+"}{__STRINGIFY(x) #x\n#define __STRINGIFY2(z) __STRINGIFY
 		  if ($self->{options}->{integer_datetimes});
 		print O "#define USE_LDAP 1\n"   if ($self->{options}->{ldap});
 		print O "#define HAVE_LIBZ 1\n"  if ($self->{options}->{zlib});
-		print O "#define USE_SSL 1\n"    if ($self->{options}->{openssl});
+		print O "#define USE_OPENSSL 1\n" if ($self->{options}->{openssl});
 		print O "#define ENABLE_NLS 1\n" if ($self->{options}->{nls});
 
 		print O "#define BLCKSZ ", 1024 * $self->{options}->{blocksize}, "\n";
@@ -624,7 +624,7 @@ sub GetFakeConfigure
 	$cfg .= ' --enable-nls' if ($self->{options}->{nls});
 	$cfg .= ' --with-ldap'  if ($self->{options}->{ldap});
 	$cfg .= ' --without-zlib' unless ($self->{options}->{zlib});
-	$cfg .= ' --with-openssl'   if ($self->{options}->{ssl});
+	$cfg .= ' --with-openssl'   if ($self->{options}->{openssl});
 	$cfg .= ' --with-ossp-uuid' if ($self->{options}->{uuid});
 	$cfg .= ' --with-libxml'    if ($self->{options}->{xml});
 	$cfg .= ' --with-libxslt'   if ($self->{options}->{xslt});
diff --git a/src/tools/msvc/config_default.pl b/src/tools/msvc/config_default.pl
index ebb47ab..7c04de0 100644
--- a/src/tools/msvc/config_default.pl
+++ b/src/tools/msvc/config_default.pl
@@ -15,7 +15,7 @@ our $config = {
 	tcl     => undef,    # --with-tls=<path>
 	perl    => undef,    # --with-perl
 	python  => undef,    # --with-python=<path>
-	openssl => undef,    # --with-ssl=<path>
+	openssl => undef,    # --with-openssl=<path>
 	uuid    => undef,    # --with-ossp-uuid
 	xml     => undef,    # --with-libxml=<path>
 	xslt    => undef,    # --with-libxslt=<path>
-- 
2.0.0

#20Jeff Janes
jeff.janes@gmail.com
In reply to: Heikki Linnakangas (#19)

On Wed, Jun 11, 2014 at 7:51 AM, Heikki Linnakangas <hlinnakangas@vmware.com>
wrote:

I did again the refactoring you did back in 2006, patch attached. One

thing

I did differently: I moved the raw, non-encrypted, read/write functions to
separate functions: pqsecure_raw_read and pqsecure_raw_write. Those
functions encapsulate the SIGPIPE handling. The OpenSSL code implements a
custom BIO, which calls to pqsecure_raw_read/write to do the low-level

I/O.

Similarly in the server-side, there are be_tls_raw_read and

pg_tls_raw_write

functions, which do the prepare_for_client_read()/client_read_ended()

dance,

so that the SSL implementation doesn't need to know about that.

I've tried your 0001 patch, reflecting this refactoring, on Linux and it
caused 'make check' to hang at 'starting postmaster'.

The hang seems to be in:

/tmp_check/install//home/jjanes/pgsql/test_ssl/bin/psql -X postgres

with a backtrace of:

#0 0x0000003550edf2f8 in __poll (fds=0x7fff610cbd50, nfds=1, timeout=-1)
at ../sysdeps/unix/sysv/linux/poll.c:83
#1 0x00007fcf372035b1 in pqSocketPoll (conn=0x2317770, forRead=1,
forWrite=0, end_time=-1) at fe-misc.c:1122
#2 pqSocketCheck (conn=0x2317770, forRead=1, forWrite=0, end_time=-1) at
fe-misc.c:1064
#3 0x00007fcf37203630 in pqWaitTimed (forRead=<value optimized out>,
forWrite=<value optimized out>, conn=0x2317770, finish_time=<value
optimized out>)
at fe-misc.c:996
#4 0x00007fcf371fe632 in connectDBComplete (conn=0x2317770) at
fe-connect.c:1498
#5 0x00007fcf371ff27f in PQconnectdbParams (keywords=<value optimized
out>, values=<value optimized out>, expand_dbname=<value optimized out>)
at fe-connect.c:462
#6 0x0000000000411bb5 in main (argc=<value optimized out>,
argv=0x7fff610cc038) at startup.c:219

The make check never times out, like it usually does when something gets
stalled.

That was on CentOS 6.5 patched up to date, but OpenSuSE 13.1 gives the same
hang.

Cheers,

Jeff

#21Andreas Karlsson
andreas@proxel.se
In reply to: Jeff Janes (#20)
1 attachment(s)
Re: Supporting Windows SChannel as OpenSSL replacement

On 06/24/2014 03:20 AM, Jeff Janes wrote:

I've tried your 0001 patch, reflecting this refactoring, on Linux and it
caused 'make check' to hang at 'starting postmaster'.

I found the bug in the code, and I have attached the a patch which you
can apply on top of the patch. The regression tests pass now on my
Debian machine.

One thing I noticed when trying to find the bug is that be-secure.c
still includes some OpenSSL headers. Those should be removed since they
have already been moved to be-secure-openssl.c.

--
Andreas Karlsson

Attachments:

0001-Invent-a-new-internal-API-for-interfacing-with-SSL-fix.patchtext/x-patch; name=0001-Invent-a-new-internal-API-for-interfacing-with-SSL-fix.patchDownload
diff --git a/src/backend/libpq/be-secure.c b/src/backend/libpq/be-secure.c
index 6d943cf..11f67c4 100644
--- a/src/backend/libpq/be-secure.c
+++ b/src/backend/libpq/be-secure.c
@@ -280,7 +280,7 @@ secure_write(Port *port, void *ptr, size_t len)
 	}
 	else
 #endif
-		n = secure_raw_read(port, ptr, len);
+		n = secure_raw_write(port, ptr, len);
 
 	return n;
 }
#22Jeff Janes
jeff.janes@gmail.com
In reply to: Andreas Karlsson (#21)
Re: Supporting Windows SChannel as OpenSSL replacement

On Thu, Jun 26, 2014 at 4:26 PM, Andreas Karlsson <andreas@proxel.se> wrote:

On 06/24/2014 03:20 AM, Jeff Janes wrote:

I've tried your 0001 patch, reflecting this refactoring, on Linux and it
caused 'make check' to hang at 'starting postmaster'.

I found the bug in the code, and I have attached the a patch which you can
apply on top of the patch. The regression tests pass now on my Debian
machine.

Your fix works for me as well. Thanks.

Is there some recipe for testing the 0002 patch? Can it be tested on an
MinGW environment, or does it need to use the MicroSoft supplied compilers?

Thanks,

Jeff

#23Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Heikki Linnakangas (#19)
Re: Supporting Windows SChannel as OpenSSL replacement

Heikki Linnakangas wrote:

I did again the refactoring you did back in 2006, patch attached.
One thing I did differently: I moved the raw, non-encrypted,
read/write functions to separate functions: pqsecure_raw_read and
pqsecure_raw_write. Those functions encapsulate the SIGPIPE
handling. The OpenSSL code implements a custom BIO, which calls to
pqsecure_raw_read/write to do the low-level I/O. Similarly in the
server-side, there are be_tls_raw_read and pg_tls_raw_write
functions, which do the
prepare_for_client_read()/client_read_ended() dance, so that the SSL
implementation doesn't need to know about that.

I'm skimming over this patch (0001). There are some issues:

* You duplicated the long comment under the IDENTIFICATION tag that was
in be-secure.c; it's now both in that file and also in
be-secure-openssl.c. I think it should be removed from be-secure.c.
Also, the hardcoded DH params are duplicated in be-secure.c, but they
belong in -openssl.c only now.

* There is some mixup regarding USE_SSL and USE_OPENSSL in be-secure.c.
I think anything that's OpenSSL-specific should be in
be-secure-openssl.c only; any new SSL implementation will need to
implement all those functions. For instance, be_tls_init().
I imagine that if we select any SSL implementation, USE_SSL would get
defined, and each SSL implementation would additionally define its own
symbol. Unless the idea is to get rid of USE_OPENSSL completely, and
use only the Makefile bit to decide which implementation to use? If
so, then USE_OPENSSL as a preprocessor symbol is useless ...

* ssl_renegotiation_limit is also duplicated. But removing this one is
probably not going to be as easy as deleting a line from be-secure.c,
because guc.c depends on that one. I think that variable should be
defined in be-secure.c (i.e. delete it from -openssl) and make sure
that all SSL implementations enforce it on their own somehow.

The DISABLE_SIGPIPE thingy looks wrong in pqsecure_write. I think it
should be like this:

ssize_t
pqsecure_write(PGconn *conn, const void *ptr, size_t len)
{
ssize_t n;

#ifdef USE_SSL
if (conn->ssl_in_use)
{
DECLARE_SIGPIPE_INFO(spinfo);

DISABLE_SIGPIPE(conn, spinfo, return -1);

n = pgtls_write(conn, ptr, len);

RESTORE_SIGPIPE(spinfo);
}
else
#endif /* USE_OPENSSL */
{
n = pqsecure_raw_write(conn, ptr, len);
}

return n;
}

You are missing the restore call, and I moved the declaration inside the
ssl_in_use block since otherwise it's not useful. The other path is
fine since pqsecure_raw_write disables and restores the flag by itself.
Also, you're missing DECLARE/DISABLE/RESTORE in the ssl_in_use block in
pqsecure_read. (The original code does not have that code in the
non-SSL path. I assume, without checking, that that's okay.) I also
assume without checking that all SSL implementations would be fine with
this SIGPIPE handling.

Another thing that seems wrong is the REMEMBER_EPIPE stuff. The
fe-secure-openssl.c code should be setting the flag, but AFAICS only the
non-SSL code is doing it.

Thanks for working on this -- I'm sure many distributors will be happy
to be able to enable other, less license-broken TLS implementations.

--
�lvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#24Heikki Linnakangas
hlinnakangas@vmware.com
In reply to: Andreas Karlsson (#21)
1 attachment(s)
Re: Supporting Windows SChannel as OpenSSL replacement

On 06/27/2014 02:26 AM, Andreas Karlsson wrote:

On 06/24/2014 03:20 AM, Jeff Janes wrote:

I've tried your 0001 patch, reflecting this refactoring, on Linux and it
caused 'make check' to hang at 'starting postmaster'.

I found the bug in the code, and I have attached the a patch which you
can apply on top of the patch. The regression tests pass now on my
Debian machine.

Ah, thanks!

One thing I noticed when trying to find the bug is that be-secure.c
still includes some OpenSSL headers. Those should be removed since they
have already been moved to be-secure-openssl.c.

Removed.

Here's a new version of the patch, rebased over master and those two
things fixed.

- Heikki

Attachments:

0001-Invent-a-new-internal-API-for-interfacing-with-SSL-2.patchtext/x-diff; name=0001-Invent-a-new-internal-API-for-interfacing-with-SSL-2.patchDownload
>From 6695016cbebd822b9a8fcd6266323da7c07127fa Mon Sep 17 00:00:00 2001
From: Heikki Linnakangas <heikki.linnakangas@iki.fi>
Date: Tue, 10 Jun 2014 23:30:51 +0300
Subject: [PATCH 1/1] Invent a new internal API for interfacing with SSL.

Refactor OpenSSL specific code to separate files, implementing the new API.
---
 configure                                |    2 +-
 configure.in                             |    2 +-
 src/backend/libpq/Makefile               |    4 +
 src/backend/libpq/auth.c                 |   14 +-
 src/backend/libpq/be-secure-openssl.c    | 1045 +++++++++++++++++++++
 src/backend/libpq/be-secure.c            | 1029 +--------------------
 src/backend/libpq/hba.c                  |    2 +-
 src/backend/postmaster/fork_process.c    |    4 +-
 src/backend/utils/init/postinit.c        |    8 +-
 src/backend/utils/misc/guc.c             |    3 -
 src/bin/psql/command.c                   |    4 +-
 src/include/libpq/libpq-be.h             |   23 +-
 src/include/libpq/libpq.h                |    9 +
 src/include/pg_config.h.in               |    6 +-
 src/include/pg_config.h.win32            |    6 +-
 src/include/port.h                       |    6 +
 src/interfaces/libpq/Makefile            |    4 +
 src/interfaces/libpq/fe-connect.c        |    7 +-
 src/interfaces/libpq/fe-misc.c           |    6 +-
 src/interfaces/libpq/fe-secure-openssl.c | 1471 ++++++++++++++++++++++++++++++
 src/interfaces/libpq/fe-secure.c         | 1467 ++---------------------------
 src/interfaces/libpq/libpq-int.h         |   37 +-
 src/interfaces/libpq/win32.mak           |   12 +-
 src/tools/msvc/Mkvcbuild.pm              |   12 +
 src/tools/msvc/Solution.pm               |    4 +-
 src/tools/msvc/config_default.pl         |    2 +-
 26 files changed, 2774 insertions(+), 2415 deletions(-)
 create mode 100644 src/backend/libpq/be-secure-openssl.c
 create mode 100644 src/interfaces/libpq/fe-secure-openssl.c

diff --git a/configure b/configure
index 0b0a656..0f435b5 100755
--- a/configure
+++ b/configure
@@ -5492,7 +5492,7 @@ if test "${with_openssl+set}" = set; then :
   case $withval in
     yes)
 
-$as_echo "#define USE_SSL 1" >>confdefs.h
+$as_echo "#define USE_OPENSSL 1" >>confdefs.h
 
       ;;
     no)
diff --git a/configure.in b/configure.in
index fd9eb71..f8a4507 100644
--- a/configure.in
+++ b/configure.in
@@ -657,7 +657,7 @@ AC_MSG_RESULT([$with_bonjour])
 #
 AC_MSG_CHECKING([whether to build with OpenSSL support])
 PGAC_ARG_BOOL(with, openssl, no, [build with OpenSSL support],
-              [AC_DEFINE([USE_SSL], 1, [Define to build with (Open)SSL support. (--with-openssl)])])
+              [AC_DEFINE([USE_OPENSSL], 1, [Define to build with OpenSSL support. (--with-openssl)])])
 AC_MSG_RESULT([$with_openssl])
 AC_SUBST(with_openssl)
 
diff --git a/src/backend/libpq/Makefile b/src/backend/libpq/Makefile
index e929864..8be0572 100644
--- a/src/backend/libpq/Makefile
+++ b/src/backend/libpq/Makefile
@@ -17,4 +17,8 @@ include $(top_builddir)/src/Makefile.global
 OBJS = be-fsstubs.o be-secure.o auth.o crypt.o hba.o ip.o md5.o pqcomm.o \
        pqformat.o pqsignal.o
 
+ifeq ($(with_openssl),yes)
+OBJS += be-secure-openssl.o
+endif
+
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c
index 70b0b93..b1974d1 100644
--- a/src/backend/libpq/auth.c
+++ b/src/backend/libpq/auth.c
@@ -161,7 +161,7 @@ static int	pg_SSPI_recvauth(Port *port);
  * RADIUS Authentication
  *----------------------------------------------------------------
  */
-#ifdef USE_SSL
+#ifdef USE_OPENSSL
 #include <openssl/rand.h>
 #endif
 static int	CheckRADIUSAuth(Port *port);
@@ -330,7 +330,7 @@ ClientAuthentication(Port *port)
 		 * already if it didn't verify ok.
 		 */
 #ifdef USE_SSL
-		if (!port->peer)
+		if (!port->peer_cert_valid)
 		{
 			ereport(FATAL,
 					(errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
@@ -378,7 +378,7 @@ ClientAuthentication(Port *port)
 					   (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
 						errmsg("pg_hba.conf rejects replication connection for host \"%s\", user \"%s\", %s",
 							   hostinfo, port->user_name,
-							   port->ssl ? _("SSL on") : _("SSL off"))));
+							   port->ssl_in_use ? _("SSL on") : _("SSL off"))));
 #else
 					ereport(FATAL,
 					   (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
@@ -394,7 +394,7 @@ ClientAuthentication(Port *port)
 						errmsg("pg_hba.conf rejects connection for host \"%s\", user \"%s\", database \"%s\", %s",
 							   hostinfo, port->user_name,
 							   port->database_name,
-							   port->ssl ? _("SSL on") : _("SSL off"))));
+							   port->ssl_in_use ? _("SSL on") : _("SSL off"))));
 #else
 					ereport(FATAL,
 					   (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
@@ -452,7 +452,7 @@ ClientAuthentication(Port *port)
 					   (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
 						errmsg("no pg_hba.conf entry for replication connection from host \"%s\", user \"%s\", %s",
 							   hostinfo, port->user_name,
-							   port->ssl ? _("SSL on") : _("SSL off")),
+							   port->ssl_in_use ? _("SSL on") : _("SSL off")),
 						HOSTNAME_LOOKUP_DETAIL(port)));
 #else
 					ereport(FATAL,
@@ -470,7 +470,7 @@ ClientAuthentication(Port *port)
 						errmsg("no pg_hba.conf entry for host \"%s\", user \"%s\", database \"%s\", %s",
 							   hostinfo, port->user_name,
 							   port->database_name,
-							   port->ssl ? _("SSL on") : _("SSL off")),
+							   port->ssl_in_use ? _("SSL on") : _("SSL off")),
 						HOSTNAME_LOOKUP_DETAIL(port)));
 #else
 					ereport(FATAL,
@@ -2315,7 +2315,7 @@ CheckRADIUSAuth(Port *port)
 	/* Construct RADIUS packet */
 	packet->code = RADIUS_ACCESS_REQUEST;
 	packet->length = RADIUS_HEADER_LENGTH;
-#ifdef USE_SSL
+#ifdef USE_OPENSSL
 	if (RAND_bytes(packet->vector, RADIUS_VECTOR_LENGTH) != 1)
 	{
 		ereport(LOG,
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
new file mode 100644
index 0000000..e3a284b
--- /dev/null
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -0,0 +1,1045 @@
+/*-------------------------------------------------------------------------
+ *
+ * be-secure-openssl.c
+ *	  functions for OpenSSL support in the backend.
+ *
+ *
+ * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  src/backend/libpq/be-secure-openssl.c
+ *
+ *	  Since the server static private key ($DataDir/server.key)
+ *	  will normally be stored unencrypted so that the database
+ *	  backend can restart automatically, it is important that
+ *	  we select an algorithm that continues to provide confidentiality
+ *	  even if the attacker has the server's private key.  Ephemeral
+ *	  DH (EDH) keys provide this, and in fact provide Perfect Forward
+ *	  Secrecy (PFS) except for situations where the session can
+ *	  be hijacked during a periodic handshake/renegotiation.
+ *	  Even that backdoor can be closed if client certificates
+ *	  are used (since the imposter will be unable to successfully
+ *	  complete renegotiation).
+ *
+ *	  N.B., the static private key should still be protected to
+ *	  the largest extent possible, to minimize the risk of
+ *	  impersonations.
+ *
+ *	  Another benefit of EDH is that it allows the backend and
+ *	  clients to use DSA keys.  DSA keys can only provide digital
+ *	  signatures, not encryption, and are often acceptable in
+ *	  jurisdictions where RSA keys are unacceptable.
+ *
+ *	  The downside to EDH is that it makes it impossible to
+ *	  use ssldump(1) if there's a problem establishing an SSL
+ *	  session.  In this case you'll need to temporarily disable
+ *	  EDH by commenting out the callback.
+ *
+ *	  ...
+ *
+ *	  Because the risk of cryptanalysis increases as large
+ *	  amounts of data are sent with the same session key, the
+ *	  session keys are periodically renegotiated.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include <sys/stat.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#ifdef HAVE_NETINET_TCP_H
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+#endif
+
+#include <openssl/ssl.h>
+#include <openssl/dh.h>
+#if SSLEAY_VERSION_NUMBER >= 0x0907000L
+#include <openssl/conf.h>
+#endif
+#if (OPENSSL_VERSION_NUMBER >= 0x0090800fL) && !defined(OPENSSL_NO_ECDH)
+#include <openssl/ec.h>
+#endif
+
+#include "libpq/libpq.h"
+#include "tcop/tcopprot.h"
+#include "utils/memutils.h"
+
+
+
+static DH  *load_dh_file(int keylength);
+static DH  *load_dh_buffer(const char *, size_t);
+static DH  *tmp_dh_cb(SSL *s, int is_export, int keylength);
+static int	verify_cb(int, X509_STORE_CTX *);
+static void info_cb(const SSL *ssl, int type, int args);
+static const char *SSLerrmessage(void);
+
+/* are we in the middle of a renegotiation? */
+static bool in_ssl_renegotiation = false;
+
+static SSL_CTX *SSL_context = NULL;
+
+/* ------------------------------------------------------------ */
+/*						 Hardcoded values						*/
+/* ------------------------------------------------------------ */
+
+/*
+ *	Hardcoded DH parameters, used in ephemeral 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 DBA... or an attacker... deletes the
+ *	$DataDir/dh*.pem files.
+ *
+ *	We could refuse SSL connections unless a good DH parameter
+ *	file exists, but some clients may quietly renegotiate an
+ *	unsecured connection without fully informing the user.
+ *	Very uncool.
+ *
+ *	Alternatively, the backend could attempt to load these files
+ *	on startup if SSL is enabled - and refuse to start if any
+ *	do not exist - but this would tend to piss off DBAs.
+ *
+ *	If you want to create your own hardcoded DH parameters
+ *	for fun and profit, review "Assigned Number for SKIP
+ *	Protocols" (http://www.skip-vpn.org/spec/numbers.html)
+ *	for suggestions.
+ */
+
+static const char file_dh512[] =
+"-----BEGIN DH PARAMETERS-----\n\
+MEYCQQD1Kv884bEpQBgRjXyEpwpy1obEAxnIByl6ypUM2Zafq9AKUJsCRtMIPWak\n\
+XUGfnHy9iUsiGSa6q6Jew1XpKgVfAgEC\n\
+-----END DH PARAMETERS-----\n";
+
+static const char file_dh1024[] =
+"-----BEGIN DH PARAMETERS-----\n\
+MIGHAoGBAPSI/VhOSdvNILSd5JEHNmszbDgNRR0PfIizHHxbLY7288kjwEPwpVsY\n\
+jY67VYy4XTjTNP18F1dDox0YbN4zISy1Kv884bEpQBgRjXyEpwpy1obEAxnIByl6\n\
+ypUM2Zafq9AKUJsCRtMIPWakXUGfnHy9iUsiGSa6q6Jew1XpL3jHAgEC\n\
+-----END DH PARAMETERS-----\n";
+
+static const char file_dh2048[] =
+"-----BEGIN DH PARAMETERS-----\n\
+MIIBCAKCAQEA9kJXtwh/CBdyorrWqULzBej5UxE5T7bxbrlLOCDaAadWoxTpj0BV\n\
+89AHxstDqZSt90xkhkn4DIO9ZekX1KHTUPj1WV/cdlJPPT2N286Z4VeSWc39uK50\n\
+T8X8dryDxUcwYc58yWb/Ffm7/ZFexwGq01uejaClcjrUGvC/RgBYK+X0iP1YTknb\n\
+zSC0neSRBzZrM2w4DUUdD3yIsxx8Wy2O9vPJI8BD8KVbGI2Ou1WMuF040zT9fBdX\n\
+Q6MdGGzeMyEstSr/POGxKUAYEY18hKcKctaGxAMZyAcpesqVDNmWn6vQClCbAkbT\n\
+CD1mpF1Bn5x8vYlLIhkmuquiXsNV6TILOwIBAg==\n\
+-----END DH PARAMETERS-----\n";
+
+static const char file_dh4096[] =
+"-----BEGIN DH PARAMETERS-----\n\
+MIICCAKCAgEA+hRyUsFN4VpJ1O8JLcCo/VWr19k3BCgJ4uk+d+KhehjdRqNDNyOQ\n\
+l/MOyQNQfWXPeGKmOmIig6Ev/nm6Nf9Z2B1h3R4hExf+zTiHnvVPeRBhjdQi81rt\n\
+Xeoh6TNrSBIKIHfUJWBh3va0TxxjQIs6IZOLeVNRLMqzeylWqMf49HsIXqbcokUS\n\
+Vt1BkvLdW48j8PPv5DsKRN3tloTxqDJGo9tKvj1Fuk74A+Xda1kNhB7KFlqMyN98\n\
+VETEJ6c7KpfOo30mnK30wqw3S8OtaIR/maYX72tGOno2ehFDkq3pnPtEbD2CScxc\n\
+alJC+EL7RPk5c/tgeTvCngvc1KZn92Y//EI7G9tPZtylj2b56sHtMftIoYJ9+ODM\n\
+sccD5Piz/rejE3Ome8EOOceUSCYAhXn8b3qvxVI1ddd1pED6FHRhFvLrZxFvBEM9\n\
+ERRMp5QqOaHJkM+Dxv8Cj6MqrCbfC4u+ZErxodzuusgDgvZiLF22uxMZbobFWyte\n\
+OvOzKGtwcTqO/1wV5gKkzu1ZVswVUQd5Gg8lJicwqRWyyNRczDDoG9jVDxmogKTH\n\
+AaqLulO7R8Ifa1SwF2DteSGVtgWEN8gDpN3RBmmPTDngyF2DHb5qmpnznwtFKdTL\n\
+KWbuHn491xNO25CQWMtem80uKw+pTnisBRF/454n1Jnhub144YRBoN8CAQI=\n\
+-----END DH PARAMETERS-----\n";
+
+/*
+ *	Write data to a secure connection.
+ */
+ssize_t
+be_tls_write(Port *port, void *ptr, size_t len)
+{
+	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);
+
+		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")));
+	}
+
+	return n;
+}
+
+/* ------------------------------------------------------------ */
+/*						OpenSSL specific code					*/
+/* ------------------------------------------------------------ */
+
+/*
+ * Private substitute BIO: this does the sending and receiving using send() and
+ * recv() instead. This is so that we can enable and disable interrupts
+ * just while calling recv(). We cannot have interrupts occurring while
+ * the bulk of openssl runs, because it uses malloc() and possibly other
+ * non-reentrant libc facilities. We also need to call send() and recv()
+ * directly so it gets passed through the socket/signals layer on Win32.
+ *
+ * These functions are closely modelled on the standard socket BIO in OpenSSL;
+ * see sock_read() and sock_write() in OpenSSL's crypto/bio/bss_sock.c.
+ * XXX OpenSSL 1.0.1e considers many more errcodes than just EINTR as reasons
+ * to retry; do we need to adopt their logic for that?
+ */
+
+static bool my_bio_initialized = false;
+static BIO_METHOD my_bio_methods;
+
+static int
+my_sock_read(BIO *h, char *buf, int size)
+{
+	int			res = 0;
+
+	if (buf != NULL)
+	{
+		res = secure_raw_read(((Port *)h->ptr), buf, size);
+		BIO_clear_retry_flags(h);
+		if (res <= 0)
+		{
+			/* If we were interrupted, tell caller to retry */
+			if (errno == EINTR)
+			{
+				BIO_set_retry_read(h);
+			}
+		}
+	}
+
+	return res;
+}
+
+static int
+my_sock_write(BIO *h, const char *buf, int size)
+{
+	int			res = 0;
+
+	res = secure_raw_write(((Port *) h->ptr), buf, size);
+	BIO_clear_retry_flags(h);
+	if (res <= 0)
+	{
+		if (errno == EINTR)
+		{
+			BIO_set_retry_write(h);
+		}
+	}
+
+	return res;
+}
+
+static BIO_METHOD *
+my_BIO_s_socket(void)
+{
+	if (!my_bio_initialized)
+	{
+		memcpy(&my_bio_methods, BIO_s_socket(), sizeof(BIO_METHOD));
+		my_bio_methods.bread = my_sock_read;
+		my_bio_methods.bwrite = my_sock_write;
+		my_bio_initialized = true;
+	}
+	return &my_bio_methods;
+}
+
+/* This should exactly match openssl's SSL_set_fd except for using my BIO */
+static int
+my_SSL_set_fd(Port *port, int fd)
+{
+	int			ret = 0;
+	BIO		   *bio = NULL;
+
+	bio = BIO_new(my_BIO_s_socket());
+
+	if (bio == NULL)
+	{
+		SSLerr(SSL_F_SSL_SET_FD, ERR_R_BUF_LIB);
+		goto err;
+	}
+	/* Use 'ptr' to store pointer to PGconn */
+	bio->ptr = port;
+
+	BIO_set_fd(bio, fd, BIO_NOCLOSE);
+	SSL_set_bio(port->ssl, bio, bio);
+	ret = 1;
+err:
+	return ret;
+}
+
+/*
+ *	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());
+			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;
+}
+
+/*
+ *	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;
+}
+
+/*
+ *	Generate an ephemeral DH key.  Because this can take a long
+ *	time to compute, we can use precomputed parameters of the
+ *	common key sizes.
+ *
+ *	Since few sites will bother to precompute these parameter
+ *	files, we also provide a fallback to the parameters provided
+ *	by the OpenSSL project.
+ *
+ *	These values can be static (once loaded or computed) since
+ *	the OpenSSL library can efficiently generate random keys from
+ *	the information provided.
+ */
+static DH  *
+tmp_dh_cb(SSL *s, int is_export, int keylength)
+{
+	DH		   *r = NULL;
+	static DH  *dh = NULL;
+	static DH  *dh512 = NULL;
+	static DH  *dh1024 = NULL;
+	static DH  *dh2048 = NULL;
+	static DH  *dh4096 = NULL;
+
+	switch (keylength)
+	{
+		case 512:
+			if (dh512 == NULL)
+				dh512 = load_dh_file(keylength);
+			if (dh512 == NULL)
+				dh512 = load_dh_buffer(file_dh512, sizeof file_dh512);
+			r = dh512;
+			break;
+
+		case 1024:
+			if (dh1024 == NULL)
+				dh1024 = load_dh_file(keylength);
+			if (dh1024 == NULL)
+				dh1024 = load_dh_buffer(file_dh1024, sizeof file_dh1024);
+			r = dh1024;
+			break;
+
+		case 2048:
+			if (dh2048 == NULL)
+				dh2048 = load_dh_file(keylength);
+			if (dh2048 == NULL)
+				dh2048 = load_dh_buffer(file_dh2048, sizeof file_dh2048);
+			r = dh2048;
+			break;
+
+		case 4096:
+			if (dh4096 == NULL)
+				dh4096 = load_dh_file(keylength);
+			if (dh4096 == NULL)
+				dh4096 = load_dh_buffer(file_dh4096, sizeof file_dh4096);
+			r = dh4096;
+			break;
+
+		default:
+			if (dh == NULL)
+				dh = load_dh_file(keylength);
+			r = dh;
+	}
+
+	/* this may take a long time, but it may be necessary... */
+	if (r == NULL || 8 * DH_size(r) < keylength)
+	{
+		ereport(DEBUG2,
+				(errmsg_internal("DH: generating parameters (%d bits)",
+								 keylength)));
+		r = DH_generate_parameters(keylength, DH_GENERATOR_2, NULL, NULL);
+	}
+
+	return r;
+}
+
+/*
+ *	Certificate verification callback
+ *
+ *	This callback allows us to log intermediate problems during
+ *	verification, but for now we'll see if the final error message
+ *	contains enough information.
+ *
+ *	This callback also allows us to override the default acceptance
+ *	criteria (e.g., accepting self-signed or expired certs), but
+ *	for now we accept the default checks.
+ */
+static int
+verify_cb(int ok, X509_STORE_CTX *ctx)
+{
+	return ok;
+}
+
+/*
+ *	This callback is used to copy SSL information messages
+ *	into the PostgreSQL log.
+ */
+static void
+info_cb(const SSL *ssl, int type, int args)
+{
+	switch (type)
+	{
+		case SSL_CB_HANDSHAKE_START:
+			ereport(DEBUG4,
+					(errmsg_internal("SSL: handshake start")));
+			break;
+		case SSL_CB_HANDSHAKE_DONE:
+			ereport(DEBUG4,
+					(errmsg_internal("SSL: handshake done")));
+			break;
+		case SSL_CB_ACCEPT_LOOP:
+			ereport(DEBUG4,
+					(errmsg_internal("SSL: accept loop")));
+			break;
+		case SSL_CB_ACCEPT_EXIT:
+			ereport(DEBUG4,
+					(errmsg_internal("SSL: accept exit (%d)", args)));
+			break;
+		case SSL_CB_CONNECT_LOOP:
+			ereport(DEBUG4,
+					(errmsg_internal("SSL: connect loop")));
+			break;
+		case SSL_CB_CONNECT_EXIT:
+			ereport(DEBUG4,
+					(errmsg_internal("SSL: connect exit (%d)", args)));
+			break;
+		case SSL_CB_READ_ALERT:
+			ereport(DEBUG4,
+					(errmsg_internal("SSL: read alert (0x%04x)", args)));
+			break;
+		case SSL_CB_WRITE_ALERT:
+			ereport(DEBUG4,
+					(errmsg_internal("SSL: write alert (0x%04x)", args)));
+			break;
+	}
+}
+
+#if (OPENSSL_VERSION_NUMBER >= 0x0090800fL) && !defined(OPENSSL_NO_ECDH)
+static void
+initialize_ecdh(void)
+{
+	EC_KEY	   *ecdh;
+	int			nid;
+
+	nid = OBJ_sn2nid(SSLECDHCurve);
+	if (!nid)
+		ereport(FATAL,
+				(errmsg("ECDH: unrecognized curve name: %s", SSLECDHCurve)));
+
+	ecdh = EC_KEY_new_by_curve_name(nid);
+	if (!ecdh)
+		ereport(FATAL,
+				(errmsg("ECDH: could not create key")));
+
+	SSL_CTX_set_options(SSL_context, SSL_OP_SINGLE_ECDH_USE);
+	SSL_CTX_set_tmp_ecdh(SSL_context, ecdh);
+	EC_KEY_free(ecdh);
+}
+#else
+#define initialize_ecdh()
+#endif
+
+/*
+ *	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);
+	}
+}
+
+/*
+ *	Attempt to negotiate SSL connection.
+ */
+int
+be_tls_open_server(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())));
+		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:
+#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;
+		}
+		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;
+}
+
+/*
+ *	Close SSL connection.
+ */
+void
+be_tls_close(Port *port)
+{
+	if (port->ssl)
+	{
+		SSL_shutdown(port->ssl);
+		SSL_free(port->ssl);
+		port->ssl = NULL;
+		port->ssl_in_use = false;
+	}
+
+	if (port->peer)
+	{
+		X509_free(port->peer);
+		port->peer = NULL;
+	}
+
+	if (port->peer_cn)
+	{
+		pfree(port->peer_cn);
+		port->peer_cn = NULL;
+	}
+}
+
+ssize_t
+be_tls_read(Port *port, void *ptr, size_t len)
+{
+	ssize_t		n;
+	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)));
+			errno = ECONNRESET;
+			n = -1;
+			break;
+	}
+
+	return n;
+}
+
+/*
+ * Obtain reason string for last SSL error
+ *
+ * Some caution is needed here since ERR_reason_error_string will
+ * return NULL if it doesn't recognize the error code.  We don't
+ * want to return NULL ever.
+ */
+static const char *
+SSLerrmessage(void)
+{
+	unsigned long errcode;
+	const char *errreason;
+	static char errbuf[32];
+
+	errcode = ERR_get_error();
+	if (errcode == 0)
+		return _("no SSL error reported");
+	errreason = ERR_reason_error_string(errcode);
+	if (errreason != NULL)
+		return errreason;
+	snprintf(errbuf, sizeof(errbuf), _("SSL error code %lu"), errcode);
+	return errbuf;
+}
+
diff --git a/src/backend/libpq/be-secure.c b/src/backend/libpq/be-secure.c
index 59204cf..d378abc 100644
--- a/src/backend/libpq/be-secure.c
+++ b/src/backend/libpq/be-secure.c
@@ -13,38 +13,6 @@
  * IDENTIFICATION
  *	  src/backend/libpq/be-secure.c
  *
- *	  Since the server static private key ($DataDir/server.key)
- *	  will normally be stored unencrypted so that the database
- *	  backend can restart automatically, it is important that
- *	  we select an algorithm that continues to provide confidentiality
- *	  even if the attacker has the server's private key.  Ephemeral
- *	  DH (EDH) keys provide this, and in fact provide Perfect Forward
- *	  Secrecy (PFS) except for situations where the session can
- *	  be hijacked during a periodic handshake/renegotiation.
- *	  Even that backdoor can be closed if client certificates
- *	  are used (since the imposter will be unable to successfully
- *	  complete renegotiation).
- *
- *	  N.B., the static private key should still be protected to
- *	  the largest extent possible, to minimize the risk of
- *	  impersonations.
- *
- *	  Another benefit of EDH is that it allows the backend and
- *	  clients to use DSA keys.  DSA keys can only provide digital
- *	  signatures, not encryption, and are often acceptable in
- *	  jurisdictions where RSA keys are unacceptable.
- *
- *	  The downside to EDH is that it makes it impossible to
- *	  use ssldump(1) if there's a problem establishing an SSL
- *	  session.  In this case you'll need to temporarily disable
- *	  EDH by commenting out the callback.
- *
- *	  ...
- *
- *	  Because the risk of cryptanalysis increases as large
- *	  amounts of data are sent with the same session key, the
- *	  session keys are periodically renegotiated.
- *
  *-------------------------------------------------------------------------
  */
 
@@ -63,35 +31,11 @@
 #include <arpa/inet.h>
 #endif
 
-#ifdef USE_SSL
-#include <openssl/ssl.h>
-#include <openssl/dh.h>
-#if SSLEAY_VERSION_NUMBER >= 0x0907000L
-#include <openssl/conf.h>
-#endif
-#if (OPENSSL_VERSION_NUMBER >= 0x0090800fL) && !defined(OPENSSL_NO_ECDH)
-#include <openssl/ec.h>
-#endif
-#endif   /* USE_SSL */
-
 #include "libpq/libpq.h"
 #include "tcop/tcopprot.h"
 #include "utils/memutils.h"
 
 
-#ifdef USE_SSL
-
-static DH  *load_dh_file(int keylength);
-static DH  *load_dh_buffer(const char *, size_t);
-static DH  *tmp_dh_cb(SSL *s, int is_export, int keylength);
-static int	verify_cb(int, X509_STORE_CTX *);
-static void info_cb(const SSL *ssl, int type, int args);
-static void initialize_SSL(void);
-static int	open_server_SSL(Port *);
-static void close_SSL(Port *);
-static const char *SSLerrmessage(void);
-#endif
-
 char	   *ssl_cert_file;
 char	   *ssl_key_file;
 char	   *ssl_ca_file;
@@ -105,11 +49,7 @@ char	   *ssl_crl_file;
 int			ssl_renegotiation_limit;
 
 #ifdef USE_SSL
-/* are we in the middle of a renegotiation? */
-static bool in_ssl_renegotiation = false;
-
-static SSL_CTX *SSL_context = NULL;
-static bool ssl_loaded_verify_locations = false;
+bool ssl_loaded_verify_locations = false;
 #endif
 
 /* GUC variable controlling SSL cipher list */
@@ -122,73 +62,6 @@ char	   *SSLECDHCurve;
 bool		SSLPreferServerCiphers;
 
 /* ------------------------------------------------------------ */
-/*						 Hardcoded values						*/
-/* ------------------------------------------------------------ */
-
-/*
- *	Hardcoded DH parameters, used in ephemeral 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 DBA... or an attacker... deletes the
- *	$DataDir/dh*.pem files.
- *
- *	We could refuse SSL connections unless a good DH parameter
- *	file exists, but some clients may quietly renegotiate an
- *	unsecured connection without fully informing the user.
- *	Very uncool.
- *
- *	Alternatively, the backend could attempt to load these files
- *	on startup if SSL is enabled - and refuse to start if any
- *	do not exist - but this would tend to piss off DBAs.
- *
- *	If you want to create your own hardcoded DH parameters
- *	for fun and profit, review "Assigned Number for SKIP
- *	Protocols" (http://www.skip-vpn.org/spec/numbers.html)
- *	for suggestions.
- */
-#ifdef USE_SSL
-
-static const char file_dh512[] =
-"-----BEGIN DH PARAMETERS-----\n\
-MEYCQQD1Kv884bEpQBgRjXyEpwpy1obEAxnIByl6ypUM2Zafq9AKUJsCRtMIPWak\n\
-XUGfnHy9iUsiGSa6q6Jew1XpKgVfAgEC\n\
------END DH PARAMETERS-----\n";
-
-static const char file_dh1024[] =
-"-----BEGIN DH PARAMETERS-----\n\
-MIGHAoGBAPSI/VhOSdvNILSd5JEHNmszbDgNRR0PfIizHHxbLY7288kjwEPwpVsY\n\
-jY67VYy4XTjTNP18F1dDox0YbN4zISy1Kv884bEpQBgRjXyEpwpy1obEAxnIByl6\n\
-ypUM2Zafq9AKUJsCRtMIPWakXUGfnHy9iUsiGSa6q6Jew1XpL3jHAgEC\n\
------END DH PARAMETERS-----\n";
-
-static const char file_dh2048[] =
-"-----BEGIN DH PARAMETERS-----\n\
-MIIBCAKCAQEA9kJXtwh/CBdyorrWqULzBej5UxE5T7bxbrlLOCDaAadWoxTpj0BV\n\
-89AHxstDqZSt90xkhkn4DIO9ZekX1KHTUPj1WV/cdlJPPT2N286Z4VeSWc39uK50\n\
-T8X8dryDxUcwYc58yWb/Ffm7/ZFexwGq01uejaClcjrUGvC/RgBYK+X0iP1YTknb\n\
-zSC0neSRBzZrM2w4DUUdD3yIsxx8Wy2O9vPJI8BD8KVbGI2Ou1WMuF040zT9fBdX\n\
-Q6MdGGzeMyEstSr/POGxKUAYEY18hKcKctaGxAMZyAcpesqVDNmWn6vQClCbAkbT\n\
-CD1mpF1Bn5x8vYlLIhkmuquiXsNV6TILOwIBAg==\n\
------END DH PARAMETERS-----\n";
-
-static const char file_dh4096[] =
-"-----BEGIN DH PARAMETERS-----\n\
-MIICCAKCAgEA+hRyUsFN4VpJ1O8JLcCo/VWr19k3BCgJ4uk+d+KhehjdRqNDNyOQ\n\
-l/MOyQNQfWXPeGKmOmIig6Ev/nm6Nf9Z2B1h3R4hExf+zTiHnvVPeRBhjdQi81rt\n\
-Xeoh6TNrSBIKIHfUJWBh3va0TxxjQIs6IZOLeVNRLMqzeylWqMf49HsIXqbcokUS\n\
-Vt1BkvLdW48j8PPv5DsKRN3tloTxqDJGo9tKvj1Fuk74A+Xda1kNhB7KFlqMyN98\n\
-VETEJ6c7KpfOo30mnK30wqw3S8OtaIR/maYX72tGOno2ehFDkq3pnPtEbD2CScxc\n\
-alJC+EL7RPk5c/tgeTvCngvc1KZn92Y//EI7G9tPZtylj2b56sHtMftIoYJ9+ODM\n\
-sccD5Piz/rejE3Ome8EOOceUSCYAhXn8b3qvxVI1ddd1pED6FHRhFvLrZxFvBEM9\n\
-ERRMp5QqOaHJkM+Dxv8Cj6MqrCbfC4u+ZErxodzuusgDgvZiLF22uxMZbobFWyte\n\
-OvOzKGtwcTqO/1wV5gKkzu1ZVswVUQd5Gg8lJicwqRWyyNRczDDoG9jVDxmogKTH\n\
-AaqLulO7R8Ifa1SwF2DteSGVtgWEN8gDpN3RBmmPTDngyF2DHb5qmpnznwtFKdTL\n\
-KWbuHn491xNO25CQWMtem80uKw+pTnisBRF/454n1Jnhub144YRBoN8CAQI=\n\
------END DH PARAMETERS-----\n";
-#endif
-
-/* ------------------------------------------------------------ */
 /*			 Procedures common to all secure sessions			*/
 /* ------------------------------------------------------------ */
 
@@ -198,8 +71,8 @@ KWbuHn491xNO25CQWMtem80uKw+pTnisBRF/454n1Jnhub144YRBoN8CAQI=\n\
 int
 secure_initialize(void)
 {
-#ifdef USE_SSL
-	initialize_SSL();
+#ifdef USE_OPENSSL
+	be_tls_init();
 #endif
 
 	return 0;
@@ -227,7 +100,7 @@ secure_open_server(Port *port)
 	int			r = 0;
 
 #ifdef USE_SSL
-	r = open_server_SSL(port);
+	r = be_tls_open_server(port);
 #endif
 
 	return r;
@@ -240,8 +113,8 @@ void
 secure_close(Port *port)
 {
 #ifdef USE_SSL
-	if (port->ssl)
-		close_SSL(port);
+	if (port->ssl_in_use)
+		be_tls_close(port);
 #endif
 }
 
@@ -254,908 +127,56 @@ secure_read(Port *port, void *ptr, size_t len)
 	ssize_t		n;
 
 #ifdef USE_SSL
-	if (port->ssl)
+	if (port->ssl_in_use)
 	{
-		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)));
-				errno = ECONNRESET;
-				n = -1;
-				break;
-		}
+		n = be_tls_read(port, ptr, len);
 	}
 	else
 #endif
 	{
-		prepare_for_client_read();
-
-		n = recv(port->sock, ptr, len, 0);
-
-		client_read_ended();
+		n = secure_raw_read(port, ptr, len);
 	}
 
 	return n;
 }
 
-/*
- *	Write data to a secure connection.
- */
 ssize_t
-secure_write(Port *port, void *ptr, size_t len)
+secure_raw_read(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;
-}
-
-/* ------------------------------------------------------------ */
-/*						  SSL specific code						*/
-/* ------------------------------------------------------------ */
-#ifdef USE_SSL
-
-/*
- * Private substitute BIO: this does the sending and receiving using send() and
- * recv() instead. This is so that we can enable and disable interrupts
- * just while calling recv(). We cannot have interrupts occurring while
- * the bulk of openssl runs, because it uses malloc() and possibly other
- * non-reentrant libc facilities. We also need to call send() and recv()
- * directly so it gets passed through the socket/signals layer on Win32.
- *
- * These functions are closely modelled on the standard socket BIO in OpenSSL;
- * see sock_read() and sock_write() in OpenSSL's crypto/bio/bss_sock.c.
- * XXX OpenSSL 1.0.1e considers many more errcodes than just EINTR as reasons
- * to retry; do we need to adopt their logic for that?
- */
-
-static bool my_bio_initialized = false;
-static BIO_METHOD my_bio_methods;
-
-static int
-my_sock_read(BIO *h, char *buf, int size)
-{
-	int			res = 0;
-
 	prepare_for_client_read();
 
-	if (buf != NULL)
-	{
-		res = recv(h->num, buf, size, 0);
-		BIO_clear_retry_flags(h);
-		if (res <= 0)
-		{
-			/* If we were interrupted, tell caller to retry */
-			if (errno == EINTR)
-			{
-				BIO_set_retry_read(h);
-			}
-		}
-	}
+	n = recv(port->sock, ptr, len, 0);
 
 	client_read_ended();
 
-	return res;
-}
-
-static int
-my_sock_write(BIO *h, const char *buf, int size)
-{
-	int			res = 0;
-
-	res = send(h->num, buf, size, 0);
-	BIO_clear_retry_flags(h);
-	if (res <= 0)
-	{
-		if (errno == EINTR)
-		{
-			BIO_set_retry_write(h);
-		}
-	}
-
-	return res;
-}
-
-static BIO_METHOD *
-my_BIO_s_socket(void)
-{
-	if (!my_bio_initialized)
-	{
-		memcpy(&my_bio_methods, BIO_s_socket(), sizeof(BIO_METHOD));
-		my_bio_methods.bread = my_sock_read;
-		my_bio_methods.bwrite = my_sock_write;
-		my_bio_initialized = true;
-	}
-	return &my_bio_methods;
-}
-
-/* This should exactly match openssl's SSL_set_fd except for using my BIO */
-static int
-my_SSL_set_fd(SSL *s, int fd)
-{
-	int			ret = 0;
-	BIO		   *bio = NULL;
-
-	bio = BIO_new(my_BIO_s_socket());
-
-	if (bio == NULL)
-	{
-		SSLerr(SSL_F_SSL_SET_FD, ERR_R_BUF_LIB);
-		goto err;
-	}
-	BIO_set_fd(bio, fd, BIO_NOCLOSE);
-	SSL_set_bio(s, bio, bio);
-	ret = 1;
-err:
-	return ret;
-}
-
-/*
- *	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());
-			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;
-}
-
-/*
- *	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;
-}
-
-/*
- *	Generate an ephemeral DH key.  Because this can take a long
- *	time to compute, we can use precomputed parameters of the
- *	common key sizes.
- *
- *	Since few sites will bother to precompute these parameter
- *	files, we also provide a fallback to the parameters provided
- *	by the OpenSSL project.
- *
- *	These values can be static (once loaded or computed) since
- *	the OpenSSL library can efficiently generate random keys from
- *	the information provided.
- */
-static DH  *
-tmp_dh_cb(SSL *s, int is_export, int keylength)
-{
-	DH		   *r = NULL;
-	static DH  *dh = NULL;
-	static DH  *dh512 = NULL;
-	static DH  *dh1024 = NULL;
-	static DH  *dh2048 = NULL;
-	static DH  *dh4096 = NULL;
-
-	switch (keylength)
-	{
-		case 512:
-			if (dh512 == NULL)
-				dh512 = load_dh_file(keylength);
-			if (dh512 == NULL)
-				dh512 = load_dh_buffer(file_dh512, sizeof file_dh512);
-			r = dh512;
-			break;
-
-		case 1024:
-			if (dh1024 == NULL)
-				dh1024 = load_dh_file(keylength);
-			if (dh1024 == NULL)
-				dh1024 = load_dh_buffer(file_dh1024, sizeof file_dh1024);
-			r = dh1024;
-			break;
-
-		case 2048:
-			if (dh2048 == NULL)
-				dh2048 = load_dh_file(keylength);
-			if (dh2048 == NULL)
-				dh2048 = load_dh_buffer(file_dh2048, sizeof file_dh2048);
-			r = dh2048;
-			break;
-
-		case 4096:
-			if (dh4096 == NULL)
-				dh4096 = load_dh_file(keylength);
-			if (dh4096 == NULL)
-				dh4096 = load_dh_buffer(file_dh4096, sizeof file_dh4096);
-			r = dh4096;
-			break;
-
-		default:
-			if (dh == NULL)
-				dh = load_dh_file(keylength);
-			r = dh;
-	}
-
-	/* this may take a long time, but it may be necessary... */
-	if (r == NULL || 8 * DH_size(r) < keylength)
-	{
-		ereport(DEBUG2,
-				(errmsg_internal("DH: generating parameters (%d bits)",
-								 keylength)));
-		r = DH_generate_parameters(keylength, DH_GENERATOR_2, NULL, NULL);
-	}
-
-	return r;
-}
-
-/*
- *	Certificate verification callback
- *
- *	This callback allows us to log intermediate problems during
- *	verification, but for now we'll see if the final error message
- *	contains enough information.
- *
- *	This callback also allows us to override the default acceptance
- *	criteria (e.g., accepting self-signed or expired certs), but
- *	for now we accept the default checks.
- */
-static int
-verify_cb(int ok, X509_STORE_CTX *ctx)
-{
-	return ok;
-}
-
-/*
- *	This callback is used to copy SSL information messages
- *	into the PostgreSQL log.
- */
-static void
-info_cb(const SSL *ssl, int type, int args)
-{
-	switch (type)
-	{
-		case SSL_CB_HANDSHAKE_START:
-			ereport(DEBUG4,
-					(errmsg_internal("SSL: handshake start")));
-			break;
-		case SSL_CB_HANDSHAKE_DONE:
-			ereport(DEBUG4,
-					(errmsg_internal("SSL: handshake done")));
-			break;
-		case SSL_CB_ACCEPT_LOOP:
-			ereport(DEBUG4,
-					(errmsg_internal("SSL: accept loop")));
-			break;
-		case SSL_CB_ACCEPT_EXIT:
-			ereport(DEBUG4,
-					(errmsg_internal("SSL: accept exit (%d)", args)));
-			break;
-		case SSL_CB_CONNECT_LOOP:
-			ereport(DEBUG4,
-					(errmsg_internal("SSL: connect loop")));
-			break;
-		case SSL_CB_CONNECT_EXIT:
-			ereport(DEBUG4,
-					(errmsg_internal("SSL: connect exit (%d)", args)));
-			break;
-		case SSL_CB_READ_ALERT:
-			ereport(DEBUG4,
-					(errmsg_internal("SSL: read alert (0x%04x)", args)));
-			break;
-		case SSL_CB_WRITE_ALERT:
-			ereport(DEBUG4,
-					(errmsg_internal("SSL: write alert (0x%04x)", args)));
-			break;
-	}
-}
-
-#if (OPENSSL_VERSION_NUMBER >= 0x0090800fL) && !defined(OPENSSL_NO_ECDH)
-static void
-initialize_ecdh(void)
-{
-	EC_KEY	   *ecdh;
-	int			nid;
-
-	nid = OBJ_sn2nid(SSLECDHCurve);
-	if (!nid)
-		ereport(FATAL,
-				(errmsg("ECDH: unrecognized curve name: %s", SSLECDHCurve)));
-
-	ecdh = EC_KEY_new_by_curve_name(nid);
-	if (!ecdh)
-		ereport(FATAL,
-				(errmsg("ECDH: could not create key")));
-
-	SSL_CTX_set_options(SSL_context, SSL_OP_SINGLE_ECDH_USE);
-	SSL_CTX_set_tmp_ecdh(SSL_context, ecdh);
-	EC_KEY_free(ecdh);
+	return n;
 }
-#else
-#define initialize_ecdh()
-#endif
-
-/*
- *	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();
-
-		/*
-		 * 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);
-	}
-}
 
 /*
- *	Attempt to negotiate SSL connection.
+ *	Write data to a secure connection.
  */
-static int
-open_server_SSL(Port *port)
+ssize_t
+secure_write(Port *port, void *ptr, size_t len)
 {
-	int			r;
-	int			err;
-
-	Assert(!port->ssl);
-	Assert(!port->peer);
+	ssize_t		n;
 
-	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))
+#ifdef USE_SSL
+	if (port->ssl_in_use)
 	{
-		ereport(COMMERROR,
-				(errcode(ERRCODE_PROTOCOL_VIOLATION),
-				 errmsg("could not set SSL socket: %s",
-						SSLerrmessage())));
-		close_SSL(port);
-		return -1;
+		n = be_tls_write(port, ptr, len);
 	}
-
-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);
+	else
 #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);
+		n = secure_raw_write(port, ptr, len);
 
-	return 0;
-}
-
-/*
- *	Close SSL connection.
- */
-static void
-close_SSL(Port *port)
-{
-	if (port->ssl)
-	{
-		SSL_shutdown(port->ssl);
-		SSL_free(port->ssl);
-		port->ssl = NULL;
-	}
-
-	if (port->peer)
-	{
-		X509_free(port->peer);
-		port->peer = NULL;
-	}
-
-	if (port->peer_cn)
-	{
-		pfree(port->peer_cn);
-		port->peer_cn = NULL;
-	}
+	return n;
 }
 
-/*
- * Obtain reason string for last SSL error
- *
- * Some caution is needed here since ERR_reason_error_string will
- * return NULL if it doesn't recognize the error code.  We don't
- * want to return NULL ever.
- */
-static const char *
-SSLerrmessage(void)
+ssize_t
+secure_raw_write(Port *port, const void *ptr, size_t len)
 {
-	unsigned long errcode;
-	const char *errreason;
-	static char errbuf[32];
-
-	errcode = ERR_get_error();
-	if (errcode == 0)
-		return _("no SSL error reported");
-	errreason = ERR_reason_error_string(errcode);
-	if (errreason != NULL)
-		return errreason;
-	snprintf(errbuf, sizeof(errbuf), _("SSL error code %lu"), errcode);
-	return errbuf;
+	return send(port->sock, ptr, len, 0);
 }
-
-#endif   /* USE_SSL */
diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c
index fd98c60..84da823 100644
--- a/src/backend/libpq/hba.c
+++ b/src/backend/libpq/hba.c
@@ -1685,7 +1685,7 @@ check_hba(hbaPort *port)
 
 			/* Check SSL state */
 #ifdef USE_SSL
-			if (port->ssl)
+			if (port->ssl_in_use)
 			{
 				/* Connection is SSL, match both "host" and "hostssl" */
 				if (hba->conntype == ctHostNoSSL)
diff --git a/src/backend/postmaster/fork_process.c b/src/backend/postmaster/fork_process.c
index 5e5bd35..dd3abab 100644
--- a/src/backend/postmaster/fork_process.c
+++ b/src/backend/postmaster/fork_process.c
@@ -17,7 +17,7 @@
 #include <sys/stat.h>
 #include <sys/time.h>
 #include <unistd.h>
-#ifdef USE_SSL
+#ifdef USE_OPENSSL
 #include <openssl/rand.h>
 #endif
 
@@ -110,7 +110,7 @@ fork_process(void)
 		/*
 		 * Make sure processes do not share OpenSSL randomness state.
 		 */
-#ifdef USE_SSL
+#ifdef USE_OPENSSL
 		RAND_cleanup();
 #endif
 	}
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
index 28243ad..a5b9821 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -231,8 +231,8 @@ PerformAuthentication(Port *port)
 	{
 		if (am_walsender)
 		{
-#ifdef USE_SSL
-			if (port->ssl)
+#ifdef USE_OPENSSL
+			if (port->ssl_in_use)
 				ereport(LOG,
 						(errmsg("replication connection authorized: user=%s SSL enabled (protocol=%s, cipher=%s, compression=%s)",
 								port->user_name, SSL_get_version(port->ssl), SSL_get_cipher(port->ssl),
@@ -245,8 +245,8 @@ PerformAuthentication(Port *port)
 		}
 		else
 		{
-#ifdef USE_SSL
-			if (port->ssl)
+#ifdef USE_OPENSSL
+			if (port->ssl_in_use)
 				ereport(LOG,
 						(errmsg("connection authorized: user=%s database=%s SSL enabled (protocol=%s, cipher=%s, compression=%s)",
 								port->user_name, port->database_name, SSL_get_version(port->ssl), SSL_get_cipher(port->ssl),
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 6c52db8..9aa1bc4 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -125,9 +125,6 @@ extern char *default_tablespace;
 extern char *temp_tablespaces;
 extern bool ignore_checksum_failure;
 extern bool synchronize_seqscans;
-extern char *SSLCipherSuites;
-extern char *SSLECDHCurve;
-extern bool SSLPreferServerCiphers;
 
 #ifdef TRACE_SORT
 extern bool trace_sort;
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 161de75..958505d 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -30,7 +30,7 @@
 #include <sys/types.h>			/* for umask() */
 #include <sys/stat.h>			/* for stat() */
 #endif
-#ifdef USE_SSL
+#ifdef USE_OPENSSL
 #include <openssl/ssl.h>
 #endif
 
@@ -1791,7 +1791,7 @@ connection_warnings(bool in_startup)
 static void
 printSSLInfo(void)
 {
-#ifdef USE_SSL
+#ifdef USE_OPENSSL
 	int			sslbits = -1;
 	SSL		   *ssl;
 
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index e78c565..1682623 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -21,7 +21,7 @@
 #ifdef HAVE_SYS_TIME_H
 #include <sys/time.h>
 #endif
-#ifdef USE_SSL
+#ifdef USE_OPENSSL
 #include <openssl/ssl.h>
 #include <openssl/err.h>
 #endif
@@ -184,17 +184,34 @@ typedef struct Port
 #endif
 
 	/*
-	 * SSL structures (keep these last so that USE_SSL doesn't affect
+	 * SSL structures (keep these last so that USE_OPENSSL doesn't affect
 	 * locations of other fields)
 	 */
 #ifdef USE_SSL
+	bool		ssl_in_use;
+	char	   *peer_cn;
+	bool		peer_cert_valid;
+#endif
+#ifdef USE_OPENSSL
 	SSL		   *ssl;
 	X509	   *peer;
-	char	   *peer_cn;
 	unsigned long count;
 #endif
+
 } Port;
 
+#ifdef USE_SSL
+/*
+ * These functions are implemented by the glue code specific to each
+ * SSL implementation (e.g. be-secure-openssl.c)
+ */
+extern void be_tls_init(void);
+extern int be_tls_open_server(Port *port);
+extern void be_tls_close(Port *port);
+extern ssize_t be_tls_read(Port *port, void *ptr, size_t len);
+extern ssize_t be_tls_write(Port *port, void *ptr, size_t len);
+
+#endif
 
 extern ProtocolVersion FrontendProtocol;
 
diff --git a/src/include/libpq/libpq.h b/src/include/libpq/libpq.h
index e4e354d..5da9d8d 100644
--- a/src/include/libpq/libpq.h
+++ b/src/include/libpq/libpq.h
@@ -82,5 +82,14 @@ extern int	secure_open_server(Port *port);
 extern void secure_close(Port *port);
 extern ssize_t secure_read(Port *port, void *ptr, size_t len);
 extern ssize_t secure_write(Port *port, void *ptr, size_t len);
+extern ssize_t secure_raw_read(Port *port, void *ptr, size_t len);
+extern ssize_t secure_raw_write(Port *port, const void *ptr, size_t len);
+
+extern bool ssl_loaded_verify_locations;
+
+/* GUCs */
+extern char *SSLCipherSuites;
+extern char *SSLECDHCurve;
+extern bool SSLPreferServerCiphers;
 
 #endif   /* LIBPQ_H */
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index 4383ad5..5bdfa47 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -778,15 +778,15 @@
 /* Define to select named POSIX semaphores. */
 #undef USE_NAMED_POSIX_SEMAPHORES
 
+/* Define to build with OpenSSL support. (--with-openssl) */
+#undef USE_OPENSSL
+
 /* Define to 1 to build with PAM support. (--with-pam) */
 #undef USE_PAM
 
 /* Use replacement snprintf() functions. */
 #undef USE_REPL_SNPRINTF
 
-/* Define to build with (Open)SSL support. (--with-openssl) */
-#undef USE_SSL
-
 /* Define to select SysV-style semaphores. */
 #undef USE_SYSV_SEMAPHORES
 
diff --git a/src/include/pg_config.h.win32 b/src/include/pg_config.h.win32
index f7c2419..00be15f 100644
--- a/src/include/pg_config.h.win32
+++ b/src/include/pg_config.h.win32
@@ -628,15 +628,15 @@
 /* Define to select named POSIX semaphores. */
 /* #undef USE_NAMED_POSIX_SEMAPHORES */
 
+/* Define to build with OpenSSL support. (--with-openssl) */
+/* #undef USE_OPENSSL */
+
 /* Define to 1 to build with PAM support. (--with-pam) */
 /* #undef USE_PAM */
 
 /* Use replacement snprintf() functions. */
 #define USE_REPL_SNPRINTF 1
 
-/* Define to build with (Open)SSL support. (--with-openssl) */
-/* #undef USE_SSL */
-
 /* Define to select SysV-style semaphores. */
 /* #undef USE_SYSV_SEMAPHORES */
 
diff --git a/src/include/port.h b/src/include/port.h
index 9f8465e..6c35df7 100644
--- a/src/include/port.h
+++ b/src/include/port.h
@@ -479,4 +479,10 @@ extern char *escape_single_quotes_ascii(const char *src);
 /* port/wait_error.c */
 extern char *wait_result_to_str(int exit_status);
 
+/* If we're building with OpenSSL, then we have SSL support */
+/* XXX: this doesn't belong here.. I couldn't find a precedence to follow */
+#ifdef USE_OPENSSL
+#define USE_SSL
+#endif
+
 #endif   /* PG_PORT_H */
diff --git a/src/interfaces/libpq/Makefile b/src/interfaces/libpq/Makefile
index 718ecd6..a90cb89 100644
--- a/src/interfaces/libpq/Makefile
+++ b/src/interfaces/libpq/Makefile
@@ -44,6 +44,10 @@ OBJS += ip.o md5.o
 # utils/mb
 OBJS += encnames.o wchar.o
 
+ifeq ($(with_openssl),yes)
+OBJS += fe-secure-openssl.o
+endif
+
 ifeq ($(PORTNAME), cygwin)
 override shlib = cyg$(NAME)$(DLSUFFIX)
 endif
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index 540426c..b0b0e1a 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -1961,7 +1961,7 @@ keep_going:						/* We will come back to here until there is
 					conn->allow_ssl_try = false;
 				}
 				if (conn->allow_ssl_try && !conn->wait_ssl_try &&
-					conn->ssl == NULL)
+					!conn->ssl_in_use)
 				{
 					ProtocolVersion pv;
 
@@ -2040,7 +2040,7 @@ keep_going:						/* We will come back to here until there is
 				 * On first time through, get the postmaster's response to our
 				 * SSL negotiation packet.
 				 */
-				if (conn->ssl == NULL)
+				if (!conn->ssl_in_use)
 				{
 					/*
 					 * We use pqReadData here since it has the logic to
@@ -2310,7 +2310,7 @@ keep_going:						/* We will come back to here until there is
 					 * connection already, then retry with an SSL connection
 					 */
 					if (conn->sslmode[0] == 'a' /* "allow" */
-						&& conn->ssl == NULL
+						&& !conn->ssl_in_use
 						&& conn->allow_ssl_try
 						&& conn->wait_ssl_try)
 					{
@@ -2709,6 +2709,7 @@ makeEmptyPGconn(void)
 #ifdef USE_SSL
 	conn->allow_ssl_try = true;
 	conn->wait_ssl_try = false;
+	conn->ssl_in_use = false;
 #endif
 
 	/*
diff --git a/src/interfaces/libpq/fe-misc.c b/src/interfaces/libpq/fe-misc.c
index 7a213bf..d561aa5 100644
--- a/src/interfaces/libpq/fe-misc.c
+++ b/src/interfaces/libpq/fe-misc.c
@@ -743,13 +743,13 @@ retry3:
 	 * since in normal practice we should not be trying to read data unless
 	 * the file selected for reading already.
 	 *
-	 * In SSL mode it's even worse: SSL_read() could say WANT_READ and then
+	 * With OpenSSL it's even worse: SSL_read() could say WANT_READ and then
 	 * data could arrive before we make the pqReadReady() test.  So we must
 	 * play dumb and assume there is more data, relying on the SSL layer to
 	 * detect true EOF.
 	 */
 
-#ifdef USE_SSL
+#ifdef USE_OPENSSL
 	if (conn->ssl)
 		return 0;
 #endif
@@ -1050,7 +1050,7 @@ pqSocketCheck(PGconn *conn, int forRead, int forWrite, time_t end_time)
 		return -1;
 	}
 
-#ifdef USE_SSL
+#ifdef USE_OPENSSL
 	/* Check for SSL library buffering read bytes */
 	if (forRead && conn->ssl && SSL_pending(conn->ssl) > 0)
 	{
diff --git a/src/interfaces/libpq/fe-secure-openssl.c b/src/interfaces/libpq/fe-secure-openssl.c
new file mode 100644
index 0000000..559fe11
--- /dev/null
+++ b/src/interfaces/libpq/fe-secure-openssl.c
@@ -0,0 +1,1471 @@
+/*-------------------------------------------------------------------------
+ *
+ * fe-secure-openssl.c
+ *	  OpenSSL support
+ *
+ *
+ * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  src/interfaces/libpq/fe-secure-openssl.c
+ *
+ * NOTES
+ *
+ *	  We don't provide informational callbacks here (like
+ *	  info_cb() in be-secure.c), since there's no good mechanism to
+ *	  display such information to the user.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres_fe.h"
+
+#include <signal.h>
+#include <fcntl.h>
+#include <ctype.h>
+
+#include "libpq-fe.h"
+#include "fe-auth.h"
+#include "libpq-int.h"
+
+#ifdef WIN32
+#include "win32.h"
+#else
+#include <sys/socket.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#ifdef HAVE_NETINET_TCP_H
+#include <netinet/tcp.h>
+#endif
+#include <arpa/inet.h>
+#endif
+
+#include <sys/stat.h>
+
+#ifdef ENABLE_THREAD_SAFETY
+#ifdef WIN32
+#include "pthread-win32.h"
+#else
+#include <pthread.h>
+#endif
+#endif
+
+#include <openssl/ssl.h>
+#if (SSLEAY_VERSION_NUMBER >= 0x00907000L)
+#include <openssl/conf.h>
+#endif
+#ifdef USE_SSL_ENGINE
+#include <openssl/engine.h>
+#endif
+
+static bool verify_peer_name_matches_certificate(PGconn *);
+static int	verify_cb(int ok, X509_STORE_CTX *ctx);
+static void destroy_ssl_system(void);
+static int	initialize_SSL(PGconn *conn);
+static PostgresPollingStatusType open_client_SSL(PGconn *);
+static char *SSLerrmessage(void);
+static void SSLerrfree(char *buf);
+
+static int my_sock_read(BIO *h, char *buf, int size);
+static int my_sock_write(BIO *h, const char *buf, int size);
+static BIO_METHOD *my_BIO_s_socket(void);
+static int my_SSL_set_fd(PGconn *conn, int fd);
+
+
+static bool pq_init_ssl_lib = true;
+static bool pq_init_crypto_lib = true;
+
+/*
+ * SSL_context is currently shared between threads and therefore we need to be
+ * careful to lock around any usage of it when providing thread safety.
+ * ssl_config_mutex is the mutex that we use to protect it.
+ */
+static SSL_CTX *SSL_context = NULL;
+
+#ifdef ENABLE_THREAD_SAFETY
+static long ssl_open_connections = 0;
+
+#ifndef WIN32
+static pthread_mutex_t ssl_config_mutex = PTHREAD_MUTEX_INITIALIZER;
+#else
+static pthread_mutex_t ssl_config_mutex = NULL;
+static long win32_ssl_create_mutex = 0;
+#endif
+#endif   /* ENABLE_THREAD_SAFETY */
+
+
+/* ------------------------------------------------------------ */
+/*			 Procedures common to all secure sessions			*/
+/* ------------------------------------------------------------ */
+
+/*
+ *	Exported function to allow application to tell us it's already
+ *	initialized OpenSSL and/or libcrypto.
+ */
+void
+pgtls_init_library(bool do_ssl, int do_crypto)
+{
+#ifdef ENABLE_THREAD_SAFETY
+
+	/*
+	 * Disallow changing the flags while we have open connections, else we'd
+	 * get completely confused.
+	 */
+	if (ssl_open_connections != 0)
+		return;
+#endif
+
+	pq_init_ssl_lib = do_ssl;
+	pq_init_crypto_lib = do_crypto;
+}
+
+/*
+ *	Begin or continue negotiating a secure session.
+ */
+PostgresPollingStatusType
+pgtls_open_client(PGconn *conn)
+{
+	/* First time through? */
+	if (conn->ssl == NULL)
+	{
+#ifdef ENABLE_THREAD_SAFETY
+		int			rc;
+#endif
+
+		/* We cannot use MSG_NOSIGNAL to block SIGPIPE when using SSL */
+		conn->sigpipe_flag = false;
+
+#ifdef ENABLE_THREAD_SAFETY
+		if ((rc = pthread_mutex_lock(&ssl_config_mutex)))
+		{
+			printfPQExpBuffer(&conn->errorMessage,
+			   libpq_gettext("could not acquire mutex: %s\n"), strerror(rc));
+			return PGRES_POLLING_FAILED;
+		}
+#endif
+		/* Create a connection-specific SSL object */
+		if (!(conn->ssl = SSL_new(SSL_context)) ||
+			!SSL_set_app_data(conn->ssl, conn) ||
+			!my_SSL_set_fd(conn, conn->sock))
+		{
+			char	   *err = SSLerrmessage();
+
+			printfPQExpBuffer(&conn->errorMessage,
+				   libpq_gettext("could not establish SSL connection: %s\n"),
+							  err);
+			SSLerrfree(err);
+#ifdef ENABLE_THREAD_SAFETY
+			pthread_mutex_unlock(&ssl_config_mutex);
+#endif
+			pgtls_close(conn);
+
+			return PGRES_POLLING_FAILED;
+		}
+		conn->ssl_in_use = true;
+
+#ifdef ENABLE_THREAD_SAFETY
+		pthread_mutex_unlock(&ssl_config_mutex);
+#endif
+
+		/*
+		 * Load client certificate, private key, and trusted CA certs.
+		 */
+		if (initialize_SSL(conn) != 0)
+		{
+			/* initialize_SSL already put a message in conn->errorMessage */
+			pgtls_close(conn);
+			return PGRES_POLLING_FAILED;
+		}
+	}
+
+	/* Begin or continue the actual handshake */
+	return open_client_SSL(conn);
+}
+
+/*
+ *	Read data from a secure connection.
+ *
+ * On failure, this function is responsible for putting a suitable message
+ * into conn->errorMessage.  The caller must still inspect errno, but only
+ * to determine whether to continue/retry after error.
+ */
+ssize_t
+pgtls_read(PGconn *conn, void *ptr, size_t len)
+{
+	ssize_t		n;
+	int			result_errno = 0;
+	char		sebuf[256];
+	int			err;
+
+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 */
+				printfPQExpBuffer(&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;
+				if (result_errno == EPIPE ||
+					result_errno == ECONNRESET)
+					printfPQExpBuffer(&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
+					printfPQExpBuffer(&conn->errorMessage,
+									libpq_gettext("SSL SYSCALL error: %s\n"),
+									  SOCK_STRERROR(result_errno,
+													sebuf, sizeof(sebuf)));
+			}
+			else
+			{
+				printfPQExpBuffer(&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();
+
+				printfPQExpBuffer(&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.
+			 */
+			printfPQExpBuffer(&conn->errorMessage,
+							  libpq_gettext("SSL connection has been closed unexpectedly\n"));
+			result_errno = ECONNRESET;
+			n = -1;
+			break;
+		default:
+			printfPQExpBuffer(&conn->errorMessage,
+						  libpq_gettext("unrecognized SSL error code: %d\n"),
+							  err);
+			/* assume the connection is broken */
+			result_errno = ECONNRESET;
+			n = -1;
+			break;
+	}
+
+	/* ensure we return the intended errno to caller */
+	SOCK_ERRNO_SET(result_errno);
+
+	return n;
+}
+
+/*
+ *	Write data to a secure connection.
+ *
+ * On failure, this function is responsible for putting a suitable message
+ * into conn->errorMessage.  The caller must still inspect errno, but only
+ * to determine whether to continue/retry after error.
+ */
+ssize_t
+pgtls_write(PGconn *conn, const void *ptr, size_t len)
+{
+	ssize_t		n;
+	int			result_errno = 0;
+	char		sebuf[256];
+	int			err;
+
+	SOCK_ERRNO_SET(0);
+	n = SSL_write(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 */
+				printfPQExpBuffer(&conn->errorMessage,
+								  "SSL_write failed but did not provide error information\n");
+				/* assume the connection is broken */
+				result_errno = ECONNRESET;
+			}
+			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:
+			if (n < 0)
+			{
+				result_errno = SOCK_ERRNO;
+				if (result_errno == EPIPE || result_errno == ECONNRESET)
+					printfPQExpBuffer(&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
+					printfPQExpBuffer(&conn->errorMessage,
+									libpq_gettext("SSL SYSCALL error: %s\n"),
+									  SOCK_STRERROR(result_errno,
+													sebuf, sizeof(sebuf)));
+			}
+			else
+			{
+				printfPQExpBuffer(&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();
+
+				printfPQExpBuffer(&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.
+			 */
+			printfPQExpBuffer(&conn->errorMessage,
+							  libpq_gettext("SSL connection has been closed unexpectedly\n"));
+			result_errno = ECONNRESET;
+			n = -1;
+			break;
+		default:
+			printfPQExpBuffer(&conn->errorMessage,
+						  libpq_gettext("unrecognized SSL error code: %d\n"),
+							  err);
+			/* assume the connection is broken */
+			result_errno = ECONNRESET;
+			n = -1;
+			break;
+	}
+
+	/* ensure we return the intended errno to caller */
+	SOCK_ERRNO_SET(result_errno);
+
+	return n;
+}
+
+/* ------------------------------------------------------------ */
+/*						OpenSSL specific code					*/
+/* ------------------------------------------------------------ */
+
+/*
+ *	Certificate verification callback
+ *
+ *	This callback allows us to log intermediate problems during
+ *	verification, but there doesn't seem to be a clean way to get
+ *	our PGconn * structure.  So we can't log anything!
+ *
+ *	This callback also allows us to override the default acceptance
+ *	criteria (e.g., accepting self-signed or expired certs), but
+ *	for now we accept the default checks.
+ */
+static int
+verify_cb(int ok, X509_STORE_CTX *ctx)
+{
+	return ok;
+}
+
+
+/*
+ * Check if a wildcard certificate matches the server hostname.
+ *
+ * The rule for this is:
+ *	1. We only match the '*' character as wildcard
+ *	2. We match only wildcards at the start of the string
+ *	3. The '*' character does *not* match '.', meaning that we match only
+ *	   a single pathname component.
+ *	4. We don't support more than one '*' in a single pattern.
+ *
+ * This is roughly in line with RFC2818, but contrary to what most browsers
+ * appear to be implementing (point 3 being the difference)
+ *
+ * Matching is always case-insensitive, since DNS is case insensitive.
+ */
+static int
+wildcard_certificate_match(const char *pattern, const char *string)
+{
+	int			lenpat = strlen(pattern);
+	int			lenstr = strlen(string);
+
+	/* If we don't start with a wildcard, it's not a match (rule 1 & 2) */
+	if (lenpat < 3 ||
+		pattern[0] != '*' ||
+		pattern[1] != '.')
+		return 0;
+
+	if (lenpat > lenstr)
+		/* If pattern is longer than the string, we can never match */
+		return 0;
+
+	if (pg_strcasecmp(pattern + 1, string + lenstr - lenpat + 1) != 0)
+
+		/*
+		 * If string does not end in pattern (minus the wildcard), we don't
+		 * match
+		 */
+		return 0;
+
+	if (strchr(string, '.') < string + lenstr - lenpat)
+
+		/*
+		 * If there is a dot left of where the pattern started to match, we
+		 * don't match (rule 3)
+		 */
+		return 0;
+
+	/* String ended with pattern, and didn't have a dot before, so we match */
+	return 1;
+}
+
+
+/*
+ *	Verify that common name resolves to peer.
+ */
+static bool
+verify_peer_name_matches_certificate(PGconn *conn)
+{
+	char	   *peer_cn;
+	int			r;
+	int			len;
+	bool		result;
+
+	/*
+	 * If told not to verify the peer name, don't do it. Return true
+	 * indicating that the verification was successful.
+	 */
+	if (strcmp(conn->sslmode, "verify-full") != 0)
+		return true;
+
+	/*
+	 * Extract the common name from the certificate.
+	 *
+	 * XXX: Should support alternate names here
+	 */
+	/* First find out the name's length and allocate a buffer for it. */
+	len = X509_NAME_get_text_by_NID(X509_get_subject_name(conn->peer),
+									NID_commonName, NULL, 0);
+	if (len == -1)
+	{
+		printfPQExpBuffer(&conn->errorMessage,
+						  libpq_gettext("could not get server common name from server certificate\n"));
+		return false;
+	}
+	peer_cn = malloc(len + 1);
+	if (peer_cn == NULL)
+	{
+		printfPQExpBuffer(&conn->errorMessage,
+						  libpq_gettext("out of memory\n"));
+		return false;
+	}
+
+	r = X509_NAME_get_text_by_NID(X509_get_subject_name(conn->peer),
+								  NID_commonName, peer_cn, len + 1);
+	if (r != len)
+	{
+		/* Got different length than on the first call. Shouldn't happen. */
+		printfPQExpBuffer(&conn->errorMessage,
+						  libpq_gettext("could not get server common name from server certificate\n"));
+		free(peer_cn);
+		return false;
+	}
+	peer_cn[len] = '\0';
+
+	/*
+	 * Reject embedded NULLs in certificate common name to prevent attacks
+	 * like CVE-2009-4034.
+	 */
+	if (len != strlen(peer_cn))
+	{
+		printfPQExpBuffer(&conn->errorMessage,
+						  libpq_gettext("SSL certificate's common name contains embedded null\n"));
+		free(peer_cn);
+		return false;
+	}
+
+	/*
+	 * We got the peer's common name. Now compare it against the originally
+	 * given hostname.
+	 */
+	if (!(conn->pghost && conn->pghost[0] != '\0'))
+	{
+		printfPQExpBuffer(&conn->errorMessage,
+						  libpq_gettext("host name must be specified for a verified SSL connection\n"));
+		result = false;
+	}
+	else
+	{
+		if (pg_strcasecmp(peer_cn, conn->pghost) == 0)
+			/* Exact name match */
+			result = true;
+		else if (wildcard_certificate_match(peer_cn, conn->pghost))
+			/* Matched wildcard certificate */
+			result = true;
+		else
+		{
+			printfPQExpBuffer(&conn->errorMessage,
+							  libpq_gettext("server common name \"%s\" does not match host name \"%s\"\n"),
+							  peer_cn, conn->pghost);
+			result = false;
+		}
+	}
+
+	free(peer_cn);
+	return result;
+}
+
+#ifdef ENABLE_THREAD_SAFETY
+/*
+ *	Callback functions for OpenSSL internal locking
+ */
+
+static unsigned long
+pq_threadidcallback(void)
+{
+	/*
+	 * This is not standards-compliant.  pthread_self() returns pthread_t, and
+	 * shouldn't be cast to unsigned long, but CRYPTO_set_id_callback requires
+	 * it, so we have to do it.
+	 */
+	return (unsigned long) pthread_self();
+}
+
+static pthread_mutex_t *pq_lockarray;
+
+static void
+pq_lockingcallback(int mode, int n, const char *file, int line)
+{
+	if (mode & CRYPTO_LOCK)
+	{
+		if (pthread_mutex_lock(&pq_lockarray[n]))
+			PGTHREAD_ERROR("failed to lock mutex");
+	}
+	else
+	{
+		if (pthread_mutex_unlock(&pq_lockarray[n]))
+			PGTHREAD_ERROR("failed to unlock mutex");
+	}
+}
+#endif   /* ENABLE_THREAD_SAFETY */
+
+/*
+ * Initialize SSL system, in particular creating the SSL_context object
+ * that will be shared by all SSL-using connections in this process.
+ *
+ * In threadsafe mode, this includes setting up libcrypto callback functions
+ * to do thread locking.
+ *
+ * If the caller has told us (through PQinitOpenSSL) that he's taking care
+ * of libcrypto, we expect that callbacks are already set, and won't try to
+ * override it.
+ *
+ * The conn parameter is only used to be able to pass back an error
+ * message - no connection-local setup is made here.
+ *
+ * Returns 0 if OK, -1 on failure (with a message in conn->errorMessage).
+ */
+int
+pgtls_init(PGconn *conn)
+{
+#ifdef ENABLE_THREAD_SAFETY
+#ifdef WIN32
+	/* Also see similar code in fe-connect.c, default_threadlock() */
+	if (ssl_config_mutex == NULL)
+	{
+		while (InterlockedExchange(&win32_ssl_create_mutex, 1) == 1)
+			 /* loop, another thread own the lock */ ;
+		if (ssl_config_mutex == NULL)
+		{
+			if (pthread_mutex_init(&ssl_config_mutex, NULL))
+				return -1;
+		}
+		InterlockedExchange(&win32_ssl_create_mutex, 0);
+	}
+#endif
+	if (pthread_mutex_lock(&ssl_config_mutex))
+		return -1;
+
+	if (pq_init_crypto_lib)
+	{
+		/*
+		 * If necessary, set up an array to hold locks for libcrypto.
+		 * libcrypto will tell us how big to make this array.
+		 */
+		if (pq_lockarray == NULL)
+		{
+			int			i;
+
+			pq_lockarray = malloc(sizeof(pthread_mutex_t) * CRYPTO_num_locks());
+			if (!pq_lockarray)
+			{
+				pthread_mutex_unlock(&ssl_config_mutex);
+				return -1;
+			}
+			for (i = 0; i < CRYPTO_num_locks(); i++)
+			{
+				if (pthread_mutex_init(&pq_lockarray[i], NULL))
+				{
+					free(pq_lockarray);
+					pq_lockarray = NULL;
+					pthread_mutex_unlock(&ssl_config_mutex);
+					return -1;
+				}
+			}
+		}
+
+		if (ssl_open_connections++ == 0)
+		{
+			/* These are only required for threaded libcrypto applications */
+			CRYPTO_set_id_callback(pq_threadidcallback);
+			CRYPTO_set_locking_callback(pq_lockingcallback);
+		}
+	}
+#endif   /* ENABLE_THREAD_SAFETY */
+
+	if (!SSL_context)
+	{
+		if (pq_init_ssl_lib)
+		{
+#if SSLEAY_VERSION_NUMBER >= 0x00907000L
+			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)
+		{
+			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(&ssl_config_mutex);
+#endif
+			return -1;
+		}
+
+		/* Disable old protocol versions */
+		SSL_CTX_set_options(SSL_context, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
+
+		/*
+		 * 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);
+	}
+
+#ifdef ENABLE_THREAD_SAFETY
+	pthread_mutex_unlock(&ssl_config_mutex);
+#endif
+	return 0;
+}
+
+/*
+ *	This function is needed because if the libpq library is unloaded
+ *	from the application, the callback functions will no longer exist when
+ *	libcrypto is used by other parts of the system.  For this reason,
+ *	we unregister the callback functions when the last libpq
+ *	connection is closed.  (The same would apply for OpenSSL callbacks
+ *	if we had any.)
+ *
+ *	Callbacks are only set when we're compiled in threadsafe mode, so
+ *	we only need to remove them in this case.
+ */
+static void
+destroy_ssl_system(void)
+{
+#ifdef ENABLE_THREAD_SAFETY
+	/* Mutex is created in initialize_ssl_system() */
+	if (pthread_mutex_lock(&ssl_config_mutex))
+		return;
+
+	if (pq_init_crypto_lib && ssl_open_connections > 0)
+		--ssl_open_connections;
+
+	if (pq_init_crypto_lib && ssl_open_connections == 0)
+	{
+		/* No connections left, unregister libcrypto callbacks */
+		CRYPTO_set_locking_callback(NULL);
+		CRYPTO_set_id_callback(NULL);
+
+		/*
+		 * We don't free the lock array or the SSL_context. If we get another
+		 * connection in this process, we will just re-use them with the
+		 * existing mutexes.
+		 *
+		 * This means we leak a little memory on repeated load/unload of the
+		 * library.
+		 */
+	}
+
+	pthread_mutex_unlock(&ssl_config_mutex);
+#endif
+}
+
+/*
+ *	Initialize (potentially) per-connection SSL data, namely the
+ *	client certificate, private key, and trusted CA certs.
+ *
+ *	conn->ssl must already be created.  It receives the connection's client
+ *	certificate and private key.  Note however that certificates also get
+ *	loaded into the SSL_context object, and are therefore accessible to all
+ *	connections in this process.  This should be OK as long as there aren't
+ *	any hash collisions among the certs.
+ *
+ *	Returns 0 if OK, -1 on failure (with a message in conn->errorMessage).
+ */
+static int
+initialize_SSL(PGconn *conn)
+{
+	struct stat buf;
+	char		homedir[MAXPGPATH];
+	char		fnbuf[MAXPGPATH];
+	char		sebuf[256];
+	bool		have_homedir;
+	bool		have_cert;
+	EVP_PKEY   *pkey = NULL;
+
+	/*
+	 * We'll need the home directory if any of the relevant parameters are
+	 * defaulted.  If pqGetHomeDirectory fails, act as though none of the
+	 * files could be found.
+	 */
+	if (!(conn->sslcert && strlen(conn->sslcert) > 0) ||
+		!(conn->sslkey && strlen(conn->sslkey) > 0) ||
+		!(conn->sslrootcert && strlen(conn->sslrootcert) > 0) ||
+		!(conn->sslcrl && strlen(conn->sslcrl) > 0))
+		have_homedir = pqGetHomeDirectory(homedir, sizeof(homedir));
+	else	/* won't need it */
+		have_homedir = false;
+
+	/* Read the client certificate file */
+	if (conn->sslcert && strlen(conn->sslcert) > 0)
+		strncpy(fnbuf, conn->sslcert, sizeof(fnbuf));
+	else if (have_homedir)
+		snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, USER_CERT_FILE);
+	else
+		fnbuf[0] = '\0';
+
+	if (fnbuf[0] == '\0')
+	{
+		/* no home directory, proceed without a client cert */
+		have_cert = false;
+	}
+	else if (stat(fnbuf, &buf) != 0)
+	{
+		/*
+		 * If file is not present, just go on without a client cert; server
+		 * might or might not accept the connection.  Any other error,
+		 * however, is grounds for complaint.
+		 */
+		if (errno != ENOENT && errno != ENOTDIR)
+		{
+			printfPQExpBuffer(&conn->errorMessage,
+			   libpq_gettext("could not open certificate file \"%s\": %s\n"),
+							  fnbuf, pqStrerror(errno, sebuf, sizeof(sebuf)));
+			return -1;
+		}
+		have_cert = false;
+	}
+	else
+	{
+		/*
+		 * Cert file exists, so load it.  Since OpenSSL doesn't provide the
+		 * equivalent of "SSL_use_certificate_chain_file", we actually have to
+		 * load the file twice.  The first call loads any extra certs after
+		 * the first one into chain-cert storage associated with the
+		 * SSL_context.  The second call loads the first cert (only) into the
+		 * SSL object, where it will be correctly paired with the private key
+		 * we load below.  We do it this way so that each connection
+		 * understands which subject cert to present, in case different
+		 * sslcert settings are used for different connections in the same
+		 * process.
+		 *
+		 * NOTE: This function may also modify our SSL_context and therefore
+		 * we have to lock around this call and any places where we use the
+		 * SSL_context struct.
+		 */
+#ifdef ENABLE_THREAD_SAFETY
+		int			rc;
+
+		if ((rc = pthread_mutex_lock(&ssl_config_mutex)))
+		{
+			printfPQExpBuffer(&conn->errorMessage,
+			   libpq_gettext("could not acquire mutex: %s\n"), strerror(rc));
+			return -1;
+		}
+#endif
+		if (SSL_CTX_use_certificate_chain_file(SSL_context, fnbuf) != 1)
+		{
+			char	   *err = SSLerrmessage();
+
+			printfPQExpBuffer(&conn->errorMessage,
+			   libpq_gettext("could not read certificate file \"%s\": %s\n"),
+							  fnbuf, err);
+			SSLerrfree(err);
+
+#ifdef ENABLE_THREAD_SAFETY
+			pthread_mutex_unlock(&ssl_config_mutex);
+#endif
+			return -1;
+		}
+
+		if (SSL_use_certificate_file(conn->ssl, fnbuf, SSL_FILETYPE_PEM) != 1)
+		{
+			char	   *err = SSLerrmessage();
+
+			printfPQExpBuffer(&conn->errorMessage,
+			   libpq_gettext("could not read certificate file \"%s\": %s\n"),
+							  fnbuf, err);
+			SSLerrfree(err);
+#ifdef ENABLE_THREAD_SAFETY
+			pthread_mutex_unlock(&ssl_config_mutex);
+#endif
+			return -1;
+		}
+
+		/* need to load the associated private key, too */
+		have_cert = true;
+
+#ifdef ENABLE_THREAD_SAFETY
+		pthread_mutex_unlock(&ssl_config_mutex);
+#endif
+	}
+
+	/*
+	 * Read the SSL key. If a key is specified, treat it as an engine:key
+	 * combination if there is colon present - we don't support files with
+	 * colon in the name. The exception is if the second character is a colon,
+	 * in which case it can be a Windows filename with drive specification.
+	 */
+	if (have_cert && conn->sslkey && strlen(conn->sslkey) > 0)
+	{
+#ifdef USE_SSL_ENGINE
+		if (strchr(conn->sslkey, ':')
+#ifdef WIN32
+			&& conn->sslkey[1] != ':'
+#endif
+			)
+		{
+			/* Colon, but not in second character, treat as engine:key */
+			char	   *engine_str = strdup(conn->sslkey);
+			char	   *engine_colon;
+
+			if (engine_str == NULL)
+			{
+				printfPQExpBuffer(&conn->errorMessage,
+								  libpq_gettext("out of memory\n"));
+				return -1;
+			}
+
+			/* cannot return NULL because we already checked before strdup */
+			engine_colon = strchr(engine_str, ':');
+
+			*engine_colon = '\0';		/* engine_str now has engine name */
+			engine_colon++;		/* engine_colon now has key name */
+
+			conn->engine = ENGINE_by_id(engine_str);
+			if (conn->engine == NULL)
+			{
+				char	   *err = SSLerrmessage();
+
+				printfPQExpBuffer(&conn->errorMessage,
+					 libpq_gettext("could not load SSL engine \"%s\": %s\n"),
+								  engine_str, err);
+				SSLerrfree(err);
+				free(engine_str);
+				return -1;
+			}
+
+			if (ENGINE_init(conn->engine) == 0)
+			{
+				char	   *err = SSLerrmessage();
+
+				printfPQExpBuffer(&conn->errorMessage,
+				libpq_gettext("could not initialize SSL engine \"%s\": %s\n"),
+								  engine_str, err);
+				SSLerrfree(err);
+				ENGINE_free(conn->engine);
+				conn->engine = NULL;
+				free(engine_str);
+				return -1;
+			}
+
+			pkey = ENGINE_load_private_key(conn->engine, engine_colon,
+										   NULL, NULL);
+			if (pkey == NULL)
+			{
+				char	   *err = SSLerrmessage();
+
+				printfPQExpBuffer(&conn->errorMessage,
+								  libpq_gettext("could not read private SSL key \"%s\" from engine \"%s\": %s\n"),
+								  engine_colon, engine_str, err);
+				SSLerrfree(err);
+				ENGINE_finish(conn->engine);
+				ENGINE_free(conn->engine);
+				conn->engine = NULL;
+				free(engine_str);
+				return -1;
+			}
+			if (SSL_use_PrivateKey(conn->ssl, pkey) != 1)
+			{
+				char	   *err = SSLerrmessage();
+
+				printfPQExpBuffer(&conn->errorMessage,
+								  libpq_gettext("could not load private SSL key \"%s\" from engine \"%s\": %s\n"),
+								  engine_colon, engine_str, err);
+				SSLerrfree(err);
+				ENGINE_finish(conn->engine);
+				ENGINE_free(conn->engine);
+				conn->engine = NULL;
+				free(engine_str);
+				return -1;
+			}
+
+			free(engine_str);
+
+			fnbuf[0] = '\0';	/* indicate we're not going to load from a
+								 * file */
+		}
+		else
+#endif   /* USE_SSL_ENGINE */
+		{
+			/* PGSSLKEY is not an engine, treat it as a filename */
+			strncpy(fnbuf, conn->sslkey, sizeof(fnbuf));
+		}
+	}
+	else if (have_homedir)
+	{
+		/* No PGSSLKEY specified, load default file */
+		snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, USER_KEY_FILE);
+	}
+	else
+		fnbuf[0] = '\0';
+
+	if (have_cert && fnbuf[0] != '\0')
+	{
+		/* read the client key from file */
+
+		if (stat(fnbuf, &buf) != 0)
+		{
+			printfPQExpBuffer(&conn->errorMessage,
+							  libpq_gettext("certificate present, but not private key file \"%s\"\n"),
+							  fnbuf);
+			return -1;
+		}
+#ifndef WIN32
+		if (!S_ISREG(buf.st_mode) || buf.st_mode & (S_IRWXG | S_IRWXO))
+		{
+			printfPQExpBuffer(&conn->errorMessage,
+							  libpq_gettext("private key file \"%s\" has group or world access; permissions should be u=rw (0600) or less\n"),
+							  fnbuf);
+			return -1;
+		}
+#endif
+
+		if (SSL_use_PrivateKey_file(conn->ssl, fnbuf, SSL_FILETYPE_PEM) != 1)
+		{
+			char	   *err = SSLerrmessage();
+
+			printfPQExpBuffer(&conn->errorMessage,
+			   libpq_gettext("could not load private key file \"%s\": %s\n"),
+							  fnbuf, err);
+			SSLerrfree(err);
+			return -1;
+		}
+	}
+
+	/* verify that the cert and key go together */
+	if (have_cert &&
+		SSL_check_private_key(conn->ssl) != 1)
+	{
+		char	   *err = SSLerrmessage();
+
+		printfPQExpBuffer(&conn->errorMessage,
+						  libpq_gettext("certificate does not match private key file \"%s\": %s\n"),
+						  fnbuf, err);
+		SSLerrfree(err);
+		return -1;
+	}
+
+	/*
+	 * If the root cert file exists, load it so we can perform certificate
+	 * verification. If sslmode is "verify-full" we will also do further
+	 * verification after the connection has been completed.
+	 */
+	if (conn->sslrootcert && strlen(conn->sslrootcert) > 0)
+		strncpy(fnbuf, conn->sslrootcert, sizeof(fnbuf));
+	else if (have_homedir)
+		snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, ROOT_CERT_FILE);
+	else
+		fnbuf[0] = '\0';
+
+	if (fnbuf[0] != '\0' &&
+		stat(fnbuf, &buf) == 0)
+	{
+		X509_STORE *cvstore;
+
+#ifdef ENABLE_THREAD_SAFETY
+		int			rc;
+
+		if ((rc = pthread_mutex_lock(&ssl_config_mutex)))
+		{
+			printfPQExpBuffer(&conn->errorMessage,
+			   libpq_gettext("could not acquire mutex: %s\n"), strerror(rc));
+			return -1;
+		}
+#endif
+		if (SSL_CTX_load_verify_locations(SSL_context, fnbuf, NULL) != 1)
+		{
+			char	   *err = SSLerrmessage();
+
+			printfPQExpBuffer(&conn->errorMessage,
+							  libpq_gettext("could not read root certificate file \"%s\": %s\n"),
+							  fnbuf, err);
+			SSLerrfree(err);
+#ifdef ENABLE_THREAD_SAFETY
+			pthread_mutex_unlock(&ssl_config_mutex);
+#endif
+			return -1;
+		}
+
+		if ((cvstore = SSL_CTX_get_cert_store(SSL_context)) != NULL)
+		{
+			if (conn->sslcrl && strlen(conn->sslcrl) > 0)
+				strncpy(fnbuf, conn->sslcrl, sizeof(fnbuf));
+			else if (have_homedir)
+				snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, ROOT_CRL_FILE);
+			else
+				fnbuf[0] = '\0';
+
+			/* Set the flags to check against the complete CRL chain */
+			if (fnbuf[0] != '\0' &&
+				X509_STORE_load_locations(cvstore, fnbuf, 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
+				char	   *err = SSLerrmessage();
+
+				printfPQExpBuffer(&conn->errorMessage,
+								  libpq_gettext("SSL library does not support CRL certificates (file \"%s\")\n"),
+								  fnbuf);
+				SSLerrfree(err);
+#ifdef ENABLE_THREAD_SAFETY
+				pthread_mutex_unlock(&ssl_config_mutex);
+#endif
+				return -1;
+#endif
+			}
+			/* if not found, silently ignore;  we do not require CRL */
+		}
+#ifdef ENABLE_THREAD_SAFETY
+		pthread_mutex_unlock(&ssl_config_mutex);
+#endif
+
+		SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, verify_cb);
+	}
+	else
+	{
+		/*
+		 * stat() failed; assume root file doesn't exist.  If sslmode is
+		 * verify-ca or verify-full, this is an error.  Otherwise, continue
+		 * without performing any server cert verification.
+		 */
+		if (conn->sslmode[0] == 'v')	/* "verify-ca" or "verify-full" */
+		{
+			/*
+			 * The only way to reach here with an empty filename is if
+			 * pqGetHomeDirectory failed.  That's a sufficiently unusual case
+			 * that it seems worth having a specialized error message for it.
+			 */
+			if (fnbuf[0] == '\0')
+				printfPQExpBuffer(&conn->errorMessage,
+								  libpq_gettext("could not get home directory to locate root certificate file\n"
+												"Either provide the file or change sslmode to disable server certificate verification.\n"));
+			else
+				printfPQExpBuffer(&conn->errorMessage,
+				libpq_gettext("root certificate file \"%s\" does not exist\n"
+							  "Either provide the file or change sslmode to disable server certificate verification.\n"), fnbuf);
+			return -1;
+		}
+	}
+
+	/*
+	 * If the OpenSSL version used supports it (from 1.0.0 on) and the user
+	 * requested it, disable SSL compression.
+	 */
+#ifdef SSL_OP_NO_COMPRESSION
+	if (conn->sslcompression && conn->sslcompression[0] == '0')
+	{
+		SSL_set_options(conn->ssl, SSL_OP_NO_COMPRESSION);
+	}
+#endif
+
+	return 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"));
+					pgtls_close(conn);
+					return PGRES_POLLING_FAILED;
+				}
+			case SSL_ERROR_SSL:
+				{
+					char	   *err = SSLerrmessage();
+
+					printfPQExpBuffer(&conn->errorMessage,
+									  libpq_gettext("SSL error: %s\n"),
+									  err);
+					SSLerrfree(err);
+					pgtls_close(conn);
+					return PGRES_POLLING_FAILED;
+				}
+
+			default:
+				printfPQExpBuffer(&conn->errorMessage,
+						  libpq_gettext("unrecognized SSL error code: %d\n"),
+								  err);
+				pgtls_close(conn);
+				return PGRES_POLLING_FAILED;
+		}
+	}
+
+	/*
+	 * We already checked the server certificate in initialize_SSL() using
+	 * SSL_CTX_set_verify(), if root.crt exists.
+	 */
+
+	/* get server certificate */
+	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);
+		pgtls_close(conn);
+		return PGRES_POLLING_FAILED;
+	}
+
+	if (!verify_peer_name_matches_certificate(conn))
+	{
+		pgtls_close(conn);
+		return PGRES_POLLING_FAILED;
+	}
+
+	/* SSL handshake is complete */
+	return PGRES_POLLING_OK;
+}
+
+/*
+ *	Close SSL connection.
+ */
+void
+pgtls_close(PGconn *conn)
+{
+	bool		destroy_needed = false;
+
+	if (conn->ssl)
+	{
+		/*
+		 * We can't destroy everything SSL-related here due to the possible
+		 * later calls to OpenSSL routines which may need our thread
+		 * callbacks, so set a flag here and check at the end.
+		 */
+		destroy_needed = true;
+
+		SSL_shutdown(conn->ssl);
+		SSL_free(conn->ssl);
+		conn->ssl = NULL;
+		conn->ssl_in_use = false;
+	}
+
+	if (conn->peer)
+	{
+		X509_free(conn->peer);
+		conn->peer = NULL;
+	}
+
+#ifdef USE_SSL_ENGINE
+	if (conn->engine)
+	{
+		ENGINE_finish(conn->engine);
+		ENGINE_free(conn->engine);
+		conn->engine = NULL;
+	}
+#endif
+
+	/*
+	 * This will remove our SSL locking hooks, if this is the last SSL
+	 * connection, which means we must wait to call it until after all SSL
+	 * calls have been made, otherwise we can end up with a race condition and
+	 * possible deadlocks.
+	 *
+	 * See comments above destroy_ssl_system().
+	 */
+	if (destroy_needed)
+		destroy_ssl_system();
+}
+
+
+/*
+ * Obtain reason string for last SSL error
+ *
+ * Some caution is needed here since ERR_reason_error_string will
+ * return NULL if it doesn't recognize the error code.  We don't
+ * want to return NULL ever.
+ */
+static char ssl_nomem[] = "out of memory allocating error description";
+
+#define SSL_ERR_LEN 128
+
+static char *
+SSLerrmessage(void)
+{
+	unsigned long errcode;
+	const char *errreason;
+	char	   *errbuf;
+
+	errbuf = malloc(SSL_ERR_LEN);
+	if (!errbuf)
+		return ssl_nomem;
+	errcode = ERR_get_error();
+	if (errcode == 0)
+	{
+		snprintf(errbuf, SSL_ERR_LEN, libpq_gettext("no SSL error reported"));
+		return errbuf;
+	}
+	errreason = ERR_reason_error_string(errcode);
+	if (errreason != NULL)
+	{
+		strlcpy(errbuf, errreason, SSL_ERR_LEN);
+		return errbuf;
+	}
+	snprintf(errbuf, SSL_ERR_LEN, libpq_gettext("SSL error code %lu"), errcode);
+	return errbuf;
+}
+
+static void
+SSLerrfree(char *buf)
+{
+	if (buf != ssl_nomem)
+		free(buf);
+}
+
+/*
+ *	Return pointer to OpenSSL object.
+ */
+void *
+PQgetssl(PGconn *conn)
+{
+	if (!conn)
+		return NULL;
+	return conn->ssl;
+}
+
+
+/*
+ * Private substitute BIO: this does the sending and receiving using send() and
+ * recv() instead. This is so that we can enable and disable interrupts
+ * just while calling recv(). We cannot have interrupts occurring while
+ * the bulk of openssl runs, because it uses malloc() and possibly other
+ * non-reentrant libc facilities. We also need to call send() and recv()
+ * directly so it gets passed through the socket/signals layer on Win32.
+ *
+ * These functions are closely modelled on the standard socket BIO in OpenSSL;
+ * see sock_read() and sock_write() in OpenSSL's crypto/bio/bss_sock.c.
+ * XXX OpenSSL 1.0.1e considers many more errcodes than just EINTR as reasons
+ * to retry; do we need to adopt their logic for that?
+ */
+
+static bool my_bio_initialized = false;
+static BIO_METHOD my_bio_methods;
+
+static int
+my_sock_read(BIO *h, char *buf, int size)
+{
+	int			res;
+	int			save_errno;
+
+	res = pqsecure_raw_read((PGconn *) h->ptr, buf, size);
+	save_errno = errno;
+	BIO_clear_retry_flags(h);
+	if (res < 0)
+	{
+		switch (save_errno)
+		{
+#ifdef EAGAIN
+			case EAGAIN:
+#endif
+#if defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN))
+			case EWOULDBLOCK:
+#endif
+			case EINTR:
+				BIO_set_retry_read(h);
+				break;
+
+			default:
+				break;
+		}
+	}
+
+	errno = save_errno;
+	return res;
+}
+
+static int
+my_sock_write(BIO *h, const char *buf, int size)
+{
+	int			res;
+	int			save_errno;
+
+	res = pqsecure_raw_write((PGconn *) h->ptr, buf, size);
+	save_errno = errno;
+	BIO_clear_retry_flags(h);
+	if (res <= 0)
+	{
+		if (save_errno == EINTR)
+		{
+			BIO_set_retry_write(h);
+		}
+	}
+
+	return res;
+}
+
+static BIO_METHOD *
+my_BIO_s_socket(void)
+{
+	if (!my_bio_initialized)
+	{
+		memcpy(&my_bio_methods, BIO_s_socket(), sizeof(BIO_METHOD));
+		my_bio_methods.bread = my_sock_read;
+		my_bio_methods.bwrite = my_sock_write;
+		my_bio_initialized = true;
+	}
+	return &my_bio_methods;
+}
+
+/* This should exactly match openssl's SSL_set_fd except for using my BIO */
+static int
+my_SSL_set_fd(PGconn *conn, int fd)
+{
+	int			ret = 0;
+	BIO		   *bio = NULL;
+
+	bio = BIO_new(my_BIO_s_socket());
+	if (bio == NULL)
+	{
+		SSLerr(SSL_F_SSL_SET_FD, ERR_R_BUF_LIB);
+		goto err;
+	}
+	/* Use 'ptr' to store pointer to PGconn */
+	bio->ptr = conn;
+
+	SSL_set_bio(conn->ssl, bio, bio);
+	BIO_set_fd(bio, fd, BIO_NOCLOSE);
+	ret = 1;
+err:
+	return ret;
+}
diff --git a/src/interfaces/libpq/fe-secure.c b/src/interfaces/libpq/fe-secure.c
index 9ba3567..012c718 100644
--- a/src/interfaces/libpq/fe-secure.c
+++ b/src/interfaces/libpq/fe-secure.c
@@ -55,64 +55,6 @@
 #endif
 #endif
 
-#ifdef USE_SSL
-
-#include <openssl/ssl.h>
-#if (SSLEAY_VERSION_NUMBER >= 0x00907000L)
-#include <openssl/conf.h>
-#endif
-#ifdef USE_SSL_ENGINE
-#include <openssl/engine.h>
-#endif
-
-
-#ifndef WIN32
-#define USER_CERT_FILE		".postgresql/postgresql.crt"
-#define USER_KEY_FILE		".postgresql/postgresql.key"
-#define ROOT_CERT_FILE		".postgresql/root.crt"
-#define ROOT_CRL_FILE		".postgresql/root.crl"
-#else
-/* On Windows, the "home" directory is already PostgreSQL-specific */
-#define USER_CERT_FILE		"postgresql.crt"
-#define USER_KEY_FILE		"postgresql.key"
-#define ROOT_CERT_FILE		"root.crt"
-#define ROOT_CRL_FILE		"root.crl"
-#endif
-
-static bool verify_peer_name_matches_certificate(PGconn *);
-static int	verify_cb(int ok, X509_STORE_CTX *ctx);
-static int	init_ssl_system(PGconn *conn);
-static void destroy_ssl_system(void);
-static int	initialize_SSL(PGconn *conn);
-static void destroySSL(void);
-static PostgresPollingStatusType open_client_SSL(PGconn *);
-static void close_SSL(PGconn *);
-static char *SSLerrmessage(void);
-static void SSLerrfree(char *buf);
-
-static bool pq_init_ssl_lib = true;
-static bool pq_init_crypto_lib = true;
-
-/*
- * SSL_context is currently shared between threads and therefore we need to be
- * careful to lock around any usage of it when providing thread safety.
- * ssl_config_mutex is the mutex that we use to protect it.
- */
-static SSL_CTX *SSL_context = NULL;
-
-#ifdef ENABLE_THREAD_SAFETY
-static long ssl_open_connections = 0;
-
-#ifndef WIN32
-static pthread_mutex_t ssl_config_mutex = PTHREAD_MUTEX_INITIALIZER;
-#else
-static pthread_mutex_t ssl_config_mutex = NULL;
-static long win32_ssl_create_mutex = 0;
-#endif
-#endif   /* ENABLE_THREAD_SAFETY */
-#endif   /* SSL */
-
-
 /*
  * Macros to handle disabling and then restoring the state of SIGPIPE handling.
  * On Windows, these are all no-ops since there's no SIGPIPEs.
@@ -194,7 +136,9 @@ struct sigpipe_info
 void
 PQinitSSL(int do_init)
 {
-	PQinitOpenSSL(do_init, do_init);
+#ifdef USE_SSL
+	pgtls_init_library(do_init, do_init);
+#endif
 }
 
 /*
@@ -205,18 +149,7 @@ void
 PQinitOpenSSL(int do_ssl, int do_crypto)
 {
 #ifdef USE_SSL
-#ifdef ENABLE_THREAD_SAFETY
-
-	/*
-	 * Disallow changing the flags while we have open connections, else we'd
-	 * get completely confused.
-	 */
-	if (ssl_open_connections != 0)
-		return;
-#endif
-
-	pq_init_ssl_lib = do_ssl;
-	pq_init_crypto_lib = do_crypto;
+	pgtls_init_library(do_ssl, do_crypto);
 #endif
 }
 
@@ -229,83 +162,20 @@ pqsecure_initialize(PGconn *conn)
 	int			r = 0;
 
 #ifdef USE_SSL
-	r = init_ssl_system(conn);
+	r = pgtls_init(conn);
 #endif
 
 	return r;
 }
 
 /*
- *	Destroy global context
- */
-void
-pqsecure_destroy(void)
-{
-#ifdef USE_SSL
-	destroySSL();
-#endif
-}
-
-/*
  *	Begin or continue negotiating a secure session.
  */
 PostgresPollingStatusType
 pqsecure_open_client(PGconn *conn)
 {
 #ifdef USE_SSL
-	/* First time through? */
-	if (conn->ssl == NULL)
-	{
-#ifdef ENABLE_THREAD_SAFETY
-		int			rc;
-#endif
-
-		/* We cannot use MSG_NOSIGNAL to block SIGPIPE when using SSL */
-		conn->sigpipe_flag = false;
-
-#ifdef ENABLE_THREAD_SAFETY
-		if ((rc = pthread_mutex_lock(&ssl_config_mutex)))
-		{
-			printfPQExpBuffer(&conn->errorMessage,
-			   libpq_gettext("could not acquire mutex: %s\n"), strerror(rc));
-			return PGRES_POLLING_FAILED;
-		}
-#endif
-		/* 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);
-#ifdef ENABLE_THREAD_SAFETY
-			pthread_mutex_unlock(&ssl_config_mutex);
-#endif
-			close_SSL(conn);
-
-			return PGRES_POLLING_FAILED;
-		}
-#ifdef ENABLE_THREAD_SAFETY
-		pthread_mutex_unlock(&ssl_config_mutex);
-#endif
-
-		/*
-		 * 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);
+	return pgtls_open_client(conn);
 #else
 	/* shouldn't get here */
 	return PGRES_POLLING_FAILED;
@@ -319,8 +189,8 @@ void
 pqsecure_close(PGconn *conn)
 {
 #ifdef USE_SSL
-	if (conn->ssl)
-		close_SSL(conn);
+	if (conn->ssl_in_use)
+		pgtls_close(conn);
 #endif
 }
 
@@ -335,149 +205,63 @@ 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)
+	if (conn->ssl_in_use)
+	{
+		n = pgtls_read(conn, ptr, len);
+	}
+	else
+#endif
 	{
-		int			err;
+		n = pqsecure_raw_read(conn, ptr, len);
+	}
 
-		DECLARE_SIGPIPE_INFO(spinfo);
+	return n;
+}
 
-		/* SSL_read can write to the socket, so we need to disable SIGPIPE */
-		DISABLE_SIGPIPE(conn, spinfo, return -1);
+ssize_t
+pqsecure_raw_read(PGconn *conn, void *ptr, size_t len)
+{
+	ssize_t		n;
+	int			result_errno = 0;
+	char		sebuf[256];
 
-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 */
-					printfPQExpBuffer(&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)
-						printfPQExpBuffer(&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
-						printfPQExpBuffer(&conn->errorMessage,
-									libpq_gettext("SSL SYSCALL error: %s\n"),
-										  SOCK_STRERROR(result_errno,
-													  sebuf, sizeof(sebuf)));
-				}
-				else
-				{
-					printfPQExpBuffer(&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();
-
-					printfPQExpBuffer(&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.
-				 */
-				printfPQExpBuffer(&conn->errorMessage,
-								  libpq_gettext("SSL connection has been closed unexpectedly\n"));
-				result_errno = ECONNRESET;
-				n = -1;
-				break;
-			default:
-				printfPQExpBuffer(&conn->errorMessage,
-						  libpq_gettext("unrecognized SSL error code: %d\n"),
-								  err);
-				/* assume the connection is broken */
-				result_errno = ECONNRESET;
-				n = -1;
-				break;
-		}
+	n = recv(conn->sock, ptr, len, 0);
 
-		RESTORE_SIGPIPE(conn, spinfo);
-	}
-	else
-#endif   /* USE_SSL */
+	if (n < 0)
 	{
-		n = recv(conn->sock, ptr, len, 0);
+		result_errno = SOCK_ERRNO;
 
-		if (n < 0)
+		/* Set error message if appropriate */
+		switch (result_errno)
 		{
-			result_errno = SOCK_ERRNO;
-
-			/* Set error message if appropriate */
-			switch (result_errno)
-			{
 #ifdef EAGAIN
-				case EAGAIN:
+			case EAGAIN:
 #endif
 #if defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN))
-				case EWOULDBLOCK:
+			case EWOULDBLOCK:
 #endif
-				case EINTR:
-					/* no error message, caller is expected to retry */
-					break;
+			case EINTR:
+				/* no error message, caller is expected to retry */
+				break;
 
 #ifdef ECONNRESET
-				case ECONNRESET:
-					printfPQExpBuffer(&conn->errorMessage,
-									  libpq_gettext(
+			case ECONNRESET:
+				printfPQExpBuffer(&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"));
-					break;
+				break;
 #endif
 
-				default:
-					printfPQExpBuffer(&conn->errorMessage,
+			default:
+				printfPQExpBuffer(&conn->errorMessage,
 					libpq_gettext("could not receive data from server: %s\n"),
-									  SOCK_STRERROR(result_errno,
-													sebuf, sizeof(sebuf)));
-					break;
-			}
+								  SOCK_STRERROR(result_errno,
+												sebuf, sizeof(sebuf)));
+				break;
 		}
 	}
 
@@ -498,175 +282,98 @@ ssize_t
 pqsecure_write(PGconn *conn, const void *ptr, size_t len)
 {
 	ssize_t		n;
-	int			result_errno = 0;
-	char		sebuf[256];
 
 	DECLARE_SIGPIPE_INFO(spinfo);
 
 #ifdef USE_SSL
-	if (conn->ssl)
+	if (conn->ssl_in_use)
 	{
-		int			err;
-
 		DISABLE_SIGPIPE(conn, spinfo, return -1);
 
-		SOCK_ERRNO_SET(0);
-		n = SSL_write(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 */
-					printfPQExpBuffer(&conn->errorMessage,
-									  "SSL_write failed but did not provide error information\n");
-					/* assume the connection is broken */
-					result_errno = ECONNRESET;
-				}
-				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:
-				if (n < 0)
-				{
-					result_errno = SOCK_ERRNO;
-					REMEMBER_EPIPE(spinfo, result_errno == EPIPE);
-					if (result_errno == EPIPE ||
-						result_errno == ECONNRESET)
-						printfPQExpBuffer(&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
-						printfPQExpBuffer(&conn->errorMessage,
-									libpq_gettext("SSL SYSCALL error: %s\n"),
-										  SOCK_STRERROR(result_errno,
-													  sebuf, sizeof(sebuf)));
-				}
-				else
-				{
-					printfPQExpBuffer(&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();
-
-					printfPQExpBuffer(&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.
-				 */
-				printfPQExpBuffer(&conn->errorMessage,
-								  libpq_gettext("SSL connection has been closed unexpectedly\n"));
-				result_errno = ECONNRESET;
-				n = -1;
-				break;
-			default:
-				printfPQExpBuffer(&conn->errorMessage,
-						  libpq_gettext("unrecognized SSL error code: %d\n"),
-								  err);
-				/* assume the connection is broken */
-				result_errno = ECONNRESET;
-				n = -1;
-				break;
-		}
+		n = pgtls_write(conn, ptr, len);
 	}
-	else
-#endif   /* USE_SSL */
+	else 
+#endif   /* USE_OPENSSL */
 	{
-		int			flags = 0;
+		n = pqsecure_raw_write(conn, ptr, len);
+	}
+
+	return n;
+}
+
+ssize_t
+pqsecure_raw_write(PGconn *conn, const void *ptr, size_t len)
+{
+	ssize_t		n;
+	int			flags = 0;
+	int			result_errno = 0;
+	char		sebuf[256];
+
+	DECLARE_SIGPIPE_INFO(spinfo);
 
 #ifdef MSG_NOSIGNAL
-		if (conn->sigpipe_flag)
-			flags |= MSG_NOSIGNAL;
+	if (conn->sigpipe_flag)
+		flags |= MSG_NOSIGNAL;
 
 retry_masked:
 #endif   /* MSG_NOSIGNAL */
 
-		DISABLE_SIGPIPE(conn, spinfo, return -1);
+	DISABLE_SIGPIPE(conn, spinfo, return -1);
 
-		n = send(conn->sock, ptr, len, flags);
+	n = send(conn->sock, ptr, len, flags);
 
-		if (n < 0)
-		{
-			result_errno = SOCK_ERRNO;
+	if (n < 0)
+	{
+		result_errno = SOCK_ERRNO;
 
-			/*
-			 * If we see an EINVAL, it may be because MSG_NOSIGNAL isn't
-			 * available on this machine.  So, clear sigpipe_flag so we don't
-			 * try the flag again, and retry the send().
-			 */
+		/*
+		 * If we see an EINVAL, it may be because MSG_NOSIGNAL isn't
+		 * available on this machine.  So, clear sigpipe_flag so we don't
+		 * try the flag again, and retry the send().
+		 */
 #ifdef MSG_NOSIGNAL
-			if (flags != 0 && result_errno == EINVAL)
-			{
-				conn->sigpipe_flag = false;
-				flags = 0;
-				goto retry_masked;
-			}
+		if (flags != 0 && result_errno == EINVAL)
+		{
+			conn->sigpipe_flag = false;
+			flags = 0;
+			goto retry_masked;
+		}
 #endif   /* MSG_NOSIGNAL */
 
-			/* Set error message if appropriate */
-			switch (result_errno)
-			{
+		/* Set error message if appropriate */
+		switch (result_errno)
+		{
 #ifdef EAGAIN
-				case EAGAIN:
+			case EAGAIN:
 #endif
 #if defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN))
-				case EWOULDBLOCK:
+			case EWOULDBLOCK:
 #endif
-				case EINTR:
-					/* no error message, caller is expected to retry */
-					break;
+			case EINTR:
+				/* no error message, caller is expected to retry */
+				break;
 
-				case EPIPE:
-					/* Set flag for EPIPE */
-					REMEMBER_EPIPE(spinfo, true);
-					/* FALL THRU */
+			case EPIPE:
+				/* Set flag for EPIPE */
+				REMEMBER_EPIPE(spinfo, true);
+				/* FALL THRU */
 
 #ifdef ECONNRESET
-				case ECONNRESET:
+			case ECONNRESET:
 #endif
-					printfPQExpBuffer(&conn->errorMessage,
-									  libpq_gettext(
+				printfPQExpBuffer(&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"));
-					break;
+				break;
 
-				default:
-					printfPQExpBuffer(&conn->errorMessage,
+			default:
+				printfPQExpBuffer(&conn->errorMessage,
 						libpq_gettext("could not send data to server: %s\n"),
 									  SOCK_STRERROR(result_errno,
 													sebuf, sizeof(sebuf)));
-					break;
-			}
+				break;
 		}
 	}
 
@@ -678,981 +385,7 @@ retry_masked:
 	return n;
 }
 
-/* ------------------------------------------------------------ */
-/*						  SSL specific code						*/
-/* ------------------------------------------------------------ */
-#ifdef USE_SSL
-
-/*
- *	Certificate verification callback
- *
- *	This callback allows us to log intermediate problems during
- *	verification, but there doesn't seem to be a clean way to get
- *	our PGconn * structure.  So we can't log anything!
- *
- *	This callback also allows us to override the default acceptance
- *	criteria (e.g., accepting self-signed or expired certs), but
- *	for now we accept the default checks.
- */
-static int
-verify_cb(int ok, X509_STORE_CTX *ctx)
-{
-	return ok;
-}
-
-
-/*
- * Check if a wildcard certificate matches the server hostname.
- *
- * The rule for this is:
- *	1. We only match the '*' character as wildcard
- *	2. We match only wildcards at the start of the string
- *	3. The '*' character does *not* match '.', meaning that we match only
- *	   a single pathname component.
- *	4. We don't support more than one '*' in a single pattern.
- *
- * This is roughly in line with RFC2818, but contrary to what most browsers
- * appear to be implementing (point 3 being the difference)
- *
- * Matching is always case-insensitive, since DNS is case insensitive.
- */
-static int
-wildcard_certificate_match(const char *pattern, const char *string)
-{
-	int			lenpat = strlen(pattern);
-	int			lenstr = strlen(string);
-
-	/* If we don't start with a wildcard, it's not a match (rule 1 & 2) */
-	if (lenpat < 3 ||
-		pattern[0] != '*' ||
-		pattern[1] != '.')
-		return 0;
-
-	if (lenpat > lenstr)
-		/* If pattern is longer than the string, we can never match */
-		return 0;
-
-	if (pg_strcasecmp(pattern + 1, string + lenstr - lenpat + 1) != 0)
-
-		/*
-		 * If string does not end in pattern (minus the wildcard), we don't
-		 * match
-		 */
-		return 0;
-
-	if (strchr(string, '.') < string + lenstr - lenpat)
-
-		/*
-		 * If there is a dot left of where the pattern started to match, we
-		 * don't match (rule 3)
-		 */
-		return 0;
-
-	/* String ended with pattern, and didn't have a dot before, so we match */
-	return 1;
-}
-
-
-/*
- *	Verify that common name resolves to peer.
- */
-static bool
-verify_peer_name_matches_certificate(PGconn *conn)
-{
-	char	   *peer_cn;
-	int			r;
-	int			len;
-	bool		result;
-
-	/*
-	 * If told not to verify the peer name, don't do it. Return true
-	 * indicating that the verification was successful.
-	 */
-	if (strcmp(conn->sslmode, "verify-full") != 0)
-		return true;
-
-	/*
-	 * Extract the common name from the certificate.
-	 *
-	 * XXX: Should support alternate names here
-	 */
-	/* First find out the name's length and allocate a buffer for it. */
-	len = X509_NAME_get_text_by_NID(X509_get_subject_name(conn->peer),
-									NID_commonName, NULL, 0);
-	if (len == -1)
-	{
-		printfPQExpBuffer(&conn->errorMessage,
-						  libpq_gettext("could not get server common name from server certificate\n"));
-		return false;
-	}
-	peer_cn = malloc(len + 1);
-	if (peer_cn == NULL)
-	{
-		printfPQExpBuffer(&conn->errorMessage,
-						  libpq_gettext("out of memory\n"));
-		return false;
-	}
-
-	r = X509_NAME_get_text_by_NID(X509_get_subject_name(conn->peer),
-								  NID_commonName, peer_cn, len + 1);
-	if (r != len)
-	{
-		/* Got different length than on the first call. Shouldn't happen. */
-		printfPQExpBuffer(&conn->errorMessage,
-						  libpq_gettext("could not get server common name from server certificate\n"));
-		free(peer_cn);
-		return false;
-	}
-	peer_cn[len] = '\0';
-
-	/*
-	 * Reject embedded NULLs in certificate common name to prevent attacks
-	 * like CVE-2009-4034.
-	 */
-	if (len != strlen(peer_cn))
-	{
-		printfPQExpBuffer(&conn->errorMessage,
-						  libpq_gettext("SSL certificate's common name contains embedded null\n"));
-		free(peer_cn);
-		return false;
-	}
-
-	/*
-	 * We got the peer's common name. Now compare it against the originally
-	 * given hostname.
-	 */
-	if (!(conn->pghost && conn->pghost[0] != '\0'))
-	{
-		printfPQExpBuffer(&conn->errorMessage,
-						  libpq_gettext("host name must be specified for a verified SSL connection\n"));
-		result = false;
-	}
-	else
-	{
-		if (pg_strcasecmp(peer_cn, conn->pghost) == 0)
-			/* Exact name match */
-			result = true;
-		else if (wildcard_certificate_match(peer_cn, conn->pghost))
-			/* Matched wildcard certificate */
-			result = true;
-		else
-		{
-			printfPQExpBuffer(&conn->errorMessage,
-							  libpq_gettext("server common name \"%s\" does not match host name \"%s\"\n"),
-							  peer_cn, conn->pghost);
-			result = false;
-		}
-	}
-
-	free(peer_cn);
-	return result;
-}
-
-#ifdef ENABLE_THREAD_SAFETY
-/*
- *	Callback functions for OpenSSL internal locking
- */
-
-static unsigned long
-pq_threadidcallback(void)
-{
-	/*
-	 * This is not standards-compliant.  pthread_self() returns pthread_t, and
-	 * shouldn't be cast to unsigned long, but CRYPTO_set_id_callback requires
-	 * it, so we have to do it.
-	 */
-	return (unsigned long) pthread_self();
-}
-
-static pthread_mutex_t *pq_lockarray;
-
-static void
-pq_lockingcallback(int mode, int n, const char *file, int line)
-{
-	if (mode & CRYPTO_LOCK)
-	{
-		if (pthread_mutex_lock(&pq_lockarray[n]))
-			PGTHREAD_ERROR("failed to lock mutex");
-	}
-	else
-	{
-		if (pthread_mutex_unlock(&pq_lockarray[n]))
-			PGTHREAD_ERROR("failed to unlock mutex");
-	}
-}
-#endif   /* ENABLE_THREAD_SAFETY */
-
-/*
- * Initialize SSL system, in particular creating the SSL_context object
- * that will be shared by all SSL-using connections in this process.
- *
- * In threadsafe mode, this includes setting up libcrypto callback functions
- * to do thread locking.
- *
- * If the caller has told us (through PQinitOpenSSL) that he's taking care
- * of libcrypto, we expect that callbacks are already set, and won't try to
- * override it.
- *
- * The conn parameter is only used to be able to pass back an error
- * message - no connection-local setup is made here.
- *
- * Returns 0 if OK, -1 on failure (with a message in conn->errorMessage).
- */
-static int
-init_ssl_system(PGconn *conn)
-{
-#ifdef ENABLE_THREAD_SAFETY
-#ifdef WIN32
-	/* Also see similar code in fe-connect.c, default_threadlock() */
-	if (ssl_config_mutex == NULL)
-	{
-		while (InterlockedExchange(&win32_ssl_create_mutex, 1) == 1)
-			 /* loop, another thread own the lock */ ;
-		if (ssl_config_mutex == NULL)
-		{
-			if (pthread_mutex_init(&ssl_config_mutex, NULL))
-				return -1;
-		}
-		InterlockedExchange(&win32_ssl_create_mutex, 0);
-	}
-#endif
-	if (pthread_mutex_lock(&ssl_config_mutex))
-		return -1;
-
-	if (pq_init_crypto_lib)
-	{
-		/*
-		 * If necessary, set up an array to hold locks for libcrypto.
-		 * libcrypto will tell us how big to make this array.
-		 */
-		if (pq_lockarray == NULL)
-		{
-			int			i;
-
-			pq_lockarray = malloc(sizeof(pthread_mutex_t) * CRYPTO_num_locks());
-			if (!pq_lockarray)
-			{
-				pthread_mutex_unlock(&ssl_config_mutex);
-				return -1;
-			}
-			for (i = 0; i < CRYPTO_num_locks(); i++)
-			{
-				if (pthread_mutex_init(&pq_lockarray[i], NULL))
-				{
-					free(pq_lockarray);
-					pq_lockarray = NULL;
-					pthread_mutex_unlock(&ssl_config_mutex);
-					return -1;
-				}
-			}
-		}
-
-		if (ssl_open_connections++ == 0)
-		{
-			/* These are only required for threaded libcrypto applications */
-			CRYPTO_set_id_callback(pq_threadidcallback);
-			CRYPTO_set_locking_callback(pq_lockingcallback);
-		}
-	}
-#endif   /* ENABLE_THREAD_SAFETY */
-
-	if (!SSL_context)
-	{
-		if (pq_init_ssl_lib)
-		{
-#if SSLEAY_VERSION_NUMBER >= 0x00907000L
-			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)
-		{
-			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(&ssl_config_mutex);
-#endif
-			return -1;
-		}
-
-		/* Disable old protocol versions */
-		SSL_CTX_set_options(SSL_context, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
-
-		/*
-		 * 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);
-	}
-
-#ifdef ENABLE_THREAD_SAFETY
-	pthread_mutex_unlock(&ssl_config_mutex);
-#endif
-	return 0;
-}
-
-/*
- *	This function is needed because if the libpq library is unloaded
- *	from the application, the callback functions will no longer exist when
- *	libcrypto is used by other parts of the system.  For this reason,
- *	we unregister the callback functions when the last libpq
- *	connection is closed.  (The same would apply for OpenSSL callbacks
- *	if we had any.)
- *
- *	Callbacks are only set when we're compiled in threadsafe mode, so
- *	we only need to remove them in this case.
- */
-static void
-destroy_ssl_system(void)
-{
-#ifdef ENABLE_THREAD_SAFETY
-	/* Mutex is created in initialize_ssl_system() */
-	if (pthread_mutex_lock(&ssl_config_mutex))
-		return;
-
-	if (pq_init_crypto_lib && ssl_open_connections > 0)
-		--ssl_open_connections;
-
-	if (pq_init_crypto_lib && ssl_open_connections == 0)
-	{
-		/* No connections left, unregister libcrypto callbacks */
-		CRYPTO_set_locking_callback(NULL);
-		CRYPTO_set_id_callback(NULL);
-
-		/*
-		 * We don't free the lock array or the SSL_context. If we get another
-		 * connection in this process, we will just re-use them with the
-		 * existing mutexes.
-		 *
-		 * This means we leak a little memory on repeated load/unload of the
-		 * library.
-		 */
-	}
-
-	pthread_mutex_unlock(&ssl_config_mutex);
-#endif
-}
-
-/*
- *	Initialize (potentially) per-connection SSL data, namely the
- *	client certificate, private key, and trusted CA certs.
- *
- *	conn->ssl must already be created.  It receives the connection's client
- *	certificate and private key.  Note however that certificates also get
- *	loaded into the SSL_context object, and are therefore accessible to all
- *	connections in this process.  This should be OK as long as there aren't
- *	any hash collisions among the certs.
- *
- *	Returns 0 if OK, -1 on failure (with a message in conn->errorMessage).
- */
-static int
-initialize_SSL(PGconn *conn)
-{
-	struct stat buf;
-	char		homedir[MAXPGPATH];
-	char		fnbuf[MAXPGPATH];
-	char		sebuf[256];
-	bool		have_homedir;
-	bool		have_cert;
-	EVP_PKEY   *pkey = NULL;
-
-	/*
-	 * We'll need the home directory if any of the relevant parameters are
-	 * defaulted.  If pqGetHomeDirectory fails, act as though none of the
-	 * files could be found.
-	 */
-	if (!(conn->sslcert && strlen(conn->sslcert) > 0) ||
-		!(conn->sslkey && strlen(conn->sslkey) > 0) ||
-		!(conn->sslrootcert && strlen(conn->sslrootcert) > 0) ||
-		!(conn->sslcrl && strlen(conn->sslcrl) > 0))
-		have_homedir = pqGetHomeDirectory(homedir, sizeof(homedir));
-	else	/* won't need it */
-		have_homedir = false;
-
-	/* Read the client certificate file */
-	if (conn->sslcert && strlen(conn->sslcert) > 0)
-		strncpy(fnbuf, conn->sslcert, sizeof(fnbuf));
-	else if (have_homedir)
-		snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, USER_CERT_FILE);
-	else
-		fnbuf[0] = '\0';
-
-	if (fnbuf[0] == '\0')
-	{
-		/* no home directory, proceed without a client cert */
-		have_cert = false;
-	}
-	else if (stat(fnbuf, &buf) != 0)
-	{
-		/*
-		 * If file is not present, just go on without a client cert; server
-		 * might or might not accept the connection.  Any other error,
-		 * however, is grounds for complaint.
-		 */
-		if (errno != ENOENT && errno != ENOTDIR)
-		{
-			printfPQExpBuffer(&conn->errorMessage,
-			   libpq_gettext("could not open certificate file \"%s\": %s\n"),
-							  fnbuf, pqStrerror(errno, sebuf, sizeof(sebuf)));
-			return -1;
-		}
-		have_cert = false;
-	}
-	else
-	{
-		/*
-		 * Cert file exists, so load it.  Since OpenSSL doesn't provide the
-		 * equivalent of "SSL_use_certificate_chain_file", we actually have to
-		 * load the file twice.  The first call loads any extra certs after
-		 * the first one into chain-cert storage associated with the
-		 * SSL_context.  The second call loads the first cert (only) into the
-		 * SSL object, where it will be correctly paired with the private key
-		 * we load below.  We do it this way so that each connection
-		 * understands which subject cert to present, in case different
-		 * sslcert settings are used for different connections in the same
-		 * process.
-		 *
-		 * NOTE: This function may also modify our SSL_context and therefore
-		 * we have to lock around this call and any places where we use the
-		 * SSL_context struct.
-		 */
-#ifdef ENABLE_THREAD_SAFETY
-		int			rc;
-
-		if ((rc = pthread_mutex_lock(&ssl_config_mutex)))
-		{
-			printfPQExpBuffer(&conn->errorMessage,
-			   libpq_gettext("could not acquire mutex: %s\n"), strerror(rc));
-			return -1;
-		}
-#endif
-		if (SSL_CTX_use_certificate_chain_file(SSL_context, fnbuf) != 1)
-		{
-			char	   *err = SSLerrmessage();
-
-			printfPQExpBuffer(&conn->errorMessage,
-			   libpq_gettext("could not read certificate file \"%s\": %s\n"),
-							  fnbuf, err);
-			SSLerrfree(err);
-
-#ifdef ENABLE_THREAD_SAFETY
-			pthread_mutex_unlock(&ssl_config_mutex);
-#endif
-			return -1;
-		}
-
-		if (SSL_use_certificate_file(conn->ssl, fnbuf, SSL_FILETYPE_PEM) != 1)
-		{
-			char	   *err = SSLerrmessage();
-
-			printfPQExpBuffer(&conn->errorMessage,
-			   libpq_gettext("could not read certificate file \"%s\": %s\n"),
-							  fnbuf, err);
-			SSLerrfree(err);
-#ifdef ENABLE_THREAD_SAFETY
-			pthread_mutex_unlock(&ssl_config_mutex);
-#endif
-			return -1;
-		}
-
-		/* need to load the associated private key, too */
-		have_cert = true;
-
-#ifdef ENABLE_THREAD_SAFETY
-		pthread_mutex_unlock(&ssl_config_mutex);
-#endif
-	}
-
-	/*
-	 * Read the SSL key. If a key is specified, treat it as an engine:key
-	 * combination if there is colon present - we don't support files with
-	 * colon in the name. The exception is if the second character is a colon,
-	 * in which case it can be a Windows filename with drive specification.
-	 */
-	if (have_cert && conn->sslkey && strlen(conn->sslkey) > 0)
-	{
-#ifdef USE_SSL_ENGINE
-		if (strchr(conn->sslkey, ':')
-#ifdef WIN32
-			&& conn->sslkey[1] != ':'
-#endif
-			)
-		{
-			/* Colon, but not in second character, treat as engine:key */
-			char	   *engine_str = strdup(conn->sslkey);
-			char	   *engine_colon;
-
-			if (engine_str == NULL)
-			{
-				printfPQExpBuffer(&conn->errorMessage,
-								  libpq_gettext("out of memory\n"));
-				return -1;
-			}
-
-			/* cannot return NULL because we already checked before strdup */
-			engine_colon = strchr(engine_str, ':');
-
-			*engine_colon = '\0';		/* engine_str now has engine name */
-			engine_colon++;		/* engine_colon now has key name */
-
-			conn->engine = ENGINE_by_id(engine_str);
-			if (conn->engine == NULL)
-			{
-				char	   *err = SSLerrmessage();
-
-				printfPQExpBuffer(&conn->errorMessage,
-					 libpq_gettext("could not load SSL engine \"%s\": %s\n"),
-								  engine_str, err);
-				SSLerrfree(err);
-				free(engine_str);
-				return -1;
-			}
-
-			if (ENGINE_init(conn->engine) == 0)
-			{
-				char	   *err = SSLerrmessage();
-
-				printfPQExpBuffer(&conn->errorMessage,
-				libpq_gettext("could not initialize SSL engine \"%s\": %s\n"),
-								  engine_str, err);
-				SSLerrfree(err);
-				ENGINE_free(conn->engine);
-				conn->engine = NULL;
-				free(engine_str);
-				return -1;
-			}
-
-			pkey = ENGINE_load_private_key(conn->engine, engine_colon,
-										   NULL, NULL);
-			if (pkey == NULL)
-			{
-				char	   *err = SSLerrmessage();
-
-				printfPQExpBuffer(&conn->errorMessage,
-								  libpq_gettext("could not read private SSL key \"%s\" from engine \"%s\": %s\n"),
-								  engine_colon, engine_str, err);
-				SSLerrfree(err);
-				ENGINE_finish(conn->engine);
-				ENGINE_free(conn->engine);
-				conn->engine = NULL;
-				free(engine_str);
-				return -1;
-			}
-			if (SSL_use_PrivateKey(conn->ssl, pkey) != 1)
-			{
-				char	   *err = SSLerrmessage();
-
-				printfPQExpBuffer(&conn->errorMessage,
-								  libpq_gettext("could not load private SSL key \"%s\" from engine \"%s\": %s\n"),
-								  engine_colon, engine_str, err);
-				SSLerrfree(err);
-				ENGINE_finish(conn->engine);
-				ENGINE_free(conn->engine);
-				conn->engine = NULL;
-				free(engine_str);
-				return -1;
-			}
-
-			free(engine_str);
-
-			fnbuf[0] = '\0';	/* indicate we're not going to load from a
-								 * file */
-		}
-		else
-#endif   /* USE_SSL_ENGINE */
-		{
-			/* PGSSLKEY is not an engine, treat it as a filename */
-			strncpy(fnbuf, conn->sslkey, sizeof(fnbuf));
-		}
-	}
-	else if (have_homedir)
-	{
-		/* No PGSSLKEY specified, load default file */
-		snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, USER_KEY_FILE);
-	}
-	else
-		fnbuf[0] = '\0';
-
-	if (have_cert && fnbuf[0] != '\0')
-	{
-		/* read the client key from file */
-
-		if (stat(fnbuf, &buf) != 0)
-		{
-			printfPQExpBuffer(&conn->errorMessage,
-							  libpq_gettext("certificate present, but not private key file \"%s\"\n"),
-							  fnbuf);
-			return -1;
-		}
-#ifndef WIN32
-		if (!S_ISREG(buf.st_mode) || buf.st_mode & (S_IRWXG | S_IRWXO))
-		{
-			printfPQExpBuffer(&conn->errorMessage,
-							  libpq_gettext("private key file \"%s\" has group or world access; permissions should be u=rw (0600) or less\n"),
-							  fnbuf);
-			return -1;
-		}
-#endif
-
-		if (SSL_use_PrivateKey_file(conn->ssl, fnbuf, SSL_FILETYPE_PEM) != 1)
-		{
-			char	   *err = SSLerrmessage();
-
-			printfPQExpBuffer(&conn->errorMessage,
-			   libpq_gettext("could not load private key file \"%s\": %s\n"),
-							  fnbuf, err);
-			SSLerrfree(err);
-			return -1;
-		}
-	}
-
-	/* verify that the cert and key go together */
-	if (have_cert &&
-		SSL_check_private_key(conn->ssl) != 1)
-	{
-		char	   *err = SSLerrmessage();
-
-		printfPQExpBuffer(&conn->errorMessage,
-						  libpq_gettext("certificate does not match private key file \"%s\": %s\n"),
-						  fnbuf, err);
-		SSLerrfree(err);
-		return -1;
-	}
-
-	/*
-	 * If the root cert file exists, load it so we can perform certificate
-	 * verification. If sslmode is "verify-full" we will also do further
-	 * verification after the connection has been completed.
-	 */
-	if (conn->sslrootcert && strlen(conn->sslrootcert) > 0)
-		strncpy(fnbuf, conn->sslrootcert, sizeof(fnbuf));
-	else if (have_homedir)
-		snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, ROOT_CERT_FILE);
-	else
-		fnbuf[0] = '\0';
-
-	if (fnbuf[0] != '\0' &&
-		stat(fnbuf, &buf) == 0)
-	{
-		X509_STORE *cvstore;
-
-#ifdef ENABLE_THREAD_SAFETY
-		int			rc;
-
-		if ((rc = pthread_mutex_lock(&ssl_config_mutex)))
-		{
-			printfPQExpBuffer(&conn->errorMessage,
-			   libpq_gettext("could not acquire mutex: %s\n"), strerror(rc));
-			return -1;
-		}
-#endif
-		if (SSL_CTX_load_verify_locations(SSL_context, fnbuf, NULL) != 1)
-		{
-			char	   *err = SSLerrmessage();
-
-			printfPQExpBuffer(&conn->errorMessage,
-							  libpq_gettext("could not read root certificate file \"%s\": %s\n"),
-							  fnbuf, err);
-			SSLerrfree(err);
-#ifdef ENABLE_THREAD_SAFETY
-			pthread_mutex_unlock(&ssl_config_mutex);
-#endif
-			return -1;
-		}
-
-		if ((cvstore = SSL_CTX_get_cert_store(SSL_context)) != NULL)
-		{
-			if (conn->sslcrl && strlen(conn->sslcrl) > 0)
-				strncpy(fnbuf, conn->sslcrl, sizeof(fnbuf));
-			else if (have_homedir)
-				snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, ROOT_CRL_FILE);
-			else
-				fnbuf[0] = '\0';
-
-			/* Set the flags to check against the complete CRL chain */
-			if (fnbuf[0] != '\0' &&
-				X509_STORE_load_locations(cvstore, fnbuf, 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
-				char	   *err = SSLerrmessage();
-
-				printfPQExpBuffer(&conn->errorMessage,
-								  libpq_gettext("SSL library does not support CRL certificates (file \"%s\")\n"),
-								  fnbuf);
-				SSLerrfree(err);
-#ifdef ENABLE_THREAD_SAFETY
-				pthread_mutex_unlock(&ssl_config_mutex);
-#endif
-				return -1;
-#endif
-			}
-			/* if not found, silently ignore;  we do not require CRL */
-		}
-#ifdef ENABLE_THREAD_SAFETY
-		pthread_mutex_unlock(&ssl_config_mutex);
-#endif
-
-		SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, verify_cb);
-	}
-	else
-	{
-		/*
-		 * stat() failed; assume root file doesn't exist.  If sslmode is
-		 * verify-ca or verify-full, this is an error.  Otherwise, continue
-		 * without performing any server cert verification.
-		 */
-		if (conn->sslmode[0] == 'v')	/* "verify-ca" or "verify-full" */
-		{
-			/*
-			 * The only way to reach here with an empty filename is if
-			 * pqGetHomeDirectory failed.  That's a sufficiently unusual case
-			 * that it seems worth having a specialized error message for it.
-			 */
-			if (fnbuf[0] == '\0')
-				printfPQExpBuffer(&conn->errorMessage,
-								  libpq_gettext("could not get home directory to locate root certificate file\n"
-												"Either provide the file or change sslmode to disable server certificate verification.\n"));
-			else
-				printfPQExpBuffer(&conn->errorMessage,
-				libpq_gettext("root certificate file \"%s\" does not exist\n"
-							  "Either provide the file or change sslmode to disable server certificate verification.\n"), fnbuf);
-			return -1;
-		}
-	}
-
-	/*
-	 * If the OpenSSL version used supports it (from 1.0.0 on) and the user
-	 * requested it, disable SSL compression.
-	 */
-#ifdef SSL_OP_NO_COMPRESSION
-	if (conn->sslcompression && conn->sslcompression[0] == '0')
-	{
-		SSL_set_options(conn->ssl, SSL_OP_NO_COMPRESSION);
-	}
-#endif
-
-	return 0;
-}
-
-static void
-destroySSL(void)
-{
-	destroy_ssl_system();
-}
-
-/*
- *	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:
-				{
-					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;
-		}
-	}
-
-	/*
-	 * We already checked the server certificate in initialize_SSL() using
-	 * SSL_CTX_set_verify(), if root.crt exists.
-	 */
-
-	/* get server certificate */
-	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;
-	}
-
-	if (!verify_peer_name_matches_certificate(conn))
-	{
-		close_SSL(conn);
-		return PGRES_POLLING_FAILED;
-	}
-
-	/* SSL handshake is complete */
-	return PGRES_POLLING_OK;
-}
-
-/*
- *	Close SSL connection.
- */
-static void
-close_SSL(PGconn *conn)
-{
-	bool		destroy_needed = false;
-
-	if (conn->ssl)
-	{
-		DECLARE_SIGPIPE_INFO(spinfo);
-
-		/*
-		 * We can't destroy everything SSL-related here due to the possible
-		 * later calls to OpenSSL routines which may need our thread
-		 * callbacks, so set a flag here and check at the end.
-		 */
-		destroy_needed = true;
-
-		DISABLE_SIGPIPE(conn, spinfo, (void) 0);
-		SSL_shutdown(conn->ssl);
-		SSL_free(conn->ssl);
-		conn->ssl = NULL;
-		/* We have to assume we got EPIPE */
-		REMEMBER_EPIPE(spinfo, true);
-		RESTORE_SIGPIPE(conn, spinfo);
-	}
-
-	if (conn->peer)
-	{
-		X509_free(conn->peer);
-		conn->peer = NULL;
-	}
-
-#ifdef USE_SSL_ENGINE
-	if (conn->engine)
-	{
-		ENGINE_finish(conn->engine);
-		ENGINE_free(conn->engine);
-		conn->engine = NULL;
-	}
-#endif
-
-	/*
-	 * This will remove our SSL locking hooks, if this is the last SSL
-	 * connection, which means we must wait to call it until after all SSL
-	 * calls have been made, otherwise we can end up with a race condition and
-	 * possible deadlocks.
-	 *
-	 * See comments above destroy_ssl_system().
-	 */
-	if (destroy_needed)
-		pqsecure_destroy();
-}
-
-/*
- * Obtain reason string for last SSL error
- *
- * Some caution is needed here since ERR_reason_error_string will
- * return NULL if it doesn't recognize the error code.  We don't
- * want to return NULL ever.
- */
-static char ssl_nomem[] = "out of memory allocating error description";
-
-#define SSL_ERR_LEN 128
-
-static char *
-SSLerrmessage(void)
-{
-	unsigned long errcode;
-	const char *errreason;
-	char	   *errbuf;
-
-	errbuf = malloc(SSL_ERR_LEN);
-	if (!errbuf)
-		return ssl_nomem;
-	errcode = ERR_get_error();
-	if (errcode == 0)
-	{
-		snprintf(errbuf, SSL_ERR_LEN, libpq_gettext("no SSL error reported"));
-		return errbuf;
-	}
-	errreason = ERR_reason_error_string(errcode);
-	if (errreason != NULL)
-	{
-		strlcpy(errbuf, errreason, SSL_ERR_LEN);
-		return errbuf;
-	}
-	snprintf(errbuf, SSL_ERR_LEN, libpq_gettext("SSL error code %lu"), errcode);
-	return errbuf;
-}
-
-static void
-SSLerrfree(char *buf)
-{
-	if (buf != ssl_nomem)
-		free(buf);
-}
-
-/*
- *	Return pointer to OpenSSL object.
- */
-void *
-PQgetssl(PGconn *conn)
-{
-	if (!conn)
-		return NULL;
-	return conn->ssl;
-}
-#else							/* !USE_SSL */
-
+#ifndef USE_SSL
 void *
 PQgetssl(PGconn *conn)
 {
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index 4aeb4fa..6032904 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -73,14 +73,14 @@ typedef struct
 #endif
 #endif   /* ENABLE_SSPI */
 
-#ifdef USE_SSL
+#ifdef USE_OPENSSL
 #include <openssl/ssl.h>
 #include <openssl/err.h>
 
 #if (SSLEAY_VERSION_NUMBER >= 0x00907000L) && !defined(OPENSSL_NO_ENGINE)
 #define USE_SSL_ENGINE
 #endif
-#endif   /* USE_SSL */
+#endif   /* USE_OPENSSL */
 
 /*
  * POSTGRES backend dependent Constants.
@@ -427,6 +427,8 @@ struct pg_conn
 	bool		allow_ssl_try;	/* Allowed to try SSL negotiation */
 	bool		wait_ssl_try;	/* Delay SSL negotiation until after
 								 * attempting normal connection */
+	bool		ssl_in_use;
+#ifdef USE_OPENSSL
 	SSL		   *ssl;			/* SSL status, if have SSL connection */
 	X509	   *peer;			/* X509 cert of server */
 #ifdef USE_SSL_ENGINE
@@ -435,6 +437,7 @@ struct pg_conn
 	void	   *engine;			/* dummy field to keep struct the same if
 								 * OpenSSL version changes */
 #endif
+#endif   /* USE_OPENSSL */
 #endif   /* USE_SSL */
 
 #ifdef ENABLE_GSS
@@ -482,6 +485,24 @@ struct pg_cancel
  */
 extern char *const pgresStatus[];
 
+
+#ifdef USE_SSL
+
+#ifndef WIN32
+#define USER_CERT_FILE		".postgresql/postgresql.crt"
+#define USER_KEY_FILE		".postgresql/postgresql.key"
+#define ROOT_CERT_FILE		".postgresql/root.crt"
+#define ROOT_CRL_FILE		".postgresql/root.crl"
+#else
+/* On Windows, the "home" directory is already PostgreSQL-specific */
+#define USER_CERT_FILE		"postgresql.crt"
+#define USER_KEY_FILE		"postgresql.key"
+#define ROOT_CERT_FILE		"root.crt"
+#define ROOT_CRL_FILE		"root.crl"
+#endif
+
+#endif   /* USE_SSL */
+
 /* ----------------
  * Internal functions of libpq
  * Functions declared here need to be visible across files of libpq,
@@ -603,6 +624,8 @@ extern PostgresPollingStatusType pqsecure_open_client(PGconn *);
 extern void pqsecure_close(PGconn *);
 extern ssize_t pqsecure_read(PGconn *, void *ptr, size_t len);
 extern ssize_t pqsecure_write(PGconn *, const void *ptr, size_t len);
+extern ssize_t pqsecure_raw_read(PGconn *, void *ptr, size_t len);
+extern ssize_t pqsecure_raw_write(PGconn *, const void *ptr, size_t len);
 
 #if defined(ENABLE_THREAD_SAFETY) && !defined(WIN32)
 extern int	pq_block_sigpipe(sigset_t *osigset, bool *sigpipe_pending);
@@ -611,6 +634,16 @@ extern void pq_reset_sigpipe(sigset_t *osigset, bool sigpipe_pending,
 #endif
 
 /*
+ * The SSL implementatation provides these functions (fe-secure-openssl.c)
+ */
+extern void pgtls_init_library(bool do_ssl, int do_crypto);
+extern int pgtls_init(PGconn *conn);
+extern PostgresPollingStatusType pgtls_open_client(PGconn *conn);
+extern void pgtls_close(PGconn *conn);
+extern ssize_t pgtls_read(PGconn *conn, void *ptr, size_t len);
+extern ssize_t pgtls_write(PGconn *conn, const void *ptr, size_t len);
+
+/*
  * this is so that we can check if a connection is non-blocking internally
  * without the overhead of a function call
  */
diff --git a/src/interfaces/libpq/win32.mak b/src/interfaces/libpq/win32.mak
index 99fef27..39a0bc9 100644
--- a/src/interfaces/libpq/win32.mak
+++ b/src/interfaces/libpq/win32.mak
@@ -2,7 +2,7 @@
 
 # Will build a static library libpq(d).lib
 #        and a dynamic library libpq(d).dll with import library libpq(d)dll.lib
-# USE_SSL=1 will compile with OpenSSL
+# USE_OPENSSL=1 will compile with OpenSSL
 # USE_KFW=1 will compile with kfw(kerberos for Windows)
 # DEBUG=1 compiles with debugging symbols
 # ENABLE_THREAD_SAFETY=1 compiles with threading enabled
@@ -124,6 +124,9 @@ CLEAN :
 	-@erase "$(OUTDIR)\$(OUTFILENAME).dll.manifest"
 	-@erase "$(OUTDIR)\*.idb"
 	-@erase pg_config_paths.h"
+!IFDEF USE_OPENSSL
+	-@erase "$(INTDIR)\fe-secure-openssl.obj"
+!ENDIF
 
 
 LIB32=link.exe -lib
@@ -164,6 +167,9 @@ LIB32_OBJS= \
 	"$(INTDIR)\win32error.obj" \
 	"$(INTDIR)\win32setlocale.obj" \
 	"$(INTDIR)\pthread-win32.obj"
+!IFDEF USE_OPENSSL
+	LIB32_OBJS=$(LIB32_OBJS) "$(INTDIR)\fe-secure-openssl.obj"
+!ENDIF
 
 
 config: ..\..\include\pg_config.h ..\..\include\pg_config_ext.h pg_config_paths.h  ..\..\include\pg_config_os.h
@@ -189,8 +195,8 @@ CPP_PROJ=/nologo /W3 /EHsc $(OPT) /I "..\..\include" /I "..\..\include\port\win3
  /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /c  \
  /D "_CRT_SECURE_NO_DEPRECATE" $(ADD_DEFINES)
 
-!IFDEF USE_SSL
-CPP_PROJ=$(CPP_PROJ) /D USE_SSL
+!IFDEF USE_OPENSSL
+CPP_PROJ=$(CPP_PROJ) /D USE_OPENSSL
 SSL_LIBS=ssleay32.lib libeay32.lib gdi32.lib
 !ENDIF
 
diff --git a/src/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pm
index b71da67..5d809ac 100644
--- a/src/tools/msvc/Mkvcbuild.pm
+++ b/src/tools/msvc/Mkvcbuild.pm
@@ -117,6 +117,12 @@ sub mkvcbuild
 	$postgres->AddLibrary('ws2_32.lib');
 	$postgres->AddLibrary('wldap32.lib') if ($solution->{options}->{ldap});
 	$postgres->FullExportDLL('postgres.lib');
+	# The OBJS scraper doesn't know about ifdefs, so remove fe-secure-openssl.c
+	# if building without OpenSSL
+	if (!$solution->{options}->{openssl})
+	{
+		$postgres->RemoveFile('src\backend\libpq\be-secure-openssl.c');
+	}
 
 	my $snowball = $solution->AddProject('dict_snowball', 'dll', '',
 		'src\backend\snowball');
@@ -276,6 +282,12 @@ sub mkvcbuild
 	$libpq->ReplaceFile('src\interfaces\libpq\libpqrc.c',
 		'src\interfaces\libpq\libpq.rc');
 	$libpq->AddReference($libpgport);
+	# The OBJS scraper doesn't know about ifdefs, so remove fe-secure-openssl.c
+	# if building without OpenSSL
+	if (!$solution->{options}->{openssl})
+	{
+		$libpq->RemoveFile('src\interfaces\libpq\fe-secure-openssl.c');
+	}
 
 	my $libpqwalreceiver =
 	  $solution->AddProject('libpqwalreceiver', 'dll', '',
diff --git a/src/tools/msvc/Solution.pm b/src/tools/msvc/Solution.pm
index e49c3f4..39e41f6 100644
--- a/src/tools/msvc/Solution.pm
+++ b/src/tools/msvc/Solution.pm
@@ -182,7 +182,7 @@ sub GenerateFiles
 		  if ($self->{options}->{integer_datetimes});
 		print O "#define USE_LDAP 1\n"   if ($self->{options}->{ldap});
 		print O "#define HAVE_LIBZ 1\n"  if ($self->{options}->{zlib});
-		print O "#define USE_SSL 1\n"    if ($self->{options}->{openssl});
+		print O "#define USE_OPENSSL 1\n" if ($self->{options}->{openssl});
 		print O "#define ENABLE_NLS 1\n" if ($self->{options}->{nls});
 
 		print O "#define BLCKSZ ", 1024 * $self->{options}->{blocksize}, "\n";
@@ -628,7 +628,7 @@ sub GetFakeConfigure
 	$cfg .= ' --with-ldap'  if ($self->{options}->{ldap});
 	$cfg .= ' --without-zlib' unless ($self->{options}->{zlib});
 	$cfg .= ' --with-extra-version' if ($self->{options}->{extraver});
-	$cfg .= ' --with-openssl'       if ($self->{options}->{ssl});
+	$cfg .= ' --with-openssl'       if ($self->{options}->{openssl});
 	$cfg .= ' --with-ossp-uuid'     if ($self->{options}->{uuid});
 	$cfg .= ' --with-libxml'        if ($self->{options}->{xml});
 	$cfg .= ' --with-libxslt'       if ($self->{options}->{xslt});
diff --git a/src/tools/msvc/config_default.pl b/src/tools/msvc/config_default.pl
index 20aee8b..e4d4810 100644
--- a/src/tools/msvc/config_default.pl
+++ b/src/tools/msvc/config_default.pl
@@ -16,7 +16,7 @@ our $config = {
 	tcl      => undef,   # --with-tls=<path>
 	perl     => undef,   # --with-perl
 	python   => undef,   # --with-python=<path>
-	openssl  => undef,   # --with-ssl=<path>
+	openssl  => undef,   # --with-openssl=<path>
 	uuid     => undef,   # --with-ossp-uuid
 	xml      => undef,   # --with-libxml=<path>
 	xslt     => undef,   # --with-libxslt=<path>
-- 
2.0.1

#25Heikki Linnakangas
hlinnakangas@vmware.com
In reply to: Alvaro Herrera (#23)
Re: Supporting Windows SChannel as OpenSSL replacement

On 07/11/2014 08:39 PM, Alvaro Herrera wrote:

Heikki Linnakangas wrote:

I did again the refactoring you did back in 2006, patch attached.
One thing I did differently: I moved the raw, non-encrypted,
read/write functions to separate functions: pqsecure_raw_read and
pqsecure_raw_write. Those functions encapsulate the SIGPIPE
handling. The OpenSSL code implements a custom BIO, which calls to
pqsecure_raw_read/write to do the low-level I/O. Similarly in the
server-side, there are be_tls_raw_read and pg_tls_raw_write
functions, which do the
prepare_for_client_read()/client_read_ended() dance, so that the SSL
implementation doesn't need to know about that.

I'm skimming over this patch (0001). There are some issues:

* You duplicated the long comment under the IDENTIFICATION tag that was
in be-secure.c; it's now both in that file and also in
be-secure-openssl.c. I think it should be removed from be-secure.c.
Also, the hardcoded DH params are duplicated in be-secure.c, but they
belong in -openssl.c only now.

Hmm. Once we add other SSL implementations, shouldn't they share the
hardcoded DH parameters? That would warrant keeping them in be-secure.c.

* There is some mixup regarding USE_SSL and USE_OPENSSL in be-secure.c.
I think anything that's OpenSSL-specific should be in
be-secure-openssl.c only; any new SSL implementation will need to
implement all those functions. For instance, be_tls_init().

Agreed.

I imagine that if we select any SSL implementation, USE_SSL would get
defined, and each SSL implementation would additionally define its own
symbol.

Yeah, that was the idea.

* ssl_renegotiation_limit is also duplicated. But removing this one is
probably not going to be as easy as deleting a line from be-secure.c,
because guc.c depends on that one. I think that variable should be
defined in be-secure.c (i.e. delete it from -openssl) and make sure
that all SSL implementations enforce it on their own somehow.

Agreed.

The DISABLE_SIGPIPE thingy looks wrong in pqsecure_write. I think it
should be like this:

ssize_t
pqsecure_write(PGconn *conn, const void *ptr, size_t len)
{
ssize_t n;

#ifdef USE_SSL
if (conn->ssl_in_use)
{
DECLARE_SIGPIPE_INFO(spinfo);

DISABLE_SIGPIPE(conn, spinfo, return -1);

n = pgtls_write(conn, ptr, len);

RESTORE_SIGPIPE(spinfo);
}
else
#endif /* USE_OPENSSL */
{
n = pqsecure_raw_write(conn, ptr, len);
}

return n;
}

You are missing the restore call, and I moved the declaration inside the
ssl_in_use block since otherwise it's not useful. The other path is
fine since pqsecure_raw_write disables and restores the flag by itself.
Also, you're missing DECLARE/DISABLE/RESTORE in the ssl_in_use block in
pqsecure_read. (The original code does not have that code in the
non-SSL path. I assume, without checking, that that's okay.) I also
assume without checking that all SSL implementations would be fine with
this SIGPIPE handling.

Another thing that seems wrong is the REMEMBER_EPIPE stuff. The
fe-secure-openssl.c code should be setting the flag, but AFAICS only the
non-SSL code is doing it.

I think you're missing a change to the way fe-secure-openssl.c now uses
the OpenSSL library: it defines custom read/write functions,
my_sock_read and my_sock_write, which in turn call
pqsecure_raw_read/write. So all the actual I/O now goes through
pqsecure_raw_read/write. I believe it's therefore enough to put do the
REMEMBER_EPIPE in pqsecure_raw_write. Come to think of it,
pqsecure_write() shouldn't be doing any SIGPIGE stuff at all anymore.

- Heikki

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#26Heikki Linnakangas
hlinnakangas@vmware.com
In reply to: Jeff Janes (#22)
Re: Supporting Windows SChannel as OpenSSL replacement

On 07/08/2014 08:11 PM, Jeff Janes wrote:

Is there some recipe for testing the 0002 patch? Can it be tested on an
MinGW environment, or does it need to use the MicroSoft supplied compilers?

I used MSVC. It ought to work with MinGW, I think, although you might
need to tweak the Makefiles to make it compile. Certainly we should
eventually make it work, before committing. If you try it out, let me
know how it goes.

- Heikki

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#27Jeff Janes
jeff.janes@gmail.com
In reply to: Heikki Linnakangas (#26)
Re: Supporting Windows SChannel as OpenSSL replacement

On Fri, Aug 1, 2014 at 10:58 AM, Heikki Linnakangas <hlinnakangas@vmware.com

wrote:

On 07/08/2014 08:11 PM, Jeff Janes wrote:

Is there some recipe for testing the 0002 patch? Can it be tested on an
MinGW environment, or does it need to use the MicroSoft supplied
compilers?

I used MSVC. It ought to work with MinGW, I think, although you might need
to tweak the Makefiles to make it compile. Certainly we should eventually
make it work, before committing. If you try it out, let me know how it goes.

I couldn't it to work when I tried it long time ago, but I didn't record
the error and it may have been a failure to use the correct arguments to
the configure. I think it was taking a path through all the #ifdef that
resulted in a function never getting defined.

But now it looks like 0002 needs a rebase....

Cheers,

Jeff

#28Heikki Linnakangas
hlinnakangas@vmware.com
In reply to: Heikki Linnakangas (#24)
1 attachment(s)
Re: Supporting Windows SChannel as OpenSSL replacement

Here's a new version of the refactoring patch. I've fixed the issues
reported so far.

Upon looking closer at the SIGPIPE stuff in libpq, I realized that we
can remove this line from fe-secure-openssl.c:

- /* We cannot use MSG_NOSIGNAL to block SIGPIPE when using SSL */
- conn->sigpipe_flag = false;

That's because all the I/O now goes through our wrapper functions that
do the send/recv, and will use MSG_NOSIGNAL if it's available. That
avoids two syscalls per send. I haven't measured the performance impact
of that - it's probably negligible compared to doing encryption - but
it's still nice to avoid it.

This patch is just refactoring of existing code. It doesn't have any
user-visible changes; the libpq functions that expose OpenSSL stuff are
still intact. Doing something about those are the next phase of this
project.

Please review. I think this is in a good shape, ready for commit.

- Heikki

Attachments:

0001-Invent-a-new-internal-API-for-interfacing-with-SSL-3.patchtext/x-diff; name=0001-Invent-a-new-internal-API-for-interfacing-with-SSL-3.patchDownload
>From dd4298d59464101f9df418a62fa884a554ff01e2 Mon Sep 17 00:00:00 2001
From: Heikki Linnakangas <heikki.linnakangas@iki.fi>
Date: Tue, 10 Jun 2014 23:30:51 +0300
Subject: [PATCH] Invent a new internal API for interfacing with SSL.

Refactor OpenSSL specific code to separate files, implementing the new API.

diff --git a/configure b/configure
index 0b0a656..4934795 100755
--- a/configure
+++ b/configure
@@ -5492,7 +5492,7 @@ if test "${with_openssl+set}" = set; then :
   case $withval in
     yes)
 
-$as_echo "#define USE_SSL 1" >>confdefs.h
+$as_echo "#define USE_OPENSSL 1" >>confdefs.h
 
       ;;
     no)
@@ -5513,6 +5513,7 @@ fi
 $as_echo "$with_openssl" >&6; }
 
 
+
 #
 # SELinux
 #
diff --git a/configure.in b/configure.in
index fd9eb71..a72c34c 100644
--- a/configure.in
+++ b/configure.in
@@ -657,9 +657,17 @@ AC_MSG_RESULT([$with_bonjour])
 #
 AC_MSG_CHECKING([whether to build with OpenSSL support])
 PGAC_ARG_BOOL(with, openssl, no, [build with OpenSSL support],
-              [AC_DEFINE([USE_SSL], 1, [Define to build with (Open)SSL support. (--with-openssl)])])
+              [AC_DEFINE([USE_OPENSSL], 1, [Define to build with OpenSSL support. (--with-openssl)])])
 AC_MSG_RESULT([$with_openssl])
 AC_SUBST(with_openssl)
+AH_VERBATIM([USE_SSL],
+[/*
+ * USE_SSL is defined when building with any SSL implementation (currently,
+ * only OpenSSL is supported).
+ */
+#ifdef USE_OPENSSL
+#define USE_SSL
+#endif])
 
 #
 # SELinux
diff --git a/src/backend/libpq/Makefile b/src/backend/libpq/Makefile
index e929864..8be0572 100644
--- a/src/backend/libpq/Makefile
+++ b/src/backend/libpq/Makefile
@@ -17,4 +17,8 @@ include $(top_builddir)/src/Makefile.global
 OBJS = be-fsstubs.o be-secure.o auth.o crypt.o hba.o ip.o md5.o pqcomm.o \
        pqformat.o pqsignal.o
 
+ifeq ($(with_openssl),yes)
+OBJS += be-secure-openssl.o
+endif
+
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c
index 70b0b93..b1974d1 100644
--- a/src/backend/libpq/auth.c
+++ b/src/backend/libpq/auth.c
@@ -161,7 +161,7 @@ static int	pg_SSPI_recvauth(Port *port);
  * RADIUS Authentication
  *----------------------------------------------------------------
  */
-#ifdef USE_SSL
+#ifdef USE_OPENSSL
 #include <openssl/rand.h>
 #endif
 static int	CheckRADIUSAuth(Port *port);
@@ -330,7 +330,7 @@ ClientAuthentication(Port *port)
 		 * already if it didn't verify ok.
 		 */
 #ifdef USE_SSL
-		if (!port->peer)
+		if (!port->peer_cert_valid)
 		{
 			ereport(FATAL,
 					(errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
@@ -378,7 +378,7 @@ ClientAuthentication(Port *port)
 					   (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
 						errmsg("pg_hba.conf rejects replication connection for host \"%s\", user \"%s\", %s",
 							   hostinfo, port->user_name,
-							   port->ssl ? _("SSL on") : _("SSL off"))));
+							   port->ssl_in_use ? _("SSL on") : _("SSL off"))));
 #else
 					ereport(FATAL,
 					   (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
@@ -394,7 +394,7 @@ ClientAuthentication(Port *port)
 						errmsg("pg_hba.conf rejects connection for host \"%s\", user \"%s\", database \"%s\", %s",
 							   hostinfo, port->user_name,
 							   port->database_name,
-							   port->ssl ? _("SSL on") : _("SSL off"))));
+							   port->ssl_in_use ? _("SSL on") : _("SSL off"))));
 #else
 					ereport(FATAL,
 					   (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
@@ -452,7 +452,7 @@ ClientAuthentication(Port *port)
 					   (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
 						errmsg("no pg_hba.conf entry for replication connection from host \"%s\", user \"%s\", %s",
 							   hostinfo, port->user_name,
-							   port->ssl ? _("SSL on") : _("SSL off")),
+							   port->ssl_in_use ? _("SSL on") : _("SSL off")),
 						HOSTNAME_LOOKUP_DETAIL(port)));
 #else
 					ereport(FATAL,
@@ -470,7 +470,7 @@ ClientAuthentication(Port *port)
 						errmsg("no pg_hba.conf entry for host \"%s\", user \"%s\", database \"%s\", %s",
 							   hostinfo, port->user_name,
 							   port->database_name,
-							   port->ssl ? _("SSL on") : _("SSL off")),
+							   port->ssl_in_use ? _("SSL on") : _("SSL off")),
 						HOSTNAME_LOOKUP_DETAIL(port)));
 #else
 					ereport(FATAL,
@@ -2315,7 +2315,7 @@ CheckRADIUSAuth(Port *port)
 	/* Construct RADIUS packet */
 	packet->code = RADIUS_ACCESS_REQUEST;
 	packet->length = RADIUS_HEADER_LENGTH;
-#ifdef USE_SSL
+#ifdef USE_OPENSSL
 	if (RAND_bytes(packet->vector, RADIUS_VECTOR_LENGTH) != 1)
 	{
 		ereport(LOG,
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
new file mode 100644
index 0000000..e3a284b
--- /dev/null
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -0,0 +1,1045 @@
+/*-------------------------------------------------------------------------
+ *
+ * be-secure-openssl.c
+ *	  functions for OpenSSL support in the backend.
+ *
+ *
+ * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  src/backend/libpq/be-secure-openssl.c
+ *
+ *	  Since the server static private key ($DataDir/server.key)
+ *	  will normally be stored unencrypted so that the database
+ *	  backend can restart automatically, it is important that
+ *	  we select an algorithm that continues to provide confidentiality
+ *	  even if the attacker has the server's private key.  Ephemeral
+ *	  DH (EDH) keys provide this, and in fact provide Perfect Forward
+ *	  Secrecy (PFS) except for situations where the session can
+ *	  be hijacked during a periodic handshake/renegotiation.
+ *	  Even that backdoor can be closed if client certificates
+ *	  are used (since the imposter will be unable to successfully
+ *	  complete renegotiation).
+ *
+ *	  N.B., the static private key should still be protected to
+ *	  the largest extent possible, to minimize the risk of
+ *	  impersonations.
+ *
+ *	  Another benefit of EDH is that it allows the backend and
+ *	  clients to use DSA keys.  DSA keys can only provide digital
+ *	  signatures, not encryption, and are often acceptable in
+ *	  jurisdictions where RSA keys are unacceptable.
+ *
+ *	  The downside to EDH is that it makes it impossible to
+ *	  use ssldump(1) if there's a problem establishing an SSL
+ *	  session.  In this case you'll need to temporarily disable
+ *	  EDH by commenting out the callback.
+ *
+ *	  ...
+ *
+ *	  Because the risk of cryptanalysis increases as large
+ *	  amounts of data are sent with the same session key, the
+ *	  session keys are periodically renegotiated.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include <sys/stat.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#ifdef HAVE_NETINET_TCP_H
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+#endif
+
+#include <openssl/ssl.h>
+#include <openssl/dh.h>
+#if SSLEAY_VERSION_NUMBER >= 0x0907000L
+#include <openssl/conf.h>
+#endif
+#if (OPENSSL_VERSION_NUMBER >= 0x0090800fL) && !defined(OPENSSL_NO_ECDH)
+#include <openssl/ec.h>
+#endif
+
+#include "libpq/libpq.h"
+#include "tcop/tcopprot.h"
+#include "utils/memutils.h"
+
+
+
+static DH  *load_dh_file(int keylength);
+static DH  *load_dh_buffer(const char *, size_t);
+static DH  *tmp_dh_cb(SSL *s, int is_export, int keylength);
+static int	verify_cb(int, X509_STORE_CTX *);
+static void info_cb(const SSL *ssl, int type, int args);
+static const char *SSLerrmessage(void);
+
+/* are we in the middle of a renegotiation? */
+static bool in_ssl_renegotiation = false;
+
+static SSL_CTX *SSL_context = NULL;
+
+/* ------------------------------------------------------------ */
+/*						 Hardcoded values						*/
+/* ------------------------------------------------------------ */
+
+/*
+ *	Hardcoded DH parameters, used in ephemeral 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 DBA... or an attacker... deletes the
+ *	$DataDir/dh*.pem files.
+ *
+ *	We could refuse SSL connections unless a good DH parameter
+ *	file exists, but some clients may quietly renegotiate an
+ *	unsecured connection without fully informing the user.
+ *	Very uncool.
+ *
+ *	Alternatively, the backend could attempt to load these files
+ *	on startup if SSL is enabled - and refuse to start if any
+ *	do not exist - but this would tend to piss off DBAs.
+ *
+ *	If you want to create your own hardcoded DH parameters
+ *	for fun and profit, review "Assigned Number for SKIP
+ *	Protocols" (http://www.skip-vpn.org/spec/numbers.html)
+ *	for suggestions.
+ */
+
+static const char file_dh512[] =
+"-----BEGIN DH PARAMETERS-----\n\
+MEYCQQD1Kv884bEpQBgRjXyEpwpy1obEAxnIByl6ypUM2Zafq9AKUJsCRtMIPWak\n\
+XUGfnHy9iUsiGSa6q6Jew1XpKgVfAgEC\n\
+-----END DH PARAMETERS-----\n";
+
+static const char file_dh1024[] =
+"-----BEGIN DH PARAMETERS-----\n\
+MIGHAoGBAPSI/VhOSdvNILSd5JEHNmszbDgNRR0PfIizHHxbLY7288kjwEPwpVsY\n\
+jY67VYy4XTjTNP18F1dDox0YbN4zISy1Kv884bEpQBgRjXyEpwpy1obEAxnIByl6\n\
+ypUM2Zafq9AKUJsCRtMIPWakXUGfnHy9iUsiGSa6q6Jew1XpL3jHAgEC\n\
+-----END DH PARAMETERS-----\n";
+
+static const char file_dh2048[] =
+"-----BEGIN DH PARAMETERS-----\n\
+MIIBCAKCAQEA9kJXtwh/CBdyorrWqULzBej5UxE5T7bxbrlLOCDaAadWoxTpj0BV\n\
+89AHxstDqZSt90xkhkn4DIO9ZekX1KHTUPj1WV/cdlJPPT2N286Z4VeSWc39uK50\n\
+T8X8dryDxUcwYc58yWb/Ffm7/ZFexwGq01uejaClcjrUGvC/RgBYK+X0iP1YTknb\n\
+zSC0neSRBzZrM2w4DUUdD3yIsxx8Wy2O9vPJI8BD8KVbGI2Ou1WMuF040zT9fBdX\n\
+Q6MdGGzeMyEstSr/POGxKUAYEY18hKcKctaGxAMZyAcpesqVDNmWn6vQClCbAkbT\n\
+CD1mpF1Bn5x8vYlLIhkmuquiXsNV6TILOwIBAg==\n\
+-----END DH PARAMETERS-----\n";
+
+static const char file_dh4096[] =
+"-----BEGIN DH PARAMETERS-----\n\
+MIICCAKCAgEA+hRyUsFN4VpJ1O8JLcCo/VWr19k3BCgJ4uk+d+KhehjdRqNDNyOQ\n\
+l/MOyQNQfWXPeGKmOmIig6Ev/nm6Nf9Z2B1h3R4hExf+zTiHnvVPeRBhjdQi81rt\n\
+Xeoh6TNrSBIKIHfUJWBh3va0TxxjQIs6IZOLeVNRLMqzeylWqMf49HsIXqbcokUS\n\
+Vt1BkvLdW48j8PPv5DsKRN3tloTxqDJGo9tKvj1Fuk74A+Xda1kNhB7KFlqMyN98\n\
+VETEJ6c7KpfOo30mnK30wqw3S8OtaIR/maYX72tGOno2ehFDkq3pnPtEbD2CScxc\n\
+alJC+EL7RPk5c/tgeTvCngvc1KZn92Y//EI7G9tPZtylj2b56sHtMftIoYJ9+ODM\n\
+sccD5Piz/rejE3Ome8EOOceUSCYAhXn8b3qvxVI1ddd1pED6FHRhFvLrZxFvBEM9\n\
+ERRMp5QqOaHJkM+Dxv8Cj6MqrCbfC4u+ZErxodzuusgDgvZiLF22uxMZbobFWyte\n\
+OvOzKGtwcTqO/1wV5gKkzu1ZVswVUQd5Gg8lJicwqRWyyNRczDDoG9jVDxmogKTH\n\
+AaqLulO7R8Ifa1SwF2DteSGVtgWEN8gDpN3RBmmPTDngyF2DHb5qmpnznwtFKdTL\n\
+KWbuHn491xNO25CQWMtem80uKw+pTnisBRF/454n1Jnhub144YRBoN8CAQI=\n\
+-----END DH PARAMETERS-----\n";
+
+/*
+ *	Write data to a secure connection.
+ */
+ssize_t
+be_tls_write(Port *port, void *ptr, size_t len)
+{
+	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);
+
+		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")));
+	}
+
+	return n;
+}
+
+/* ------------------------------------------------------------ */
+/*						OpenSSL specific code					*/
+/* ------------------------------------------------------------ */
+
+/*
+ * Private substitute BIO: this does the sending and receiving using send() and
+ * recv() instead. This is so that we can enable and disable interrupts
+ * just while calling recv(). We cannot have interrupts occurring while
+ * the bulk of openssl runs, because it uses malloc() and possibly other
+ * non-reentrant libc facilities. We also need to call send() and recv()
+ * directly so it gets passed through the socket/signals layer on Win32.
+ *
+ * These functions are closely modelled on the standard socket BIO in OpenSSL;
+ * see sock_read() and sock_write() in OpenSSL's crypto/bio/bss_sock.c.
+ * XXX OpenSSL 1.0.1e considers many more errcodes than just EINTR as reasons
+ * to retry; do we need to adopt their logic for that?
+ */
+
+static bool my_bio_initialized = false;
+static BIO_METHOD my_bio_methods;
+
+static int
+my_sock_read(BIO *h, char *buf, int size)
+{
+	int			res = 0;
+
+	if (buf != NULL)
+	{
+		res = secure_raw_read(((Port *)h->ptr), buf, size);
+		BIO_clear_retry_flags(h);
+		if (res <= 0)
+		{
+			/* If we were interrupted, tell caller to retry */
+			if (errno == EINTR)
+			{
+				BIO_set_retry_read(h);
+			}
+		}
+	}
+
+	return res;
+}
+
+static int
+my_sock_write(BIO *h, const char *buf, int size)
+{
+	int			res = 0;
+
+	res = secure_raw_write(((Port *) h->ptr), buf, size);
+	BIO_clear_retry_flags(h);
+	if (res <= 0)
+	{
+		if (errno == EINTR)
+		{
+			BIO_set_retry_write(h);
+		}
+	}
+
+	return res;
+}
+
+static BIO_METHOD *
+my_BIO_s_socket(void)
+{
+	if (!my_bio_initialized)
+	{
+		memcpy(&my_bio_methods, BIO_s_socket(), sizeof(BIO_METHOD));
+		my_bio_methods.bread = my_sock_read;
+		my_bio_methods.bwrite = my_sock_write;
+		my_bio_initialized = true;
+	}
+	return &my_bio_methods;
+}
+
+/* This should exactly match openssl's SSL_set_fd except for using my BIO */
+static int
+my_SSL_set_fd(Port *port, int fd)
+{
+	int			ret = 0;
+	BIO		   *bio = NULL;
+
+	bio = BIO_new(my_BIO_s_socket());
+
+	if (bio == NULL)
+	{
+		SSLerr(SSL_F_SSL_SET_FD, ERR_R_BUF_LIB);
+		goto err;
+	}
+	/* Use 'ptr' to store pointer to PGconn */
+	bio->ptr = port;
+
+	BIO_set_fd(bio, fd, BIO_NOCLOSE);
+	SSL_set_bio(port->ssl, bio, bio);
+	ret = 1;
+err:
+	return ret;
+}
+
+/*
+ *	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());
+			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;
+}
+
+/*
+ *	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;
+}
+
+/*
+ *	Generate an ephemeral DH key.  Because this can take a long
+ *	time to compute, we can use precomputed parameters of the
+ *	common key sizes.
+ *
+ *	Since few sites will bother to precompute these parameter
+ *	files, we also provide a fallback to the parameters provided
+ *	by the OpenSSL project.
+ *
+ *	These values can be static (once loaded or computed) since
+ *	the OpenSSL library can efficiently generate random keys from
+ *	the information provided.
+ */
+static DH  *
+tmp_dh_cb(SSL *s, int is_export, int keylength)
+{
+	DH		   *r = NULL;
+	static DH  *dh = NULL;
+	static DH  *dh512 = NULL;
+	static DH  *dh1024 = NULL;
+	static DH  *dh2048 = NULL;
+	static DH  *dh4096 = NULL;
+
+	switch (keylength)
+	{
+		case 512:
+			if (dh512 == NULL)
+				dh512 = load_dh_file(keylength);
+			if (dh512 == NULL)
+				dh512 = load_dh_buffer(file_dh512, sizeof file_dh512);
+			r = dh512;
+			break;
+
+		case 1024:
+			if (dh1024 == NULL)
+				dh1024 = load_dh_file(keylength);
+			if (dh1024 == NULL)
+				dh1024 = load_dh_buffer(file_dh1024, sizeof file_dh1024);
+			r = dh1024;
+			break;
+
+		case 2048:
+			if (dh2048 == NULL)
+				dh2048 = load_dh_file(keylength);
+			if (dh2048 == NULL)
+				dh2048 = load_dh_buffer(file_dh2048, sizeof file_dh2048);
+			r = dh2048;
+			break;
+
+		case 4096:
+			if (dh4096 == NULL)
+				dh4096 = load_dh_file(keylength);
+			if (dh4096 == NULL)
+				dh4096 = load_dh_buffer(file_dh4096, sizeof file_dh4096);
+			r = dh4096;
+			break;
+
+		default:
+			if (dh == NULL)
+				dh = load_dh_file(keylength);
+			r = dh;
+	}
+
+	/* this may take a long time, but it may be necessary... */
+	if (r == NULL || 8 * DH_size(r) < keylength)
+	{
+		ereport(DEBUG2,
+				(errmsg_internal("DH: generating parameters (%d bits)",
+								 keylength)));
+		r = DH_generate_parameters(keylength, DH_GENERATOR_2, NULL, NULL);
+	}
+
+	return r;
+}
+
+/*
+ *	Certificate verification callback
+ *
+ *	This callback allows us to log intermediate problems during
+ *	verification, but for now we'll see if the final error message
+ *	contains enough information.
+ *
+ *	This callback also allows us to override the default acceptance
+ *	criteria (e.g., accepting self-signed or expired certs), but
+ *	for now we accept the default checks.
+ */
+static int
+verify_cb(int ok, X509_STORE_CTX *ctx)
+{
+	return ok;
+}
+
+/*
+ *	This callback is used to copy SSL information messages
+ *	into the PostgreSQL log.
+ */
+static void
+info_cb(const SSL *ssl, int type, int args)
+{
+	switch (type)
+	{
+		case SSL_CB_HANDSHAKE_START:
+			ereport(DEBUG4,
+					(errmsg_internal("SSL: handshake start")));
+			break;
+		case SSL_CB_HANDSHAKE_DONE:
+			ereport(DEBUG4,
+					(errmsg_internal("SSL: handshake done")));
+			break;
+		case SSL_CB_ACCEPT_LOOP:
+			ereport(DEBUG4,
+					(errmsg_internal("SSL: accept loop")));
+			break;
+		case SSL_CB_ACCEPT_EXIT:
+			ereport(DEBUG4,
+					(errmsg_internal("SSL: accept exit (%d)", args)));
+			break;
+		case SSL_CB_CONNECT_LOOP:
+			ereport(DEBUG4,
+					(errmsg_internal("SSL: connect loop")));
+			break;
+		case SSL_CB_CONNECT_EXIT:
+			ereport(DEBUG4,
+					(errmsg_internal("SSL: connect exit (%d)", args)));
+			break;
+		case SSL_CB_READ_ALERT:
+			ereport(DEBUG4,
+					(errmsg_internal("SSL: read alert (0x%04x)", args)));
+			break;
+		case SSL_CB_WRITE_ALERT:
+			ereport(DEBUG4,
+					(errmsg_internal("SSL: write alert (0x%04x)", args)));
+			break;
+	}
+}
+
+#if (OPENSSL_VERSION_NUMBER >= 0x0090800fL) && !defined(OPENSSL_NO_ECDH)
+static void
+initialize_ecdh(void)
+{
+	EC_KEY	   *ecdh;
+	int			nid;
+
+	nid = OBJ_sn2nid(SSLECDHCurve);
+	if (!nid)
+		ereport(FATAL,
+				(errmsg("ECDH: unrecognized curve name: %s", SSLECDHCurve)));
+
+	ecdh = EC_KEY_new_by_curve_name(nid);
+	if (!ecdh)
+		ereport(FATAL,
+				(errmsg("ECDH: could not create key")));
+
+	SSL_CTX_set_options(SSL_context, SSL_OP_SINGLE_ECDH_USE);
+	SSL_CTX_set_tmp_ecdh(SSL_context, ecdh);
+	EC_KEY_free(ecdh);
+}
+#else
+#define initialize_ecdh()
+#endif
+
+/*
+ *	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);
+	}
+}
+
+/*
+ *	Attempt to negotiate SSL connection.
+ */
+int
+be_tls_open_server(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())));
+		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:
+#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;
+		}
+		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;
+}
+
+/*
+ *	Close SSL connection.
+ */
+void
+be_tls_close(Port *port)
+{
+	if (port->ssl)
+	{
+		SSL_shutdown(port->ssl);
+		SSL_free(port->ssl);
+		port->ssl = NULL;
+		port->ssl_in_use = false;
+	}
+
+	if (port->peer)
+	{
+		X509_free(port->peer);
+		port->peer = NULL;
+	}
+
+	if (port->peer_cn)
+	{
+		pfree(port->peer_cn);
+		port->peer_cn = NULL;
+	}
+}
+
+ssize_t
+be_tls_read(Port *port, void *ptr, size_t len)
+{
+	ssize_t		n;
+	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)));
+			errno = ECONNRESET;
+			n = -1;
+			break;
+	}
+
+	return n;
+}
+
+/*
+ * Obtain reason string for last SSL error
+ *
+ * Some caution is needed here since ERR_reason_error_string will
+ * return NULL if it doesn't recognize the error code.  We don't
+ * want to return NULL ever.
+ */
+static const char *
+SSLerrmessage(void)
+{
+	unsigned long errcode;
+	const char *errreason;
+	static char errbuf[32];
+
+	errcode = ERR_get_error();
+	if (errcode == 0)
+		return _("no SSL error reported");
+	errreason = ERR_reason_error_string(errcode);
+	if (errreason != NULL)
+		return errreason;
+	snprintf(errbuf, sizeof(errbuf), _("SSL error code %lu"), errcode);
+	return errbuf;
+}
+
diff --git a/src/backend/libpq/be-secure.c b/src/backend/libpq/be-secure.c
index 59204cf..41ec1ad 100644
--- a/src/backend/libpq/be-secure.c
+++ b/src/backend/libpq/be-secure.c
@@ -13,38 +13,6 @@
  * IDENTIFICATION
  *	  src/backend/libpq/be-secure.c
  *
- *	  Since the server static private key ($DataDir/server.key)
- *	  will normally be stored unencrypted so that the database
- *	  backend can restart automatically, it is important that
- *	  we select an algorithm that continues to provide confidentiality
- *	  even if the attacker has the server's private key.  Ephemeral
- *	  DH (EDH) keys provide this, and in fact provide Perfect Forward
- *	  Secrecy (PFS) except for situations where the session can
- *	  be hijacked during a periodic handshake/renegotiation.
- *	  Even that backdoor can be closed if client certificates
- *	  are used (since the imposter will be unable to successfully
- *	  complete renegotiation).
- *
- *	  N.B., the static private key should still be protected to
- *	  the largest extent possible, to minimize the risk of
- *	  impersonations.
- *
- *	  Another benefit of EDH is that it allows the backend and
- *	  clients to use DSA keys.  DSA keys can only provide digital
- *	  signatures, not encryption, and are often acceptable in
- *	  jurisdictions where RSA keys are unacceptable.
- *
- *	  The downside to EDH is that it makes it impossible to
- *	  use ssldump(1) if there's a problem establishing an SSL
- *	  session.  In this case you'll need to temporarily disable
- *	  EDH by commenting out the callback.
- *
- *	  ...
- *
- *	  Because the risk of cryptanalysis increases as large
- *	  amounts of data are sent with the same session key, the
- *	  session keys are periodically renegotiated.
- *
  *-------------------------------------------------------------------------
  */
 
@@ -63,35 +31,11 @@
 #include <arpa/inet.h>
 #endif
 
-#ifdef USE_SSL
-#include <openssl/ssl.h>
-#include <openssl/dh.h>
-#if SSLEAY_VERSION_NUMBER >= 0x0907000L
-#include <openssl/conf.h>
-#endif
-#if (OPENSSL_VERSION_NUMBER >= 0x0090800fL) && !defined(OPENSSL_NO_ECDH)
-#include <openssl/ec.h>
-#endif
-#endif   /* USE_SSL */
-
 #include "libpq/libpq.h"
 #include "tcop/tcopprot.h"
 #include "utils/memutils.h"
 
 
-#ifdef USE_SSL
-
-static DH  *load_dh_file(int keylength);
-static DH  *load_dh_buffer(const char *, size_t);
-static DH  *tmp_dh_cb(SSL *s, int is_export, int keylength);
-static int	verify_cb(int, X509_STORE_CTX *);
-static void info_cb(const SSL *ssl, int type, int args);
-static void initialize_SSL(void);
-static int	open_server_SSL(Port *);
-static void close_SSL(Port *);
-static const char *SSLerrmessage(void);
-#endif
-
 char	   *ssl_cert_file;
 char	   *ssl_key_file;
 char	   *ssl_ca_file;
@@ -105,11 +49,7 @@ char	   *ssl_crl_file;
 int			ssl_renegotiation_limit;
 
 #ifdef USE_SSL
-/* are we in the middle of a renegotiation? */
-static bool in_ssl_renegotiation = false;
-
-static SSL_CTX *SSL_context = NULL;
-static bool ssl_loaded_verify_locations = false;
+bool ssl_loaded_verify_locations = false;
 #endif
 
 /* GUC variable controlling SSL cipher list */
@@ -122,73 +62,6 @@ char	   *SSLECDHCurve;
 bool		SSLPreferServerCiphers;
 
 /* ------------------------------------------------------------ */
-/*						 Hardcoded values						*/
-/* ------------------------------------------------------------ */
-
-/*
- *	Hardcoded DH parameters, used in ephemeral 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 DBA... or an attacker... deletes the
- *	$DataDir/dh*.pem files.
- *
- *	We could refuse SSL connections unless a good DH parameter
- *	file exists, but some clients may quietly renegotiate an
- *	unsecured connection without fully informing the user.
- *	Very uncool.
- *
- *	Alternatively, the backend could attempt to load these files
- *	on startup if SSL is enabled - and refuse to start if any
- *	do not exist - but this would tend to piss off DBAs.
- *
- *	If you want to create your own hardcoded DH parameters
- *	for fun and profit, review "Assigned Number for SKIP
- *	Protocols" (http://www.skip-vpn.org/spec/numbers.html)
- *	for suggestions.
- */
-#ifdef USE_SSL
-
-static const char file_dh512[] =
-"-----BEGIN DH PARAMETERS-----\n\
-MEYCQQD1Kv884bEpQBgRjXyEpwpy1obEAxnIByl6ypUM2Zafq9AKUJsCRtMIPWak\n\
-XUGfnHy9iUsiGSa6q6Jew1XpKgVfAgEC\n\
------END DH PARAMETERS-----\n";
-
-static const char file_dh1024[] =
-"-----BEGIN DH PARAMETERS-----\n\
-MIGHAoGBAPSI/VhOSdvNILSd5JEHNmszbDgNRR0PfIizHHxbLY7288kjwEPwpVsY\n\
-jY67VYy4XTjTNP18F1dDox0YbN4zISy1Kv884bEpQBgRjXyEpwpy1obEAxnIByl6\n\
-ypUM2Zafq9AKUJsCRtMIPWakXUGfnHy9iUsiGSa6q6Jew1XpL3jHAgEC\n\
------END DH PARAMETERS-----\n";
-
-static const char file_dh2048[] =
-"-----BEGIN DH PARAMETERS-----\n\
-MIIBCAKCAQEA9kJXtwh/CBdyorrWqULzBej5UxE5T7bxbrlLOCDaAadWoxTpj0BV\n\
-89AHxstDqZSt90xkhkn4DIO9ZekX1KHTUPj1WV/cdlJPPT2N286Z4VeSWc39uK50\n\
-T8X8dryDxUcwYc58yWb/Ffm7/ZFexwGq01uejaClcjrUGvC/RgBYK+X0iP1YTknb\n\
-zSC0neSRBzZrM2w4DUUdD3yIsxx8Wy2O9vPJI8BD8KVbGI2Ou1WMuF040zT9fBdX\n\
-Q6MdGGzeMyEstSr/POGxKUAYEY18hKcKctaGxAMZyAcpesqVDNmWn6vQClCbAkbT\n\
-CD1mpF1Bn5x8vYlLIhkmuquiXsNV6TILOwIBAg==\n\
------END DH PARAMETERS-----\n";
-
-static const char file_dh4096[] =
-"-----BEGIN DH PARAMETERS-----\n\
-MIICCAKCAgEA+hRyUsFN4VpJ1O8JLcCo/VWr19k3BCgJ4uk+d+KhehjdRqNDNyOQ\n\
-l/MOyQNQfWXPeGKmOmIig6Ev/nm6Nf9Z2B1h3R4hExf+zTiHnvVPeRBhjdQi81rt\n\
-Xeoh6TNrSBIKIHfUJWBh3va0TxxjQIs6IZOLeVNRLMqzeylWqMf49HsIXqbcokUS\n\
-Vt1BkvLdW48j8PPv5DsKRN3tloTxqDJGo9tKvj1Fuk74A+Xda1kNhB7KFlqMyN98\n\
-VETEJ6c7KpfOo30mnK30wqw3S8OtaIR/maYX72tGOno2ehFDkq3pnPtEbD2CScxc\n\
-alJC+EL7RPk5c/tgeTvCngvc1KZn92Y//EI7G9tPZtylj2b56sHtMftIoYJ9+ODM\n\
-sccD5Piz/rejE3Ome8EOOceUSCYAhXn8b3qvxVI1ddd1pED6FHRhFvLrZxFvBEM9\n\
-ERRMp5QqOaHJkM+Dxv8Cj6MqrCbfC4u+ZErxodzuusgDgvZiLF22uxMZbobFWyte\n\
-OvOzKGtwcTqO/1wV5gKkzu1ZVswVUQd5Gg8lJicwqRWyyNRczDDoG9jVDxmogKTH\n\
-AaqLulO7R8Ifa1SwF2DteSGVtgWEN8gDpN3RBmmPTDngyF2DHb5qmpnznwtFKdTL\n\
-KWbuHn491xNO25CQWMtem80uKw+pTnisBRF/454n1Jnhub144YRBoN8CAQI=\n\
------END DH PARAMETERS-----\n";
-#endif
-
-/* ------------------------------------------------------------ */
 /*			 Procedures common to all secure sessions			*/
 /* ------------------------------------------------------------ */
 
@@ -199,7 +72,7 @@ int
 secure_initialize(void)
 {
 #ifdef USE_SSL
-	initialize_SSL();
+	be_tls_init();
 #endif
 
 	return 0;
@@ -227,7 +100,7 @@ secure_open_server(Port *port)
 	int			r = 0;
 
 #ifdef USE_SSL
-	r = open_server_SSL(port);
+	r = be_tls_open_server(port);
 #endif
 
 	return r;
@@ -240,8 +113,8 @@ void
 secure_close(Port *port)
 {
 #ifdef USE_SSL
-	if (port->ssl)
-		close_SSL(port);
+	if (port->ssl_in_use)
+		be_tls_close(port);
 #endif
 }
 
@@ -254,908 +127,56 @@ secure_read(Port *port, void *ptr, size_t len)
 	ssize_t		n;
 
 #ifdef USE_SSL
-	if (port->ssl)
+	if (port->ssl_in_use)
 	{
-		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)));
-				errno = ECONNRESET;
-				n = -1;
-				break;
-		}
+		n = be_tls_read(port, ptr, len);
 	}
 	else
 #endif
 	{
-		prepare_for_client_read();
-
-		n = recv(port->sock, ptr, len, 0);
-
-		client_read_ended();
+		n = secure_raw_read(port, ptr, len);
 	}
 
 	return n;
 }
 
-/*
- *	Write data to a secure connection.
- */
 ssize_t
-secure_write(Port *port, void *ptr, size_t len)
+secure_raw_read(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;
-}
-
-/* ------------------------------------------------------------ */
-/*						  SSL specific code						*/
-/* ------------------------------------------------------------ */
-#ifdef USE_SSL
-
-/*
- * Private substitute BIO: this does the sending and receiving using send() and
- * recv() instead. This is so that we can enable and disable interrupts
- * just while calling recv(). We cannot have interrupts occurring while
- * the bulk of openssl runs, because it uses malloc() and possibly other
- * non-reentrant libc facilities. We also need to call send() and recv()
- * directly so it gets passed through the socket/signals layer on Win32.
- *
- * These functions are closely modelled on the standard socket BIO in OpenSSL;
- * see sock_read() and sock_write() in OpenSSL's crypto/bio/bss_sock.c.
- * XXX OpenSSL 1.0.1e considers many more errcodes than just EINTR as reasons
- * to retry; do we need to adopt their logic for that?
- */
-
-static bool my_bio_initialized = false;
-static BIO_METHOD my_bio_methods;
-
-static int
-my_sock_read(BIO *h, char *buf, int size)
-{
-	int			res = 0;
-
 	prepare_for_client_read();
 
-	if (buf != NULL)
-	{
-		res = recv(h->num, buf, size, 0);
-		BIO_clear_retry_flags(h);
-		if (res <= 0)
-		{
-			/* If we were interrupted, tell caller to retry */
-			if (errno == EINTR)
-			{
-				BIO_set_retry_read(h);
-			}
-		}
-	}
+	n = recv(port->sock, ptr, len, 0);
 
 	client_read_ended();
 
-	return res;
-}
-
-static int
-my_sock_write(BIO *h, const char *buf, int size)
-{
-	int			res = 0;
-
-	res = send(h->num, buf, size, 0);
-	BIO_clear_retry_flags(h);
-	if (res <= 0)
-	{
-		if (errno == EINTR)
-		{
-			BIO_set_retry_write(h);
-		}
-	}
-
-	return res;
-}
-
-static BIO_METHOD *
-my_BIO_s_socket(void)
-{
-	if (!my_bio_initialized)
-	{
-		memcpy(&my_bio_methods, BIO_s_socket(), sizeof(BIO_METHOD));
-		my_bio_methods.bread = my_sock_read;
-		my_bio_methods.bwrite = my_sock_write;
-		my_bio_initialized = true;
-	}
-	return &my_bio_methods;
-}
-
-/* This should exactly match openssl's SSL_set_fd except for using my BIO */
-static int
-my_SSL_set_fd(SSL *s, int fd)
-{
-	int			ret = 0;
-	BIO		   *bio = NULL;
-
-	bio = BIO_new(my_BIO_s_socket());
-
-	if (bio == NULL)
-	{
-		SSLerr(SSL_F_SSL_SET_FD, ERR_R_BUF_LIB);
-		goto err;
-	}
-	BIO_set_fd(bio, fd, BIO_NOCLOSE);
-	SSL_set_bio(s, bio, bio);
-	ret = 1;
-err:
-	return ret;
-}
-
-/*
- *	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());
-			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;
-}
-
-/*
- *	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;
-}
-
-/*
- *	Generate an ephemeral DH key.  Because this can take a long
- *	time to compute, we can use precomputed parameters of the
- *	common key sizes.
- *
- *	Since few sites will bother to precompute these parameter
- *	files, we also provide a fallback to the parameters provided
- *	by the OpenSSL project.
- *
- *	These values can be static (once loaded or computed) since
- *	the OpenSSL library can efficiently generate random keys from
- *	the information provided.
- */
-static DH  *
-tmp_dh_cb(SSL *s, int is_export, int keylength)
-{
-	DH		   *r = NULL;
-	static DH  *dh = NULL;
-	static DH  *dh512 = NULL;
-	static DH  *dh1024 = NULL;
-	static DH  *dh2048 = NULL;
-	static DH  *dh4096 = NULL;
-
-	switch (keylength)
-	{
-		case 512:
-			if (dh512 == NULL)
-				dh512 = load_dh_file(keylength);
-			if (dh512 == NULL)
-				dh512 = load_dh_buffer(file_dh512, sizeof file_dh512);
-			r = dh512;
-			break;
-
-		case 1024:
-			if (dh1024 == NULL)
-				dh1024 = load_dh_file(keylength);
-			if (dh1024 == NULL)
-				dh1024 = load_dh_buffer(file_dh1024, sizeof file_dh1024);
-			r = dh1024;
-			break;
-
-		case 2048:
-			if (dh2048 == NULL)
-				dh2048 = load_dh_file(keylength);
-			if (dh2048 == NULL)
-				dh2048 = load_dh_buffer(file_dh2048, sizeof file_dh2048);
-			r = dh2048;
-			break;
-
-		case 4096:
-			if (dh4096 == NULL)
-				dh4096 = load_dh_file(keylength);
-			if (dh4096 == NULL)
-				dh4096 = load_dh_buffer(file_dh4096, sizeof file_dh4096);
-			r = dh4096;
-			break;
-
-		default:
-			if (dh == NULL)
-				dh = load_dh_file(keylength);
-			r = dh;
-	}
-
-	/* this may take a long time, but it may be necessary... */
-	if (r == NULL || 8 * DH_size(r) < keylength)
-	{
-		ereport(DEBUG2,
-				(errmsg_internal("DH: generating parameters (%d bits)",
-								 keylength)));
-		r = DH_generate_parameters(keylength, DH_GENERATOR_2, NULL, NULL);
-	}
-
-	return r;
-}
-
-/*
- *	Certificate verification callback
- *
- *	This callback allows us to log intermediate problems during
- *	verification, but for now we'll see if the final error message
- *	contains enough information.
- *
- *	This callback also allows us to override the default acceptance
- *	criteria (e.g., accepting self-signed or expired certs), but
- *	for now we accept the default checks.
- */
-static int
-verify_cb(int ok, X509_STORE_CTX *ctx)
-{
-	return ok;
-}
-
-/*
- *	This callback is used to copy SSL information messages
- *	into the PostgreSQL log.
- */
-static void
-info_cb(const SSL *ssl, int type, int args)
-{
-	switch (type)
-	{
-		case SSL_CB_HANDSHAKE_START:
-			ereport(DEBUG4,
-					(errmsg_internal("SSL: handshake start")));
-			break;
-		case SSL_CB_HANDSHAKE_DONE:
-			ereport(DEBUG4,
-					(errmsg_internal("SSL: handshake done")));
-			break;
-		case SSL_CB_ACCEPT_LOOP:
-			ereport(DEBUG4,
-					(errmsg_internal("SSL: accept loop")));
-			break;
-		case SSL_CB_ACCEPT_EXIT:
-			ereport(DEBUG4,
-					(errmsg_internal("SSL: accept exit (%d)", args)));
-			break;
-		case SSL_CB_CONNECT_LOOP:
-			ereport(DEBUG4,
-					(errmsg_internal("SSL: connect loop")));
-			break;
-		case SSL_CB_CONNECT_EXIT:
-			ereport(DEBUG4,
-					(errmsg_internal("SSL: connect exit (%d)", args)));
-			break;
-		case SSL_CB_READ_ALERT:
-			ereport(DEBUG4,
-					(errmsg_internal("SSL: read alert (0x%04x)", args)));
-			break;
-		case SSL_CB_WRITE_ALERT:
-			ereport(DEBUG4,
-					(errmsg_internal("SSL: write alert (0x%04x)", args)));
-			break;
-	}
+	return n;
 }
 
-#if (OPENSSL_VERSION_NUMBER >= 0x0090800fL) && !defined(OPENSSL_NO_ECDH)
-static void
-initialize_ecdh(void)
-{
-	EC_KEY	   *ecdh;
-	int			nid;
-
-	nid = OBJ_sn2nid(SSLECDHCurve);
-	if (!nid)
-		ereport(FATAL,
-				(errmsg("ECDH: unrecognized curve name: %s", SSLECDHCurve)));
-
-	ecdh = EC_KEY_new_by_curve_name(nid);
-	if (!ecdh)
-		ereport(FATAL,
-				(errmsg("ECDH: could not create key")));
-
-	SSL_CTX_set_options(SSL_context, SSL_OP_SINGLE_ECDH_USE);
-	SSL_CTX_set_tmp_ecdh(SSL_context, ecdh);
-	EC_KEY_free(ecdh);
-}
-#else
-#define initialize_ecdh()
-#endif
 
 /*
- *	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();
-
-		/*
-		 * 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);
-	}
-}
-
-/*
- *	Attempt to negotiate SSL connection.
+ *	Write data to a secure connection.
  */
-static int
-open_server_SSL(Port *port)
+ssize_t
+secure_write(Port *port, void *ptr, size_t len)
 {
-	int			r;
-	int			err;
-
-	Assert(!port->ssl);
-	Assert(!port->peer);
+	ssize_t		n;
 
-	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))
+#ifdef USE_SSL
+	if (port->ssl_in_use)
 	{
-		ereport(COMMERROR,
-				(errcode(ERRCODE_PROTOCOL_VIOLATION),
-				 errmsg("could not set SSL socket: %s",
-						SSLerrmessage())));
-		close_SSL(port);
-		return -1;
+		n = be_tls_write(port, ptr, len);
 	}
-
-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);
+	else
 #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;
+		n = secure_raw_write(port, ptr, 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;
-}
-
-/*
- *	Close SSL connection.
- */
-static void
-close_SSL(Port *port)
-{
-	if (port->ssl)
-	{
-		SSL_shutdown(port->ssl);
-		SSL_free(port->ssl);
-		port->ssl = NULL;
-	}
-
-	if (port->peer)
-	{
-		X509_free(port->peer);
-		port->peer = NULL;
-	}
-
-	if (port->peer_cn)
-	{
-		pfree(port->peer_cn);
-		port->peer_cn = NULL;
-	}
+	return n;
 }
 
-/*
- * Obtain reason string for last SSL error
- *
- * Some caution is needed here since ERR_reason_error_string will
- * return NULL if it doesn't recognize the error code.  We don't
- * want to return NULL ever.
- */
-static const char *
-SSLerrmessage(void)
+ssize_t
+secure_raw_write(Port *port, const void *ptr, size_t len)
 {
-	unsigned long errcode;
-	const char *errreason;
-	static char errbuf[32];
-
-	errcode = ERR_get_error();
-	if (errcode == 0)
-		return _("no SSL error reported");
-	errreason = ERR_reason_error_string(errcode);
-	if (errreason != NULL)
-		return errreason;
-	snprintf(errbuf, sizeof(errbuf), _("SSL error code %lu"), errcode);
-	return errbuf;
+	return send(port->sock, ptr, len, 0);
 }
-
-#endif   /* USE_SSL */
diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c
index fd98c60..84da823 100644
--- a/src/backend/libpq/hba.c
+++ b/src/backend/libpq/hba.c
@@ -1685,7 +1685,7 @@ check_hba(hbaPort *port)
 
 			/* Check SSL state */
 #ifdef USE_SSL
-			if (port->ssl)
+			if (port->ssl_in_use)
 			{
 				/* Connection is SSL, match both "host" and "hostssl" */
 				if (hba->conntype == ctHostNoSSL)
diff --git a/src/backend/postmaster/fork_process.c b/src/backend/postmaster/fork_process.c
index 5e5bd35..dd3abab 100644
--- a/src/backend/postmaster/fork_process.c
+++ b/src/backend/postmaster/fork_process.c
@@ -17,7 +17,7 @@
 #include <sys/stat.h>
 #include <sys/time.h>
 #include <unistd.h>
-#ifdef USE_SSL
+#ifdef USE_OPENSSL
 #include <openssl/rand.h>
 #endif
 
@@ -110,7 +110,7 @@ fork_process(void)
 		/*
 		 * Make sure processes do not share OpenSSL randomness state.
 		 */
-#ifdef USE_SSL
+#ifdef USE_OPENSSL
 		RAND_cleanup();
 #endif
 	}
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
index 28243ad..a5b9821 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -231,8 +231,8 @@ PerformAuthentication(Port *port)
 	{
 		if (am_walsender)
 		{
-#ifdef USE_SSL
-			if (port->ssl)
+#ifdef USE_OPENSSL
+			if (port->ssl_in_use)
 				ereport(LOG,
 						(errmsg("replication connection authorized: user=%s SSL enabled (protocol=%s, cipher=%s, compression=%s)",
 								port->user_name, SSL_get_version(port->ssl), SSL_get_cipher(port->ssl),
@@ -245,8 +245,8 @@ PerformAuthentication(Port *port)
 		}
 		else
 		{
-#ifdef USE_SSL
-			if (port->ssl)
+#ifdef USE_OPENSSL
+			if (port->ssl_in_use)
 				ereport(LOG,
 						(errmsg("connection authorized: user=%s database=%s SSL enabled (protocol=%s, cipher=%s, compression=%s)",
 								port->user_name, port->database_name, SSL_get_version(port->ssl), SSL_get_cipher(port->ssl),
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 6c52db8..9aa1bc4 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -125,9 +125,6 @@ extern char *default_tablespace;
 extern char *temp_tablespaces;
 extern bool ignore_checksum_failure;
 extern bool synchronize_seqscans;
-extern char *SSLCipherSuites;
-extern char *SSLECDHCurve;
-extern bool SSLPreferServerCiphers;
 
 #ifdef TRACE_SORT
 extern bool trace_sort;
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 161de75..958505d 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -30,7 +30,7 @@
 #include <sys/types.h>			/* for umask() */
 #include <sys/stat.h>			/* for stat() */
 #endif
-#ifdef USE_SSL
+#ifdef USE_OPENSSL
 #include <openssl/ssl.h>
 #endif
 
@@ -1791,7 +1791,7 @@ connection_warnings(bool in_startup)
 static void
 printSSLInfo(void)
 {
-#ifdef USE_SSL
+#ifdef USE_OPENSSL
 	int			sslbits = -1;
 	SSL		   *ssl;
 
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index e78c565..34e52e4 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -21,7 +21,7 @@
 #ifdef HAVE_SYS_TIME_H
 #include <sys/time.h>
 #endif
-#ifdef USE_SSL
+#ifdef USE_OPENSSL
 #include <openssl/ssl.h>
 #include <openssl/err.h>
 #endif
@@ -184,17 +184,33 @@ typedef struct Port
 #endif
 
 	/*
-	 * SSL structures (keep these last so that USE_SSL doesn't affect
-	 * locations of other fields)
+	 * SSL structures (keep these last so that the locations of other fields
+	 * are the same whether or not you build with SSL)
 	 */
 #ifdef USE_SSL
+	bool		ssl_in_use;
+	char	   *peer_cn;
+	bool		peer_cert_valid;
+#endif
+#ifdef USE_OPENSSL
 	SSL		   *ssl;
 	X509	   *peer;
-	char	   *peer_cn;
 	unsigned long count;
 #endif
 } Port;
 
+#ifdef USE_SSL
+/*
+ * These functions are implemented by the glue code specific to each
+ * SSL implementation (e.g. be-secure-openssl.c)
+ */
+extern void be_tls_init(void);
+extern int be_tls_open_server(Port *port);
+extern void be_tls_close(Port *port);
+extern ssize_t be_tls_read(Port *port, void *ptr, size_t len);
+extern ssize_t be_tls_write(Port *port, void *ptr, size_t len);
+
+#endif
 
 extern ProtocolVersion FrontendProtocol;
 
diff --git a/src/include/libpq/libpq.h b/src/include/libpq/libpq.h
index e4e354d..5da9d8d 100644
--- a/src/include/libpq/libpq.h
+++ b/src/include/libpq/libpq.h
@@ -82,5 +82,14 @@ extern int	secure_open_server(Port *port);
 extern void secure_close(Port *port);
 extern ssize_t secure_read(Port *port, void *ptr, size_t len);
 extern ssize_t secure_write(Port *port, void *ptr, size_t len);
+extern ssize_t secure_raw_read(Port *port, void *ptr, size_t len);
+extern ssize_t secure_raw_write(Port *port, const void *ptr, size_t len);
+
+extern bool ssl_loaded_verify_locations;
+
+/* GUCs */
+extern char *SSLCipherSuites;
+extern char *SSLECDHCurve;
+extern bool SSLPreferServerCiphers;
 
 #endif   /* LIBPQ_H */
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index 4383ad5..7894573 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -778,14 +778,22 @@
 /* Define to select named POSIX semaphores. */
 #undef USE_NAMED_POSIX_SEMAPHORES
 
+/* Define to build with OpenSSL support. (--with-openssl) */
+#undef USE_OPENSSL
+
 /* Define to 1 to build with PAM support. (--with-pam) */
 #undef USE_PAM
 
 /* Use replacement snprintf() functions. */
 #undef USE_REPL_SNPRINTF
 
-/* Define to build with (Open)SSL support. (--with-openssl) */
-#undef USE_SSL
+/*
+ * USE_SSL is defined when building with any SSL implementation (currently,
+ * only OpenSSL is supported).
+ */
+#ifdef USE_OPENSSL
+#define USE_SSL
+#endif
 
 /* Define to select SysV-style semaphores. */
 #undef USE_SYSV_SEMAPHORES
diff --git a/src/include/pg_config.h.win32 b/src/include/pg_config.h.win32
index f7c2419..d802019 100644
--- a/src/include/pg_config.h.win32
+++ b/src/include/pg_config.h.win32
@@ -628,14 +628,22 @@
 /* Define to select named POSIX semaphores. */
 /* #undef USE_NAMED_POSIX_SEMAPHORES */
 
+/* Define to build with OpenSSL support. (--with-openssl) */
+/* #undef USE_OPENSSL */
+
 /* Define to 1 to build with PAM support. (--with-pam) */
 /* #undef USE_PAM */
 
 /* Use replacement snprintf() functions. */
 #define USE_REPL_SNPRINTF 1
 
-/* Define to build with (Open)SSL support. (--with-openssl) */
-/* #undef USE_SSL */
+/*
+ * USE_SSL is defined when building with any SSL implementation (currently,
+ * only OpenSSL is supported).
+ */
+#ifdef USE_OPENSSL
+#define USE_SSL
+#endif
 
 /* Define to select SysV-style semaphores. */
 /* #undef USE_SYSV_SEMAPHORES */
diff --git a/src/interfaces/libpq/Makefile b/src/interfaces/libpq/Makefile
index 718ecd6..a90cb89 100644
--- a/src/interfaces/libpq/Makefile
+++ b/src/interfaces/libpq/Makefile
@@ -44,6 +44,10 @@ OBJS += ip.o md5.o
 # utils/mb
 OBJS += encnames.o wchar.o
 
+ifeq ($(with_openssl),yes)
+OBJS += fe-secure-openssl.o
+endif
+
 ifeq ($(PORTNAME), cygwin)
 override shlib = cyg$(NAME)$(DLSUFFIX)
 endif
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index 540426c..b0b0e1a 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -1961,7 +1961,7 @@ keep_going:						/* We will come back to here until there is
 					conn->allow_ssl_try = false;
 				}
 				if (conn->allow_ssl_try && !conn->wait_ssl_try &&
-					conn->ssl == NULL)
+					!conn->ssl_in_use)
 				{
 					ProtocolVersion pv;
 
@@ -2040,7 +2040,7 @@ keep_going:						/* We will come back to here until there is
 				 * On first time through, get the postmaster's response to our
 				 * SSL negotiation packet.
 				 */
-				if (conn->ssl == NULL)
+				if (!conn->ssl_in_use)
 				{
 					/*
 					 * We use pqReadData here since it has the logic to
@@ -2310,7 +2310,7 @@ keep_going:						/* We will come back to here until there is
 					 * connection already, then retry with an SSL connection
 					 */
 					if (conn->sslmode[0] == 'a' /* "allow" */
-						&& conn->ssl == NULL
+						&& !conn->ssl_in_use
 						&& conn->allow_ssl_try
 						&& conn->wait_ssl_try)
 					{
@@ -2709,6 +2709,7 @@ makeEmptyPGconn(void)
 #ifdef USE_SSL
 	conn->allow_ssl_try = true;
 	conn->wait_ssl_try = false;
+	conn->ssl_in_use = false;
 #endif
 
 	/*
diff --git a/src/interfaces/libpq/fe-misc.c b/src/interfaces/libpq/fe-misc.c
index a75db19..fc930bd 100644
--- a/src/interfaces/libpq/fe-misc.c
+++ b/src/interfaces/libpq/fe-misc.c
@@ -751,7 +751,7 @@ retry3:
 	 */
 
 #ifdef USE_SSL
-	if (conn->ssl)
+	if (conn->ssl_in_use)
 		return 0;
 #endif
 
@@ -1051,7 +1051,7 @@ pqSocketCheck(PGconn *conn, int forRead, int forWrite, time_t end_time)
 		return -1;
 	}
 
-#ifdef USE_SSL
+#ifdef USE_OPENSSL
 	/* Check for SSL library buffering read bytes */
 	if (forRead && conn->ssl && SSL_pending(conn->ssl) > 0)
 	{
diff --git a/src/interfaces/libpq/fe-secure-openssl.c b/src/interfaces/libpq/fe-secure-openssl.c
new file mode 100644
index 0000000..f950fc3
--- /dev/null
+++ b/src/interfaces/libpq/fe-secure-openssl.c
@@ -0,0 +1,1468 @@
+/*-------------------------------------------------------------------------
+ *
+ * fe-secure-openssl.c
+ *	  OpenSSL support
+ *
+ *
+ * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  src/interfaces/libpq/fe-secure-openssl.c
+ *
+ * NOTES
+ *
+ *	  We don't provide informational callbacks here (like
+ *	  info_cb() in be-secure.c), since there's no good mechanism to
+ *	  display such information to the user.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres_fe.h"
+
+#include <signal.h>
+#include <fcntl.h>
+#include <ctype.h>
+
+#include "libpq-fe.h"
+#include "fe-auth.h"
+#include "libpq-int.h"
+
+#ifdef WIN32
+#include "win32.h"
+#else
+#include <sys/socket.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#ifdef HAVE_NETINET_TCP_H
+#include <netinet/tcp.h>
+#endif
+#include <arpa/inet.h>
+#endif
+
+#include <sys/stat.h>
+
+#ifdef ENABLE_THREAD_SAFETY
+#ifdef WIN32
+#include "pthread-win32.h"
+#else
+#include <pthread.h>
+#endif
+#endif
+
+#include <openssl/ssl.h>
+#if (SSLEAY_VERSION_NUMBER >= 0x00907000L)
+#include <openssl/conf.h>
+#endif
+#ifdef USE_SSL_ENGINE
+#include <openssl/engine.h>
+#endif
+
+static bool verify_peer_name_matches_certificate(PGconn *);
+static int	verify_cb(int ok, X509_STORE_CTX *ctx);
+static void destroy_ssl_system(void);
+static int	initialize_SSL(PGconn *conn);
+static PostgresPollingStatusType open_client_SSL(PGconn *);
+static char *SSLerrmessage(void);
+static void SSLerrfree(char *buf);
+
+static int my_sock_read(BIO *h, char *buf, int size);
+static int my_sock_write(BIO *h, const char *buf, int size);
+static BIO_METHOD *my_BIO_s_socket(void);
+static int my_SSL_set_fd(PGconn *conn, int fd);
+
+
+static bool pq_init_ssl_lib = true;
+static bool pq_init_crypto_lib = true;
+
+/*
+ * SSL_context is currently shared between threads and therefore we need to be
+ * careful to lock around any usage of it when providing thread safety.
+ * ssl_config_mutex is the mutex that we use to protect it.
+ */
+static SSL_CTX *SSL_context = NULL;
+
+#ifdef ENABLE_THREAD_SAFETY
+static long ssl_open_connections = 0;
+
+#ifndef WIN32
+static pthread_mutex_t ssl_config_mutex = PTHREAD_MUTEX_INITIALIZER;
+#else
+static pthread_mutex_t ssl_config_mutex = NULL;
+static long win32_ssl_create_mutex = 0;
+#endif
+#endif   /* ENABLE_THREAD_SAFETY */
+
+
+/* ------------------------------------------------------------ */
+/*			 Procedures common to all secure sessions			*/
+/* ------------------------------------------------------------ */
+
+/*
+ *	Exported function to allow application to tell us it's already
+ *	initialized OpenSSL and/or libcrypto.
+ */
+void
+pgtls_init_library(bool do_ssl, int do_crypto)
+{
+#ifdef ENABLE_THREAD_SAFETY
+
+	/*
+	 * Disallow changing the flags while we have open connections, else we'd
+	 * get completely confused.
+	 */
+	if (ssl_open_connections != 0)
+		return;
+#endif
+
+	pq_init_ssl_lib = do_ssl;
+	pq_init_crypto_lib = do_crypto;
+}
+
+/*
+ *	Begin or continue negotiating a secure session.
+ */
+PostgresPollingStatusType
+pgtls_open_client(PGconn *conn)
+{
+	/* First time through? */
+	if (conn->ssl == NULL)
+	{
+#ifdef ENABLE_THREAD_SAFETY
+		int			rc;
+#endif
+
+#ifdef ENABLE_THREAD_SAFETY
+		if ((rc = pthread_mutex_lock(&ssl_config_mutex)))
+		{
+			printfPQExpBuffer(&conn->errorMessage,
+			   libpq_gettext("could not acquire mutex: %s\n"), strerror(rc));
+			return PGRES_POLLING_FAILED;
+		}
+#endif
+		/* Create a connection-specific SSL object */
+		if (!(conn->ssl = SSL_new(SSL_context)) ||
+			!SSL_set_app_data(conn->ssl, conn) ||
+			!my_SSL_set_fd(conn, conn->sock))
+		{
+			char	   *err = SSLerrmessage();
+
+			printfPQExpBuffer(&conn->errorMessage,
+				   libpq_gettext("could not establish SSL connection: %s\n"),
+							  err);
+			SSLerrfree(err);
+#ifdef ENABLE_THREAD_SAFETY
+			pthread_mutex_unlock(&ssl_config_mutex);
+#endif
+			pgtls_close(conn);
+
+			return PGRES_POLLING_FAILED;
+		}
+		conn->ssl_in_use = true;
+
+#ifdef ENABLE_THREAD_SAFETY
+		pthread_mutex_unlock(&ssl_config_mutex);
+#endif
+
+		/*
+		 * Load client certificate, private key, and trusted CA certs.
+		 */
+		if (initialize_SSL(conn) != 0)
+		{
+			/* initialize_SSL already put a message in conn->errorMessage */
+			pgtls_close(conn);
+			return PGRES_POLLING_FAILED;
+		}
+	}
+
+	/* Begin or continue the actual handshake */
+	return open_client_SSL(conn);
+}
+
+/*
+ *	Read data from a secure connection.
+ *
+ * On failure, this function is responsible for putting a suitable message
+ * into conn->errorMessage.  The caller must still inspect errno, but only
+ * to determine whether to continue/retry after error.
+ */
+ssize_t
+pgtls_read(PGconn *conn, void *ptr, size_t len)
+{
+	ssize_t		n;
+	int			result_errno = 0;
+	char		sebuf[256];
+	int			err;
+
+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 */
+				printfPQExpBuffer(&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;
+				if (result_errno == EPIPE ||
+					result_errno == ECONNRESET)
+					printfPQExpBuffer(&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
+					printfPQExpBuffer(&conn->errorMessage,
+									libpq_gettext("SSL SYSCALL error: %s\n"),
+									  SOCK_STRERROR(result_errno,
+													sebuf, sizeof(sebuf)));
+			}
+			else
+			{
+				printfPQExpBuffer(&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();
+
+				printfPQExpBuffer(&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.
+			 */
+			printfPQExpBuffer(&conn->errorMessage,
+							  libpq_gettext("SSL connection has been closed unexpectedly\n"));
+			result_errno = ECONNRESET;
+			n = -1;
+			break;
+		default:
+			printfPQExpBuffer(&conn->errorMessage,
+						  libpq_gettext("unrecognized SSL error code: %d\n"),
+							  err);
+			/* assume the connection is broken */
+			result_errno = ECONNRESET;
+			n = -1;
+			break;
+	}
+
+	/* ensure we return the intended errno to caller */
+	SOCK_ERRNO_SET(result_errno);
+
+	return n;
+}
+
+/*
+ *	Write data to a secure connection.
+ *
+ * On failure, this function is responsible for putting a suitable message
+ * into conn->errorMessage.  The caller must still inspect errno, but only
+ * to determine whether to continue/retry after error.
+ */
+ssize_t
+pgtls_write(PGconn *conn, const void *ptr, size_t len)
+{
+	ssize_t		n;
+	int			result_errno = 0;
+	char		sebuf[256];
+	int			err;
+
+	SOCK_ERRNO_SET(0);
+	n = SSL_write(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 */
+				printfPQExpBuffer(&conn->errorMessage,
+								  "SSL_write failed but did not provide error information\n");
+				/* assume the connection is broken */
+				result_errno = ECONNRESET;
+			}
+			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:
+			if (n < 0)
+			{
+				result_errno = SOCK_ERRNO;
+				if (result_errno == EPIPE || result_errno == ECONNRESET)
+					printfPQExpBuffer(&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
+					printfPQExpBuffer(&conn->errorMessage,
+									libpq_gettext("SSL SYSCALL error: %s\n"),
+									  SOCK_STRERROR(result_errno,
+													sebuf, sizeof(sebuf)));
+			}
+			else
+			{
+				printfPQExpBuffer(&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();
+
+				printfPQExpBuffer(&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.
+			 */
+			printfPQExpBuffer(&conn->errorMessage,
+							  libpq_gettext("SSL connection has been closed unexpectedly\n"));
+			result_errno = ECONNRESET;
+			n = -1;
+			break;
+		default:
+			printfPQExpBuffer(&conn->errorMessage,
+						  libpq_gettext("unrecognized SSL error code: %d\n"),
+							  err);
+			/* assume the connection is broken */
+			result_errno = ECONNRESET;
+			n = -1;
+			break;
+	}
+
+	/* ensure we return the intended errno to caller */
+	SOCK_ERRNO_SET(result_errno);
+
+	return n;
+}
+
+/* ------------------------------------------------------------ */
+/*						OpenSSL specific code					*/
+/* ------------------------------------------------------------ */
+
+/*
+ *	Certificate verification callback
+ *
+ *	This callback allows us to log intermediate problems during
+ *	verification, but there doesn't seem to be a clean way to get
+ *	our PGconn * structure.  So we can't log anything!
+ *
+ *	This callback also allows us to override the default acceptance
+ *	criteria (e.g., accepting self-signed or expired certs), but
+ *	for now we accept the default checks.
+ */
+static int
+verify_cb(int ok, X509_STORE_CTX *ctx)
+{
+	return ok;
+}
+
+
+/*
+ * Check if a wildcard certificate matches the server hostname.
+ *
+ * The rule for this is:
+ *	1. We only match the '*' character as wildcard
+ *	2. We match only wildcards at the start of the string
+ *	3. The '*' character does *not* match '.', meaning that we match only
+ *	   a single pathname component.
+ *	4. We don't support more than one '*' in a single pattern.
+ *
+ * This is roughly in line with RFC2818, but contrary to what most browsers
+ * appear to be implementing (point 3 being the difference)
+ *
+ * Matching is always case-insensitive, since DNS is case insensitive.
+ */
+static int
+wildcard_certificate_match(const char *pattern, const char *string)
+{
+	int			lenpat = strlen(pattern);
+	int			lenstr = strlen(string);
+
+	/* If we don't start with a wildcard, it's not a match (rule 1 & 2) */
+	if (lenpat < 3 ||
+		pattern[0] != '*' ||
+		pattern[1] != '.')
+		return 0;
+
+	if (lenpat > lenstr)
+		/* If pattern is longer than the string, we can never match */
+		return 0;
+
+	if (pg_strcasecmp(pattern + 1, string + lenstr - lenpat + 1) != 0)
+
+		/*
+		 * If string does not end in pattern (minus the wildcard), we don't
+		 * match
+		 */
+		return 0;
+
+	if (strchr(string, '.') < string + lenstr - lenpat)
+
+		/*
+		 * If there is a dot left of where the pattern started to match, we
+		 * don't match (rule 3)
+		 */
+		return 0;
+
+	/* String ended with pattern, and didn't have a dot before, so we match */
+	return 1;
+}
+
+
+/*
+ *	Verify that common name resolves to peer.
+ */
+static bool
+verify_peer_name_matches_certificate(PGconn *conn)
+{
+	char	   *peer_cn;
+	int			r;
+	int			len;
+	bool		result;
+
+	/*
+	 * If told not to verify the peer name, don't do it. Return true
+	 * indicating that the verification was successful.
+	 */
+	if (strcmp(conn->sslmode, "verify-full") != 0)
+		return true;
+
+	/*
+	 * Extract the common name from the certificate.
+	 *
+	 * XXX: Should support alternate names here
+	 */
+	/* First find out the name's length and allocate a buffer for it. */
+	len = X509_NAME_get_text_by_NID(X509_get_subject_name(conn->peer),
+									NID_commonName, NULL, 0);
+	if (len == -1)
+	{
+		printfPQExpBuffer(&conn->errorMessage,
+						  libpq_gettext("could not get server common name from server certificate\n"));
+		return false;
+	}
+	peer_cn = malloc(len + 1);
+	if (peer_cn == NULL)
+	{
+		printfPQExpBuffer(&conn->errorMessage,
+						  libpq_gettext("out of memory\n"));
+		return false;
+	}
+
+	r = X509_NAME_get_text_by_NID(X509_get_subject_name(conn->peer),
+								  NID_commonName, peer_cn, len + 1);
+	if (r != len)
+	{
+		/* Got different length than on the first call. Shouldn't happen. */
+		printfPQExpBuffer(&conn->errorMessage,
+						  libpq_gettext("could not get server common name from server certificate\n"));
+		free(peer_cn);
+		return false;
+	}
+	peer_cn[len] = '\0';
+
+	/*
+	 * Reject embedded NULLs in certificate common name to prevent attacks
+	 * like CVE-2009-4034.
+	 */
+	if (len != strlen(peer_cn))
+	{
+		printfPQExpBuffer(&conn->errorMessage,
+						  libpq_gettext("SSL certificate's common name contains embedded null\n"));
+		free(peer_cn);
+		return false;
+	}
+
+	/*
+	 * We got the peer's common name. Now compare it against the originally
+	 * given hostname.
+	 */
+	if (!(conn->pghost && conn->pghost[0] != '\0'))
+	{
+		printfPQExpBuffer(&conn->errorMessage,
+						  libpq_gettext("host name must be specified for a verified SSL connection\n"));
+		result = false;
+	}
+	else
+	{
+		if (pg_strcasecmp(peer_cn, conn->pghost) == 0)
+			/* Exact name match */
+			result = true;
+		else if (wildcard_certificate_match(peer_cn, conn->pghost))
+			/* Matched wildcard certificate */
+			result = true;
+		else
+		{
+			printfPQExpBuffer(&conn->errorMessage,
+							  libpq_gettext("server common name \"%s\" does not match host name \"%s\"\n"),
+							  peer_cn, conn->pghost);
+			result = false;
+		}
+	}
+
+	free(peer_cn);
+	return result;
+}
+
+#ifdef ENABLE_THREAD_SAFETY
+/*
+ *	Callback functions for OpenSSL internal locking
+ */
+
+static unsigned long
+pq_threadidcallback(void)
+{
+	/*
+	 * This is not standards-compliant.  pthread_self() returns pthread_t, and
+	 * shouldn't be cast to unsigned long, but CRYPTO_set_id_callback requires
+	 * it, so we have to do it.
+	 */
+	return (unsigned long) pthread_self();
+}
+
+static pthread_mutex_t *pq_lockarray;
+
+static void
+pq_lockingcallback(int mode, int n, const char *file, int line)
+{
+	if (mode & CRYPTO_LOCK)
+	{
+		if (pthread_mutex_lock(&pq_lockarray[n]))
+			PGTHREAD_ERROR("failed to lock mutex");
+	}
+	else
+	{
+		if (pthread_mutex_unlock(&pq_lockarray[n]))
+			PGTHREAD_ERROR("failed to unlock mutex");
+	}
+}
+#endif   /* ENABLE_THREAD_SAFETY */
+
+/*
+ * Initialize SSL system, in particular creating the SSL_context object
+ * that will be shared by all SSL-using connections in this process.
+ *
+ * In threadsafe mode, this includes setting up libcrypto callback functions
+ * to do thread locking.
+ *
+ * If the caller has told us (through PQinitOpenSSL) that he's taking care
+ * of libcrypto, we expect that callbacks are already set, and won't try to
+ * override it.
+ *
+ * The conn parameter is only used to be able to pass back an error
+ * message - no connection-local setup is made here.
+ *
+ * Returns 0 if OK, -1 on failure (with a message in conn->errorMessage).
+ */
+int
+pgtls_init(PGconn *conn)
+{
+#ifdef ENABLE_THREAD_SAFETY
+#ifdef WIN32
+	/* Also see similar code in fe-connect.c, default_threadlock() */
+	if (ssl_config_mutex == NULL)
+	{
+		while (InterlockedExchange(&win32_ssl_create_mutex, 1) == 1)
+			 /* loop, another thread own the lock */ ;
+		if (ssl_config_mutex == NULL)
+		{
+			if (pthread_mutex_init(&ssl_config_mutex, NULL))
+				return -1;
+		}
+		InterlockedExchange(&win32_ssl_create_mutex, 0);
+	}
+#endif
+	if (pthread_mutex_lock(&ssl_config_mutex))
+		return -1;
+
+	if (pq_init_crypto_lib)
+	{
+		/*
+		 * If necessary, set up an array to hold locks for libcrypto.
+		 * libcrypto will tell us how big to make this array.
+		 */
+		if (pq_lockarray == NULL)
+		{
+			int			i;
+
+			pq_lockarray = malloc(sizeof(pthread_mutex_t) * CRYPTO_num_locks());
+			if (!pq_lockarray)
+			{
+				pthread_mutex_unlock(&ssl_config_mutex);
+				return -1;
+			}
+			for (i = 0; i < CRYPTO_num_locks(); i++)
+			{
+				if (pthread_mutex_init(&pq_lockarray[i], NULL))
+				{
+					free(pq_lockarray);
+					pq_lockarray = NULL;
+					pthread_mutex_unlock(&ssl_config_mutex);
+					return -1;
+				}
+			}
+		}
+
+		if (ssl_open_connections++ == 0)
+		{
+			/* These are only required for threaded libcrypto applications */
+			CRYPTO_set_id_callback(pq_threadidcallback);
+			CRYPTO_set_locking_callback(pq_lockingcallback);
+		}
+	}
+#endif   /* ENABLE_THREAD_SAFETY */
+
+	if (!SSL_context)
+	{
+		if (pq_init_ssl_lib)
+		{
+#if SSLEAY_VERSION_NUMBER >= 0x00907000L
+			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)
+		{
+			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(&ssl_config_mutex);
+#endif
+			return -1;
+		}
+
+		/* Disable old protocol versions */
+		SSL_CTX_set_options(SSL_context, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
+
+		/*
+		 * 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);
+	}
+
+#ifdef ENABLE_THREAD_SAFETY
+	pthread_mutex_unlock(&ssl_config_mutex);
+#endif
+	return 0;
+}
+
+/*
+ *	This function is needed because if the libpq library is unloaded
+ *	from the application, the callback functions will no longer exist when
+ *	libcrypto is used by other parts of the system.  For this reason,
+ *	we unregister the callback functions when the last libpq
+ *	connection is closed.  (The same would apply for OpenSSL callbacks
+ *	if we had any.)
+ *
+ *	Callbacks are only set when we're compiled in threadsafe mode, so
+ *	we only need to remove them in this case.
+ */
+static void
+destroy_ssl_system(void)
+{
+#ifdef ENABLE_THREAD_SAFETY
+	/* Mutex is created in initialize_ssl_system() */
+	if (pthread_mutex_lock(&ssl_config_mutex))
+		return;
+
+	if (pq_init_crypto_lib && ssl_open_connections > 0)
+		--ssl_open_connections;
+
+	if (pq_init_crypto_lib && ssl_open_connections == 0)
+	{
+		/* No connections left, unregister libcrypto callbacks */
+		CRYPTO_set_locking_callback(NULL);
+		CRYPTO_set_id_callback(NULL);
+
+		/*
+		 * We don't free the lock array or the SSL_context. If we get another
+		 * connection in this process, we will just re-use them with the
+		 * existing mutexes.
+		 *
+		 * This means we leak a little memory on repeated load/unload of the
+		 * library.
+		 */
+	}
+
+	pthread_mutex_unlock(&ssl_config_mutex);
+#endif
+}
+
+/*
+ *	Initialize (potentially) per-connection SSL data, namely the
+ *	client certificate, private key, and trusted CA certs.
+ *
+ *	conn->ssl must already be created.  It receives the connection's client
+ *	certificate and private key.  Note however that certificates also get
+ *	loaded into the SSL_context object, and are therefore accessible to all
+ *	connections in this process.  This should be OK as long as there aren't
+ *	any hash collisions among the certs.
+ *
+ *	Returns 0 if OK, -1 on failure (with a message in conn->errorMessage).
+ */
+static int
+initialize_SSL(PGconn *conn)
+{
+	struct stat buf;
+	char		homedir[MAXPGPATH];
+	char		fnbuf[MAXPGPATH];
+	char		sebuf[256];
+	bool		have_homedir;
+	bool		have_cert;
+	EVP_PKEY   *pkey = NULL;
+
+	/*
+	 * We'll need the home directory if any of the relevant parameters are
+	 * defaulted.  If pqGetHomeDirectory fails, act as though none of the
+	 * files could be found.
+	 */
+	if (!(conn->sslcert && strlen(conn->sslcert) > 0) ||
+		!(conn->sslkey && strlen(conn->sslkey) > 0) ||
+		!(conn->sslrootcert && strlen(conn->sslrootcert) > 0) ||
+		!(conn->sslcrl && strlen(conn->sslcrl) > 0))
+		have_homedir = pqGetHomeDirectory(homedir, sizeof(homedir));
+	else	/* won't need it */
+		have_homedir = false;
+
+	/* Read the client certificate file */
+	if (conn->sslcert && strlen(conn->sslcert) > 0)
+		strncpy(fnbuf, conn->sslcert, sizeof(fnbuf));
+	else if (have_homedir)
+		snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, USER_CERT_FILE);
+	else
+		fnbuf[0] = '\0';
+
+	if (fnbuf[0] == '\0')
+	{
+		/* no home directory, proceed without a client cert */
+		have_cert = false;
+	}
+	else if (stat(fnbuf, &buf) != 0)
+	{
+		/*
+		 * If file is not present, just go on without a client cert; server
+		 * might or might not accept the connection.  Any other error,
+		 * however, is grounds for complaint.
+		 */
+		if (errno != ENOENT && errno != ENOTDIR)
+		{
+			printfPQExpBuffer(&conn->errorMessage,
+			   libpq_gettext("could not open certificate file \"%s\": %s\n"),
+							  fnbuf, pqStrerror(errno, sebuf, sizeof(sebuf)));
+			return -1;
+		}
+		have_cert = false;
+	}
+	else
+	{
+		/*
+		 * Cert file exists, so load it.  Since OpenSSL doesn't provide the
+		 * equivalent of "SSL_use_certificate_chain_file", we actually have to
+		 * load the file twice.  The first call loads any extra certs after
+		 * the first one into chain-cert storage associated with the
+		 * SSL_context.  The second call loads the first cert (only) into the
+		 * SSL object, where it will be correctly paired with the private key
+		 * we load below.  We do it this way so that each connection
+		 * understands which subject cert to present, in case different
+		 * sslcert settings are used for different connections in the same
+		 * process.
+		 *
+		 * NOTE: This function may also modify our SSL_context and therefore
+		 * we have to lock around this call and any places where we use the
+		 * SSL_context struct.
+		 */
+#ifdef ENABLE_THREAD_SAFETY
+		int			rc;
+
+		if ((rc = pthread_mutex_lock(&ssl_config_mutex)))
+		{
+			printfPQExpBuffer(&conn->errorMessage,
+			   libpq_gettext("could not acquire mutex: %s\n"), strerror(rc));
+			return -1;
+		}
+#endif
+		if (SSL_CTX_use_certificate_chain_file(SSL_context, fnbuf) != 1)
+		{
+			char	   *err = SSLerrmessage();
+
+			printfPQExpBuffer(&conn->errorMessage,
+			   libpq_gettext("could not read certificate file \"%s\": %s\n"),
+							  fnbuf, err);
+			SSLerrfree(err);
+
+#ifdef ENABLE_THREAD_SAFETY
+			pthread_mutex_unlock(&ssl_config_mutex);
+#endif
+			return -1;
+		}
+
+		if (SSL_use_certificate_file(conn->ssl, fnbuf, SSL_FILETYPE_PEM) != 1)
+		{
+			char	   *err = SSLerrmessage();
+
+			printfPQExpBuffer(&conn->errorMessage,
+			   libpq_gettext("could not read certificate file \"%s\": %s\n"),
+							  fnbuf, err);
+			SSLerrfree(err);
+#ifdef ENABLE_THREAD_SAFETY
+			pthread_mutex_unlock(&ssl_config_mutex);
+#endif
+			return -1;
+		}
+
+		/* need to load the associated private key, too */
+		have_cert = true;
+
+#ifdef ENABLE_THREAD_SAFETY
+		pthread_mutex_unlock(&ssl_config_mutex);
+#endif
+	}
+
+	/*
+	 * Read the SSL key. If a key is specified, treat it as an engine:key
+	 * combination if there is colon present - we don't support files with
+	 * colon in the name. The exception is if the second character is a colon,
+	 * in which case it can be a Windows filename with drive specification.
+	 */
+	if (have_cert && conn->sslkey && strlen(conn->sslkey) > 0)
+	{
+#ifdef USE_SSL_ENGINE
+		if (strchr(conn->sslkey, ':')
+#ifdef WIN32
+			&& conn->sslkey[1] != ':'
+#endif
+			)
+		{
+			/* Colon, but not in second character, treat as engine:key */
+			char	   *engine_str = strdup(conn->sslkey);
+			char	   *engine_colon;
+
+			if (engine_str == NULL)
+			{
+				printfPQExpBuffer(&conn->errorMessage,
+								  libpq_gettext("out of memory\n"));
+				return -1;
+			}
+
+			/* cannot return NULL because we already checked before strdup */
+			engine_colon = strchr(engine_str, ':');
+
+			*engine_colon = '\0';		/* engine_str now has engine name */
+			engine_colon++;		/* engine_colon now has key name */
+
+			conn->engine = ENGINE_by_id(engine_str);
+			if (conn->engine == NULL)
+			{
+				char	   *err = SSLerrmessage();
+
+				printfPQExpBuffer(&conn->errorMessage,
+					 libpq_gettext("could not load SSL engine \"%s\": %s\n"),
+								  engine_str, err);
+				SSLerrfree(err);
+				free(engine_str);
+				return -1;
+			}
+
+			if (ENGINE_init(conn->engine) == 0)
+			{
+				char	   *err = SSLerrmessage();
+
+				printfPQExpBuffer(&conn->errorMessage,
+				libpq_gettext("could not initialize SSL engine \"%s\": %s\n"),
+								  engine_str, err);
+				SSLerrfree(err);
+				ENGINE_free(conn->engine);
+				conn->engine = NULL;
+				free(engine_str);
+				return -1;
+			}
+
+			pkey = ENGINE_load_private_key(conn->engine, engine_colon,
+										   NULL, NULL);
+			if (pkey == NULL)
+			{
+				char	   *err = SSLerrmessage();
+
+				printfPQExpBuffer(&conn->errorMessage,
+								  libpq_gettext("could not read private SSL key \"%s\" from engine \"%s\": %s\n"),
+								  engine_colon, engine_str, err);
+				SSLerrfree(err);
+				ENGINE_finish(conn->engine);
+				ENGINE_free(conn->engine);
+				conn->engine = NULL;
+				free(engine_str);
+				return -1;
+			}
+			if (SSL_use_PrivateKey(conn->ssl, pkey) != 1)
+			{
+				char	   *err = SSLerrmessage();
+
+				printfPQExpBuffer(&conn->errorMessage,
+								  libpq_gettext("could not load private SSL key \"%s\" from engine \"%s\": %s\n"),
+								  engine_colon, engine_str, err);
+				SSLerrfree(err);
+				ENGINE_finish(conn->engine);
+				ENGINE_free(conn->engine);
+				conn->engine = NULL;
+				free(engine_str);
+				return -1;
+			}
+
+			free(engine_str);
+
+			fnbuf[0] = '\0';	/* indicate we're not going to load from a
+								 * file */
+		}
+		else
+#endif   /* USE_SSL_ENGINE */
+		{
+			/* PGSSLKEY is not an engine, treat it as a filename */
+			strncpy(fnbuf, conn->sslkey, sizeof(fnbuf));
+		}
+	}
+	else if (have_homedir)
+	{
+		/* No PGSSLKEY specified, load default file */
+		snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, USER_KEY_FILE);
+	}
+	else
+		fnbuf[0] = '\0';
+
+	if (have_cert && fnbuf[0] != '\0')
+	{
+		/* read the client key from file */
+
+		if (stat(fnbuf, &buf) != 0)
+		{
+			printfPQExpBuffer(&conn->errorMessage,
+							  libpq_gettext("certificate present, but not private key file \"%s\"\n"),
+							  fnbuf);
+			return -1;
+		}
+#ifndef WIN32
+		if (!S_ISREG(buf.st_mode) || buf.st_mode & (S_IRWXG | S_IRWXO))
+		{
+			printfPQExpBuffer(&conn->errorMessage,
+							  libpq_gettext("private key file \"%s\" has group or world access; permissions should be u=rw (0600) or less\n"),
+							  fnbuf);
+			return -1;
+		}
+#endif
+
+		if (SSL_use_PrivateKey_file(conn->ssl, fnbuf, SSL_FILETYPE_PEM) != 1)
+		{
+			char	   *err = SSLerrmessage();
+
+			printfPQExpBuffer(&conn->errorMessage,
+			   libpq_gettext("could not load private key file \"%s\": %s\n"),
+							  fnbuf, err);
+			SSLerrfree(err);
+			return -1;
+		}
+	}
+
+	/* verify that the cert and key go together */
+	if (have_cert &&
+		SSL_check_private_key(conn->ssl) != 1)
+	{
+		char	   *err = SSLerrmessage();
+
+		printfPQExpBuffer(&conn->errorMessage,
+						  libpq_gettext("certificate does not match private key file \"%s\": %s\n"),
+						  fnbuf, err);
+		SSLerrfree(err);
+		return -1;
+	}
+
+	/*
+	 * If the root cert file exists, load it so we can perform certificate
+	 * verification. If sslmode is "verify-full" we will also do further
+	 * verification after the connection has been completed.
+	 */
+	if (conn->sslrootcert && strlen(conn->sslrootcert) > 0)
+		strncpy(fnbuf, conn->sslrootcert, sizeof(fnbuf));
+	else if (have_homedir)
+		snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, ROOT_CERT_FILE);
+	else
+		fnbuf[0] = '\0';
+
+	if (fnbuf[0] != '\0' &&
+		stat(fnbuf, &buf) == 0)
+	{
+		X509_STORE *cvstore;
+
+#ifdef ENABLE_THREAD_SAFETY
+		int			rc;
+
+		if ((rc = pthread_mutex_lock(&ssl_config_mutex)))
+		{
+			printfPQExpBuffer(&conn->errorMessage,
+			   libpq_gettext("could not acquire mutex: %s\n"), strerror(rc));
+			return -1;
+		}
+#endif
+		if (SSL_CTX_load_verify_locations(SSL_context, fnbuf, NULL) != 1)
+		{
+			char	   *err = SSLerrmessage();
+
+			printfPQExpBuffer(&conn->errorMessage,
+							  libpq_gettext("could not read root certificate file \"%s\": %s\n"),
+							  fnbuf, err);
+			SSLerrfree(err);
+#ifdef ENABLE_THREAD_SAFETY
+			pthread_mutex_unlock(&ssl_config_mutex);
+#endif
+			return -1;
+		}
+
+		if ((cvstore = SSL_CTX_get_cert_store(SSL_context)) != NULL)
+		{
+			if (conn->sslcrl && strlen(conn->sslcrl) > 0)
+				strncpy(fnbuf, conn->sslcrl, sizeof(fnbuf));
+			else if (have_homedir)
+				snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, ROOT_CRL_FILE);
+			else
+				fnbuf[0] = '\0';
+
+			/* Set the flags to check against the complete CRL chain */
+			if (fnbuf[0] != '\0' &&
+				X509_STORE_load_locations(cvstore, fnbuf, 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
+				char	   *err = SSLerrmessage();
+
+				printfPQExpBuffer(&conn->errorMessage,
+								  libpq_gettext("SSL library does not support CRL certificates (file \"%s\")\n"),
+								  fnbuf);
+				SSLerrfree(err);
+#ifdef ENABLE_THREAD_SAFETY
+				pthread_mutex_unlock(&ssl_config_mutex);
+#endif
+				return -1;
+#endif
+			}
+			/* if not found, silently ignore;  we do not require CRL */
+		}
+#ifdef ENABLE_THREAD_SAFETY
+		pthread_mutex_unlock(&ssl_config_mutex);
+#endif
+
+		SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, verify_cb);
+	}
+	else
+	{
+		/*
+		 * stat() failed; assume root file doesn't exist.  If sslmode is
+		 * verify-ca or verify-full, this is an error.  Otherwise, continue
+		 * without performing any server cert verification.
+		 */
+		if (conn->sslmode[0] == 'v')	/* "verify-ca" or "verify-full" */
+		{
+			/*
+			 * The only way to reach here with an empty filename is if
+			 * pqGetHomeDirectory failed.  That's a sufficiently unusual case
+			 * that it seems worth having a specialized error message for it.
+			 */
+			if (fnbuf[0] == '\0')
+				printfPQExpBuffer(&conn->errorMessage,
+								  libpq_gettext("could not get home directory to locate root certificate file\n"
+												"Either provide the file or change sslmode to disable server certificate verification.\n"));
+			else
+				printfPQExpBuffer(&conn->errorMessage,
+				libpq_gettext("root certificate file \"%s\" does not exist\n"
+							  "Either provide the file or change sslmode to disable server certificate verification.\n"), fnbuf);
+			return -1;
+		}
+	}
+
+	/*
+	 * If the OpenSSL version used supports it (from 1.0.0 on) and the user
+	 * requested it, disable SSL compression.
+	 */
+#ifdef SSL_OP_NO_COMPRESSION
+	if (conn->sslcompression && conn->sslcompression[0] == '0')
+	{
+		SSL_set_options(conn->ssl, SSL_OP_NO_COMPRESSION);
+	}
+#endif
+
+	return 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"));
+					pgtls_close(conn);
+					return PGRES_POLLING_FAILED;
+				}
+			case SSL_ERROR_SSL:
+				{
+					char	   *err = SSLerrmessage();
+
+					printfPQExpBuffer(&conn->errorMessage,
+									  libpq_gettext("SSL error: %s\n"),
+									  err);
+					SSLerrfree(err);
+					pgtls_close(conn);
+					return PGRES_POLLING_FAILED;
+				}
+
+			default:
+				printfPQExpBuffer(&conn->errorMessage,
+						  libpq_gettext("unrecognized SSL error code: %d\n"),
+								  err);
+				pgtls_close(conn);
+				return PGRES_POLLING_FAILED;
+		}
+	}
+
+	/*
+	 * We already checked the server certificate in initialize_SSL() using
+	 * SSL_CTX_set_verify(), if root.crt exists.
+	 */
+
+	/* get server certificate */
+	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);
+		pgtls_close(conn);
+		return PGRES_POLLING_FAILED;
+	}
+
+	if (!verify_peer_name_matches_certificate(conn))
+	{
+		pgtls_close(conn);
+		return PGRES_POLLING_FAILED;
+	}
+
+	/* SSL handshake is complete */
+	return PGRES_POLLING_OK;
+}
+
+/*
+ *	Close SSL connection.
+ */
+void
+pgtls_close(PGconn *conn)
+{
+	bool		destroy_needed = false;
+
+	if (conn->ssl)
+	{
+		/*
+		 * We can't destroy everything SSL-related here due to the possible
+		 * later calls to OpenSSL routines which may need our thread
+		 * callbacks, so set a flag here and check at the end.
+		 */
+		destroy_needed = true;
+
+		SSL_shutdown(conn->ssl);
+		SSL_free(conn->ssl);
+		conn->ssl = NULL;
+		conn->ssl_in_use = false;
+	}
+
+	if (conn->peer)
+	{
+		X509_free(conn->peer);
+		conn->peer = NULL;
+	}
+
+#ifdef USE_SSL_ENGINE
+	if (conn->engine)
+	{
+		ENGINE_finish(conn->engine);
+		ENGINE_free(conn->engine);
+		conn->engine = NULL;
+	}
+#endif
+
+	/*
+	 * This will remove our SSL locking hooks, if this is the last SSL
+	 * connection, which means we must wait to call it until after all SSL
+	 * calls have been made, otherwise we can end up with a race condition and
+	 * possible deadlocks.
+	 *
+	 * See comments above destroy_ssl_system().
+	 */
+	if (destroy_needed)
+		destroy_ssl_system();
+}
+
+
+/*
+ * Obtain reason string for last SSL error
+ *
+ * Some caution is needed here since ERR_reason_error_string will
+ * return NULL if it doesn't recognize the error code.  We don't
+ * want to return NULL ever.
+ */
+static char ssl_nomem[] = "out of memory allocating error description";
+
+#define SSL_ERR_LEN 128
+
+static char *
+SSLerrmessage(void)
+{
+	unsigned long errcode;
+	const char *errreason;
+	char	   *errbuf;
+
+	errbuf = malloc(SSL_ERR_LEN);
+	if (!errbuf)
+		return ssl_nomem;
+	errcode = ERR_get_error();
+	if (errcode == 0)
+	{
+		snprintf(errbuf, SSL_ERR_LEN, libpq_gettext("no SSL error reported"));
+		return errbuf;
+	}
+	errreason = ERR_reason_error_string(errcode);
+	if (errreason != NULL)
+	{
+		strlcpy(errbuf, errreason, SSL_ERR_LEN);
+		return errbuf;
+	}
+	snprintf(errbuf, SSL_ERR_LEN, libpq_gettext("SSL error code %lu"), errcode);
+	return errbuf;
+}
+
+static void
+SSLerrfree(char *buf)
+{
+	if (buf != ssl_nomem)
+		free(buf);
+}
+
+/*
+ *	Return pointer to OpenSSL object.
+ */
+void *
+PQgetssl(PGconn *conn)
+{
+	if (!conn)
+		return NULL;
+	return conn->ssl;
+}
+
+
+/*
+ * Private substitute BIO: this does the sending and receiving using send() and
+ * recv() instead. This is so that we can enable and disable interrupts
+ * just while calling recv(). We cannot have interrupts occurring while
+ * the bulk of openssl runs, because it uses malloc() and possibly other
+ * non-reentrant libc facilities. We also need to call send() and recv()
+ * directly so it gets passed through the socket/signals layer on Win32.
+ *
+ * These functions are closely modelled on the standard socket BIO in OpenSSL;
+ * see sock_read() and sock_write() in OpenSSL's crypto/bio/bss_sock.c.
+ * XXX OpenSSL 1.0.1e considers many more errcodes than just EINTR as reasons
+ * to retry; do we need to adopt their logic for that?
+ */
+
+static bool my_bio_initialized = false;
+static BIO_METHOD my_bio_methods;
+
+static int
+my_sock_read(BIO *h, char *buf, int size)
+{
+	int			res;
+	int			save_errno;
+
+	res = pqsecure_raw_read((PGconn *) h->ptr, buf, size);
+	save_errno = errno;
+	BIO_clear_retry_flags(h);
+	if (res < 0)
+	{
+		switch (save_errno)
+		{
+#ifdef EAGAIN
+			case EAGAIN:
+#endif
+#if defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN))
+			case EWOULDBLOCK:
+#endif
+			case EINTR:
+				BIO_set_retry_read(h);
+				break;
+
+			default:
+				break;
+		}
+	}
+
+	errno = save_errno;
+	return res;
+}
+
+static int
+my_sock_write(BIO *h, const char *buf, int size)
+{
+	int			res;
+	int			save_errno;
+
+	res = pqsecure_raw_write((PGconn *) h->ptr, buf, size);
+	save_errno = errno;
+	BIO_clear_retry_flags(h);
+	if (res <= 0)
+	{
+		if (save_errno == EINTR)
+		{
+			BIO_set_retry_write(h);
+		}
+	}
+
+	return res;
+}
+
+static BIO_METHOD *
+my_BIO_s_socket(void)
+{
+	if (!my_bio_initialized)
+	{
+		memcpy(&my_bio_methods, BIO_s_socket(), sizeof(BIO_METHOD));
+		my_bio_methods.bread = my_sock_read;
+		my_bio_methods.bwrite = my_sock_write;
+		my_bio_initialized = true;
+	}
+	return &my_bio_methods;
+}
+
+/* This should exactly match openssl's SSL_set_fd except for using my BIO */
+static int
+my_SSL_set_fd(PGconn *conn, int fd)
+{
+	int			ret = 0;
+	BIO		   *bio = NULL;
+
+	bio = BIO_new(my_BIO_s_socket());
+	if (bio == NULL)
+	{
+		SSLerr(SSL_F_SSL_SET_FD, ERR_R_BUF_LIB);
+		goto err;
+	}
+	/* Use 'ptr' to store pointer to PGconn */
+	bio->ptr = conn;
+
+	SSL_set_bio(conn->ssl, bio, bio);
+	BIO_set_fd(bio, fd, BIO_NOCLOSE);
+	ret = 1;
+err:
+	return ret;
+}
diff --git a/src/interfaces/libpq/fe-secure.c b/src/interfaces/libpq/fe-secure.c
index 9ba3567..66778b2 100644
--- a/src/interfaces/libpq/fe-secure.c
+++ b/src/interfaces/libpq/fe-secure.c
@@ -55,64 +55,6 @@
 #endif
 #endif
 
-#ifdef USE_SSL
-
-#include <openssl/ssl.h>
-#if (SSLEAY_VERSION_NUMBER >= 0x00907000L)
-#include <openssl/conf.h>
-#endif
-#ifdef USE_SSL_ENGINE
-#include <openssl/engine.h>
-#endif
-
-
-#ifndef WIN32
-#define USER_CERT_FILE		".postgresql/postgresql.crt"
-#define USER_KEY_FILE		".postgresql/postgresql.key"
-#define ROOT_CERT_FILE		".postgresql/root.crt"
-#define ROOT_CRL_FILE		".postgresql/root.crl"
-#else
-/* On Windows, the "home" directory is already PostgreSQL-specific */
-#define USER_CERT_FILE		"postgresql.crt"
-#define USER_KEY_FILE		"postgresql.key"
-#define ROOT_CERT_FILE		"root.crt"
-#define ROOT_CRL_FILE		"root.crl"
-#endif
-
-static bool verify_peer_name_matches_certificate(PGconn *);
-static int	verify_cb(int ok, X509_STORE_CTX *ctx);
-static int	init_ssl_system(PGconn *conn);
-static void destroy_ssl_system(void);
-static int	initialize_SSL(PGconn *conn);
-static void destroySSL(void);
-static PostgresPollingStatusType open_client_SSL(PGconn *);
-static void close_SSL(PGconn *);
-static char *SSLerrmessage(void);
-static void SSLerrfree(char *buf);
-
-static bool pq_init_ssl_lib = true;
-static bool pq_init_crypto_lib = true;
-
-/*
- * SSL_context is currently shared between threads and therefore we need to be
- * careful to lock around any usage of it when providing thread safety.
- * ssl_config_mutex is the mutex that we use to protect it.
- */
-static SSL_CTX *SSL_context = NULL;
-
-#ifdef ENABLE_THREAD_SAFETY
-static long ssl_open_connections = 0;
-
-#ifndef WIN32
-static pthread_mutex_t ssl_config_mutex = PTHREAD_MUTEX_INITIALIZER;
-#else
-static pthread_mutex_t ssl_config_mutex = NULL;
-static long win32_ssl_create_mutex = 0;
-#endif
-#endif   /* ENABLE_THREAD_SAFETY */
-#endif   /* SSL */
-
-
 /*
  * Macros to handle disabling and then restoring the state of SIGPIPE handling.
  * On Windows, these are all no-ops since there's no SIGPIPEs.
@@ -194,7 +136,9 @@ struct sigpipe_info
 void
 PQinitSSL(int do_init)
 {
-	PQinitOpenSSL(do_init, do_init);
+#ifdef USE_SSL
+	pgtls_init_library(do_init, do_init);
+#endif
 }
 
 /*
@@ -205,18 +149,7 @@ void
 PQinitOpenSSL(int do_ssl, int do_crypto)
 {
 #ifdef USE_SSL
-#ifdef ENABLE_THREAD_SAFETY
-
-	/*
-	 * Disallow changing the flags while we have open connections, else we'd
-	 * get completely confused.
-	 */
-	if (ssl_open_connections != 0)
-		return;
-#endif
-
-	pq_init_ssl_lib = do_ssl;
-	pq_init_crypto_lib = do_crypto;
+	pgtls_init_library(do_ssl, do_crypto);
 #endif
 }
 
@@ -229,83 +162,20 @@ pqsecure_initialize(PGconn *conn)
 	int			r = 0;
 
 #ifdef USE_SSL
-	r = init_ssl_system(conn);
+	r = pgtls_init(conn);
 #endif
 
 	return r;
 }
 
 /*
- *	Destroy global context
- */
-void
-pqsecure_destroy(void)
-{
-#ifdef USE_SSL
-	destroySSL();
-#endif
-}
-
-/*
  *	Begin or continue negotiating a secure session.
  */
 PostgresPollingStatusType
 pqsecure_open_client(PGconn *conn)
 {
 #ifdef USE_SSL
-	/* First time through? */
-	if (conn->ssl == NULL)
-	{
-#ifdef ENABLE_THREAD_SAFETY
-		int			rc;
-#endif
-
-		/* We cannot use MSG_NOSIGNAL to block SIGPIPE when using SSL */
-		conn->sigpipe_flag = false;
-
-#ifdef ENABLE_THREAD_SAFETY
-		if ((rc = pthread_mutex_lock(&ssl_config_mutex)))
-		{
-			printfPQExpBuffer(&conn->errorMessage,
-			   libpq_gettext("could not acquire mutex: %s\n"), strerror(rc));
-			return PGRES_POLLING_FAILED;
-		}
-#endif
-		/* 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);
-#ifdef ENABLE_THREAD_SAFETY
-			pthread_mutex_unlock(&ssl_config_mutex);
-#endif
-			close_SSL(conn);
-
-			return PGRES_POLLING_FAILED;
-		}
-#ifdef ENABLE_THREAD_SAFETY
-		pthread_mutex_unlock(&ssl_config_mutex);
-#endif
-
-		/*
-		 * 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);
+	return pgtls_open_client(conn);
 #else
 	/* shouldn't get here */
 	return PGRES_POLLING_FAILED;
@@ -319,8 +189,8 @@ void
 pqsecure_close(PGconn *conn)
 {
 #ifdef USE_SSL
-	if (conn->ssl)
-		close_SSL(conn);
+	if (conn->ssl_in_use)
+		pgtls_close(conn);
 #endif
 }
 
@@ -335,149 +205,63 @@ 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)
+	if (conn->ssl_in_use)
+	{
+		n = pgtls_read(conn, ptr, len);
+	}
+	else
+#endif
 	{
-		int			err;
+		n = pqsecure_raw_read(conn, ptr, len);
+	}
 
-		DECLARE_SIGPIPE_INFO(spinfo);
+	return n;
+}
 
-		/* SSL_read can write to the socket, so we need to disable SIGPIPE */
-		DISABLE_SIGPIPE(conn, spinfo, return -1);
+ssize_t
+pqsecure_raw_read(PGconn *conn, void *ptr, size_t len)
+{
+	ssize_t		n;
+	int			result_errno = 0;
+	char		sebuf[256];
 
-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 */
-					printfPQExpBuffer(&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)
-						printfPQExpBuffer(&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
-						printfPQExpBuffer(&conn->errorMessage,
-									libpq_gettext("SSL SYSCALL error: %s\n"),
-										  SOCK_STRERROR(result_errno,
-													  sebuf, sizeof(sebuf)));
-				}
-				else
-				{
-					printfPQExpBuffer(&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();
-
-					printfPQExpBuffer(&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.
-				 */
-				printfPQExpBuffer(&conn->errorMessage,
-								  libpq_gettext("SSL connection has been closed unexpectedly\n"));
-				result_errno = ECONNRESET;
-				n = -1;
-				break;
-			default:
-				printfPQExpBuffer(&conn->errorMessage,
-						  libpq_gettext("unrecognized SSL error code: %d\n"),
-								  err);
-				/* assume the connection is broken */
-				result_errno = ECONNRESET;
-				n = -1;
-				break;
-		}
+	n = recv(conn->sock, ptr, len, 0);
 
-		RESTORE_SIGPIPE(conn, spinfo);
-	}
-	else
-#endif   /* USE_SSL */
+	if (n < 0)
 	{
-		n = recv(conn->sock, ptr, len, 0);
+		result_errno = SOCK_ERRNO;
 
-		if (n < 0)
+		/* Set error message if appropriate */
+		switch (result_errno)
 		{
-			result_errno = SOCK_ERRNO;
-
-			/* Set error message if appropriate */
-			switch (result_errno)
-			{
 #ifdef EAGAIN
-				case EAGAIN:
+			case EAGAIN:
 #endif
 #if defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN))
-				case EWOULDBLOCK:
+			case EWOULDBLOCK:
 #endif
-				case EINTR:
-					/* no error message, caller is expected to retry */
-					break;
+			case EINTR:
+				/* no error message, caller is expected to retry */
+				break;
 
 #ifdef ECONNRESET
-				case ECONNRESET:
-					printfPQExpBuffer(&conn->errorMessage,
-									  libpq_gettext(
+			case ECONNRESET:
+				printfPQExpBuffer(&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"));
-					break;
+				break;
 #endif
 
-				default:
-					printfPQExpBuffer(&conn->errorMessage,
+			default:
+				printfPQExpBuffer(&conn->errorMessage,
 					libpq_gettext("could not receive data from server: %s\n"),
-									  SOCK_STRERROR(result_errno,
-													sebuf, sizeof(sebuf)));
-					break;
-			}
+								  SOCK_STRERROR(result_errno,
+												sebuf, sizeof(sebuf)));
+				break;
 		}
 	}
 
@@ -498,175 +282,94 @@ ssize_t
 pqsecure_write(PGconn *conn, const void *ptr, size_t len)
 {
 	ssize_t		n;
-	int			result_errno = 0;
-	char		sebuf[256];
-
-	DECLARE_SIGPIPE_INFO(spinfo);
 
 #ifdef USE_SSL
-	if (conn->ssl)
+	if (conn->ssl_in_use)
 	{
-		int			err;
-
-		DISABLE_SIGPIPE(conn, spinfo, return -1);
-
-		SOCK_ERRNO_SET(0);
-		n = SSL_write(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 */
-					printfPQExpBuffer(&conn->errorMessage,
-									  "SSL_write failed but did not provide error information\n");
-					/* assume the connection is broken */
-					result_errno = ECONNRESET;
-				}
-				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:
-				if (n < 0)
-				{
-					result_errno = SOCK_ERRNO;
-					REMEMBER_EPIPE(spinfo, result_errno == EPIPE);
-					if (result_errno == EPIPE ||
-						result_errno == ECONNRESET)
-						printfPQExpBuffer(&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
-						printfPQExpBuffer(&conn->errorMessage,
-									libpq_gettext("SSL SYSCALL error: %s\n"),
-										  SOCK_STRERROR(result_errno,
-													  sebuf, sizeof(sebuf)));
-				}
-				else
-				{
-					printfPQExpBuffer(&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();
-
-					printfPQExpBuffer(&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.
-				 */
-				printfPQExpBuffer(&conn->errorMessage,
-								  libpq_gettext("SSL connection has been closed unexpectedly\n"));
-				result_errno = ECONNRESET;
-				n = -1;
-				break;
-			default:
-				printfPQExpBuffer(&conn->errorMessage,
-						  libpq_gettext("unrecognized SSL error code: %d\n"),
-								  err);
-				/* assume the connection is broken */
-				result_errno = ECONNRESET;
-				n = -1;
-				break;
-		}
+		n = pgtls_write(conn, ptr, len);
 	}
 	else
-#endif   /* USE_SSL */
+#endif
 	{
-		int			flags = 0;
+		n = pqsecure_raw_write(conn, ptr, len);
+	}
+
+	return n;
+}
+
+ssize_t
+pqsecure_raw_write(PGconn *conn, const void *ptr, size_t len)
+{
+	ssize_t		n;
+	int			flags = 0;
+	int			result_errno = 0;
+	char		sebuf[256];
+
+	DECLARE_SIGPIPE_INFO(spinfo);
 
 #ifdef MSG_NOSIGNAL
-		if (conn->sigpipe_flag)
-			flags |= MSG_NOSIGNAL;
+	if (conn->sigpipe_flag)
+		flags |= MSG_NOSIGNAL;
 
 retry_masked:
 #endif   /* MSG_NOSIGNAL */
 
-		DISABLE_SIGPIPE(conn, spinfo, return -1);
+	DISABLE_SIGPIPE(conn, spinfo, return -1);
 
-		n = send(conn->sock, ptr, len, flags);
+	n = send(conn->sock, ptr, len, flags);
 
-		if (n < 0)
-		{
-			result_errno = SOCK_ERRNO;
+	if (n < 0)
+	{
+		result_errno = SOCK_ERRNO;
 
-			/*
-			 * If we see an EINVAL, it may be because MSG_NOSIGNAL isn't
-			 * available on this machine.  So, clear sigpipe_flag so we don't
-			 * try the flag again, and retry the send().
-			 */
+		/*
+		 * If we see an EINVAL, it may be because MSG_NOSIGNAL isn't
+		 * available on this machine.  So, clear sigpipe_flag so we don't
+		 * try the flag again, and retry the send().
+		 */
 #ifdef MSG_NOSIGNAL
-			if (flags != 0 && result_errno == EINVAL)
-			{
-				conn->sigpipe_flag = false;
-				flags = 0;
-				goto retry_masked;
-			}
+		if (flags != 0 && result_errno == EINVAL)
+		{
+			conn->sigpipe_flag = false;
+			flags = 0;
+			goto retry_masked;
+		}
 #endif   /* MSG_NOSIGNAL */
 
-			/* Set error message if appropriate */
-			switch (result_errno)
-			{
+		/* Set error message if appropriate */
+		switch (result_errno)
+		{
 #ifdef EAGAIN
-				case EAGAIN:
+			case EAGAIN:
 #endif
 #if defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN))
-				case EWOULDBLOCK:
+			case EWOULDBLOCK:
 #endif
-				case EINTR:
-					/* no error message, caller is expected to retry */
-					break;
+			case EINTR:
+				/* no error message, caller is expected to retry */
+				break;
 
-				case EPIPE:
-					/* Set flag for EPIPE */
-					REMEMBER_EPIPE(spinfo, true);
-					/* FALL THRU */
+			case EPIPE:
+				/* Set flag for EPIPE */
+				REMEMBER_EPIPE(spinfo, true);
+				/* FALL THRU */
 
 #ifdef ECONNRESET
-				case ECONNRESET:
+			case ECONNRESET:
 #endif
-					printfPQExpBuffer(&conn->errorMessage,
-									  libpq_gettext(
+				printfPQExpBuffer(&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"));
-					break;
+				break;
 
-				default:
-					printfPQExpBuffer(&conn->errorMessage,
+			default:
+				printfPQExpBuffer(&conn->errorMessage,
 						libpq_gettext("could not send data to server: %s\n"),
 									  SOCK_STRERROR(result_errno,
 													sebuf, sizeof(sebuf)));
-					break;
-			}
+				break;
 		}
 	}
 
@@ -678,981 +381,7 @@ retry_masked:
 	return n;
 }
 
-/* ------------------------------------------------------------ */
-/*						  SSL specific code						*/
-/* ------------------------------------------------------------ */
-#ifdef USE_SSL
-
-/*
- *	Certificate verification callback
- *
- *	This callback allows us to log intermediate problems during
- *	verification, but there doesn't seem to be a clean way to get
- *	our PGconn * structure.  So we can't log anything!
- *
- *	This callback also allows us to override the default acceptance
- *	criteria (e.g., accepting self-signed or expired certs), but
- *	for now we accept the default checks.
- */
-static int
-verify_cb(int ok, X509_STORE_CTX *ctx)
-{
-	return ok;
-}
-
-
-/*
- * Check if a wildcard certificate matches the server hostname.
- *
- * The rule for this is:
- *	1. We only match the '*' character as wildcard
- *	2. We match only wildcards at the start of the string
- *	3. The '*' character does *not* match '.', meaning that we match only
- *	   a single pathname component.
- *	4. We don't support more than one '*' in a single pattern.
- *
- * This is roughly in line with RFC2818, but contrary to what most browsers
- * appear to be implementing (point 3 being the difference)
- *
- * Matching is always case-insensitive, since DNS is case insensitive.
- */
-static int
-wildcard_certificate_match(const char *pattern, const char *string)
-{
-	int			lenpat = strlen(pattern);
-	int			lenstr = strlen(string);
-
-	/* If we don't start with a wildcard, it's not a match (rule 1 & 2) */
-	if (lenpat < 3 ||
-		pattern[0] != '*' ||
-		pattern[1] != '.')
-		return 0;
-
-	if (lenpat > lenstr)
-		/* If pattern is longer than the string, we can never match */
-		return 0;
-
-	if (pg_strcasecmp(pattern + 1, string + lenstr - lenpat + 1) != 0)
-
-		/*
-		 * If string does not end in pattern (minus the wildcard), we don't
-		 * match
-		 */
-		return 0;
-
-	if (strchr(string, '.') < string + lenstr - lenpat)
-
-		/*
-		 * If there is a dot left of where the pattern started to match, we
-		 * don't match (rule 3)
-		 */
-		return 0;
-
-	/* String ended with pattern, and didn't have a dot before, so we match */
-	return 1;
-}
-
-
-/*
- *	Verify that common name resolves to peer.
- */
-static bool
-verify_peer_name_matches_certificate(PGconn *conn)
-{
-	char	   *peer_cn;
-	int			r;
-	int			len;
-	bool		result;
-
-	/*
-	 * If told not to verify the peer name, don't do it. Return true
-	 * indicating that the verification was successful.
-	 */
-	if (strcmp(conn->sslmode, "verify-full") != 0)
-		return true;
-
-	/*
-	 * Extract the common name from the certificate.
-	 *
-	 * XXX: Should support alternate names here
-	 */
-	/* First find out the name's length and allocate a buffer for it. */
-	len = X509_NAME_get_text_by_NID(X509_get_subject_name(conn->peer),
-									NID_commonName, NULL, 0);
-	if (len == -1)
-	{
-		printfPQExpBuffer(&conn->errorMessage,
-						  libpq_gettext("could not get server common name from server certificate\n"));
-		return false;
-	}
-	peer_cn = malloc(len + 1);
-	if (peer_cn == NULL)
-	{
-		printfPQExpBuffer(&conn->errorMessage,
-						  libpq_gettext("out of memory\n"));
-		return false;
-	}
-
-	r = X509_NAME_get_text_by_NID(X509_get_subject_name(conn->peer),
-								  NID_commonName, peer_cn, len + 1);
-	if (r != len)
-	{
-		/* Got different length than on the first call. Shouldn't happen. */
-		printfPQExpBuffer(&conn->errorMessage,
-						  libpq_gettext("could not get server common name from server certificate\n"));
-		free(peer_cn);
-		return false;
-	}
-	peer_cn[len] = '\0';
-
-	/*
-	 * Reject embedded NULLs in certificate common name to prevent attacks
-	 * like CVE-2009-4034.
-	 */
-	if (len != strlen(peer_cn))
-	{
-		printfPQExpBuffer(&conn->errorMessage,
-						  libpq_gettext("SSL certificate's common name contains embedded null\n"));
-		free(peer_cn);
-		return false;
-	}
-
-	/*
-	 * We got the peer's common name. Now compare it against the originally
-	 * given hostname.
-	 */
-	if (!(conn->pghost && conn->pghost[0] != '\0'))
-	{
-		printfPQExpBuffer(&conn->errorMessage,
-						  libpq_gettext("host name must be specified for a verified SSL connection\n"));
-		result = false;
-	}
-	else
-	{
-		if (pg_strcasecmp(peer_cn, conn->pghost) == 0)
-			/* Exact name match */
-			result = true;
-		else if (wildcard_certificate_match(peer_cn, conn->pghost))
-			/* Matched wildcard certificate */
-			result = true;
-		else
-		{
-			printfPQExpBuffer(&conn->errorMessage,
-							  libpq_gettext("server common name \"%s\" does not match host name \"%s\"\n"),
-							  peer_cn, conn->pghost);
-			result = false;
-		}
-	}
-
-	free(peer_cn);
-	return result;
-}
-
-#ifdef ENABLE_THREAD_SAFETY
-/*
- *	Callback functions for OpenSSL internal locking
- */
-
-static unsigned long
-pq_threadidcallback(void)
-{
-	/*
-	 * This is not standards-compliant.  pthread_self() returns pthread_t, and
-	 * shouldn't be cast to unsigned long, but CRYPTO_set_id_callback requires
-	 * it, so we have to do it.
-	 */
-	return (unsigned long) pthread_self();
-}
-
-static pthread_mutex_t *pq_lockarray;
-
-static void
-pq_lockingcallback(int mode, int n, const char *file, int line)
-{
-	if (mode & CRYPTO_LOCK)
-	{
-		if (pthread_mutex_lock(&pq_lockarray[n]))
-			PGTHREAD_ERROR("failed to lock mutex");
-	}
-	else
-	{
-		if (pthread_mutex_unlock(&pq_lockarray[n]))
-			PGTHREAD_ERROR("failed to unlock mutex");
-	}
-}
-#endif   /* ENABLE_THREAD_SAFETY */
-
-/*
- * Initialize SSL system, in particular creating the SSL_context object
- * that will be shared by all SSL-using connections in this process.
- *
- * In threadsafe mode, this includes setting up libcrypto callback functions
- * to do thread locking.
- *
- * If the caller has told us (through PQinitOpenSSL) that he's taking care
- * of libcrypto, we expect that callbacks are already set, and won't try to
- * override it.
- *
- * The conn parameter is only used to be able to pass back an error
- * message - no connection-local setup is made here.
- *
- * Returns 0 if OK, -1 on failure (with a message in conn->errorMessage).
- */
-static int
-init_ssl_system(PGconn *conn)
-{
-#ifdef ENABLE_THREAD_SAFETY
-#ifdef WIN32
-	/* Also see similar code in fe-connect.c, default_threadlock() */
-	if (ssl_config_mutex == NULL)
-	{
-		while (InterlockedExchange(&win32_ssl_create_mutex, 1) == 1)
-			 /* loop, another thread own the lock */ ;
-		if (ssl_config_mutex == NULL)
-		{
-			if (pthread_mutex_init(&ssl_config_mutex, NULL))
-				return -1;
-		}
-		InterlockedExchange(&win32_ssl_create_mutex, 0);
-	}
-#endif
-	if (pthread_mutex_lock(&ssl_config_mutex))
-		return -1;
-
-	if (pq_init_crypto_lib)
-	{
-		/*
-		 * If necessary, set up an array to hold locks for libcrypto.
-		 * libcrypto will tell us how big to make this array.
-		 */
-		if (pq_lockarray == NULL)
-		{
-			int			i;
-
-			pq_lockarray = malloc(sizeof(pthread_mutex_t) * CRYPTO_num_locks());
-			if (!pq_lockarray)
-			{
-				pthread_mutex_unlock(&ssl_config_mutex);
-				return -1;
-			}
-			for (i = 0; i < CRYPTO_num_locks(); i++)
-			{
-				if (pthread_mutex_init(&pq_lockarray[i], NULL))
-				{
-					free(pq_lockarray);
-					pq_lockarray = NULL;
-					pthread_mutex_unlock(&ssl_config_mutex);
-					return -1;
-				}
-			}
-		}
-
-		if (ssl_open_connections++ == 0)
-		{
-			/* These are only required for threaded libcrypto applications */
-			CRYPTO_set_id_callback(pq_threadidcallback);
-			CRYPTO_set_locking_callback(pq_lockingcallback);
-		}
-	}
-#endif   /* ENABLE_THREAD_SAFETY */
-
-	if (!SSL_context)
-	{
-		if (pq_init_ssl_lib)
-		{
-#if SSLEAY_VERSION_NUMBER >= 0x00907000L
-			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)
-		{
-			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(&ssl_config_mutex);
-#endif
-			return -1;
-		}
-
-		/* Disable old protocol versions */
-		SSL_CTX_set_options(SSL_context, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
-
-		/*
-		 * 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);
-	}
-
-#ifdef ENABLE_THREAD_SAFETY
-	pthread_mutex_unlock(&ssl_config_mutex);
-#endif
-	return 0;
-}
-
-/*
- *	This function is needed because if the libpq library is unloaded
- *	from the application, the callback functions will no longer exist when
- *	libcrypto is used by other parts of the system.  For this reason,
- *	we unregister the callback functions when the last libpq
- *	connection is closed.  (The same would apply for OpenSSL callbacks
- *	if we had any.)
- *
- *	Callbacks are only set when we're compiled in threadsafe mode, so
- *	we only need to remove them in this case.
- */
-static void
-destroy_ssl_system(void)
-{
-#ifdef ENABLE_THREAD_SAFETY
-	/* Mutex is created in initialize_ssl_system() */
-	if (pthread_mutex_lock(&ssl_config_mutex))
-		return;
-
-	if (pq_init_crypto_lib && ssl_open_connections > 0)
-		--ssl_open_connections;
-
-	if (pq_init_crypto_lib && ssl_open_connections == 0)
-	{
-		/* No connections left, unregister libcrypto callbacks */
-		CRYPTO_set_locking_callback(NULL);
-		CRYPTO_set_id_callback(NULL);
-
-		/*
-		 * We don't free the lock array or the SSL_context. If we get another
-		 * connection in this process, we will just re-use them with the
-		 * existing mutexes.
-		 *
-		 * This means we leak a little memory on repeated load/unload of the
-		 * library.
-		 */
-	}
-
-	pthread_mutex_unlock(&ssl_config_mutex);
-#endif
-}
-
-/*
- *	Initialize (potentially) per-connection SSL data, namely the
- *	client certificate, private key, and trusted CA certs.
- *
- *	conn->ssl must already be created.  It receives the connection's client
- *	certificate and private key.  Note however that certificates also get
- *	loaded into the SSL_context object, and are therefore accessible to all
- *	connections in this process.  This should be OK as long as there aren't
- *	any hash collisions among the certs.
- *
- *	Returns 0 if OK, -1 on failure (with a message in conn->errorMessage).
- */
-static int
-initialize_SSL(PGconn *conn)
-{
-	struct stat buf;
-	char		homedir[MAXPGPATH];
-	char		fnbuf[MAXPGPATH];
-	char		sebuf[256];
-	bool		have_homedir;
-	bool		have_cert;
-	EVP_PKEY   *pkey = NULL;
-
-	/*
-	 * We'll need the home directory if any of the relevant parameters are
-	 * defaulted.  If pqGetHomeDirectory fails, act as though none of the
-	 * files could be found.
-	 */
-	if (!(conn->sslcert && strlen(conn->sslcert) > 0) ||
-		!(conn->sslkey && strlen(conn->sslkey) > 0) ||
-		!(conn->sslrootcert && strlen(conn->sslrootcert) > 0) ||
-		!(conn->sslcrl && strlen(conn->sslcrl) > 0))
-		have_homedir = pqGetHomeDirectory(homedir, sizeof(homedir));
-	else	/* won't need it */
-		have_homedir = false;
-
-	/* Read the client certificate file */
-	if (conn->sslcert && strlen(conn->sslcert) > 0)
-		strncpy(fnbuf, conn->sslcert, sizeof(fnbuf));
-	else if (have_homedir)
-		snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, USER_CERT_FILE);
-	else
-		fnbuf[0] = '\0';
-
-	if (fnbuf[0] == '\0')
-	{
-		/* no home directory, proceed without a client cert */
-		have_cert = false;
-	}
-	else if (stat(fnbuf, &buf) != 0)
-	{
-		/*
-		 * If file is not present, just go on without a client cert; server
-		 * might or might not accept the connection.  Any other error,
-		 * however, is grounds for complaint.
-		 */
-		if (errno != ENOENT && errno != ENOTDIR)
-		{
-			printfPQExpBuffer(&conn->errorMessage,
-			   libpq_gettext("could not open certificate file \"%s\": %s\n"),
-							  fnbuf, pqStrerror(errno, sebuf, sizeof(sebuf)));
-			return -1;
-		}
-		have_cert = false;
-	}
-	else
-	{
-		/*
-		 * Cert file exists, so load it.  Since OpenSSL doesn't provide the
-		 * equivalent of "SSL_use_certificate_chain_file", we actually have to
-		 * load the file twice.  The first call loads any extra certs after
-		 * the first one into chain-cert storage associated with the
-		 * SSL_context.  The second call loads the first cert (only) into the
-		 * SSL object, where it will be correctly paired with the private key
-		 * we load below.  We do it this way so that each connection
-		 * understands which subject cert to present, in case different
-		 * sslcert settings are used for different connections in the same
-		 * process.
-		 *
-		 * NOTE: This function may also modify our SSL_context and therefore
-		 * we have to lock around this call and any places where we use the
-		 * SSL_context struct.
-		 */
-#ifdef ENABLE_THREAD_SAFETY
-		int			rc;
-
-		if ((rc = pthread_mutex_lock(&ssl_config_mutex)))
-		{
-			printfPQExpBuffer(&conn->errorMessage,
-			   libpq_gettext("could not acquire mutex: %s\n"), strerror(rc));
-			return -1;
-		}
-#endif
-		if (SSL_CTX_use_certificate_chain_file(SSL_context, fnbuf) != 1)
-		{
-			char	   *err = SSLerrmessage();
-
-			printfPQExpBuffer(&conn->errorMessage,
-			   libpq_gettext("could not read certificate file \"%s\": %s\n"),
-							  fnbuf, err);
-			SSLerrfree(err);
-
-#ifdef ENABLE_THREAD_SAFETY
-			pthread_mutex_unlock(&ssl_config_mutex);
-#endif
-			return -1;
-		}
-
-		if (SSL_use_certificate_file(conn->ssl, fnbuf, SSL_FILETYPE_PEM) != 1)
-		{
-			char	   *err = SSLerrmessage();
-
-			printfPQExpBuffer(&conn->errorMessage,
-			   libpq_gettext("could not read certificate file \"%s\": %s\n"),
-							  fnbuf, err);
-			SSLerrfree(err);
-#ifdef ENABLE_THREAD_SAFETY
-			pthread_mutex_unlock(&ssl_config_mutex);
-#endif
-			return -1;
-		}
-
-		/* need to load the associated private key, too */
-		have_cert = true;
-
-#ifdef ENABLE_THREAD_SAFETY
-		pthread_mutex_unlock(&ssl_config_mutex);
-#endif
-	}
-
-	/*
-	 * Read the SSL key. If a key is specified, treat it as an engine:key
-	 * combination if there is colon present - we don't support files with
-	 * colon in the name. The exception is if the second character is a colon,
-	 * in which case it can be a Windows filename with drive specification.
-	 */
-	if (have_cert && conn->sslkey && strlen(conn->sslkey) > 0)
-	{
-#ifdef USE_SSL_ENGINE
-		if (strchr(conn->sslkey, ':')
-#ifdef WIN32
-			&& conn->sslkey[1] != ':'
-#endif
-			)
-		{
-			/* Colon, but not in second character, treat as engine:key */
-			char	   *engine_str = strdup(conn->sslkey);
-			char	   *engine_colon;
-
-			if (engine_str == NULL)
-			{
-				printfPQExpBuffer(&conn->errorMessage,
-								  libpq_gettext("out of memory\n"));
-				return -1;
-			}
-
-			/* cannot return NULL because we already checked before strdup */
-			engine_colon = strchr(engine_str, ':');
-
-			*engine_colon = '\0';		/* engine_str now has engine name */
-			engine_colon++;		/* engine_colon now has key name */
-
-			conn->engine = ENGINE_by_id(engine_str);
-			if (conn->engine == NULL)
-			{
-				char	   *err = SSLerrmessage();
-
-				printfPQExpBuffer(&conn->errorMessage,
-					 libpq_gettext("could not load SSL engine \"%s\": %s\n"),
-								  engine_str, err);
-				SSLerrfree(err);
-				free(engine_str);
-				return -1;
-			}
-
-			if (ENGINE_init(conn->engine) == 0)
-			{
-				char	   *err = SSLerrmessage();
-
-				printfPQExpBuffer(&conn->errorMessage,
-				libpq_gettext("could not initialize SSL engine \"%s\": %s\n"),
-								  engine_str, err);
-				SSLerrfree(err);
-				ENGINE_free(conn->engine);
-				conn->engine = NULL;
-				free(engine_str);
-				return -1;
-			}
-
-			pkey = ENGINE_load_private_key(conn->engine, engine_colon,
-										   NULL, NULL);
-			if (pkey == NULL)
-			{
-				char	   *err = SSLerrmessage();
-
-				printfPQExpBuffer(&conn->errorMessage,
-								  libpq_gettext("could not read private SSL key \"%s\" from engine \"%s\": %s\n"),
-								  engine_colon, engine_str, err);
-				SSLerrfree(err);
-				ENGINE_finish(conn->engine);
-				ENGINE_free(conn->engine);
-				conn->engine = NULL;
-				free(engine_str);
-				return -1;
-			}
-			if (SSL_use_PrivateKey(conn->ssl, pkey) != 1)
-			{
-				char	   *err = SSLerrmessage();
-
-				printfPQExpBuffer(&conn->errorMessage,
-								  libpq_gettext("could not load private SSL key \"%s\" from engine \"%s\": %s\n"),
-								  engine_colon, engine_str, err);
-				SSLerrfree(err);
-				ENGINE_finish(conn->engine);
-				ENGINE_free(conn->engine);
-				conn->engine = NULL;
-				free(engine_str);
-				return -1;
-			}
-
-			free(engine_str);
-
-			fnbuf[0] = '\0';	/* indicate we're not going to load from a
-								 * file */
-		}
-		else
-#endif   /* USE_SSL_ENGINE */
-		{
-			/* PGSSLKEY is not an engine, treat it as a filename */
-			strncpy(fnbuf, conn->sslkey, sizeof(fnbuf));
-		}
-	}
-	else if (have_homedir)
-	{
-		/* No PGSSLKEY specified, load default file */
-		snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, USER_KEY_FILE);
-	}
-	else
-		fnbuf[0] = '\0';
-
-	if (have_cert && fnbuf[0] != '\0')
-	{
-		/* read the client key from file */
-
-		if (stat(fnbuf, &buf) != 0)
-		{
-			printfPQExpBuffer(&conn->errorMessage,
-							  libpq_gettext("certificate present, but not private key file \"%s\"\n"),
-							  fnbuf);
-			return -1;
-		}
-#ifndef WIN32
-		if (!S_ISREG(buf.st_mode) || buf.st_mode & (S_IRWXG | S_IRWXO))
-		{
-			printfPQExpBuffer(&conn->errorMessage,
-							  libpq_gettext("private key file \"%s\" has group or world access; permissions should be u=rw (0600) or less\n"),
-							  fnbuf);
-			return -1;
-		}
-#endif
-
-		if (SSL_use_PrivateKey_file(conn->ssl, fnbuf, SSL_FILETYPE_PEM) != 1)
-		{
-			char	   *err = SSLerrmessage();
-
-			printfPQExpBuffer(&conn->errorMessage,
-			   libpq_gettext("could not load private key file \"%s\": %s\n"),
-							  fnbuf, err);
-			SSLerrfree(err);
-			return -1;
-		}
-	}
-
-	/* verify that the cert and key go together */
-	if (have_cert &&
-		SSL_check_private_key(conn->ssl) != 1)
-	{
-		char	   *err = SSLerrmessage();
-
-		printfPQExpBuffer(&conn->errorMessage,
-						  libpq_gettext("certificate does not match private key file \"%s\": %s\n"),
-						  fnbuf, err);
-		SSLerrfree(err);
-		return -1;
-	}
-
-	/*
-	 * If the root cert file exists, load it so we can perform certificate
-	 * verification. If sslmode is "verify-full" we will also do further
-	 * verification after the connection has been completed.
-	 */
-	if (conn->sslrootcert && strlen(conn->sslrootcert) > 0)
-		strncpy(fnbuf, conn->sslrootcert, sizeof(fnbuf));
-	else if (have_homedir)
-		snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, ROOT_CERT_FILE);
-	else
-		fnbuf[0] = '\0';
-
-	if (fnbuf[0] != '\0' &&
-		stat(fnbuf, &buf) == 0)
-	{
-		X509_STORE *cvstore;
-
-#ifdef ENABLE_THREAD_SAFETY
-		int			rc;
-
-		if ((rc = pthread_mutex_lock(&ssl_config_mutex)))
-		{
-			printfPQExpBuffer(&conn->errorMessage,
-			   libpq_gettext("could not acquire mutex: %s\n"), strerror(rc));
-			return -1;
-		}
-#endif
-		if (SSL_CTX_load_verify_locations(SSL_context, fnbuf, NULL) != 1)
-		{
-			char	   *err = SSLerrmessage();
-
-			printfPQExpBuffer(&conn->errorMessage,
-							  libpq_gettext("could not read root certificate file \"%s\": %s\n"),
-							  fnbuf, err);
-			SSLerrfree(err);
-#ifdef ENABLE_THREAD_SAFETY
-			pthread_mutex_unlock(&ssl_config_mutex);
-#endif
-			return -1;
-		}
-
-		if ((cvstore = SSL_CTX_get_cert_store(SSL_context)) != NULL)
-		{
-			if (conn->sslcrl && strlen(conn->sslcrl) > 0)
-				strncpy(fnbuf, conn->sslcrl, sizeof(fnbuf));
-			else if (have_homedir)
-				snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, ROOT_CRL_FILE);
-			else
-				fnbuf[0] = '\0';
-
-			/* Set the flags to check against the complete CRL chain */
-			if (fnbuf[0] != '\0' &&
-				X509_STORE_load_locations(cvstore, fnbuf, 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
-				char	   *err = SSLerrmessage();
-
-				printfPQExpBuffer(&conn->errorMessage,
-								  libpq_gettext("SSL library does not support CRL certificates (file \"%s\")\n"),
-								  fnbuf);
-				SSLerrfree(err);
-#ifdef ENABLE_THREAD_SAFETY
-				pthread_mutex_unlock(&ssl_config_mutex);
-#endif
-				return -1;
-#endif
-			}
-			/* if not found, silently ignore;  we do not require CRL */
-		}
-#ifdef ENABLE_THREAD_SAFETY
-		pthread_mutex_unlock(&ssl_config_mutex);
-#endif
-
-		SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, verify_cb);
-	}
-	else
-	{
-		/*
-		 * stat() failed; assume root file doesn't exist.  If sslmode is
-		 * verify-ca or verify-full, this is an error.  Otherwise, continue
-		 * without performing any server cert verification.
-		 */
-		if (conn->sslmode[0] == 'v')	/* "verify-ca" or "verify-full" */
-		{
-			/*
-			 * The only way to reach here with an empty filename is if
-			 * pqGetHomeDirectory failed.  That's a sufficiently unusual case
-			 * that it seems worth having a specialized error message for it.
-			 */
-			if (fnbuf[0] == '\0')
-				printfPQExpBuffer(&conn->errorMessage,
-								  libpq_gettext("could not get home directory to locate root certificate file\n"
-												"Either provide the file or change sslmode to disable server certificate verification.\n"));
-			else
-				printfPQExpBuffer(&conn->errorMessage,
-				libpq_gettext("root certificate file \"%s\" does not exist\n"
-							  "Either provide the file or change sslmode to disable server certificate verification.\n"), fnbuf);
-			return -1;
-		}
-	}
-
-	/*
-	 * If the OpenSSL version used supports it (from 1.0.0 on) and the user
-	 * requested it, disable SSL compression.
-	 */
-#ifdef SSL_OP_NO_COMPRESSION
-	if (conn->sslcompression && conn->sslcompression[0] == '0')
-	{
-		SSL_set_options(conn->ssl, SSL_OP_NO_COMPRESSION);
-	}
-#endif
-
-	return 0;
-}
-
-static void
-destroySSL(void)
-{
-	destroy_ssl_system();
-}
-
-/*
- *	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:
-				{
-					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;
-		}
-	}
-
-	/*
-	 * We already checked the server certificate in initialize_SSL() using
-	 * SSL_CTX_set_verify(), if root.crt exists.
-	 */
-
-	/* get server certificate */
-	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;
-	}
-
-	if (!verify_peer_name_matches_certificate(conn))
-	{
-		close_SSL(conn);
-		return PGRES_POLLING_FAILED;
-	}
-
-	/* SSL handshake is complete */
-	return PGRES_POLLING_OK;
-}
-
-/*
- *	Close SSL connection.
- */
-static void
-close_SSL(PGconn *conn)
-{
-	bool		destroy_needed = false;
-
-	if (conn->ssl)
-	{
-		DECLARE_SIGPIPE_INFO(spinfo);
-
-		/*
-		 * We can't destroy everything SSL-related here due to the possible
-		 * later calls to OpenSSL routines which may need our thread
-		 * callbacks, so set a flag here and check at the end.
-		 */
-		destroy_needed = true;
-
-		DISABLE_SIGPIPE(conn, spinfo, (void) 0);
-		SSL_shutdown(conn->ssl);
-		SSL_free(conn->ssl);
-		conn->ssl = NULL;
-		/* We have to assume we got EPIPE */
-		REMEMBER_EPIPE(spinfo, true);
-		RESTORE_SIGPIPE(conn, spinfo);
-	}
-
-	if (conn->peer)
-	{
-		X509_free(conn->peer);
-		conn->peer = NULL;
-	}
-
-#ifdef USE_SSL_ENGINE
-	if (conn->engine)
-	{
-		ENGINE_finish(conn->engine);
-		ENGINE_free(conn->engine);
-		conn->engine = NULL;
-	}
-#endif
-
-	/*
-	 * This will remove our SSL locking hooks, if this is the last SSL
-	 * connection, which means we must wait to call it until after all SSL
-	 * calls have been made, otherwise we can end up with a race condition and
-	 * possible deadlocks.
-	 *
-	 * See comments above destroy_ssl_system().
-	 */
-	if (destroy_needed)
-		pqsecure_destroy();
-}
-
-/*
- * Obtain reason string for last SSL error
- *
- * Some caution is needed here since ERR_reason_error_string will
- * return NULL if it doesn't recognize the error code.  We don't
- * want to return NULL ever.
- */
-static char ssl_nomem[] = "out of memory allocating error description";
-
-#define SSL_ERR_LEN 128
-
-static char *
-SSLerrmessage(void)
-{
-	unsigned long errcode;
-	const char *errreason;
-	char	   *errbuf;
-
-	errbuf = malloc(SSL_ERR_LEN);
-	if (!errbuf)
-		return ssl_nomem;
-	errcode = ERR_get_error();
-	if (errcode == 0)
-	{
-		snprintf(errbuf, SSL_ERR_LEN, libpq_gettext("no SSL error reported"));
-		return errbuf;
-	}
-	errreason = ERR_reason_error_string(errcode);
-	if (errreason != NULL)
-	{
-		strlcpy(errbuf, errreason, SSL_ERR_LEN);
-		return errbuf;
-	}
-	snprintf(errbuf, SSL_ERR_LEN, libpq_gettext("SSL error code %lu"), errcode);
-	return errbuf;
-}
-
-static void
-SSLerrfree(char *buf)
-{
-	if (buf != ssl_nomem)
-		free(buf);
-}
-
-/*
- *	Return pointer to OpenSSL object.
- */
-void *
-PQgetssl(PGconn *conn)
-{
-	if (!conn)
-		return NULL;
-	return conn->ssl;
-}
-#else							/* !USE_SSL */
-
+#ifndef USE_SSL
 void *
 PQgetssl(PGconn *conn)
 {
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index 4aeb4fa..6032904 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -73,14 +73,14 @@ typedef struct
 #endif
 #endif   /* ENABLE_SSPI */
 
-#ifdef USE_SSL
+#ifdef USE_OPENSSL
 #include <openssl/ssl.h>
 #include <openssl/err.h>
 
 #if (SSLEAY_VERSION_NUMBER >= 0x00907000L) && !defined(OPENSSL_NO_ENGINE)
 #define USE_SSL_ENGINE
 #endif
-#endif   /* USE_SSL */
+#endif   /* USE_OPENSSL */
 
 /*
  * POSTGRES backend dependent Constants.
@@ -427,6 +427,8 @@ struct pg_conn
 	bool		allow_ssl_try;	/* Allowed to try SSL negotiation */
 	bool		wait_ssl_try;	/* Delay SSL negotiation until after
 								 * attempting normal connection */
+	bool		ssl_in_use;
+#ifdef USE_OPENSSL
 	SSL		   *ssl;			/* SSL status, if have SSL connection */
 	X509	   *peer;			/* X509 cert of server */
 #ifdef USE_SSL_ENGINE
@@ -435,6 +437,7 @@ struct pg_conn
 	void	   *engine;			/* dummy field to keep struct the same if
 								 * OpenSSL version changes */
 #endif
+#endif   /* USE_OPENSSL */
 #endif   /* USE_SSL */
 
 #ifdef ENABLE_GSS
@@ -482,6 +485,24 @@ struct pg_cancel
  */
 extern char *const pgresStatus[];
 
+
+#ifdef USE_SSL
+
+#ifndef WIN32
+#define USER_CERT_FILE		".postgresql/postgresql.crt"
+#define USER_KEY_FILE		".postgresql/postgresql.key"
+#define ROOT_CERT_FILE		".postgresql/root.crt"
+#define ROOT_CRL_FILE		".postgresql/root.crl"
+#else
+/* On Windows, the "home" directory is already PostgreSQL-specific */
+#define USER_CERT_FILE		"postgresql.crt"
+#define USER_KEY_FILE		"postgresql.key"
+#define ROOT_CERT_FILE		"root.crt"
+#define ROOT_CRL_FILE		"root.crl"
+#endif
+
+#endif   /* USE_SSL */
+
 /* ----------------
  * Internal functions of libpq
  * Functions declared here need to be visible across files of libpq,
@@ -603,6 +624,8 @@ extern PostgresPollingStatusType pqsecure_open_client(PGconn *);
 extern void pqsecure_close(PGconn *);
 extern ssize_t pqsecure_read(PGconn *, void *ptr, size_t len);
 extern ssize_t pqsecure_write(PGconn *, const void *ptr, size_t len);
+extern ssize_t pqsecure_raw_read(PGconn *, void *ptr, size_t len);
+extern ssize_t pqsecure_raw_write(PGconn *, const void *ptr, size_t len);
 
 #if defined(ENABLE_THREAD_SAFETY) && !defined(WIN32)
 extern int	pq_block_sigpipe(sigset_t *osigset, bool *sigpipe_pending);
@@ -611,6 +634,16 @@ extern void pq_reset_sigpipe(sigset_t *osigset, bool sigpipe_pending,
 #endif
 
 /*
+ * The SSL implementatation provides these functions (fe-secure-openssl.c)
+ */
+extern void pgtls_init_library(bool do_ssl, int do_crypto);
+extern int pgtls_init(PGconn *conn);
+extern PostgresPollingStatusType pgtls_open_client(PGconn *conn);
+extern void pgtls_close(PGconn *conn);
+extern ssize_t pgtls_read(PGconn *conn, void *ptr, size_t len);
+extern ssize_t pgtls_write(PGconn *conn, const void *ptr, size_t len);
+
+/*
  * this is so that we can check if a connection is non-blocking internally
  * without the overhead of a function call
  */
diff --git a/src/interfaces/libpq/win32.mak b/src/interfaces/libpq/win32.mak
index 99fef27..39a0bc9 100644
--- a/src/interfaces/libpq/win32.mak
+++ b/src/interfaces/libpq/win32.mak
@@ -2,7 +2,7 @@
 
 # Will build a static library libpq(d).lib
 #        and a dynamic library libpq(d).dll with import library libpq(d)dll.lib
-# USE_SSL=1 will compile with OpenSSL
+# USE_OPENSSL=1 will compile with OpenSSL
 # USE_KFW=1 will compile with kfw(kerberos for Windows)
 # DEBUG=1 compiles with debugging symbols
 # ENABLE_THREAD_SAFETY=1 compiles with threading enabled
@@ -124,6 +124,9 @@ CLEAN :
 	-@erase "$(OUTDIR)\$(OUTFILENAME).dll.manifest"
 	-@erase "$(OUTDIR)\*.idb"
 	-@erase pg_config_paths.h"
+!IFDEF USE_OPENSSL
+	-@erase "$(INTDIR)\fe-secure-openssl.obj"
+!ENDIF
 
 
 LIB32=link.exe -lib
@@ -164,6 +167,9 @@ LIB32_OBJS= \
 	"$(INTDIR)\win32error.obj" \
 	"$(INTDIR)\win32setlocale.obj" \
 	"$(INTDIR)\pthread-win32.obj"
+!IFDEF USE_OPENSSL
+	LIB32_OBJS=$(LIB32_OBJS) "$(INTDIR)\fe-secure-openssl.obj"
+!ENDIF
 
 
 config: ..\..\include\pg_config.h ..\..\include\pg_config_ext.h pg_config_paths.h  ..\..\include\pg_config_os.h
@@ -189,8 +195,8 @@ CPP_PROJ=/nologo /W3 /EHsc $(OPT) /I "..\..\include" /I "..\..\include\port\win3
  /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /c  \
  /D "_CRT_SECURE_NO_DEPRECATE" $(ADD_DEFINES)
 
-!IFDEF USE_SSL
-CPP_PROJ=$(CPP_PROJ) /D USE_SSL
+!IFDEF USE_OPENSSL
+CPP_PROJ=$(CPP_PROJ) /D USE_OPENSSL
 SSL_LIBS=ssleay32.lib libeay32.lib gdi32.lib
 !ENDIF
 
diff --git a/src/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pm
index b71da67..5d809ac 100644
--- a/src/tools/msvc/Mkvcbuild.pm
+++ b/src/tools/msvc/Mkvcbuild.pm
@@ -117,6 +117,12 @@ sub mkvcbuild
 	$postgres->AddLibrary('ws2_32.lib');
 	$postgres->AddLibrary('wldap32.lib') if ($solution->{options}->{ldap});
 	$postgres->FullExportDLL('postgres.lib');
+	# The OBJS scraper doesn't know about ifdefs, so remove fe-secure-openssl.c
+	# if building without OpenSSL
+	if (!$solution->{options}->{openssl})
+	{
+		$postgres->RemoveFile('src\backend\libpq\be-secure-openssl.c');
+	}
 
 	my $snowball = $solution->AddProject('dict_snowball', 'dll', '',
 		'src\backend\snowball');
@@ -276,6 +282,12 @@ sub mkvcbuild
 	$libpq->ReplaceFile('src\interfaces\libpq\libpqrc.c',
 		'src\interfaces\libpq\libpq.rc');
 	$libpq->AddReference($libpgport);
+	# The OBJS scraper doesn't know about ifdefs, so remove fe-secure-openssl.c
+	# if building without OpenSSL
+	if (!$solution->{options}->{openssl})
+	{
+		$libpq->RemoveFile('src\interfaces\libpq\fe-secure-openssl.c');
+	}
 
 	my $libpqwalreceiver =
 	  $solution->AddProject('libpqwalreceiver', 'dll', '',
diff --git a/src/tools/msvc/Solution.pm b/src/tools/msvc/Solution.pm
index e49c3f4..39e41f6 100644
--- a/src/tools/msvc/Solution.pm
+++ b/src/tools/msvc/Solution.pm
@@ -182,7 +182,7 @@ sub GenerateFiles
 		  if ($self->{options}->{integer_datetimes});
 		print O "#define USE_LDAP 1\n"   if ($self->{options}->{ldap});
 		print O "#define HAVE_LIBZ 1\n"  if ($self->{options}->{zlib});
-		print O "#define USE_SSL 1\n"    if ($self->{options}->{openssl});
+		print O "#define USE_OPENSSL 1\n" if ($self->{options}->{openssl});
 		print O "#define ENABLE_NLS 1\n" if ($self->{options}->{nls});
 
 		print O "#define BLCKSZ ", 1024 * $self->{options}->{blocksize}, "\n";
@@ -628,7 +628,7 @@ sub GetFakeConfigure
 	$cfg .= ' --with-ldap'  if ($self->{options}->{ldap});
 	$cfg .= ' --without-zlib' unless ($self->{options}->{zlib});
 	$cfg .= ' --with-extra-version' if ($self->{options}->{extraver});
-	$cfg .= ' --with-openssl'       if ($self->{options}->{ssl});
+	$cfg .= ' --with-openssl'       if ($self->{options}->{openssl});
 	$cfg .= ' --with-ossp-uuid'     if ($self->{options}->{uuid});
 	$cfg .= ' --with-libxml'        if ($self->{options}->{xml});
 	$cfg .= ' --with-libxslt'       if ($self->{options}->{xslt});
diff --git a/src/tools/msvc/config_default.pl b/src/tools/msvc/config_default.pl
index 20aee8b..e4d4810 100644
--- a/src/tools/msvc/config_default.pl
+++ b/src/tools/msvc/config_default.pl
@@ -16,7 +16,7 @@ our $config = {
 	tcl      => undef,   # --with-tls=<path>
 	perl     => undef,   # --with-perl
 	python   => undef,   # --with-python=<path>
-	openssl  => undef,   # --with-ssl=<path>
+	openssl  => undef,   # --with-openssl=<path>
 	uuid     => undef,   # --with-ossp-uuid
 	xml      => undef,   # --with-libxml=<path>
 	xslt     => undef,   # --with-libxslt=<path>
-- 
2.0.1

#29Heikki Linnakangas
hlinnakangas@vmware.com
In reply to: Jeff Janes (#27)
1 attachment(s)
Re: Supporting Windows SChannel as OpenSSL replacement

On 08/06/2014 08:37 PM, Jeff Janes wrote:

But now it looks like 0002 needs a rebase....

I've committed the refactoring patch, and here's a rebased and improved
version of the Windows SChannel implementation over that.

Server-side support is now implemented too, but it's all very crude and
work-in-progress. CRLs are not supported, intermediary CAs are not
supported, and probably many other bells and whistles are missing too.
But the basics work, including cert authentication. Consider this a
Proof of Concept.

One issue came up with managing private keys: In the server, it's
necessary to import the private key into a permanent key container
that's managed by the Windows Crypto API. That can be done
programmatically (as I do in the patch), but the keys are permanently
stored in the system (in the user's profile). They will be left behind
even if you completely delete the data directory. That's not the end of
the world, but it would be nicer if we could use some kind of a
temporary key container that only lives in memory, but the Crypto API
doesn't seem to have such a concept. You can acquire an ephemeral
context by passing the CRYPT_VERIFYCONTEXT flag to CryptAcquireContext
function, and that's exactly what I'm doing in the client, but that
method doesn't seem to work when acting as an SSL server.

Also, the key container needs to be given a name, or we can use the
default container, but either way all the keys are shared among all
applications that use the same container. We'll have to figure out how
to set that up so that there are no conflicts, if you try to use the
same server certificate for two PostgreSQL instances running on the same
host (useful while developing/testing replication).

This isn't a showstopper, but needs some thought. As the patch stands,
it uses a single key container called "PostgreSQL server key container",
and makes no attempt to delete the keys after they're no longer used.
That works, but it leaves the key lying on the system.

- Heikki

Attachments:

windows-schannel-2.patchtext/x-diff; name=windows-schannel-2.patchDownload
diff --git a/configure b/configure
index 0f435b5..d00290a 100755
--- a/configure
+++ b/configure
@@ -707,6 +707,7 @@ XML2_CONFIG
 UUID_EXTRA_OBJS
 with_uuid
 with_selinux
+with_winschannel
 with_openssl
 krb_srvtab
 with_python
@@ -823,6 +824,7 @@ with_pam
 with_ldap
 with_bonjour
 with_openssl
+with_winschannel
 with_selinux
 with_readline
 with_libedit_preferred
@@ -1509,6 +1511,7 @@ Optional Packages:
   --with-ldap             build with LDAP support
   --with-bonjour          build with Bonjour support
   --with-openssl          build with OpenSSL support
+  --with-winschannel      build with Windows SChannel support
   --with-selinux          build with SELinux support
   --without-readline      do not use GNU Readline nor BSD Libedit for editing
   --with-libedit-preferred
@@ -5514,6 +5517,46 @@ $as_echo "$with_openssl" >&6; }
 
 
 #
+# Windows SChannel
+#
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build with native Windows SSL support" >&5
+$as_echo_n "checking whether to build with native Windows SSL support... " >&6; }
+
+
+
+# Check whether --with-winschannel was given.
+if test "${with_winschannel+set}" = set; then :
+  withval=$with_winschannel;
+  case $withval in
+    yes)
+
+$as_echo "#define USE_WINDOWS_SCHANNEL 1" >>confdefs.h
+
+      ;;
+    no)
+      :
+      ;;
+    *)
+      as_fn_error $? "no argument expected for --with-winschannel option" "$LINENO" 5
+      ;;
+  esac
+
+else
+  with_winschannel=no
+
+fi
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_winschannel" >&5
+$as_echo "$with_winschannel" >&6; }
+
+
+if test "$with_openssl" = yes -a "$with_winschannel" = yes ; then
+  as_fn_error $? "
+*** Cannot select both OpenSSL and Windows SChannel." "$LINENO" 5
+fi
+
+#
 # SELinux
 #
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build with SELinux support" >&5
diff --git a/configure.in b/configure.in
index f8a4507..132fb0a 100644
--- a/configure.in
+++ b/configure.in
@@ -662,6 +662,20 @@ AC_MSG_RESULT([$with_openssl])
 AC_SUBST(with_openssl)
 
 #
+# Windows SChannel
+#
+AC_MSG_CHECKING([whether to build with native Windows SSL support])
+PGAC_ARG_BOOL(with, winschannel, no, [build with Windows SChannel support],
+              [AC_DEFINE([USE_WINDOWS_SCHANNEL], 1, [Define to build with Windows SChannel support. (--with-winschannel)])])
+AC_MSG_RESULT([$with_winschannel])
+AC_SUBST(with_winschannel)
+
+if test "$with_openssl" = yes -a "$with_winschannel" = yes ; then
+  AC_MSG_ERROR([
+*** Cannot select both OpenSSL and Windows SChannel.])
+fi
+
+#
 # SELinux
 #
 AC_MSG_CHECKING([whether to build with SELinux support])
diff --git a/src/backend/libpq/Makefile b/src/backend/libpq/Makefile
index 8be0572..88d0b8b 100644
--- a/src/backend/libpq/Makefile
+++ b/src/backend/libpq/Makefile
@@ -21,4 +21,8 @@ ifeq ($(with_openssl),yes)
 OBJS += be-secure-openssl.o
 endif
 
+ifeq ($(with_winschannel),yes)
+OBJS += be-secure-winschannel.o
+endif
+
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index e3a284b..651162e 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -922,10 +922,6 @@ aloop:
 		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);
 
diff --git a/src/backend/libpq/be-secure-winschannel.c b/src/backend/libpq/be-secure-winschannel.c
new file mode 100644
index 0000000..306f265
--- /dev/null
+++ b/src/backend/libpq/be-secure-winschannel.c
@@ -0,0 +1,1098 @@
+/*-------------------------------------------------------------------------
+ *
+ * be-secure-winschannel.c
+ *	  SSL support using Windows SChannel APIs
+ *
+ *
+ * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  src/backend/libpq/be-secure-winschannel.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "libpq/libpq-be.h"
+#include "libpq/libpq.h"
+#include "miscadmin.h"
+#include "storage/fd.h"
+
+#include <sspi.h>
+#include <schnlsp.h>
+
+#include <wincrypt.h>
+
+/*
+ * Name of the Crypto API key container to use for storing the server
+ * certificate and private key. 
+ */
+#define PG_KEY_CONTAINER_NAME L"PostgreSQL server key container"
+
+#define SCHANNEL_DEBUG LOG
+
+struct SchannelContext
+{
+	/* SChannel handle for the connection */
+	CtxtHandle  ctxHandle;
+	bool		ctxHandle_valid;
+	CredHandle	credHandle;
+	bool		credHandle_valid;
+
+	bool		remote_shutdown;
+
+	/*
+	 * Buffer for encrypting data to be written.
+	 *
+	 * When encryptedLen > 0, the buffer contains some encrypted data that
+	 * should be written out to the client.
+	 *
+	 * When encryptedLen == 0, call encryptBuffer() to load the buffer with
+	 * more data, and encrypt it.
+	 */
+	char	   *encryptBuffer;
+	int			encryptBufferSize;
+	int			encryptBufferLen;
+
+	char	   *encryptedData;
+	size_t		encryptedLen;
+
+	/*
+	 * Number of plaintext bytes the buffer currently holds in encrypted form.
+	 * This is less than the number of encrypted bytes, because the SSL
+	 * protocol adds framing. (it could also be more, when compression is used)
+	 */
+	int			plaintextLen;
+
+	/*
+	 * Buffer for decrypting read data.
+	 *
+	 * decryptBufferSize is the allocated size of decryptBuffer.
+	 * decryptBufferLen is the number of ciphertext bytes loaded to the buffer.
+	 *
+	 * The buffer can be in one of two states. In the first state, it contains
+	 * zero or more raw bytes, and more can be loaded into it. In the other
+	 * state, it contains decrypted data that can be read out.
+	 *
+	 * When the buffer contains decrypted data, decryptedLen > 0, and
+	 * decryptedData points to the data. The buffer can also contain some
+	 * leftover raw data that has not been decrypted yet (extraData/extraLen).
+	 *
+	 * Before reading more raw data to the buffer, call
+	 * prepareDecryptBufferForRead. It moves any leftover raw data to the
+	 * beginning of the buffer.
+	 */
+	char	   *decryptBuffer;
+	int			decryptBufferSize;
+	int			decryptBufferLen; 	/* ciphertext bytes loaded in decryptBuffer */
+
+	/* decrypted data not returned to the caller yet */
+	char	   *decryptedData;
+	int			decryptedLen;
+
+	char	   *extraData;
+	int			extraLen;
+};
+
+typedef struct SchannelContext SchannelContext;
+
+/*
+ * Somewhat arbitrarily, use a 10k buffer for both input and output.
+ *
+ * XXX: A buffer size somewhat larger than 8k probably makes sense at least
+ * for input, because the pqcomm.c uses an 8k buffer. (the extra is to contain
+ * SSL headers in the raw input). But I haven't done any performance testing.
+ */
+#define BUFSIZE 10000
+
+/* Functions for managing the encrypt/decrypt buffers */
+static ssize_t encryptBuffer(SchannelContext *ctx, void *ptr, int len);
+static void prepareDecryptBufferForRead(SchannelContext *ctx);
+static int decryptBuffer(SchannelContext *ctx);
+
+static int schannel_handshake(Port *port);
+
+/* Functions for dealing with certificates */
+static void importServerKey(void);
+static CERT_CONTEXT *getServerCert(void);
+static CERT_CONTEXT *getClientRootCert(void);
+static bool validateClientCert(CERT_CONTEXT *cert);
+
+/* Functions for converting error codes to strings */
+static const char *getSecurityStatus(SECURITY_STATUS dwerror);
+
+static char *slurpFile(const char *path, int *size);
+
+/*
+ *	Write data to a secure connection.
+ */
+ssize_t
+be_tls_write(Port *port, void *ptr, size_t len)
+{
+	SchannelContext *ctx = port->windows_schannel;
+	int			sendlen;
+
+	/*
+	 * If the output buffer is empty, encrypt (some of) the input data to fill
+	 * it.
+	 */
+	if (ctx->encryptedLen == 0)
+	{
+		int			plaintextLen;
+
+		plaintextLen = encryptBuffer(ctx, ptr, len);
+		if (plaintextLen < 0)
+			return -1;
+		ctx->plaintextLen = plaintextLen;
+	}
+
+	/*
+	 * Write out the encrypted data in the buffer
+	 */
+	sendlen = secure_raw_write(port,
+							   ctx->encryptedData,
+							   ctx->encryptedLen);
+	if (sendlen <= 0)
+		return sendlen;
+
+	ctx->encryptedData += sendlen;
+	ctx->encryptedLen -= sendlen;
+
+	if (ctx->encryptedLen == 0)
+	{
+		/* sent all the data in the buffer. */
+		return ctx->plaintextLen;
+	}
+	else
+	{
+		/* return 0 to indicate that the caller has to retry. */
+		return 0;
+	}
+}
+
+/*
+ * Encrypt more data. Helper function for be_tls_write().
+ */
+static ssize_t
+encryptBuffer(SchannelContext *ctx, void *ptr, int len)
+{
+	SecPkgContext_StreamSizes sizes;
+	SecBufferDesc sbufdesc;
+	SecBuffer	sbufs[4];
+	SECURITY_STATUS rc;
+
+	/* we should've sent out all encrypted data from previous round first */
+	Assert(ctx->encryptedLen == 0);
+
+	rc = QueryContextAttributes(&ctx->ctxHandle,
+								SECPKG_ATTR_STREAM_SIZES, &sizes);
+	if (rc != SEC_E_OK)
+	{
+		elog(COMMERROR, "QueryContextAttributes failed: %s", getSecurityStatus(rc));
+		return -1;
+	}
+	if (sizes.cbHeader + sizes.cbTrailer + len > ctx->encryptBufferSize)
+		len = ctx->encryptBufferSize - (sizes.cbHeader + sizes.cbTrailer);
+
+	memcpy(&ctx->encryptBuffer[sizes.cbHeader], ptr, len);
+
+	sbufs[0].pvBuffer = ctx->encryptBuffer;
+	sbufs[0].cbBuffer = sizes.cbHeader;
+	sbufs[0].BufferType = SECBUFFER_STREAM_HEADER;
+
+	sbufs[1].pvBuffer = &ctx->encryptBuffer[sizes.cbHeader];
+	sbufs[1].cbBuffer = len;
+	sbufs[1].BufferType = SECBUFFER_DATA;
+
+	sbufs[2].pvBuffer = &ctx->encryptBuffer[sizes.cbHeader + len];
+	sbufs[2].cbBuffer = sizes.cbTrailer;
+	sbufs[2].BufferType = SECBUFFER_STREAM_TRAILER;
+
+	sbufs[3].pvBuffer = NULL;
+	sbufs[3].cbBuffer = 0;
+	sbufs[3].BufferType = SECBUFFER_EMPTY;
+
+	sbufdesc.ulVersion = SECBUFFER_VERSION;
+	sbufdesc.cBuffers = 4;
+	sbufdesc.pBuffers = sbufs;
+
+	rc = EncryptMessage(&ctx->ctxHandle, 0, &sbufdesc, 0);
+	if (rc != SEC_E_OK)
+	{
+		elog(ERROR, "EncryptMessage failed: %s", getSecurityStatus(rc));
+		return -1;
+	}
+
+	ctx->encryptedData = ctx->encryptBuffer;
+	ctx->encryptedLen = sbufs[0].cbBuffer + sbufs[1].cbBuffer + sbufs[2].cbBuffer;
+
+	return len;
+}
+
+/*
+ *	Initialize global SSL context.
+ */
+void
+be_tls_init(void)
+{
+	/*
+	 * Import the server's private key into the key container. (This only
+	 * needs to be done once at server startup.)
+	 */
+	if (IsUnderPostmaster)
+		importServerKey();
+}
+
+static int
+schannel_handshake(Port *port)
+{
+	SchannelContext *ctx = port->windows_schannel;
+	SecBufferDesc insbufdesc;
+	SecBuffer	insbufs[2];
+	SecBufferDesc outsbufdesc;
+	SecBuffer	outsbufs[1];
+	DWORD		flags;
+	DWORD		outflags;
+	SECURITY_STATUS rc;
+	ssize_t		readlen;
+	bool		handshake_complete = false;
+
+	do
+	{
+		/* read more raw data to buffer */
+		readlen = secure_raw_read(port,
+								  &ctx->decryptBuffer[ctx->decryptBufferLen],
+								  ctx->decryptBufferSize - ctx->decryptBufferLen);
+		if (readlen < 0)
+		{
+			ereport(COMMERROR,
+					(errcode_for_socket_access(),
+					 errmsg("could not accept SSL connection: %m")));
+			be_tls_close(port);
+			return -1;
+		}
+
+		ctx->decryptBufferLen += readlen;
+
+		/*
+		 * Pass the raw data read this far to AcceptSecurityContext.
+		 * Per Microsoft documentation, AcceptSecurityContext requires two
+		 * input buffers, one with SECBUFFER_TOKEN containing the input data,
+		 * and one SECBUFFER_EMPTY.
+		 */
+		insbufs[0].pvBuffer = ctx->decryptBuffer;
+		insbufs[0].cbBuffer = ctx->decryptBufferLen;
+		insbufs[0].BufferType = SECBUFFER_TOKEN;
+
+		insbufs[1].pvBuffer = NULL;
+		insbufs[1].cbBuffer = 0;
+		insbufs[1].BufferType = SECBUFFER_EMPTY;
+
+		insbufdesc.cBuffers = 2;
+		insbufdesc.pBuffers = insbufs;
+		insbufdesc.ulVersion = SECBUFFER_VERSION;
+
+		/*
+		 * The ASC_REQ_MUTUAL_AUTH means that we request a client cert. The
+		 * connection will still succeed if the client doesn't provide one.
+		 */
+		flags = ASC_REQ_ALLOCATE_MEMORY |
+			ASC_REQ_CONFIDENTIALITY |
+			ASC_RET_EXTENDED_ERROR |
+			ASC_REQ_REPLAY_DETECT |
+			ASC_REQ_SEQUENCE_DETECT |
+			ASC_REQ_MUTUAL_AUTH |
+			ASC_REQ_STREAM;
+
+		outsbufs[0].pvBuffer   = NULL;
+		outsbufs[0].BufferType = SECBUFFER_TOKEN;
+		outsbufs[0].cbBuffer   = 0;
+
+		outsbufdesc.cBuffers = 1;
+		outsbufdesc.pBuffers = outsbufs;
+		outsbufdesc.ulVersion = SECBUFFER_VERSION;
+
+		rc = AcceptSecurityContext(&ctx->credHandle,
+								   ctx->ctxHandle_valid ? &ctx->ctxHandle : NULL,
+								   &insbufdesc,
+								   flags,
+								   0,
+								   ctx->ctxHandle_valid ? NULL : &ctx->ctxHandle,
+								   &outsbufdesc,
+								   &outflags,
+								   NULL);
+		ctx->ctxHandle_valid = true;
+
+		/*
+		 * If AcceptSecurityContext returns SEC_E_OK or SEC_I_CONTINUE_NEEDED,
+		 * it has consumed (some of) the raw input. It may have also produced
+		 * some output that we need to write back to the client.
+		 */
+		if (rc == SEC_E_OK || rc == SEC_I_CONTINUE_NEEDED)
+		{
+			/*
+			 * AcceptSecurityContext might not have processed all the input
+			 * data. The number of unprocessed bytes is returned in input
+			 * buffer 1, as an SECBUFFER_EXTRA buffer.
+			 */
+			if (insbufs[1].BufferType == SECBUFFER_EXTRA)
+			{
+				size_t extraLen = insbufs[1].cbBuffer;
+
+				memmove(ctx->decryptBuffer, &ctx->decryptBuffer[ctx->decryptBufferLen - extraLen], extraLen);
+				ctx->decryptBufferLen = extraLen;
+			}
+			else
+				ctx->decryptBufferLen = 0;
+
+			if (outsbufs[0].pvBuffer)
+			{
+				ssize_t		totalsent = 0;
+				ssize_t		sendlen;
+
+				while(totalsent < outsbufs[0].cbBuffer)
+				{
+					sendlen = secure_raw_write(port,
+											   outsbufs[0].pvBuffer,
+											   outsbufs[0].cbBuffer);
+					/* Note: we assume that the socket is in blocking mode */
+					if (sendlen <= 0)
+					{
+						ereport(COMMERROR,
+								(errcode(ERRCODE_PROTOCOL_VIOLATION),
+								 errmsg("could not send")));
+						return -1;
+					}
+
+					totalsent += sendlen;
+				}
+
+				FreeContextBuffer(outsbufs[0].pvBuffer);
+				outsbufs[0].pvBuffer = NULL;
+			}
+		}
+
+		switch(rc)
+		{
+			case SEC_E_OK:
+				/* SSL handshake is complete */
+				handshake_complete = true;
+				break;
+
+			case SEC_I_COMPLETE_AND_CONTINUE:
+				elog(COMMERROR, "SEC_I_COMPLETE_AND_CONTINUE not implemented", rc);
+				be_tls_close(port);
+				return -1;
+
+			case SEC_I_COMPLETE_NEEDED:
+				elog(COMMERROR, "SEC_I_COMPLETE_NEEDED not implemented", rc);
+				be_tls_close(port);
+				return -1;
+
+			case SEC_I_CONTINUE_NEEDED:
+				/* Need more input from the client to continue */
+				break;
+
+			case SEC_I_INCOMPLETE_CREDENTIALS:
+				elog(COMMERROR, "SEC_I_INCOMPLETE_CREDENTIALS not implemented");
+				be_tls_close(port);
+				return -1;
+
+			case SEC_E_INCOMPLETE_MESSAGE:
+				/*
+				 * The input didn't contain a full SSL message. Loop back to
+				 * read more data, leaving the input already read in the
+				 * buffer, and retry.
+				 */
+				break;
+
+			default:
+				ereport(COMMERROR,
+						(errcode(ERRCODE_PROTOCOL_VIOLATION),
+						 errmsg("could not initialize SSL connection"),
+						 errdetail("AcceptSecurityContext failed: %s", getSecurityStatus(rc))));
+				be_tls_close(port);
+				return -1;
+		}
+	} while (!handshake_complete);
+
+	/* success! */
+	return 0;
+}
+
+/*
+ *	Attempt to negotiate SSL connection.
+ */
+int
+be_tls_open_server(Port *port)
+{
+	SchannelContext *ctx = port->windows_schannel;
+	SECURITY_STATUS rc;
+	SCHANNEL_CRED credData;
+	CERT_CONTEXT *serverCert;
+	CERT_CONTEXT *remoteCert;
+
+	serverCert = getServerCert();
+
+	memset(&credData, 0, sizeof(credData));
+	credData.dwVersion = SCHANNEL_CRED_VERSION;
+	credData.dwFlags |= SCH_CRED_NO_SYSTEM_MAPPER;
+	credData.cCreds = 1;
+	credData.paCred = &serverCert;
+
+	ctx = (SchannelContext *) palloc0(sizeof(SchannelContext));
+
+	rc = AcquireCredentialsHandle(NULL,
+								  UNISP_NAME,
+								  SECPKG_CRED_INBOUND,
+								  NULL,
+								  &credData,
+								  NULL,
+								  NULL,
+								  &ctx->credHandle,
+								  NULL);
+	if (rc != SEC_E_OK)
+	{
+		ereport(COMMERROR,
+				(errcode(ERRCODE_PROTOCOL_VIOLATION),
+				 errmsg("could not initialize SSL"),
+				 errdetail("AcquireCredentialsHandle failed: %s", getSecurityStatus(rc))));
+		be_tls_close(port);
+		return -1;
+	}
+	ctx->credHandle_valid = true;
+
+	ctx->encryptBufferSize = BUFSIZE;
+	ctx->encryptBuffer = palloc(ctx->encryptBufferSize);
+	ctx->decryptBufferSize = BUFSIZE;
+	ctx->decryptBuffer = palloc(ctx->encryptBufferSize);
+
+	port->windows_schannel = ctx;
+	port->ssl_in_use = true;
+
+	if (schannel_handshake(port) < 0)
+		return -1;
+
+	/* SSL handshake complete */
+	elog(SCHANNEL_DEBUG, "SSL handshake completed");
+
+	/* Get client certificate, if available */
+	rc = QueryContextAttributes(&ctx->ctxHandle,
+								SECPKG_ATTR_REMOTE_CERT_CONTEXT,
+								(PVOID) &remoteCert);
+	if (rc == SEC_E_OK)
+	{
+		/*
+		 * Validate the client cert. If it passes, extract the Common Name,
+		 * to be used later in cert authentication.
+		 */
+		if (validateClientCert(remoteCert))
+		{
+			char	   *cn;
+			DWORD		len;
+			DWORD		len2;
+
+			port->peer_cert_valid = true;
+			elog(SCHANNEL_DEBUG, "client certificate validated");
+
+			/*
+			 * Call CertGetNameString without passing a buffer, to get the
+			 * required length.
+			 */
+			len = CertGetNameString(remoteCert,
+									CERT_NAME_ATTR_TYPE,
+									0,
+									szOID_COMMON_NAME,
+									NULL,
+									0);
+			if (len > 1)
+			{
+				cn = palloc(len);
+				len2 = CertGetNameString(remoteCert,
+										 CERT_NAME_ATTR_TYPE,
+										 0,
+										 szOID_COMMON_NAME,
+										 cn,
+										 len);
+				if (len != len2)
+				{
+					/* shouldn't happen */
+					pfree(cn);
+					be_tls_close(port);
+					return -1;
+				}
+				port->peer_cn = cn;
+				elog(SCHANNEL_DEBUG, "client cert name: %s", cn);
+			}
+		}
+		else
+			elog(SCHANNEL_DEBUG, "client certificate validation failed");
+
+		CertFreeCertificateContext(remoteCert);
+	}
+
+	return 0;
+}
+
+/*
+ *	Close SSL connection.
+ */
+void
+be_tls_close(Port *port)
+{
+	SchannelContext *ctx = port->windows_schannel;
+	DWORD		token;
+	SecBufferDesc outsbufdesc;
+	SecBuffer	outsbufs[1];
+	SECURITY_STATUS rc;
+
+	if (ctx->ctxHandle_valid)
+	{
+		/*
+		 *  "Shutting Down an Schannel Connection",
+		 *  http://msdn.microsoft.com/en-us/library/windows/desktop/aa380138%28v=vs.85%29.aspx
+		 */
+		token = SCHANNEL_SHUTDOWN;
+
+		outsbufdesc.ulVersion = SECBUFFER_VERSION;
+		outsbufdesc.cBuffers = 1;
+		outsbufdesc.pBuffers = outsbufs;
+
+		outsbufs[0].BufferType = SECBUFFER_TOKEN;
+		outsbufs[0].cbBuffer = sizeof( token );
+		outsbufs[0].pvBuffer = &token;
+
+		rc = ApplyControlToken(&ctx->ctxHandle, &outsbufdesc);
+
+		/*
+		 * Use AcceptSecurityContext to finish the shutdown, just like the
+		 * initiation handshake is done.
+		 */
+		(void) schannel_handshake(port);
+
+		DeleteSecurityContext(&ctx->ctxHandle);
+		ctx->ctxHandle_valid = false;
+	}
+	if (ctx->credHandle_valid)
+	{
+		FreeCredentialsHandle(&ctx->credHandle);
+		ctx->credHandle_valid = false;
+	}
+
+	if (ctx->encryptBuffer)
+		pfree(ctx->encryptBuffer);
+	if (ctx->decryptBuffer)
+		pfree(ctx->decryptBuffer);
+
+	pfree(ctx);
+
+	if (port->peer_cn)
+	{
+		pfree(port->peer_cn);
+		port->peer_cn = NULL;
+	}
+	port->peer_cert_valid = false;
+
+	port->windows_schannel = NULL;
+	port->ssl_in_use = false;
+}
+
+/*
+ *	Read data from a secure connection.
+ */
+ssize_t
+be_tls_read(Port *port, void *ptr, size_t len)
+{
+	SchannelContext *ctx = port->windows_schannel;
+	int			readlen;
+	int			maxsize;
+	size_t		totalreadlen = 0;
+	char	   *dst = ptr;
+
+	for (;;)
+	{
+		/* if we already have some decrypted data, copy it to the target buffer */
+		if (ctx->decryptedLen > 0)
+		{
+			maxsize = len - totalreadlen;
+			if (maxsize > ctx->decryptedLen)
+				maxsize = ctx->decryptedLen;
+			memcpy(dst, &ctx->decryptedData[totalreadlen], maxsize);
+			dst += maxsize;
+			totalreadlen += maxsize;
+			ctx->decryptedLen -= maxsize;
+			ctx->decryptedData += maxsize;
+		}
+
+		/* If we have some data to return, return it. */
+		if (totalreadlen > 0)
+		{
+			return totalreadlen;
+		}
+
+		/*
+		 * there shouldn't be any decrypted data left in the buffer, we copied
+		 * it all to the target.
+		 */
+		Assert(ctx->decryptedLen == 0);
+
+		/*
+		 * Need to decrypt more data, and for that, need to read more raw
+		 * data.
+		 */
+		prepareDecryptBufferForRead(ctx);
+		while (ctx->decryptedLen == 0)
+		{
+			/* read more data to buffer */
+			maxsize = ctx->decryptBufferSize - ctx->decryptBufferLen;
+			readlen = secure_raw_read(port,
+									  &ctx->decryptBuffer[ctx->decryptBufferLen],
+									  maxsize);
+			if (readlen < 0)
+				return readlen;
+			if (readlen == 0)
+			{
+				/* this implies we're in non-blocking mode */
+				return totalreadlen;
+			}
+
+			ctx->decryptBufferLen += readlen;
+
+			/* decrypt what we got */
+			if (decryptBuffer(ctx) == -1)
+				return -1;
+			if (ctx->remote_shutdown)
+				return totalreadlen;
+
+			/* If decryptBuffer needs more data, decrypteLen is still 0. */
+		}
+	}
+
+	return totalreadlen;
+}
+
+/*
+ * Prepare the buffer for reading in more cipher text.
+ */
+static void
+prepareDecryptBufferForRead(SchannelContext *ctx)
+{
+	/*
+	 * should've consumed all the previous decrypted data in the buffer befor
+	 * decrypting more.
+	 */
+	Assert(ctx->decryptedLen == 0);
+
+	/*
+	 * If there's any ciphertext left in the buffer from previous round, move
+	 * it to beginning.
+	 */
+	if (ctx->extraLen > 0)
+		memmove(ctx->decryptBuffer,	ctx->extraData,	ctx->extraLen);
+	ctx->decryptBufferLen = ctx->extraLen;
+	ctx->extraData = NULL;
+	ctx->extraLen = 0;
+	ctx->decryptedData = NULL;
+	ctx->decryptedLen = 0;
+}
+
+/*
+ * Try to decrypt the data currently in decryptBuffer. On success,
+ * decryptedData points to the decrypted data. DecryptMessage decrypts
+ * in-place, so decryptedData points to somewhere within the buffer!
+ *
+ * On success, returns 1. On error, returns -1. If more raw data is needed
+ * to decrypt, returns 0. If the client shut down the connection, also
+ * returns 0, and sets ctx->remote_shutdown flag.
+ */
+static int
+decryptBuffer(SchannelContext *ctx)
+{
+	/*
+	 * Call DecryptBuffer until it returns some data, fails, or indicates that
+	 * it needs more input
+	 */
+	while(ctx->decryptedLen == 0)
+	{
+		SecBufferDesc sbufdesc;
+		SecBuffer	sbufs[4];
+		SECURITY_STATUS rc;
+		int			i;
+
+		Assert(ctx->decryptBufferLen > 0);
+		Assert(ctx->extraLen == 0);
+
+		sbufs[0].pvBuffer = ctx->decryptBuffer;
+		sbufs[0].cbBuffer = ctx->decryptBufferLen;
+		sbufs[0].BufferType = SECBUFFER_DATA;
+		sbufs[1].pvBuffer = NULL;
+		sbufs[1].cbBuffer = 0;
+		sbufs[1].BufferType   = SECBUFFER_EMPTY;
+		sbufs[2].pvBuffer = NULL;
+		sbufs[2].cbBuffer = 0;
+		sbufs[2].BufferType   = SECBUFFER_EMPTY;
+		sbufs[3].pvBuffer = NULL;
+		sbufs[3].cbBuffer = 0;
+		sbufs[3].BufferType   = SECBUFFER_EMPTY;
+
+		sbufdesc.ulVersion = SECBUFFER_VERSION;
+		sbufdesc.cBuffers = 4;
+		sbufdesc.pBuffers = sbufs;
+
+		rc = DecryptMessage(&ctx->ctxHandle, &sbufdesc, 0, NULL);
+
+		if (rc == SEC_E_INCOMPLETE_MESSAGE)
+		{
+			/* We don't have the full message yet. Need more data. */
+			return 0;
+		}
+		if (rc == SEC_I_CONTEXT_EXPIRED)
+		{
+			elog(SCHANNEL_DEBUG, "DecryptMessage returned SEC_I_CONTEXT_EXPIRED");
+			ctx->remote_shutdown = true;
+			return 0;
+		}
+		if (rc == SEC_I_RENEGOTIATE)
+		{
+			/* TODO */
+			ereport(ERROR,
+					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+					 errmsg("SSL renegotiated not implemented")));
+		}
+		if (rc != SEC_E_OK)
+		{
+			/* FIXME: set errno */
+			elog(COMMERROR, "DecryptMessage failed: %s", getSecurityStatus(rc));
+			return -1;
+		}
+
+		for (i = 0; i < 4; i++)
+		{
+			if (sbufs[i].BufferType == SECBUFFER_EXTRA)
+			{
+				ctx->extraData = sbufs[i].pvBuffer;
+				ctx->extraLen = sbufs[i].cbBuffer;
+			}
+			if (sbufs[i].BufferType == SECBUFFER_DATA)
+			{
+				ctx->decryptedData = sbufs[i].pvBuffer;
+				ctx->decryptedLen = sbufs[i].cbBuffer;
+			}
+		}
+		if (ctx->decryptedLen == 0)
+		{
+			/*
+			 * DecryptMessage didn't decrypt anything. Shift the extra data
+			 * to beginning of buffer, and retry.
+			 */
+			prepareDecryptBufferForRead(ctx);
+		}
+	}
+
+	return 1;
+}
+
+/**** Certificate handling functions ****/
+
+static char *
+slurpFile(const char *path, int *size)
+{
+	struct stat statbuf;
+	FILE	   *fp;
+	char	   *content;
+	int			r;
+
+	if (stat(path, &statbuf) != 0)
+	{
+		ereport(ERROR,
+				(errcode_for_file_access(),
+				 errmsg("could not stat certificate file \"%s\": %m",
+						path)));
+	}
+	fp = AllocateFile(path, "r");
+	if (!fp)
+	{
+		ereport(ERROR,
+				(errcode_for_file_access(),
+				 errmsg("could not read certificate file \"%s\": %m",
+						path)));
+	}
+	content = palloc(statbuf.st_size + 1);
+	r = fread(content, statbuf.st_size, 1, fp);
+	content[statbuf.st_size] = '\0';
+
+	if (r != 1 || ferror(fp) || FreeFile(fp))
+		ereport(ERROR,
+				(errcode_for_file_access(),
+				 errmsg("could not read file \"%s\": %m",
+						path)));
+
+	if (size)
+		*size = statbuf.st_size;
+
+	return content;
+}
+
+/*
+ * Import the server's private key into the key container.
+ */
+static void
+importServerKey(void)
+{
+	char	   *content;
+	int			filelen;
+	char		buf[50000];
+	DWORD		buflen = 50000;
+	char		privKeyBlob[50000];
+	DWORD		privKeyBlobLen = 50000;
+	HCRYPTPROV	cryptProvHandle;
+	HCRYPTKEY	keyHandle;
+
+	content = slurpFile(ssl_key_file, &filelen);
+
+	/* convert from PEM to DER */
+	buflen = 50000;
+	if (!CryptStringToBinary(content, filelen, CRYPT_STRING_BASE64HEADER,
+							 buf, &buflen, NULL, NULL))
+	{
+		elog(ERROR, "CryptStringToBinary (key) failed: 0x%x", GetLastError());		
+	}
+	pfree(content);
+
+	if (!CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
+							 PKCS_RSA_PRIVATE_KEY,
+							 buf, buflen,
+							 0,
+							 NULL,
+							 privKeyBlob,
+							 &privKeyBlobLen))
+	{
+		elog(ERROR, "CryptDecodeObjectEx (key) failed: 0x%x", GetLastError());
+	}
+
+	/* Open the key container */
+	if (!CryptAcquireContextW(&cryptProvHandle, PG_KEY_CONTAINER_NAME, MS_DEF_RSA_SCHANNEL_PROV_W, PROV_RSA_SCHANNEL, 0))
+	{
+		if (GetLastError() == NTE_BAD_KEYSET)
+		{
+			/*
+			 * The key container could not be accessed. Most probable cause
+			 * is that it doesn't exist, so try to create it.
+			 */
+			if (!CryptAcquireContextW(&cryptProvHandle, PG_KEY_CONTAINER_NAME, MS_DEF_RSA_SCHANNEL_PROV_W, PROV_RSA_SCHANNEL, CRYPT_NEWKEYSET))
+			{
+				elog(ERROR, "CryptAcquireContext failed: 0x%x", GetLastError());
+			}
+			else
+				elog(LOG, "SChannel key container created");
+		}
+		else
+			elog(ERROR, "CryptAcquireContext failed: 0x%x", GetLastError());
+	}
+
+	if (!CryptImportKey(cryptProvHandle, privKeyBlob, privKeyBlobLen,
+						(HCRYPTKEY) NULL, 0, &keyHandle))
+	{
+		elog(ERROR, "CryptImportKey failed: 0x%x", GetLastError());
+	}
+
+	CryptDestroyKey(keyHandle);
+
+	if (!CryptReleaseContext(cryptProvHandle, 0))
+		elog(ERROR, "CryptReleaseContext failed: 0x%x", GetLastError());
+}
+
+static CERT_CONTEXT *
+getServerCert(void)
+{
+	CERT_CONTEXT *cert_cxt;
+	char	   *content;
+	int			filelen;
+	char		buf[50000];
+	DWORD		buflen = 50000;
+	CRYPT_KEY_PROV_INFO keyProvInfo;
+
+	content = slurpFile(ssl_cert_file, &filelen);
+
+	/* convert from PEM to DER */
+	if (!CryptStringToBinary(content, filelen, CRYPT_STRING_BASE64HEADER,
+							 buf, &buflen, NULL, NULL))
+	{
+		elog(ERROR, "CryptStringToBinary failed: 0x%x", GetLastError());		
+	}
+
+	cert_cxt = CertCreateCertificateContext(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
+											buf,
+											buflen);
+	if (cert_cxt == NULL)
+	{
+		elog(ERROR, "CertCreateCertificateContext failed: 0x%x", GetLastError());
+	}
+	pfree(content);
+
+	/* Tell the Crypto API how to find the private key */
+	memset(&keyProvInfo, 0, sizeof(keyProvInfo));
+	keyProvInfo.pwszContainerName = PG_KEY_CONTAINER_NAME;
+	keyProvInfo.pwszProvName = MS_DEF_RSA_SCHANNEL_PROV_W;
+	keyProvInfo.dwProvType = PROV_RSA_SCHANNEL;
+	keyProvInfo.dwKeySpec = AT_KEYEXCHANGE;
+
+	if (!CertSetCertificateContextProperty(cert_cxt,
+										   CERT_KEY_PROV_INFO_PROP_ID,
+										   0,
+										   &keyProvInfo))
+	{
+		elog(ERROR, "CertSetCertificateContextProperty failed: %x", GetLastError());
+	}
+
+	return cert_cxt;
+}
+
+static bool
+validateClientCert(CERT_CONTEXT *cert)
+{
+	CERT_CONTEXT *rootCert;
+	DWORD flags;
+
+	/*
+	 * Per MSDN documentation [1], we must check the following things:
+	 *
+	 * 1. The certificate chain is complete and the root is a certificate from
+	 *    a trusted certification authority (CA).
+	 * 2. The current time is not beyond the begin and end dates for each of
+	 *    the certificates in the certificate chain.
+	 * 3. None of the certificates in the certificate chain have been revoked.
+	 * 4. The depth of the leaf certificate is not deeper than the maximum
+	 *    allowable depth specified in the certificate extension. This check is
+	 *    only necessary if there is a depth specified.
+	 * 5. The usage of the certificate is correct, for example, a client
+	 *    certificate should not be used to authenticate a server.
+	 *
+	 * [1] "Manually Validating Schannel Credential",
+	 *     http://msdn.microsoft.com/en-us/library/windows/desktop/aa378740%28v=vs.85%29.aspx
+	 */
+
+	/*
+	 * 1. Check the chain
+	 * 2. Check expiration
+	 *
+	 * TODO: Currently, we only support client certs signed by the root client
+	 * CA. Intermediary CA's are not supported.
+	 */
+	rootCert = getClientRootCert();
+
+	/* TODO: CRL's are not actually supported */
+	flags = CERT_STORE_SIGNATURE_FLAG |
+		CERT_STORE_TIME_VALIDITY_FLAG;
+	if (!CertVerifySubjectCertificateContext(
+			cert,
+			rootCert,
+			&flags))
+	{
+		elog(LOG, "failed to verify client cert");
+		return false;
+	}
+	if (flags != 0)
+	{
+		if ((flags & CERT_STORE_SIGNATURE_FLAG) != 0)
+			elog(LOG, "signature check failed on client cert");
+		if ((flags & CERT_STORE_REVOCATION_FLAG) != 0)
+			elog(LOG, "client cert was revoked");
+		if ((flags & CERT_STORE_TIME_VALIDITY_FLAG) != 0)
+			elog(LOG, "client cert is not valid at this time");
+
+		return false;
+	}
+
+	/*
+	 * We succeeded to get the issuer's certificate from the trusted root store,
+	 * and the signature on the client cert passed, so we're good to go.
+	 */
+
+	return true;
+}
+
+static CERT_CONTEXT *
+getClientRootCert(void)
+{
+	char	  *client_ca_file;
+	int			client_ca_file_len;
+	char		crtBuf[50000]; /* TODO: enlarge as needed */
+	DWORD		crtBufLen = 50000;
+	CERT_CONTEXT *client_root_cxt;
+
+	client_ca_file = slurpFile(ssl_ca_file, &client_ca_file_len);
+	if (client_ca_file == NULL)
+		return NULL;
+
+	/* convert from PEM to DER */
+	if (!CryptStringToBinary(
+			client_ca_file,
+			client_ca_file_len,
+			CRYPT_STRING_BASE64HEADER,
+			crtBuf,
+			&crtBufLen,
+			NULL,
+			NULL))
+	{
+		elog(ERROR, "failed to convert client root cert from PEM to DER format");
+	}
+
+	client_root_cxt = CertCreateCertificateContext(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
+												   crtBuf,
+												   crtBufLen);
+	if (client_root_cxt == NULL)
+		elog(ERROR, "CertCreateCertificateContext failed: 0x%x", GetLastError());
+
+	return client_root_cxt;
+}
+
+/*** Printing error codes ***/
+
+#define ERRORCODE_STR(x) { x, #x }
+
+typedef struct
+{
+	uint32 rc;
+	const char *str;
+} errorcode_str;
+
+static const char *
+getErrorStr(uint32 rc, errorcode_str *map)
+{
+	static char buf[100];
+	int i;
+
+	for (i = 0; map[i].str != NULL; i++)
+	{
+		if (map[i].rc == rc)
+			return map[i].str;
+	}
+	snprintf(buf, sizeof(buf), "0x%X", rc);
+	return buf;
+}
+
+static const char *
+getSecurityStatus(SECURITY_STATUS rc)
+{
+	static errorcode_str sec_status_codes[] = {
+		ERRORCODE_STR(SEC_E_OK),
+		ERRORCODE_STR(SEC_E_INCOMPLETE_MESSAGE),
+		ERRORCODE_STR(SEC_E_INSUFFICIENT_MEMORY),
+		ERRORCODE_STR(SEC_E_INTERNAL_ERROR),
+		ERRORCODE_STR(SEC_E_INVALID_HANDLE),
+		ERRORCODE_STR(SEC_E_INVALID_TOKEN),
+		ERRORCODE_STR(SEC_E_LOGON_DENIED),
+		ERRORCODE_STR(SEC_E_NO_AUTHENTICATING_AUTHORITY),
+		ERRORCODE_STR(SEC_E_NO_CREDENTIALS),
+		ERRORCODE_STR(SEC_I_COMPLETE_AND_CONTINUE),
+		ERRORCODE_STR(SEC_I_COMPLETE_NEEDED),
+		ERRORCODE_STR(SEC_I_CONTINUE_NEEDED),
+		ERRORCODE_STR(SEC_E_ILLEGAL_MESSAGE),
+		{ 0, NULL }
+	};
+	return getErrorStr(rc, sec_status_codes);
+}
diff --git a/src/backend/libpq/be-secure.c b/src/backend/libpq/be-secure.c
index 41ec1ad..fcd5ea2 100644
--- a/src/backend/libpq/be-secure.c
+++ b/src/backend/libpq/be-secure.c
@@ -103,6 +103,10 @@ secure_open_server(Port *port)
 	r = be_tls_open_server(port);
 #endif
 
+	ereport(DEBUG2,
+			(errmsg("SSL connection from \"%s\"",
+					port->peer_cn ? port->peer_cn : "(anonymous)")));
+
 	return r;
 }
 
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 34e52e4..7fcee98 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -53,6 +53,10 @@
 #include <security.h>
 #undef SECURITY_WIN32
 
+#ifdef USE_WINDOWS_SCHANNEL
+struct SchannelContext;
+#endif
+
 #ifndef ENABLE_GSS
 /*
  * Define a fake structure compatible with GSSAPI on Unix.
@@ -197,6 +201,9 @@ typedef struct Port
 	X509	   *peer;
 	unsigned long count;
 #endif
+#ifdef USE_WINDOWS_SCHANNEL
+	struct SchannelContext *windows_schannel;
+#endif
 } Port;
 
 #ifdef USE_SSL
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index 5bdfa47..e4d8574 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -802,6 +802,9 @@
 /* Define to select Win32-style shared memory. */
 #undef USE_WIN32_SHARED_MEMORY
 
+/* Define to build with Windows SChannel support. (--with-winschannel) */
+#undef USE_WINDOWS_SCHANNEL
+
 /* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most
    significant byte first (like Motorola and SPARC, unlike Intel). */
 #if defined AC_APPLE_UNIVERSAL_BUILD
diff --git a/src/include/pg_config.h.win32 b/src/include/pg_config.h.win32
index 00be15f..901de03 100644
--- a/src/include/pg_config.h.win32
+++ b/src/include/pg_config.h.win32
@@ -649,6 +649,9 @@
 /* Define to select Win32-style semaphores. */
 #define USE_WIN32_SEMAPHORES 1
 
+/* Define to build with Windows SChannel support. (--with-winschannel) */
+#undef USE_WINDOWS_SCHANNEL
+
 /* Number of bits in a file offset, on hosts where this is settable. */
 /* #undef _FILE_OFFSET_BITS */
 
diff --git a/src/include/pg_config_manual.h b/src/include/pg_config_manual.h
index d78f38e..54ed4b2 100644
--- a/src/include/pg_config_manual.h
+++ b/src/include/pg_config_manual.h
@@ -146,10 +146,9 @@
 
 /*
  * USE_SSL code should be compiled only when compiling with an SSL
- * implementation.  (Currently, only OpenSSL is supported, but we might add
- * more implementations in the future.)
+ * implementation.
  */
-#ifdef USE_OPENSSL
+#if defined (USE_OPENSSL) || defined(USE_WINDOWS_SCHANNEL)
 #define USE_SSL
 #endif
 
diff --git a/src/interfaces/libpq/Makefile b/src/interfaces/libpq/Makefile
index a90cb89..7968266 100644
--- a/src/interfaces/libpq/Makefile
+++ b/src/interfaces/libpq/Makefile
@@ -48,6 +48,10 @@ ifeq ($(with_openssl),yes)
 OBJS += fe-secure-openssl.o
 endif
 
+ifeq ($(with_winschannel),yes)
+OBJS += fe-secure-winschannel.o
+endif
+
 ifeq ($(PORTNAME), cygwin)
 override shlib = cyg$(NAME)$(DLSUFFIX)
 endif
diff --git a/src/interfaces/libpq/fe-secure-openssl.c b/src/interfaces/libpq/fe-secure-openssl.c
index f950fc3..cee7b2e 100644
--- a/src/interfaces/libpq/fe-secure-openssl.c
+++ b/src/interfaces/libpq/fe-secure-openssl.c
@@ -780,57 +780,21 @@ destroy_ssl_system(void)
 static int
 initialize_SSL(PGconn *conn)
 {
-	struct stat buf;
-	char		homedir[MAXPGPATH];
-	char		fnbuf[MAXPGPATH];
-	char		sebuf[256];
-	bool		have_homedir;
-	bool		have_cert;
 	EVP_PKEY   *pkey = NULL;
-
-	/*
-	 * We'll need the home directory if any of the relevant parameters are
-	 * defaulted.  If pqGetHomeDirectory fails, act as though none of the
-	 * files could be found.
-	 */
-	if (!(conn->sslcert && strlen(conn->sslcert) > 0) ||
-		!(conn->sslkey && strlen(conn->sslkey) > 0) ||
-		!(conn->sslrootcert && strlen(conn->sslrootcert) > 0) ||
-		!(conn->sslcrl && strlen(conn->sslcrl) > 0))
-		have_homedir = pqGetHomeDirectory(homedir, sizeof(homedir));
-	else	/* won't need it */
-		have_homedir = false;
-
-	/* Read the client certificate file */
-	if (conn->sslcert && strlen(conn->sslcert) > 0)
-		strncpy(fnbuf, conn->sslcert, sizeof(fnbuf));
-	else if (have_homedir)
-		snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, USER_CERT_FILE);
-	else
-		fnbuf[0] = '\0';
-
-	if (fnbuf[0] == '\0')
-	{
-		/* no home directory, proceed without a client cert */
-		have_cert = false;
-	}
-	else if (stat(fnbuf, &buf) != 0)
-	{
-		/*
-		 * If file is not present, just go on without a client cert; server
-		 * might or might not accept the connection.  Any other error,
-		 * however, is grounds for complaint.
-		 */
-		if (errno != ENOENT && errno != ENOTDIR)
-		{
-			printfPQExpBuffer(&conn->errorMessage,
-			   libpq_gettext("could not open certificate file \"%s\": %s\n"),
-							  fnbuf, pqStrerror(errno, sebuf, sizeof(sebuf)));
-			return -1;
-		}
-		have_cert = false;
-	}
-	else
+	char	   *sslcertfile = NULL;
+	char	   *engine = NULL;
+	char	   *keyname = NULL;
+	char	   *sslkeyfile = NULL;
+	char	   *sslrootcert = NULL;
+	char	   *sslcrl = NULL;
+	int			ret = -1;
+
+	if (!pqsecure_get_ssl_files(conn,
+								&sslcertfile, &sslkeyfile, &engine, &keyname,
+								&sslrootcert, &sslcrl))
+		return PGRES_POLLING_READING;
+
+	if (sslcertfile)
 	{
 		/*
 		 * Cert file exists, so load it.  Since OpenSSL doesn't provide the
@@ -855,216 +819,146 @@ initialize_SSL(PGconn *conn)
 		{
 			printfPQExpBuffer(&conn->errorMessage,
 			   libpq_gettext("could not acquire mutex: %s\n"), strerror(rc));
-			return -1;
+			goto fail;
 		}
 #endif
-		if (SSL_CTX_use_certificate_chain_file(SSL_context, fnbuf) != 1)
+		if (SSL_CTX_use_certificate_chain_file(SSL_context, sslcertfile) != 1)
 		{
 			char	   *err = SSLerrmessage();
 
 			printfPQExpBuffer(&conn->errorMessage,
 			   libpq_gettext("could not read certificate file \"%s\": %s\n"),
-							  fnbuf, err);
+							  sslcertfile, err);
 			SSLerrfree(err);
 
 #ifdef ENABLE_THREAD_SAFETY
 			pthread_mutex_unlock(&ssl_config_mutex);
 #endif
-			return -1;
+			goto fail;
 		}
 
-		if (SSL_use_certificate_file(conn->ssl, fnbuf, SSL_FILETYPE_PEM) != 1)
+		if (SSL_use_certificate_file(conn->ssl, sslcertfile, SSL_FILETYPE_PEM) != 1)
 		{
 			char	   *err = SSLerrmessage();
 
 			printfPQExpBuffer(&conn->errorMessage,
 			   libpq_gettext("could not read certificate file \"%s\": %s\n"),
-							  fnbuf, err);
+							  sslcertfile, err);
 			SSLerrfree(err);
 #ifdef ENABLE_THREAD_SAFETY
 			pthread_mutex_unlock(&ssl_config_mutex);
 #endif
-			return -1;
+			goto fail;
 		}
 
-		/* need to load the associated private key, too */
-		have_cert = true;
-
 #ifdef ENABLE_THREAD_SAFETY
 		pthread_mutex_unlock(&ssl_config_mutex);
 #endif
 	}
 
 	/*
-	 * Read the SSL key. If a key is specified, treat it as an engine:key
-	 * combination if there is colon present - we don't support files with
-	 * colon in the name. The exception is if the second character is a colon,
-	 * in which case it can be a Windows filename with drive specification.
+	 * If an engine:key specification was given, load that.
 	 */
-	if (have_cert && conn->sslkey && strlen(conn->sslkey) > 0)
+	if (engine)
 	{
 #ifdef USE_SSL_ENGINE
-		if (strchr(conn->sslkey, ':')
-#ifdef WIN32
-			&& conn->sslkey[1] != ':'
-#endif
-			)
+		conn->engine = ENGINE_by_id(engine);
+		if (conn->engine == NULL)
 		{
-			/* Colon, but not in second character, treat as engine:key */
-			char	   *engine_str = strdup(conn->sslkey);
-			char	   *engine_colon;
-
-			if (engine_str == NULL)
-			{
-				printfPQExpBuffer(&conn->errorMessage,
-								  libpq_gettext("out of memory\n"));
-				return -1;
-			}
-
-			/* cannot return NULL because we already checked before strdup */
-			engine_colon = strchr(engine_str, ':');
-
-			*engine_colon = '\0';		/* engine_str now has engine name */
-			engine_colon++;		/* engine_colon now has key name */
-
-			conn->engine = ENGINE_by_id(engine_str);
-			if (conn->engine == NULL)
-			{
-				char	   *err = SSLerrmessage();
+			char	   *err = SSLerrmessage();
 
-				printfPQExpBuffer(&conn->errorMessage,
+			printfPQExpBuffer(&conn->errorMessage,
 					 libpq_gettext("could not load SSL engine \"%s\": %s\n"),
-								  engine_str, err);
-				SSLerrfree(err);
-				free(engine_str);
-				return -1;
-			}
+							  engine, err);
+			SSLerrfree(err);
+			goto fail;
+		}
 
-			if (ENGINE_init(conn->engine) == 0)
-			{
-				char	   *err = SSLerrmessage();
+		if (ENGINE_init(conn->engine) == 0)
+		{
+			char	   *err = SSLerrmessage();
 
-				printfPQExpBuffer(&conn->errorMessage,
+			printfPQExpBuffer(&conn->errorMessage,
 				libpq_gettext("could not initialize SSL engine \"%s\": %s\n"),
-								  engine_str, err);
-				SSLerrfree(err);
-				ENGINE_free(conn->engine);
-				conn->engine = NULL;
-				free(engine_str);
-				return -1;
-			}
-
-			pkey = ENGINE_load_private_key(conn->engine, engine_colon,
-										   NULL, NULL);
-			if (pkey == NULL)
-			{
-				char	   *err = SSLerrmessage();
-
-				printfPQExpBuffer(&conn->errorMessage,
-								  libpq_gettext("could not read private SSL key \"%s\" from engine \"%s\": %s\n"),
-								  engine_colon, engine_str, err);
-				SSLerrfree(err);
-				ENGINE_finish(conn->engine);
-				ENGINE_free(conn->engine);
-				conn->engine = NULL;
-				free(engine_str);
-				return -1;
-			}
-			if (SSL_use_PrivateKey(conn->ssl, pkey) != 1)
-			{
-				char	   *err = SSLerrmessage();
-
-				printfPQExpBuffer(&conn->errorMessage,
-								  libpq_gettext("could not load private SSL key \"%s\" from engine \"%s\": %s\n"),
-								  engine_colon, engine_str, err);
-				SSLerrfree(err);
-				ENGINE_finish(conn->engine);
-				ENGINE_free(conn->engine);
-				conn->engine = NULL;
-				free(engine_str);
-				return -1;
-			}
-
-			free(engine_str);
-
-			fnbuf[0] = '\0';	/* indicate we're not going to load from a
-								 * file */
-		}
-		else
-#endif   /* USE_SSL_ENGINE */
-		{
-			/* PGSSLKEY is not an engine, treat it as a filename */
-			strncpy(fnbuf, conn->sslkey, sizeof(fnbuf));
+							  engine, err);
+			SSLerrfree(err);
+			ENGINE_free(conn->engine);
+			conn->engine = NULL;
+			goto fail;
 		}
-	}
-	else if (have_homedir)
-	{
-		/* No PGSSLKEY specified, load default file */
-		snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, USER_KEY_FILE);
-	}
-	else
-		fnbuf[0] = '\0';
 
-	if (have_cert && fnbuf[0] != '\0')
-	{
-		/* read the client key from file */
-
-		if (stat(fnbuf, &buf) != 0)
+		pkey = ENGINE_load_private_key(conn->engine, keyname, NULL, NULL);
+		if (pkey == NULL)
 		{
+			char	   *err = SSLerrmessage();
+
 			printfPQExpBuffer(&conn->errorMessage,
-							  libpq_gettext("certificate present, but not private key file \"%s\"\n"),
-							  fnbuf);
-			return -1;
+							  libpq_gettext("could not read private SSL key \"%s\" from engine \"%s\": %s\n"),
+							  keyname, engine, err);
+			SSLerrfree(err);
+			ENGINE_finish(conn->engine);
+			ENGINE_free(conn->engine);
+			conn->engine = NULL;
+			goto fail;
 		}
-#ifndef WIN32
-		if (!S_ISREG(buf.st_mode) || buf.st_mode & (S_IRWXG | S_IRWXO))
+		if (SSL_use_PrivateKey(conn->ssl, pkey) != 1)
 		{
+			char	   *err = SSLerrmessage();
+
 			printfPQExpBuffer(&conn->errorMessage,
-							  libpq_gettext("private key file \"%s\" has group or world access; permissions should be u=rw (0600) or less\n"),
-							  fnbuf);
-			return -1;
+							  libpq_gettext("could not load private SSL key \"%s\" from engine \"%s\": %s\n"),
+							  keyname, engine, err);
+			SSLerrfree(err);
+			ENGINE_finish(conn->engine);
+			ENGINE_free(conn->engine);
+			conn->engine = NULL;
+			goto fail;
 		}
-#endif
+#else
+		/*
+		 * should not happen; pqsecure_get_ssl_files doesn't return an
+		 * engine spec if not compiled with USE_SSL_ENGINE.
+		 */
+		printfPQExpBuffer(&conn->errorMessage,
+						  libpq_gettext("engine not supported\n"));
+		goto fail;
+#endif   /* USE_SSL_ENGINE */
+	}
 
-		if (SSL_use_PrivateKey_file(conn->ssl, fnbuf, SSL_FILETYPE_PEM) != 1)
+	/* Read the client private key file */
+	if (sslkeyfile)
+	{
+		if (SSL_use_PrivateKey_file(conn->ssl, sslkeyfile, SSL_FILETYPE_PEM) != 1)
 		{
 			char	   *err = SSLerrmessage();
 
 			printfPQExpBuffer(&conn->errorMessage,
 			   libpq_gettext("could not load private key file \"%s\": %s\n"),
-							  fnbuf, err);
+							  sslkeyfile, err);
 			SSLerrfree(err);
-			return -1;
+			goto fail;
 		}
 	}
 
-	/* verify that the cert and key go together */
-	if (have_cert &&
+	/* Verify that the cert and key go together */
+	if (sslcertfile &&
 		SSL_check_private_key(conn->ssl) != 1)
 	{
 		char	   *err = SSLerrmessage();
 
 		printfPQExpBuffer(&conn->errorMessage,
 						  libpq_gettext("certificate does not match private key file \"%s\": %s\n"),
-						  fnbuf, err);
+						  sslkeyfile, err);
 		SSLerrfree(err);
-		return -1;
+		goto fail;
 	}
 
 	/*
-	 * If the root cert file exists, load it so we can perform certificate
-	 * verification. If sslmode is "verify-full" we will also do further
-	 * verification after the connection has been completed.
+	 * Load root cert, if exists. (pqsecure_get_ssl_files already complained
+	 * if no root cert was configured but sslmode requires verification
+	 * of server certificates.)
 	 */
-	if (conn->sslrootcert && strlen(conn->sslrootcert) > 0)
-		strncpy(fnbuf, conn->sslrootcert, sizeof(fnbuf));
-	else if (have_homedir)
-		snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, ROOT_CERT_FILE);
-	else
-		fnbuf[0] = '\0';
-
-	if (fnbuf[0] != '\0' &&
-		stat(fnbuf, &buf) == 0)
+	if (sslrootcert)
 	{
 		X509_STORE *cvstore;
 
@@ -1075,35 +969,28 @@ initialize_SSL(PGconn *conn)
 		{
 			printfPQExpBuffer(&conn->errorMessage,
 			   libpq_gettext("could not acquire mutex: %s\n"), strerror(rc));
-			return -1;
+			goto fail;
 		}
 #endif
-		if (SSL_CTX_load_verify_locations(SSL_context, fnbuf, NULL) != 1)
+		if (SSL_CTX_load_verify_locations(SSL_context, sslrootcert, NULL) != 1)
 		{
 			char	   *err = SSLerrmessage();
 
 			printfPQExpBuffer(&conn->errorMessage,
 							  libpq_gettext("could not read root certificate file \"%s\": %s\n"),
-							  fnbuf, err);
+							  sslrootcert, err);
 			SSLerrfree(err);
 #ifdef ENABLE_THREAD_SAFETY
 			pthread_mutex_unlock(&ssl_config_mutex);
 #endif
-			return -1;
+			goto fail;
 		}
 
 		if ((cvstore = SSL_CTX_get_cert_store(SSL_context)) != NULL)
 		{
-			if (conn->sslcrl && strlen(conn->sslcrl) > 0)
-				strncpy(fnbuf, conn->sslcrl, sizeof(fnbuf));
-			else if (have_homedir)
-				snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, ROOT_CRL_FILE);
-			else
-				fnbuf[0] = '\0';
-
 			/* Set the flags to check against the complete CRL chain */
-			if (fnbuf[0] != '\0' &&
-				X509_STORE_load_locations(cvstore, fnbuf, NULL) == 1)
+			if (sslcrl &&
+				X509_STORE_load_locations(cvstore, sslcrl, NULL) == 1)
 			{
 				/* OpenSSL 0.96 does not support X509_V_FLAG_CRL_CHECK */
 #ifdef X509_V_FLAG_CRL_CHECK
@@ -1114,12 +1001,12 @@ initialize_SSL(PGconn *conn)
 
 				printfPQExpBuffer(&conn->errorMessage,
 								  libpq_gettext("SSL library does not support CRL certificates (file \"%s\")\n"),
-								  fnbuf);
+								  sslcrl);
 				SSLerrfree(err);
 #ifdef ENABLE_THREAD_SAFETY
 				pthread_mutex_unlock(&ssl_config_mutex);
 #endif
-				return -1;
+				goto fail;
 #endif
 			}
 			/* if not found, silently ignore;  we do not require CRL */
@@ -1130,31 +1017,6 @@ initialize_SSL(PGconn *conn)
 
 		SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, verify_cb);
 	}
-	else
-	{
-		/*
-		 * stat() failed; assume root file doesn't exist.  If sslmode is
-		 * verify-ca or verify-full, this is an error.  Otherwise, continue
-		 * without performing any server cert verification.
-		 */
-		if (conn->sslmode[0] == 'v')	/* "verify-ca" or "verify-full" */
-		{
-			/*
-			 * The only way to reach here with an empty filename is if
-			 * pqGetHomeDirectory failed.  That's a sufficiently unusual case
-			 * that it seems worth having a specialized error message for it.
-			 */
-			if (fnbuf[0] == '\0')
-				printfPQExpBuffer(&conn->errorMessage,
-								  libpq_gettext("could not get home directory to locate root certificate file\n"
-												"Either provide the file or change sslmode to disable server certificate verification.\n"));
-			else
-				printfPQExpBuffer(&conn->errorMessage,
-				libpq_gettext("root certificate file \"%s\" does not exist\n"
-							  "Either provide the file or change sslmode to disable server certificate verification.\n"), fnbuf);
-			return -1;
-		}
-	}
 
 	/*
 	 * If the OpenSSL version used supports it (from 1.0.0 on) and the user
@@ -1167,9 +1029,27 @@ initialize_SSL(PGconn *conn)
 	}
 #endif
 
-	return 0;
+	/* success */
+	ret = 0;
+
+fail:
+	if (sslcertfile)
+		free(sslcertfile);
+	if (engine)
+		free(engine);
+	if (keyname)
+		free(keyname);
+	if (sslkeyfile)
+		free(sslkeyfile);
+	if (sslrootcert)
+		free(sslrootcert);
+	if (sslcrl)
+		free(sslcrl);
+
+	return ret;
 }
 
+
 /*
  *	Attempt to negotiate SSL connection.
  */
diff --git a/src/interfaces/libpq/fe-secure-winschannel.c b/src/interfaces/libpq/fe-secure-winschannel.c
new file mode 100644
index 0000000..4af5936
--- /dev/null
+++ b/src/interfaces/libpq/fe-secure-winschannel.c
@@ -0,0 +1,1353 @@
+/*-------------------------------------------------------------------------
+ *
+ * fe-secure-winschannel.c
+ *	  Windows Schannel SSL implementation
+ *
+ * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  src/interfaces/libpq/fe-secure-winschannel.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres_fe.h"
+
+#include <ctype.h>
+#include <fcntl.h>
+
+#include "libpq-fe.h"
+#include "libpq-int.h"
+
+#include "win32.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <sspi.h>
+#include <schnlsp.h>
+
+#define SCHANNEL_DEBUG
+
+typedef enum
+{
+	ISC_INIT,		/* before first call to InitializeSecurityContext */
+	ISC_NEED_READ,	/* need to pass more input to InitializeSecurityContext */
+	ISC_NEED_WRITE, /* need to write data to client */
+	ISC_FINAL_WRITE, /* handshake complete after sending last output to client */
+	ISC_COMPLETED	/* handshake complete */
+} HandshakeState;
+
+struct SchannelContext
+{
+	/* SChannel handle for the connection */
+	CtxtHandle  ctxHandle;
+	bool		ctxHandle_valid;
+	CredHandle	credHandle;
+	bool		credHandle_valid;
+
+	/* client certificate to send to server */
+	CERT_CONTEXT *clientCert;
+	/* Root certificate for validating server certificates */
+	CERT_CONTEXT *rootCert;
+
+	HandshakeState handshakeState;
+	bool		remote_shutdown;
+
+	/*
+	 * Buffer for encrypting data to be written.
+	 *
+	 * When encryptedLen > 0, the buffer contains some encrypted data that
+	 * should be written out to the client.
+	 *
+	 * When encryptedLen == 0, call encryptBuffer() to load the buffer with
+	 * more data, and encrypt it.
+	 */
+	char	   *encryptBuffer;
+	int			encryptBufferSize;
+	int			encryptBufferLen;
+
+	char	   *encryptedData;
+	size_t		encryptedLen;
+
+	/*
+	 * Number of plaintext bytes the buffer currently holds in encrypted form.
+	 * This is less than the number of encrypted bytes, because the SSL
+	 * protocol adds framing. (it could also be more, when compression is used)
+	 */
+	int			plaintextLen;
+
+	/*
+	 * Buffer for decrypting read data.
+	 *
+	 * decryptBufferSize is the allocated size of decryptBuffer.
+	 * decryptBufferLen is the number of ciphertext bytes loaded to the buffer.
+	 *
+	 * The buffer can be in one of two states. In the first state, it contains
+	 * zero or more raw bytes, and more can be loaded into it. In the other
+	 * state, it contains decrypted data that can be read out.
+	 *
+	 * When the buffer contains decrypted data, decryptedLen > 0, and
+	 * decryptedData points to the data. The buffer can also contain some
+	 * leftover raw data that has not been decrypted yet (extraData/extraLen).
+	 *
+	 * Before reading more raw data to the buffer, call
+	 * prepareDecryptBufferForRead. It moves any leftover raw data to the
+	 * beginning of the buffer.
+	 */
+	char	   *decryptBuffer;
+	int			decryptBufferSize;
+	int			decryptBufferLen; 	/* ciphertext bytes loaded in decryptBuffer */
+
+	/* decrypted data not returned to the caller yet */
+	char	   *decryptedData;
+	int			decryptedLen;
+
+	char	   *extraData;
+	int			extraLen;
+};
+
+typedef struct SchannelContext SchannelContext;
+
+/*
+ * Somewhat arbitrarily, use a 10k buffer for both input and output.
+ */
+#define BUFSIZE 10000
+
+/* Functions for managing the encrypt/decrypt buffers */
+static ssize_t encryptBuffer(PGconn *conn, const void *ptr, int len);
+static void prepareDecryptBufferForRead(SchannelContext *ctx);
+static int decryptBuffer(PGconn *conn);
+
+/* Functions for dealing with certificates */
+static bool loadCerts(PGconn *conn);
+static bool validateServerCert(PGconn *conn);
+
+/* Functions for converting error codes to strings */
+static const char *getSSLPolicyStatusCode(DWORD dwerror);
+static const char *getSecurityStatus(SECURITY_STATUS dwerror);
+
+void
+pgtls_init_library(bool do_ssl, int do_crypto)
+{
+}
+
+
+/*
+ * Initialize SSL system, in particular creating the SSL_context object
+ * that will be shared by all SSL-using connections in this process.
+ *
+ * The conn parameter is only used to be able to pass back an error
+ * message - no connection-local setup is made here.
+ *
+ * Returns 0 if OK, -1 on failure (with a message in conn->errorMessage).
+ */
+int
+pgtls_init(PGconn *conn)
+{
+	/* nothing to do here ATM */
+	return 0;
+}
+
+
+/*
+ *	Begin or continue negotiating a secure session.
+ */
+PostgresPollingStatusType
+pgtls_open_client(PGconn *conn)
+{
+	SchannelContext *ctx = conn->windows_schannel;
+	SecBufferDesc outsbufdesc;
+	SecBuffer	outsbufs[1];
+	SecBufferDesc insbufdesc;
+	SecBuffer	insbufs[2];
+	SECURITY_STATUS rc;
+	DWORD		flags;
+	DWORD		outflags;
+	int			n;
+
+#ifdef SCHANNEL_DEBUG
+	fprintf(stderr, "pgtls_open_client called: %d\n", ctx ? ctx->handshakeState : -1);
+	fflush(stderr);
+#endif
+
+	/* On first call, allocate the context struct and buffers */
+	if (ctx == NULL)
+	{
+		ctx = (SchannelContext *) malloc(sizeof(SchannelContext));
+		if (ctx == NULL)
+		{
+			pgtls_close(conn);
+			printfPQExpBuffer(&conn->errorMessage,
+							  libpq_gettext("out of memory\n"));
+			return PGRES_POLLING_FAILED;
+		}
+		memset(ctx, 0, sizeof(SchannelContext));
+		conn->windows_schannel = ctx;
+
+		ctx->encryptBufferSize = BUFSIZE;
+		ctx->encryptBuffer = malloc(ctx->encryptBufferSize);
+		if (ctx->encryptBuffer == NULL)
+		{
+			pgtls_close(conn);
+			printfPQExpBuffer(&conn->errorMessage,
+							  libpq_gettext("out of memory\n"));
+			return PGRES_POLLING_FAILED;
+		}
+
+		ctx->decryptBufferSize = BUFSIZE;
+		ctx->decryptBuffer = malloc(ctx->encryptBufferSize);
+		if (ctx->decryptBuffer == NULL)
+		{
+			pgtls_close(conn);
+			printfPQExpBuffer(&conn->errorMessage,
+							  libpq_gettext("out of memory\n"));
+			return PGRES_POLLING_FAILED;
+		}
+
+		ctx->handshakeState = ISC_INIT;
+
+		conn->windows_schannel = ctx;
+		conn->ssl_in_use = true;
+	}
+
+	if (!ctx->credHandle_valid)
+	{
+		SCHANNEL_CRED credData;
+
+		if (!loadCerts(conn))
+		{
+			pgtls_close(conn);
+			return PGRES_POLLING_FAILED;
+		}
+
+		memset(&credData, 0, sizeof(credData));
+		credData.dwVersion = SCHANNEL_CRED_VERSION;
+		credData.dwFlags |= SCH_CRED_NO_SERVERNAME_CHECK |
+			SCH_CRED_NO_DEFAULT_CREDS |
+			SCH_CRED_MANUAL_CRED_VALIDATION;
+		if (ctx->clientCert)
+		{
+			credData.cCreds = 1;
+			credData.paCred = &ctx->clientCert;
+		}
+		else
+			credData.cCreds = 0;
+
+		/* FIXME: check server cert */
+
+#ifdef SCHANNEL_DEBUG
+		fprintf(stderr, "calling AcquireCredentialsHandle\n");
+		fflush(stderr);
+#endif
+		rc = AcquireCredentialsHandle(NULL,
+									  UNISP_NAME,
+									  SECPKG_CRED_OUTBOUND,
+									  NULL,
+									  &credData,
+									  NULL,
+									  NULL,
+									  &ctx->credHandle,
+									  NULL);
+		if (rc != SEC_E_OK)
+		{
+			pgtls_close(conn);
+			printfPQExpBuffer(&conn->errorMessage,
+							  libpq_gettext("could not acquire credentials handle: %s\n"),
+							  getSecurityStatus(rc));
+			return PGRES_POLLING_FAILED;
+		}
+		ctx->credHandle_valid = true;
+
+#ifdef SCHANNEL_DEBUG
+		fprintf(stderr, "AcquireCredentialsHandle succeeded\n");
+		fflush(stderr);
+#endif
+	}
+
+	/* If we have any pending outgoing data, flush that first */
+	if (ctx->handshakeState == ISC_NEED_WRITE ||
+		ctx->handshakeState == ISC_FINAL_WRITE)
+	{
+#ifdef SCHANNEL_DEBUG
+		fprintf(stderr, "Sending %d handshake bytes\n",
+				ctx->encryptedLen);
+		fflush(stderr);
+#endif
+		n = pqsecure_raw_write(conn, ctx->encryptBuffer, ctx->encryptedLen);
+		if (n < 0)
+		{
+			int result_errno = SOCK_ERRNO;
+			if (result_errno == EINTR
+#ifdef EAGAIN
+				|| result_errno == EAGAIN
+#endif
+#ifdef EWOULDBLOCK
+				|| result_errno == EWOULDBLOCK)
+#endif
+			{
+				return PGRES_POLLING_WRITING;
+			}
+			else
+			{
+				pgtls_close(conn);
+				/* pqsecure_raw_write already set error message */
+				return PGRES_POLLING_FAILED;
+			}
+		}
+		ctx->encryptedData += n;
+		ctx->encryptedLen -= n;
+		if (ctx->encryptedLen > 0)
+			return PGRES_POLLING_WRITING;
+		else
+		{
+			if (ctx->handshakeState == ISC_NEED_WRITE)
+			{
+				/*
+				 * Successfully sent the data to client that
+				 * InitializeSecurityContext asked us to sent. Now wait for
+				 * a reply.
+				 */
+				ctx->handshakeState = ISC_NEED_READ;
+			}
+			else
+			{
+				ctx->handshakeState = ISC_COMPLETED;
+				return PGRES_POLLING_OK;
+			}
+		}
+	}
+
+	/* call InitializeSecurityContext */
+
+	flags = ISC_REQ_ALLOCATE_MEMORY |
+		ISC_REQ_CONFIDENTIALITY |
+		ISC_RET_EXTENDED_ERROR |
+		ISC_REQ_REPLAY_DETECT |
+		ISC_REQ_SEQUENCE_DETECT |
+		ISC_REQ_STREAM |
+		ISC_REQ_MANUAL_CRED_VALIDATION;
+
+	outsbufs[0].pvBuffer   = NULL;
+	outsbufs[0].BufferType = SECBUFFER_TOKEN;
+	outsbufs[0].cbBuffer   = 0;
+
+	outsbufdesc.cBuffers = 1;
+	outsbufdesc.pBuffers = outsbufs;
+	outsbufdesc.ulVersion = SECBUFFER_VERSION;
+
+	/* Read some data if needed */
+	if (ctx->handshakeState == ISC_NEED_READ)
+	{
+		int			maxsize;
+		int			readlen;
+
+		/* read more data to buffer */
+		maxsize = ctx->decryptBufferSize - ctx->decryptBufferLen;
+#ifdef SCHANNEL_DEBUG
+		fprintf(stderr, "reading maxsize %d buflen %d\n", maxsize, ctx->decryptBufferLen);
+		fflush(stderr);
+#endif
+		readlen = pqsecure_raw_read(conn,
+									&ctx->decryptBuffer[ctx->decryptBufferLen],
+									maxsize);
+		fprintf(stderr, "received %d bytes in handshake\n", readlen);
+		fflush(stderr);
+		if (readlen < 0)
+		{
+			int result_errno = SOCK_ERRNO;
+			if (result_errno == EINTR
+#ifdef EAGAIN
+				|| result_errno == EAGAIN
+#endif
+#ifdef EWOULDBLOCK
+				|| result_errno == EWOULDBLOCK)
+#endif
+			{
+				return PGRES_POLLING_READING;
+			}
+
+			pgtls_close(conn);
+			/* pqsecure_raw_read set error message */
+			return PGRES_POLLING_FAILED;
+		}
+
+		ctx->decryptBufferLen += readlen;
+	}
+
+	/*
+	 * On first call, don't pass input buffer. The function will return
+	 * a context. On subsequent calls, pass any input data in a buffer,
+	 * and pass the handle we got on first call.
+	 */
+	if (ctx->handshakeState == ISC_INIT)
+	{
+		rc = InitializeSecurityContext(&ctx->credHandle,
+									   NULL,
+									   NULL,
+									   flags,
+									   0,
+									   0,
+									   NULL,
+									   0,
+									   &ctx->ctxHandle,
+									   &outsbufdesc,
+									   &outflags,
+									   NULL);
+	}
+	else
+	{
+		insbufs[0].pvBuffer = ctx->decryptBuffer;
+		insbufs[0].cbBuffer = ctx->decryptBufferLen;
+		insbufs[0].BufferType = SECBUFFER_TOKEN;
+
+		insbufs[1].pvBuffer = NULL;
+		insbufs[1].cbBuffer = 0;
+		insbufs[1].BufferType = SECBUFFER_EMPTY;
+
+		insbufdesc.cBuffers = 2;
+		insbufdesc.pBuffers = insbufs;
+		insbufdesc.ulVersion = SECBUFFER_VERSION;
+
+		rc = InitializeSecurityContext(&ctx->credHandle,
+									   &ctx->ctxHandle,
+									   NULL,
+									   flags,
+									   0,
+									   0,
+									   &insbufdesc,
+									   0,
+									   NULL,
+									   &outsbufdesc,
+									   &outflags,
+									   NULL);
+		/*
+		 * The following return codes are considered success, in the sense
+		 * that a context was created and must eventually be destroyed to
+		 * avoid leaking memory.
+		 */
+		if (rc == SEC_I_COMPLETE_AND_CONTINUE ||
+			rc == SEC_I_COMPLETE_NEEDED ||
+			rc == SEC_I_CONTINUE_NEEDED ||
+			rc == SEC_I_INCOMPLETE_CREDENTIALS ||
+			rc == SEC_E_INCOMPLETE_MESSAGE ||
+			rc == SEC_E_OK)
+		{
+			ctx->ctxHandle_valid = true;
+		}
+	}
+#ifdef SCHANNEL_DEBUG
+	fprintf(stderr, "InitializeSecurityContext returned %x\n", (unsigned int) rc);
+	fflush(stderr);
+#endif
+
+	if (rc == SEC_E_OK || rc == SEC_I_CONTINUE_NEEDED)
+	{
+		size_t outLen;
+
+		/*
+		 * InitializeSecurityContext might not have consumed all of the input
+		 * data.
+		 */
+		if (ctx->handshakeState == ISC_NEED_READ)
+		{
+			if (insbufs[1].BufferType == SECBUFFER_EXTRA)
+			{
+				int extraLen = insbufs[1].cbBuffer;
+
+				memmove(ctx->decryptBuffer,
+						&ctx->decryptBuffer[ctx->decryptBufferLen - extraLen],
+						extraLen);
+				ctx->decryptBufferLen = extraLen;
+			}
+			else
+				ctx->decryptBufferLen = 0;
+		}
+
+		/*
+		 * Must send the token that InitializeSecurityContext put in the
+		 * output buffer (if any).
+		 */
+		outLen = outsbufs[0].cbBuffer;
+		if (outLen > 0)
+		{
+			/* enlarge the output buffer if required. */
+			if (outLen > ctx->encryptBufferSize)
+			{
+				char	   *newbuf;
+				newbuf = realloc(ctx->encryptBuffer, outsbufs[0].cbBuffer);
+				if (newbuf == NULL)
+				{
+					pgtls_close(conn);
+					printfPQExpBuffer(&conn->errorMessage,
+									  libpq_gettext("out of memory\n"));
+					return PGRES_POLLING_FAILED;
+				}
+				ctx->encryptBuffer = newbuf;
+				ctx->encryptBufferSize = outLen;
+			}
+			memcpy(ctx->encryptBuffer, outsbufs[0].pvBuffer, outLen);
+			ctx->encryptedData = ctx->encryptBuffer;
+			ctx->encryptedLen = outLen;
+
+			FreeContextBuffer(outsbufs[0].pvBuffer);
+		}
+	}
+
+	switch(rc)
+	{
+		case SEC_E_OK:
+			/*
+			 * SSL handshake is complete. But we might still have some
+			 * data pending in the output buffer.
+			 */
+			if (!validateServerCert(conn))
+			{
+				pgtls_close(conn);
+				return PGRES_POLLING_FAILED;
+			}
+
+			if (ctx->encryptedLen > 0)
+			{
+				ctx->handshakeState = ISC_FINAL_WRITE;
+				return PGRES_POLLING_WRITING;
+			}
+
+			return PGRES_POLLING_OK;
+
+		case SEC_I_COMPLETE_AND_CONTINUE:
+			pgtls_close(conn);
+			printfPQExpBuffer(&conn->errorMessage,
+							  "SEC_I_COMPLETE_AND_CONTINUE not implemented\n", rc);
+			return PGRES_POLLING_FAILED;
+
+		case SEC_I_COMPLETE_NEEDED:
+			pgtls_close(conn);
+			printfPQExpBuffer(&conn->errorMessage,
+							  "SEC_I_COMPLETE_NEEDED not implemented\n", rc);
+			return PGRES_POLLING_FAILED;
+
+		case SEC_I_CONTINUE_NEEDED:
+			/*
+			 * Write the output data that InitializeSecurityContext returned,
+			 * and wait for reply.
+			 */
+			ctx->handshakeState = ISC_NEED_WRITE;
+			return PGRES_POLLING_WRITING;
+
+		case SEC_I_INCOMPLETE_CREDENTIALS:
+			pgtls_close(conn);
+			printfPQExpBuffer(&conn->errorMessage,
+							  "SEC_I_INCOMPLETE_CREDENTIALS not implemented\n", rc);
+			return PGRES_POLLING_FAILED;
+
+		case SEC_E_INCOMPLETE_MESSAGE:
+			/* read more input data and retry */
+			ctx->handshakeState = ISC_NEED_READ;
+			return PGRES_POLLING_READING;
+
+		default:
+			pgtls_close(conn);
+			printfPQExpBuffer(&conn->errorMessage,
+							  "InitializeSecurityContext failed: %s\n",
+							  getSecurityStatus(rc));
+			return PGRES_POLLING_FAILED;
+	}
+	/* not reached */
+
+fail:
+	pgtls_close(conn);
+	return PGRES_POLLING_FAILED;
+}
+
+/*
+ *	Close SSL connection.
+ */
+void
+pgtls_close(PGconn *conn)
+{
+	SchannelContext *ctx = conn->windows_schannel;
+
+	/* TODO: should do a proper SSL shutdown. */
+
+	if (ctx)
+	{
+		if (ctx->encryptBuffer)
+			free(ctx->encryptBuffer);
+		if (ctx->decryptBuffer)
+			free(ctx->decryptBuffer);
+
+		if (ctx->credHandle_valid)
+			FreeCredentialsHandle(&ctx->credHandle);
+
+		if (ctx->ctxHandle_valid)
+			DeleteSecurityContext(&ctx->ctxHandle);
+
+		free(ctx);
+		conn->windows_schannel = NULL;
+	}
+	conn->ssl_in_use = false;
+}
+
+/*
+ *	Read data from a secure connection.
+ *
+ * On failure, this function is responsible for putting a suitable message
+ * into conn->errorMessage.  The caller must still inspect errno, but only
+ * to determine whether to continue/retry after error.
+ */
+ssize_t
+pgtls_read(PGconn *conn, char *buffer, size_t len)
+{
+	SchannelContext *ctx = conn->windows_schannel;
+	int			readlen;
+	int			maxsize;
+	size_t		totalreadlen = 0;
+	char	   *dst = buffer;
+
+	for (;;)
+	{
+		/* if we already have some decrypted data, copy it to the target buffer */
+		if (ctx->decryptedLen > 0)
+		{
+			maxsize = len - totalreadlen;
+			if (maxsize > ctx->decryptedLen)
+				maxsize = ctx->decryptedLen;
+			memcpy(dst, &ctx->decryptedData[totalreadlen], maxsize);
+			dst += maxsize;
+			totalreadlen += maxsize;
+			ctx->decryptedLen -= maxsize;
+			ctx->decryptedData += maxsize;
+		}
+
+		/* If we have some data to return, return it. */
+		if (totalreadlen > 0)
+		{
+			return totalreadlen;
+		}
+
+		/*
+		 * there shouldn't be any decrypted data left in the buffer, we copied
+		 * it all to the target.
+		 */
+		Assert(ctx->decryptedLen == 0);
+
+		/*
+		 * Need to decrypt more data, and for that, need to read more raw
+		 * data.
+		 */
+		prepareDecryptBufferForRead(ctx);
+		while (ctx->decryptedLen == 0)
+		{
+			/* read more data to buffer */
+			maxsize = ctx->decryptBufferSize - ctx->decryptBufferLen;
+			readlen = pqsecure_raw_read(conn,
+										&ctx->decryptBuffer[ctx->decryptBufferLen],
+										maxsize);
+			if (readlen < 0)
+				return readlen;
+			if (readlen == 0)
+			{
+				/* this implies we're in non-blocking mode */
+				return totalreadlen;
+			}
+
+			ctx->decryptBufferLen += readlen;
+
+			/* decrypt what we got */
+			if (decryptBuffer(conn) == -1)
+				return -1;
+			if (ctx->remote_shutdown)
+				return totalreadlen;
+
+			/* If decryptBuffer needs more data, decrypteLen is still 0. */
+		}
+	}
+
+	return totalreadlen;
+}
+
+
+/*
+ * Prepare the buffer for reading in more cipher text.
+ */
+static void
+prepareDecryptBufferForRead(SchannelContext *ctx)
+{
+	/*
+	 * should've consumed all the previous decrypted data in the buffer befor
+	 * decrypting more.
+	 */
+	Assert(ctx->decryptedLen == 0);
+
+	/*
+	 * If there's any ciphertext left in the buffer from previous round, move
+	 * it to beginning.
+	 */
+	if (ctx->extraLen > 0)
+		memmove(ctx->decryptBuffer,	ctx->extraData,	ctx->extraLen);
+	ctx->decryptBufferLen = ctx->extraLen;
+	ctx->extraData = NULL;
+	ctx->extraLen = 0;
+	ctx->decryptedData = NULL;
+	ctx->decryptedLen = 0;
+}
+
+/*
+ * Try to decrypt the data currently in decryptBuffer. On success,
+ * decryptedData points to the decrypted data. DecryptMessage decrypts
+ * in-place, so decryptedData points to somewhere within the buffer!
+ *
+ * On success, returns 1. On error, returns -1. If more raw data is needed
+ * to decrypt, returns 0. If the client shut down the connection, also
+ * returns 0, and sets ctx->remote_shutdown flag.
+ */
+static int
+decryptBuffer(PGconn *conn)
+{
+	SchannelContext *ctx = conn->windows_schannel;
+	/*
+	 * Call DecryptBuffer until it returns some data, fails, or indicates that
+	 * it needs more input
+	 */
+	while(ctx->decryptedLen == 0)
+	{
+		SecBufferDesc sbufdesc;
+		SecBuffer	sbufs[4];
+		SECURITY_STATUS rc;
+		int			i;
+
+		Assert(ctx->decryptBufferLen > 0);
+		Assert(ctx->extraLen == 0);
+
+		sbufs[0].pvBuffer = ctx->decryptBuffer;
+		sbufs[0].cbBuffer = ctx->decryptBufferLen;
+		sbufs[0].BufferType = SECBUFFER_DATA;
+		sbufs[1].pvBuffer = NULL;
+		sbufs[1].cbBuffer = 0;
+		sbufs[1].BufferType   = SECBUFFER_EMPTY;
+		sbufs[2].pvBuffer = NULL;
+		sbufs[2].cbBuffer = 0;
+		sbufs[2].BufferType   = SECBUFFER_EMPTY;
+		sbufs[3].pvBuffer = NULL;
+		sbufs[3].cbBuffer = 0;
+		sbufs[3].BufferType   = SECBUFFER_EMPTY;
+
+		sbufdesc.ulVersion = SECBUFFER_VERSION;
+		sbufdesc.cBuffers = 4;
+		sbufdesc.pBuffers = sbufs;
+
+		rc = DecryptMessage(&ctx->ctxHandle, &sbufdesc, 0, NULL);
+
+		if (rc == SEC_E_INCOMPLETE_MESSAGE)
+		{
+			/* We don't have the full message yet. Need more data. */
+			return 0;
+		}
+		if (rc == SEC_I_CONTEXT_EXPIRED)
+		{
+			ctx->remote_shutdown = true;
+			return 0;
+		}
+		if (rc == SEC_I_RENEGOTIATE)
+		{
+			/* TODO */
+			printfPQExpBuffer(&conn->errorMessage,
+							  libpq_gettext("SSL renegotiation not implemented\n\n"));
+			return -1;
+		}
+		if (rc != SEC_E_OK)
+		{
+			/* FIXME: set errno */
+			printfPQExpBuffer(&conn->errorMessage,
+							  libpq_gettext("could not decrypt received data: %s\n"),
+							  getSecurityStatus(rc));
+			return -1;
+		}
+
+		for (i = 0; i < 4; i++)
+		{
+			if (sbufs[i].BufferType == SECBUFFER_EXTRA)
+			{
+				ctx->extraData = sbufs[i].pvBuffer;
+				ctx->extraLen = sbufs[i].cbBuffer;
+			}
+			if (sbufs[i].BufferType == SECBUFFER_DATA)
+			{
+				ctx->decryptedData = sbufs[i].pvBuffer;
+				ctx->decryptedLen = sbufs[i].cbBuffer;
+			}
+		}
+		if (ctx->decryptedLen == 0)
+		{
+			/*
+			 * DecryptMessage didn't decrypt anything. Shift the extra data
+			 * to beginning of buffer, and retry.
+			 */
+			prepareDecryptBufferForRead(ctx);
+		}
+	}
+
+	return 1;
+}
+
+/*
+ *	Write data to a secure connection.
+ *
+ * On failure, this function is responsible for putting a suitable message
+ * into conn->errorMessage.  The caller must still inspect errno, but only
+ * to determine whether to continue/retry after error.
+ */
+ssize_t
+pgtls_write(PGconn *conn, const void *ptr, size_t len)
+{
+	SchannelContext *ctx = conn->windows_schannel;
+	int			sendlen;
+
+	/*
+	 * If the output buffer is empty, encrypt (some of) the input data to fill
+	 * it.
+	 */
+	if (ctx->encryptedLen == 0)
+	{
+		int			plaintextLen;
+
+		plaintextLen = encryptBuffer(conn, ptr, len);
+		if (plaintextLen < 0)
+			return -1;
+		ctx->plaintextLen = plaintextLen;
+	}
+
+	/*
+	 * Write out the encrypted data in the buffer
+	 */
+	sendlen = pqsecure_raw_write(conn,
+								 ctx->encryptedData,
+								 ctx->encryptedLen);
+	if (sendlen <= 0)
+		return sendlen;
+
+	ctx->encryptedData += sendlen;
+	ctx->encryptedLen -= sendlen;
+
+	if (ctx->encryptedLen == 0)
+	{
+		/* sent all the data in the buffer. */
+		return ctx->plaintextLen;
+	}
+	else
+	{
+		/* return 0 to indicate that the caller has to retry. */
+		return 0;
+	}
+}
+
+
+/*
+ * Encrypt more data. Helper function for pgtls_write().
+ */
+static ssize_t
+encryptBuffer(PGconn *conn, const void *ptr, int len)
+{
+	SchannelContext *ctx = conn->windows_schannel;
+	SecPkgContext_StreamSizes sizes;
+	SecBufferDesc sbufdesc;
+	SecBuffer	sbufs[4];
+	SECURITY_STATUS rc;
+
+	/* we should've sent out all encrypted data from previous round first */
+	Assert(ctx->encryptedLen == 0);
+
+	rc = QueryContextAttributes(&ctx->ctxHandle,
+								SECPKG_ATTR_STREAM_SIZES, &sizes);
+	if (rc != SEC_E_OK)
+	{
+		printfPQExpBuffer(&conn->errorMessage,
+						  libpq_gettext("QueryContextAttributes failed: %x"),
+						  rc);
+		return -1;
+	}
+	if (sizes.cbHeader + sizes.cbTrailer + len > ctx->encryptBufferSize)
+		len = ctx->encryptBufferSize - (sizes.cbHeader + sizes.cbTrailer);
+
+	memcpy(&ctx->encryptBuffer[sizes.cbHeader], ptr, len);
+
+	sbufs[0].pvBuffer = ctx->encryptBuffer;
+	sbufs[0].cbBuffer = sizes.cbHeader;
+	sbufs[0].BufferType = SECBUFFER_STREAM_HEADER;
+
+	sbufs[1].pvBuffer = &ctx->encryptBuffer[sizes.cbHeader];
+	sbufs[1].cbBuffer = len;
+	sbufs[1].BufferType = SECBUFFER_DATA;
+
+	sbufs[2].pvBuffer = &ctx->encryptBuffer[sizes.cbHeader + len];
+	sbufs[2].cbBuffer = sizes.cbTrailer;
+	sbufs[2].BufferType = SECBUFFER_STREAM_TRAILER;
+
+	sbufs[3].pvBuffer = NULL;
+	sbufs[3].cbBuffer = 0;
+	sbufs[3].BufferType = SECBUFFER_EMPTY;
+
+	sbufdesc.ulVersion = SECBUFFER_VERSION;
+	sbufdesc.cBuffers = 4;
+	sbufdesc.pBuffers = sbufs;
+
+	rc = EncryptMessage(&ctx->ctxHandle, 0, &sbufdesc, 0);
+	if (rc != SEC_E_OK)
+	{
+		printfPQExpBuffer(&conn->errorMessage,
+						  libpq_gettext("EncryptMessage failed: %x"),
+						  rc);
+		return -1;
+	}
+
+	ctx->encryptedData = ctx->encryptBuffer;
+	ctx->encryptedLen = sbufs[0].cbBuffer + sbufs[1].cbBuffer + sbufs[2].cbBuffer;
+
+	return len;
+}
+
+/**** Certificate handling functions ****/
+
+static bool
+validateServerCert(PGconn *conn)
+{
+	SchannelContext *ctx = conn->windows_schannel;
+	CERT_CONTEXT *remoteCert;
+	SECURITY_STATUS rc;
+	DWORD flags;
+
+	/* Get server certificate */
+	rc = QueryContextAttributes(&ctx->ctxHandle,
+								SECPKG_ATTR_REMOTE_CERT_CONTEXT,
+								(PVOID) &remoteCert);
+	if (rc != SEC_E_OK)
+	{
+		printfPQExpBuffer(&conn->errorMessage,
+						  libpq_gettext("could not get server certificate: %s\n"),
+						  getSecurityStatus(rc));
+		return false;
+	}
+
+	/*
+	 * Per MSDN documentation [1], we must check the following things:
+	 *
+	 * 1. The certificate chain is complete and the root is a certificate from
+	 *    a trusted certification authority (CA).
+	 * 2. The current time is not beyond the begin and end dates for each of
+	 *    the certificates in the certificate chain.
+	 * 3. None of the certificates in the certificate chain have been revoked.
+	 * 4. The depth of the leaf certificate is not deeper than the maximum
+	 *    allowable depth specified in the certificate extension. This check is
+	 *    only necessary if there is a depth specified.
+	 * 5. The usage of the certificate is correct, for example, a client
+	 *    certificate should not be used to authenticate a server.
+	 *
+	 * [1] "Manually Validating Schannel Credential",
+	 *     http://msdn.microsoft.com/en-us/library/windows/desktop/aa378740%28v=vs.85%29.aspx
+	 */
+
+	/*
+	 * 1. Check the chain
+	 * 2. Check expiration
+	 *
+	 * TODO: Currently, we only support client certs signed by the root client
+	 * CA. Intermediary CA's are not supported.
+	 */
+
+	/* TODO: CRL's are not actually supported */
+	flags = CERT_STORE_SIGNATURE_FLAG |
+		CERT_STORE_TIME_VALIDITY_FLAG;
+	if (!CertVerifySubjectCertificateContext(
+			remoteCert,
+			ctx->rootCert,
+			&flags))
+	{
+		printfPQExpBuffer(&conn->errorMessage,
+					  libpq_gettext("could not verify server certificate\n"));
+		return false;
+	}
+	if (flags != 0)
+	{
+		if ((flags & CERT_STORE_SIGNATURE_FLAG) != 0)
+			printfPQExpBuffer(&conn->errorMessage,
+					  libpq_gettext("signature check failed on client cert\n"));
+		else if ((flags & CERT_STORE_REVOCATION_FLAG) != 0)
+			printfPQExpBuffer(&conn->errorMessage,
+					  libpq_gettext("server certificate was revoked\n"));
+		else if ((flags & CERT_STORE_TIME_VALIDITY_FLAG) != 0)
+			printfPQExpBuffer(&conn->errorMessage,
+					  libpq_gettext("server certificate is not valid at this time\n"));
+		else
+			printfPQExpBuffer(&conn->errorMessage,
+					  libpq_gettext("server certificate is not valid\n"));
+
+		return false;
+	}
+
+	/*
+	 * We succeeded to get the issuer's certificate from the trusted root store,
+	 * and the signature on the client cert passed, so we're good to go.
+	 */
+
+	return true;
+}
+
+
+/*
+ * Loads the certificate into a Certificate Context.
+ *
+ * on error, sets an error message in conn and returns NULL.
+ */
+static CERT_CONTEXT *
+loadCert(PGconn *conn, char *path)
+{
+	CERT_CONTEXT *cert_cxt;
+	char		crtBuf[50000]; /* TODO: enlarge as needed */
+	DWORD		crtBufLen = 50000;
+	struct stat statbuf;
+	FILE	   *fp;
+	char	   *content;
+	int			r;
+	int			filelen;
+
+	/* Read file into memory */
+	if (stat(path, &statbuf) != 0)
+	{
+		printfPQExpBuffer(&conn->errorMessage,
+					  libpq_gettext("could not stat certificate file \"%s\""),
+						  path);
+		return NULL;
+	}
+	filelen = statbuf.st_size;
+
+	fp = fopen(path, "rb");
+	if (!fp)
+	{
+		printfPQExpBuffer(&conn->errorMessage,
+					  libpq_gettext("could not open certificate file \"%s\""),
+						  path);
+		return NULL;
+	}
+	content = malloc(filelen);
+	r = fread(content, filelen, 1, fp);
+	if (r != 1 || ferror(fp) || fclose(fp))
+	{
+		free(content);
+		printfPQExpBuffer(&conn->errorMessage,
+					  libpq_gettext("could not read certificate file \"%s\""),
+						  path);
+		return NULL;
+	}
+
+	/* convert from PEM to DER */
+	if (!CryptStringToBinary(
+			content,
+			filelen,
+			CRYPT_STRING_BASE64HEADER,
+			crtBuf,
+			&crtBufLen,
+			NULL,
+			NULL))
+	{
+		free(content);
+		printfPQExpBuffer(&conn->errorMessage,
+						  libpq_gettext("could not convert certificate file \"%s\" from PEM format"),
+						  path);
+		return NULL;
+	}
+	if (crtBufLen > sizeof(crtBuf))
+	{
+		/* TODO: enlarge buffer */
+		free(content);
+		printfPQExpBuffer(&conn->errorMessage,
+						  libpq_gettext("out of memory"));
+		return NULL;
+	}
+
+	cert_cxt = CertCreateCertificateContext(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
+											crtBuf,
+											crtBufLen);
+	if (cert_cxt == NULL)
+	{
+		free(content);
+		printfPQExpBuffer(&conn->errorMessage,
+						  libpq_gettext("could not create certificate context"));
+		return NULL;
+	}
+
+	return cert_cxt;
+
+}
+
+static bool
+loadPrivateKey(PGconn *conn, CERT_CONTEXT *cert, char *path)
+{
+	char		keyBuf[50000]; /* TODO: enlarge as needed */
+	DWORD		keyBufLen = 50000;
+	CERT_KEY_PROV_INFO key;
+	HCRYPTPROV	cryptProvHandle;
+	HCRYPTKEY	keyHandle;
+	PUBLICKEYSTRUC *blob;
+	struct stat statbuf;
+	FILE	   *fp;
+	char	   *content;
+	int			r;
+	int			filelen;
+	char		privKeyBlob[50000];
+	int			privKeyBlobLen = 50000;
+
+	/* Read file into memory */
+	if (stat(path, &statbuf) != 0)
+	{
+		printfPQExpBuffer(&conn->errorMessage,
+					  libpq_gettext("could not read private key file \"%s\""),
+						  path);
+		return false;
+	}
+	filelen = statbuf.st_size;
+
+	fp = fopen(path, "rb");
+	if (!fp)
+	{
+		printfPQExpBuffer(&conn->errorMessage,
+					  libpq_gettext("could not read private key file \"%s\""),
+						  path);
+		return false;
+	}
+	content = malloc(filelen);
+	r = fread(content, filelen, 1, fp);
+	if (r != 1 || ferror(fp) || fclose(fp))
+	{
+		free(content);
+		printfPQExpBuffer(&conn->errorMessage,
+					  libpq_gettext("could not read private key file \"%s\""),
+						  path);
+		return false;
+	}
+
+	/* convert from PEM to DER */
+	if (!CryptStringToBinary(
+			content,
+			filelen,
+			CRYPT_STRING_BASE64HEADER,
+			keyBuf,
+			&keyBufLen,
+			NULL,
+			NULL))
+	{
+		free(content);
+		printfPQExpBuffer(&conn->errorMessage,
+						  libpq_gettext("could not convert private key file \"%s\" from PEM format"),
+						  path);
+		return false;
+	}
+	if (keyBufLen > sizeof(keyBuf))
+	{
+		/* TODO: enlarge buffer */
+		free(content);
+		printfPQExpBuffer(&conn->errorMessage,
+						  libpq_gettext("out of memory"));
+		return false;
+	}
+
+	/*
+	 * Load the key.
+	 */
+	if (!CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
+							 PKCS_RSA_PRIVATE_KEY,
+							 keyBuf,
+							 keyBufLen,
+							 0,
+							 NULL,
+							 privKeyBlob,
+							 &privKeyBlobLen))
+	{
+		free(content);
+		printfPQExpBuffer(&conn->errorMessage,
+					  libpq_gettext("could not parse private key file \"%s\""),
+						  path);
+		return false;
+	}
+	blob = (PUBLICKEYSTRUC *) privKeyBlob;
+
+	if (!CryptAcquireContext(&cryptProvHandle, "mycontainer", MS_ENHANCED_PROV, PROV_RSA_FULL, 0))
+	{
+		free(content);
+		printfPQExpBuffer(&conn->errorMessage,
+						  libpq_gettext("CryptAcquireContext failed"));
+		return false;
+	}
+
+	if (!CryptImportKey(cryptProvHandle, privKeyBlob, privKeyBlobLen,
+						(HCRYPTKEY) NULL, 0, &keyHandle))
+	{
+		free(content);
+		printfPQExpBuffer(&conn->errorMessage,
+						  libpq_gettext("CryptAcquireContext failed"));
+		return false;
+	}
+
+	memset(&keyCtx, 0, sizeof(keyCtx));
+	keyCtx.cbSize = sizeof(keyCtx);
+	keyCtx.hCryptProv = cryptProvHandle;
+	keyCtx.dwKeySpec = AT_KEYEXCHANGE;
+
+	if (!CertSetCertificateContextProperty(cert,
+										   CERT_KEY_CONTEXT_PROP_ID,
+										   0,
+										   &keyCtx))
+	{
+		free(content);
+		printfPQExpBuffer(&conn->errorMessage,
+						  libpq_gettext("CertSetCertificateContextProperty failed"));
+		return false;
+	}
+
+	return true;
+}
+
+static bool
+loadCerts(PGconn *conn)
+{
+	SchannelContext *ctx = conn->windows_schannel;
+	char	   *sslcertfile = NULL;
+	char	   *engine = NULL;
+	char	   *keyname = NULL;
+	char	   *sslkeyfile = NULL;
+	char	   *sslrootcert = NULL;
+	char	   *sslcrl = NULL;
+	bool		ret = false;
+
+	if (!pqsecure_get_ssl_files(conn, &sslcertfile, &engine, &keyname, &sslkeyfile,
+								&sslrootcert, &sslcrl))
+		return false;
+
+#ifdef SCHANNEL_DEBUG
+	fprintf(stderr, "files: %s %s %s\n",
+			sslcertfile ? sslcertfile : "<no client cert>",
+			sslkeyfile ? sslkeyfile : "<no client key>",
+			sslrootcert ? sslrootcert : "<no root cert>",
+			sslcrl ? sslcrl : "<no crl>");
+	fflush(stderr);
+#endif
+
+	if (engine || keyname)
+	{
+		printfPQExpBuffer(&conn->errorMessage,
+						  libpq_gettext("engines not supported"));
+		goto fail;
+	}
+
+	if (sslcertfile)
+	{
+		ctx->clientCert = loadCert(conn, sslcertfile);
+		if (ctx->clientCert == NULL)
+			goto fail;
+
+		/*
+		 * Cool, got the client cert. Also load the private key associated
+		 * with it.
+		 */
+		if (!loadPrivateKey(conn, ctx->clientCert, sslkeyfile))
+			goto fail;
+	}
+
+	/* Load root certificate for validating server certificates */
+	if (sslrootcert)
+	{
+		ctx->rootCert = loadCert(conn, sslrootcert);
+		if (ctx->rootCert == NULL)
+			goto fail;
+	}
+
+	ret = true;
+
+fail:
+	if (sslcertfile)
+		free(sslcertfile);
+	if (engine)
+		free(engine);
+	if (keyname)
+		free(keyname);
+	if (sslkeyfile)
+		free(sslkeyfile);
+	if (sslrootcert)
+		free(sslrootcert);
+	if (sslcrl)
+		free(sslcrl);
+
+#ifdef SCHANNEL_DEBUG
+	fprintf(stderr, "certs loaded: %d\n", ret);
+	fflush(stderr);
+#endif
+
+	return ret;
+}
+
+
+/*
+ *	TODO: need a new API for this.
+ */
+void *
+PQgetssl(PGconn *conn)
+{
+	/*
+	 * return non-zero, so that psql reports that SSL is used. Of course, it's
+	 * totally bogus for anyone who actually tries to get the OpenSSL
+	 * SSL struct.
+	 */
+	return (void *) 1;
+}
+
+/*** Printing error codes ***/
+
+#define ERRORCODE_STR(x) { x, #x }
+
+typedef struct
+{
+	uint32 rc;
+	const char *str;
+} errorcode_str;
+
+static const char *
+getErrorStr(uint32 rc, errorcode_str *map)
+{
+	static char buf[100];
+	int i;
+
+	for (i = 0; map[i].str != NULL; i++)
+	{
+		if (map[i].rc == rc)
+			return map[i].str;
+	}
+	snprintf(buf, sizeof(buf), "0x%X", rc);
+	return buf;
+}
+
+static const char *
+getSecurityStatus(SECURITY_STATUS rc)
+{
+	static errorcode_str sec_status_codes[] = {
+		ERRORCODE_STR(SEC_E_OK),
+		ERRORCODE_STR(SEC_E_INCOMPLETE_MESSAGE),
+		ERRORCODE_STR(SEC_E_INSUFFICIENT_MEMORY),
+		ERRORCODE_STR(SEC_E_INTERNAL_ERROR),
+		ERRORCODE_STR(SEC_E_INVALID_HANDLE),
+		ERRORCODE_STR(SEC_E_INVALID_TOKEN),
+		ERRORCODE_STR(SEC_E_LOGON_DENIED),
+		ERRORCODE_STR(SEC_E_NO_AUTHENTICATING_AUTHORITY),
+		ERRORCODE_STR(SEC_E_NO_CREDENTIALS),
+		ERRORCODE_STR(SEC_I_COMPLETE_AND_CONTINUE),
+		ERRORCODE_STR(SEC_I_COMPLETE_NEEDED),
+		ERRORCODE_STR(SEC_I_CONTINUE_NEEDED),
+
+		ERRORCODE_STR(SEC_E_ALGORITHM_MISMATCH),
+		ERRORCODE_STR(SEC_E_UNSUPPORTED_FUNCTION),
+		ERRORCODE_STR(SEC_E_UNTRUSTED_ROOT),
+		{ 0, NULL }
+	};
+	return getErrorStr(rc, sec_status_codes);
+}
diff --git a/src/interfaces/libpq/fe-secure.c b/src/interfaces/libpq/fe-secure.c
index 66778b2..e19fc61 100644
--- a/src/interfaces/libpq/fe-secure.c
+++ b/src/interfaces/libpq/fe-secure.c
@@ -475,3 +475,297 @@ pq_reset_sigpipe(sigset_t *osigset, bool sigpipe_pending, bool got_epipe)
 }
 
 #endif   /* ENABLE_THREAD_SAFETY && !WIN32 */
+
+/*
+ *	Determine the filenames of SSL related files.
+ *
+ * The filenames returned are checked to exist; the caller should throw an
+ * error if one of the returned files can not be opened. This function
+ * performs some preliminariry sanity checks, e.g. if a certificate file
+ * exists, we must also find the corresponding private key file.
+ *
+ * *sslcert is set to the path of the client's certificate, and *sslkey to
+ * the path to the corresponding private key.  If compiled with
+ * USE_SSL_ENGINE, a pathname containing a colon is interpreted as a
+ * a two-part "engin:key" string, for specifying a hardware key.
+ *
+ * *sslrootcert and *sslcrl are set to the paths to CA certificate and
+ * CRL files, used to validate the server certificate.
+ *
+ * The returned strings are malloc'd, and the caller is responsible for
+ * freeing them. On error, returns false and sets the libpq error message.
+ */
+bool
+pqsecure_get_ssl_files(PGconn *conn, char **sslcert, char **sslkey,
+					   char **engine, char **keyname,
+					   char **sslrootcert, char **sslcrl)
+{
+	struct stat buf;
+	char		homedir[MAXPGPATH];
+	char		fnbuf[MAXPGPATH];
+	char		sebuf[256];
+	bool		have_homedir;
+
+	*sslcert = NULL;
+	*sslkey = NULL;
+	*engine = NULL;
+	*keyname = NULL;
+	*sslrootcert = NULL;
+	*sslcrl = NULL;
+
+	/*
+	 * We'll need the home directory if any of the relevant parameters are
+	 * defaulted.  If pqGetHomeDirectory fails, act as though none of the
+	 * files could be found.
+	 */
+	if (!(conn->sslcert && strlen(conn->sslcert) > 0) ||
+		!(conn->sslkey && strlen(conn->sslkey) > 0) ||
+		!(conn->sslrootcert && strlen(conn->sslrootcert) > 0) ||
+		!(conn->sslcrl && strlen(conn->sslcrl) > 0))
+		have_homedir = pqGetHomeDirectory(homedir, sizeof(homedir));
+	else	/* won't need it */
+		have_homedir = false;
+
+	/* Find the client certificate file */
+	if (conn->sslcert && strlen(conn->sslcert) > 0)
+		strncpy(fnbuf, conn->sslcert, sizeof(fnbuf));
+	else if (have_homedir)
+		snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, USER_CERT_FILE);
+	else
+		fnbuf[0] = '\0';
+
+	if (fnbuf[0] == '\0')
+	{
+		/* no home directory, proceed without a client cert */
+	}
+	else if (stat(fnbuf, &buf) != 0)
+	{
+		/*
+		 * If file is not present, just go on without a client cert; server
+		 * might or might not accept the connection.  Any other error,
+		 * however, is grounds for complaint.
+		 */
+		if (errno != ENOENT && errno != ENOTDIR)
+		{
+			printfPQExpBuffer(&conn->errorMessage,
+			   libpq_gettext("could not open certificate file \"%s\": %s\n"),
+							  fnbuf, pqStrerror(errno, sebuf, sizeof(sebuf)));
+			goto fail;
+		}
+	}
+	else
+	{
+		*sslcert = strdup(fnbuf);
+		if (*sslcert == NULL)
+		{
+			printfPQExpBuffer(&conn->errorMessage,
+							  libpq_gettext("out of memory\n"));
+			goto fail;
+		}
+	}
+
+	/*
+	 * Read the SSL key. If a key is specified, treat it as an engine:key
+	 * combination if there is colon present - we don't support files with
+	 * colon in the name. The exception is if the second character is a colon,
+	 * in which case it can be a Windows filename with drive specification.
+	 */
+	if (*sslcert && conn->sslkey && strlen(conn->sslkey) > 0)
+	{
+#ifdef USE_SSL_ENGINE
+		if (strchr(conn->sslkey, ':')
+#ifdef WIN32
+			&& conn->sslkey[1] != ':'
+#endif
+			)
+		{
+			/* Colon, but not in second character, treat as engine:key */
+			char	   *engine_str = strdup(conn->sslkey);
+			char	   *engine_colon;
+
+			if (engine_str == NULL)
+			{
+				printfPQExpBuffer(&conn->errorMessage,
+								  libpq_gettext("out of memory\n"));
+				goto fail;
+			}
+
+			/* cannot return NULL because we already checked before strdup */
+			engine_colon = strchr(engine_str, ':');
+
+			*engine_colon = '\0';		/* engine_str now has engine name */
+			engine_colon++;		/* engine_colon now has key name */
+
+			*engine = strdup(engine_str);
+			if (*engine)
+				*keyname = strdup(engine_colon);
+
+			free(engine_str);
+
+			if (*engine == NULL || *keyname == NULL)
+			{
+				printfPQExpBuffer(&conn->errorMessage,
+								  libpq_gettext("out of memory\n"));
+				goto fail;
+			}
+
+			fnbuf[0] = '\0';	/* indicate we're not going to load from a
+								 * file */
+		}
+		else
+#endif   /* USE_SSL_ENGINE */
+		{
+			/* PGSSLKEY is not an engine, treat it as a filename */
+			strncpy(fnbuf, conn->sslkey, sizeof(fnbuf));
+		}
+	}
+	else if (have_homedir)
+	{
+		/* No PGSSLKEY specified, load default file */
+		snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, USER_KEY_FILE);
+	}
+	else
+		fnbuf[0] = '\0';
+
+	if (*sslcert && fnbuf[0] != '\0')
+	{
+		/* read the client key from file */
+
+		if (stat(fnbuf, &buf) != 0)
+		{
+			printfPQExpBuffer(&conn->errorMessage,
+							  libpq_gettext("certificate present, but not private key file \"%s\"\n"),
+							  fnbuf);
+			goto fail;
+		}
+#ifndef WIN32
+		if (!S_ISREG(buf.st_mode) || buf.st_mode & (S_IRWXG | S_IRWXO))
+		{
+			printfPQExpBuffer(&conn->errorMessage,
+							  libpq_gettext("private key file \"%s\" has group or world access; permissions should be u=rw (0600) or less\n"),
+							  fnbuf);
+			goto fail;
+		}
+#endif
+
+		*sslkey = strdup(fnbuf);
+		if (*sslkey == NULL)
+		{
+			printfPQExpBuffer(&conn->errorMessage,
+							  libpq_gettext("out of memory\n"));
+			goto fail;
+		}
+	}
+
+	/*
+	 * If the root cert file exists, load it so we can perform certificate
+	 * verification. If sslmode is "verify-full" we will also do further
+	 * verification after the connection has been completed.
+	 */
+	if (conn->sslrootcert && strlen(conn->sslrootcert) > 0)
+		strncpy(fnbuf, conn->sslrootcert, sizeof(fnbuf));
+	else if (have_homedir)
+		snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, ROOT_CERT_FILE);
+	else
+		fnbuf[0] = '\0';
+
+	if (fnbuf[0] != '\0' &&
+		stat(fnbuf, &buf) == 0)
+	{
+		*sslrootcert = strdup(fnbuf);
+		if (*sslrootcert == NULL)
+		{
+			printfPQExpBuffer(&conn->errorMessage,
+							  libpq_gettext("out of memory\n"));
+			goto fail;
+		}
+	}
+	else
+	{
+		/*
+		 * stat() failed; assume root file doesn't exist.  If sslmode is
+		 * verify-ca or verify-full, this is an error.  Otherwise, continue
+		 * without performing any server cert verification.
+		 */
+		if (conn->sslmode[0] == 'v')	/* "verify-ca" or "verify-full" */
+		{
+			/*
+			 * The only way to reach here with an empty filename is if
+			 * pqGetHomeDirectory failed.  That's a sufficiently unusual case
+			 * that it seems worth having a specialized error message for it.
+			 */
+			if (fnbuf[0] == '\0')
+				printfPQExpBuffer(&conn->errorMessage,
+								  libpq_gettext("could not get home directory to locate root certificate file\n"
+												"Either provide the file or change sslmode to disable server certificate verification.\n"));
+			else
+				printfPQExpBuffer(&conn->errorMessage,
+				libpq_gettext("root certificate file \"%s\" does not exist\n"
+							  "Either provide the file or change sslmode to disable server certificate verification.\n"), fnbuf);
+			goto fail;
+		}
+	}
+
+	/* If we have a root certificate, also try to load a CRL */
+	if (*sslrootcert)
+	{
+		if (conn->sslcrl && strlen(conn->sslcrl) > 0)
+			strncpy(fnbuf, conn->sslcrl, sizeof(fnbuf));
+		else if (have_homedir)
+			snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, ROOT_CRL_FILE);
+		else
+			fnbuf[0] = '\0';
+
+		if (fnbuf[0] != '\0' &&
+			stat(fnbuf, &buf) != 0)
+		{
+			/*
+			 * If not found, silently ignore;  we do not require CRL.
+			 * Any other error, however, is grounds for complaint.
+			 */
+			if (errno != ENOENT && errno != ENOTDIR)
+			{
+				printfPQExpBuffer(&conn->errorMessage,
+			   libpq_gettext("could not open certificate file \"%s\": %s\n"),
+							  fnbuf, pqStrerror(errno, sebuf, sizeof(sebuf)));
+				goto fail;
+			}
+		}
+		else
+		{
+			*sslcrl = strdup(fnbuf);
+			if (*sslcrl == NULL)
+			{
+				printfPQExpBuffer(&conn->errorMessage,
+								  libpq_gettext("out of memory\n"));
+				goto fail;
+			}
+		}
+	}
+
+	/* all done! */
+	return true;
+
+fail:
+	if (*sslcert)
+		free(*sslcert);
+	if (*engine)
+		free(*engine);
+	if (*keyname)
+		free(*keyname);
+	if (*sslkey)
+		free(*sslkey);
+	if (*sslrootcert)
+		free(*sslrootcert);
+	if (*sslcrl)
+		free(*sslcrl);
+
+	*sslcert = NULL;
+	*engine = NULL;
+	*keyname = NULL;
+	*sslkey = NULL;
+	*sslrootcert = NULL;
+	*sslcrl = NULL;
+
+	return false;
+}
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index 6032904..35b1c9e 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -82,6 +82,10 @@ typedef struct
 #endif
 #endif   /* USE_OPENSSL */
 
+#ifdef USE_WINDOWS_SCHANNEL
+struct SchannelContext;
+#endif
+
 /*
  * POSTGRES backend dependent Constants.
  */
@@ -440,6 +444,10 @@ struct pg_conn
 #endif   /* USE_OPENSSL */
 #endif   /* USE_SSL */
 
+#ifdef USE_WINDOWS_SCHANNEL
+	struct SchannelContext *windows_schannel;
+#endif   /* USE_WINDOWS_SCHANNEL */
+
 #ifdef ENABLE_GSS
 	gss_ctx_id_t gctx;			/* GSS context */
 	gss_name_t	gtarg_nam;		/* GSS target name */
@@ -627,6 +635,8 @@ extern ssize_t pqsecure_write(PGconn *, const void *ptr, size_t len);
 extern ssize_t pqsecure_raw_read(PGconn *, void *ptr, size_t len);
 extern ssize_t pqsecure_raw_write(PGconn *, const void *ptr, size_t len);
 
+extern bool pqsecure_get_ssl_files(PGconn *, char **sslcert, char **sslkey, char **engine, char **keyname, char **sslrootcert, char **sslcrl);
+
 #if defined(ENABLE_THREAD_SAFETY) && !defined(WIN32)
 extern int	pq_block_sigpipe(sigset_t *osigset, bool *sigpipe_pending);
 extern void pq_reset_sigpipe(sigset_t *osigset, bool sigpipe_pending,
diff --git a/src/interfaces/libpq/win32.mak b/src/interfaces/libpq/win32.mak
index 39a0bc9..5793fce 100644
--- a/src/interfaces/libpq/win32.mak
+++ b/src/interfaces/libpq/win32.mak
@@ -127,6 +127,9 @@ CLEAN :
 !IFDEF USE_OPENSSL
 	-@erase "$(INTDIR)\fe-secure-openssl.obj"
 !ENDIF
+!IFDEF USE_WINDOWS_SCHANNEL
+	-@erase "$(INTDIR)\fe-secure-winschannel.obj"
+!ENDIF
 
 
 LIB32=link.exe -lib
@@ -170,7 +173,9 @@ LIB32_OBJS= \
 !IFDEF USE_OPENSSL
 	LIB32_OBJS=$(LIB32_OBJS) "$(INTDIR)\fe-secure-openssl.obj"
 !ENDIF
-
+!IFDEF USE_WINDOWS_SCHANNEL
+	LIB32_OBJS=$(LIB32_OBJS) "$(INTDIR)\fe-secure-winschannel.obj"
+!ENDIF
 
 config: ..\..\include\pg_config.h ..\..\include\pg_config_ext.h pg_config_paths.h  ..\..\include\pg_config_os.h
 
@@ -200,6 +205,11 @@ CPP_PROJ=$(CPP_PROJ) /D USE_OPENSSL
 SSL_LIBS=ssleay32.lib libeay32.lib gdi32.lib
 !ENDIF
 
+!IFDEF USE_WINDOWS_SCHANNEL
+CPP_PROJ=$(CPP_PROJ) /D USE_WINDOWS_SCHANNEL
+SSL_LIBS=crypt32.lib
+!ENDIF
+
 !IFDEF USE_KFW
 CPP_PROJ=$(CPP_PROJ) /D KRB5
 KFW_LIBS=krb5_32.lib comerr32.lib gssapi32.lib
diff --git a/src/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pm
index e6fb3ec..fa2ac8c 100644
--- a/src/tools/msvc/Mkvcbuild.pm
+++ b/src/tools/msvc/Mkvcbuild.pm
@@ -115,6 +115,10 @@ sub mkvcbuild
 	$postgres->AddDefine('BUILDING_DLL');
 	$postgres->AddLibrary('secur32.lib');
 	$postgres->AddLibrary('ws2_32.lib');
+	if ($solution->{options}->{winschannel})
+	{
+		$postgres->AddLibrary('crypt32.lib');
+	}
 	$postgres->AddLibrary('wldap32.lib') if ($solution->{options}->{ldap});
 	$postgres->FullExportDLL('postgres.lib');
 	# The OBJS scraper doesn't know about ifdefs, so remove be-secure-openssl.c
@@ -123,6 +127,11 @@ sub mkvcbuild
 	{
 		$postgres->RemoveFile('src\backend\libpq\be-secure-openssl.c');
 	}
+	# and same for winschannel
+	if (!$solution->{options}->{winschannel})
+	{
+		$postgres->RemoveFile('src\backend\libpq\be-secure-winschannel.c');
+	}
 
 	my $snowball = $solution->AddProject('dict_snowball', 'dll', '',
 		'src\backend\snowball');
@@ -276,6 +285,10 @@ sub mkvcbuild
 	$libpq->AddDefine('UNSAFE_STAT_OK');
 	$libpq->AddIncludeDir('src\port');
 	$libpq->AddLibrary('secur32.lib');
+	if ($solution->{options}->{winschannel})
+	{
+		$libpq->AddLibrary('crypt32.lib');
+	}
 	$libpq->AddLibrary('ws2_32.lib');
 	$libpq->AddLibrary('wldap32.lib') if ($solution->{options}->{ldap});
 	$libpq->UseDef('src\interfaces\libpq\libpqdll.def');
@@ -288,6 +301,11 @@ sub mkvcbuild
 	{
 		$libpq->RemoveFile('src\interfaces\libpq\fe-secure-openssl.c');
 	}
+	# and same for winschannel
+	if (!$solution->{options}->{winschannel})
+	{
+		$libpq->RemoveFile('src\interfaces\libpq\fe-secure-winschannel.c');
+	}
 
 	my $libpqwalreceiver =
 	  $solution->AddProject('libpqwalreceiver', 'dll', '',
diff --git a/src/tools/msvc/Solution.pm b/src/tools/msvc/Solution.pm
index 39e41f6..7034eea 100644
--- a/src/tools/msvc/Solution.pm
+++ b/src/tools/msvc/Solution.pm
@@ -183,6 +183,8 @@ sub GenerateFiles
 		print O "#define USE_LDAP 1\n"   if ($self->{options}->{ldap});
 		print O "#define HAVE_LIBZ 1\n"  if ($self->{options}->{zlib});
 		print O "#define USE_OPENSSL 1\n" if ($self->{options}->{openssl});
+		print O "#define USE_WINDOWS_SCHANNEL 1\n"
+		  if ($self->{options}->{winschannel});
 		print O "#define ENABLE_NLS 1\n" if ($self->{options}->{nls});
 
 		print O "#define BLCKSZ ", 1024 * $self->{options}->{blocksize}, "\n";
@@ -629,6 +631,7 @@ sub GetFakeConfigure
 	$cfg .= ' --without-zlib' unless ($self->{options}->{zlib});
 	$cfg .= ' --with-extra-version' if ($self->{options}->{extraver});
 	$cfg .= ' --with-openssl'       if ($self->{options}->{openssl});
+	$cfg .= ' --with-winschannel'   if ($self->{options}->{winschannel});
 	$cfg .= ' --with-ossp-uuid'     if ($self->{options}->{uuid});
 	$cfg .= ' --with-libxml'        if ($self->{options}->{xml});
 	$cfg .= ' --with-libxslt'       if ($self->{options}->{xslt});
diff --git a/src/tools/msvc/config_default.pl b/src/tools/msvc/config_default.pl
index e4d4810..ab4001a 100644
--- a/src/tools/msvc/config_default.pl
+++ b/src/tools/msvc/config_default.pl
@@ -17,6 +17,7 @@ our $config = {
 	perl     => undef,   # --with-perl
 	python   => undef,   # --with-python=<path>
 	openssl  => undef,   # --with-openssl=<path>
+	winschannel => 1,    # --with-winschannel
 	uuid     => undef,   # --with-ossp-uuid
 	xml      => undef,   # --with-libxml=<path>
 	xslt     => undef,   # --with-libxslt=<path>
#30Robert Haas
robertmhaas@gmail.com
In reply to: Heikki Linnakangas (#29)
Re: Supporting Windows SChannel as OpenSSL replacement

On Tue, Aug 12, 2014 at 1:52 PM, Heikki Linnakangas
<hlinnakangas@vmware.com> wrote:

This isn't a showstopper, but needs some thought. As the patch stands, it
uses a single key container called "PostgreSQL server key container", and
makes no attempt to delete the keys after they're no longer used. That
works, but it leaves the key lying on the system.

What about using something like 'PostgreSQL ' || system_identifier?

Would it make sense to have pg_ctl unregister delete the key
container, or do we need a separate facility for that?

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#31Jeff Janes
jeff.janes@gmail.com
In reply to: Heikki Linnakangas (#29)
Re: Supporting Windows SChannel as OpenSSL replacement

On Tue, Aug 12, 2014 at 10:52 AM, Heikki Linnakangas <
hlinnakangas@vmware.com> wrote:

On 08/06/2014 08:37 PM, Jeff Janes wrote:

But now it looks like 0002 needs a rebase....

I've committed the refactoring patch, and here's a rebased and improved
version of the Windows SChannel implementation over that.

On MinGW, I get the following error when compiling with options
--host=x86_64-w64-mingw32 --without-zlib:

be-secure.c: In function 'secure_open_server':
be-secure.c:106:2: error: 'Port' has no member named 'peer_cn'
be-secure.c:106:2: error: 'Port' has no member named 'peer_cn'
make[3]: *** [be-secure.o] Error 1
make[2]: *** [libpq-recursive] Error 2
make[1]: *** [all-backend-recurse] Error 2
make: *** [all-src-recurse] Error 2

Should the ereport DEBUG2 be inside the "#ifdef USE_SSL"?

Thanks,

Jeff

#32Heikki Linnakangas
hlinnakangas@vmware.com
In reply to: Jeff Janes (#31)
Re: Supporting Windows SChannel as OpenSSL replacement

On 08/15/2014 08:16 PM, Jeff Janes wrote:

On Tue, Aug 12, 2014 at 10:52 AM, Heikki Linnakangas <
hlinnakangas@vmware.com> wrote:

On 08/06/2014 08:37 PM, Jeff Janes wrote:

But now it looks like 0002 needs a rebase....

I've committed the refactoring patch, and here's a rebased and improved
version of the Windows SChannel implementation over that.

On MinGW, I get the following error when compiling with options
--host=x86_64-w64-mingw32 --without-zlib:

be-secure.c: In function 'secure_open_server':
be-secure.c:106:2: error: 'Port' has no member named 'peer_cn'
be-secure.c:106:2: error: 'Port' has no member named 'peer_cn'
make[3]: *** [be-secure.o] Error 1
make[2]: *** [libpq-recursive] Error 2
make[1]: *** [all-backend-recurse] Error 2
make: *** [all-src-recurse] Error 2

Should the ereport DEBUG2 be inside the "#ifdef USE_SSL"?

Yeah.

I've been thinking though, perhaps we should always have the ssl_in_use,
peer_cn and peer_cert_valid members in the Port struct. If not compiled
with USE_SSL, they would just always be false/NULL. Then we wouldn't
need #ifdefs around all the places that check hose fields either.

- Heikki

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#33Tom Lane
tgl@sss.pgh.pa.us
In reply to: Heikki Linnakangas (#32)
Re: Supporting Windows SChannel as OpenSSL replacement

Heikki Linnakangas <hlinnakangas@vmware.com> writes:

On 08/15/2014 08:16 PM, Jeff Janes wrote:

Should the ereport DEBUG2 be inside the "#ifdef USE_SSL"?

Yeah.

I've been thinking though, perhaps we should always have the ssl_in_use,
peer_cn and peer_cert_valid members in the Port struct. If not compiled
with USE_SSL, they would just always be false/NULL. Then we wouldn't
need #ifdefs around all the places that check hose fields either.

+1. This would also make it less risky for add-on code to touch the Port
struct.

regards, tom lane

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#34Robert Haas
robertmhaas@gmail.com
In reply to: Heikki Linnakangas (#29)
Re: Supporting Windows SChannel as OpenSSL replacement

On Tue, Aug 12, 2014 at 1:52 PM, Heikki Linnakangas
<hlinnakangas@vmware.com> wrote:

On 08/06/2014 08:37 PM, Jeff Janes wrote:

But now it looks like 0002 needs a rebase....

I've committed the refactoring patch, and here's a rebased and improved
version of the Windows SChannel implementation over that.

Server-side support is now implemented too, but it's all very crude and
work-in-progress. CRLs are not supported, intermediary CAs are not
supported, and probably many other bells and whistles are missing too. But
the basics work, including cert authentication. Consider this a Proof of
Concept.

Heikki, do you have any plans to work more on this?

Or does anyone else?

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#35Tom Lane
tgl@sss.pgh.pa.us
In reply to: Robert Haas (#34)
Re: Supporting Windows SChannel as OpenSSL replacement

Robert Haas <robertmhaas@gmail.com> writes:

Heikki, do you have any plans to work more on this?
Or does anyone else?

FWIW, I have some interest in the Apple Secure Transport patch that
is in the CF queue, and will probably pick that up at some point if
no one beats me to it (but it's not real high on my to-do list).
I won't be touching the Windows version though. I suspect that the
folk who might be competent to review the Windows code may have
correspondingly little interest in the macOS patch. This is a bit
of a problem, since it would be good for someone to look at both of
them, with an eye to whether there are any places in our SSL abstraction
API that ought to be rethought now that we have actual non-OpenSSL
implementations to compare to.

regards, tom lane

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#36Robert Haas
robertmhaas@gmail.com
In reply to: Tom Lane (#35)
Re: Supporting Windows SChannel as OpenSSL replacement

On Wed, Oct 18, 2017 at 2:50 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:

Robert Haas <robertmhaas@gmail.com> writes:

Heikki, do you have any plans to work more on this?
Or does anyone else?

FWIW, I have some interest in the Apple Secure Transport patch that
is in the CF queue, and will probably pick that up at some point if
no one beats me to it (but it's not real high on my to-do list).
I won't be touching the Windows version though. I suspect that the
folk who might be competent to review the Windows code may have
correspondingly little interest in the macOS patch. This is a bit
of a problem, since it would be good for someone to look at both of
them, with an eye to whether there are any places in our SSL abstraction
API that ought to be rethought now that we have actual non-OpenSSL
implementations to compare to.

Well, the best way to handle that might be to get some of this stuff
done before we get too much later into the release cycle, so that
there's time to tinker with it before the release goes out the door
(or is deep in beta). However, if nobody's working on this and the
other patch is someplace far down your to-do list, then I guess that
isn't going to happen.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#37Satyanarayana Narlapuram
Satyanarayana.Narlapuram@microsoft.com
In reply to: Tom Lane (#35)
Re: Supporting Windows SChannel as OpenSSL replacement

Tom, Robert, Microsoft is interested in supporting windows SChannel for Postgres. Please let know how we can help taking this forward. We would love contributing to this either by enhancing the original patch provided by Heikki, or test the changes on Windows.

Thanks,
Satya

-----Original Message-----
From: pgsql-hackers-owner@postgresql.org [mailto:pgsql-hackers-owner@postgresql.org] On Behalf Of Tom Lane
Sent: Wednesday, October 18, 2017 11:51 AM
To: Robert Haas <robertmhaas@gmail.com>
Cc: hlinnaka <hlinnaka@iki.fi>; Jeff Janes <jeff.janes@gmail.com>; Andreas Karlsson <andreas@proxel.se>; Martijn van Oosterhout <kleptog@svana.org>; Magnus Hagander <magnus@hagander.net>; PostgreSQL-development <pgsql-hackers@postgresql.org>
Subject: Re: [HACKERS] Supporting Windows SChannel as OpenSSL replacement

Robert Haas <robertmhaas@gmail.com> writes:

Heikki, do you have any plans to work more on this?
Or does anyone else?

FWIW, I have some interest in the Apple Secure Transport patch that is in the CF queue, and will probably pick that up at some point if no one beats me to it (but it's not real high on my to-do list).
I won't be touching the Windows version though. I suspect that the folk who might be competent to review the Windows code may have correspondingly little interest in the macOS patch. This is a bit of a problem, since it would be good for someone to look at both of them, with an eye to whether there are any places in our SSL abstraction API that ought to be rethought now that we have actual non-OpenSSL implementations to compare to.

regards, tom lane

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription:
https://na01.safelinks.protection.outlook.com/?url=http%3A%2F%2Fwww.postgresql.org%2Fmailpref%2Fpgsql-hackers&amp;data=02%7C01%7CSatyanarayana.Narlapuram%40microsoft.com%7C99f781c4865e46f8e69408d5165965f0%7C72f988bf86f141af91ab2d7cd011db47%7C1%7C0%7C636439495588088189&amp;sdata=4I%2FYNAtDb63%2BGbSIgh6XVmfKZlbq1YewZ2mkAJkQVKE%3D&amp;reserved=0

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#38Robert Haas
robertmhaas@gmail.com
In reply to: Satyanarayana Narlapuram (#37)
Re: Supporting Windows SChannel as OpenSSL replacement

On Thu, Oct 19, 2017 at 1:15 AM, Satyanarayana Narlapuram
<Satyanarayana.Narlapuram@microsoft.com> wrote:

Tom, Robert, Microsoft is interested in supporting windows SChannel for Postgres. Please let know how we can help taking this forward. We would love contributing to this either by enhancing the original patch provided by Heikki, or test the changes on Windows.

That would be great! I think the first thing to do would be look over
Heikki's comments about what was left to be done and maybe try to do
some of those things. And then test the result. :-)

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers