Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit c89404e

Browse files
committed
Fix libpq certificate validation for SSL connections.
Add config parameter "sslverify" to control the verification. Default is to do full verification. Clean up some old SSL code that never really worked.
1 parent e7d8bfb commit c89404e

File tree

5 files changed

+139
-77
lines changed

5 files changed

+139
-77
lines changed

doc/src/sgml/libpq.sgml

+49-4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<!-- $PostgreSQL: pgsql/doc/src/sgml/libpq.sgml,v 1.268 2008/11/09 00:28:34 tgl Exp $ -->
1+
<!-- $PostgreSQL: pgsql/doc/src/sgml/libpq.sgml,v 1.269 2008/11/13 09:45:24 mha Exp $ -->
22

33
<chapter id="libpq">
44
<title><application>libpq</application> - C Library</title>
@@ -259,6 +259,33 @@
259259
</listitem>
260260
</varlistentry>
261261

262+
<varlistentry>
263+
<term><literal>sslverify</literal></term>
264+
<listitem>
265+
<para>
266+
This option controls how libpq verifies the certificate on the
267+
server when performing an <acronym>SSL</> connection. There are
268+
three options: <literal>none</> disables verification completely
269+
(not recommended!); <literal>cert</> enables verification that
270+
the certificate chains to a known CA only; <literal>cn</> will
271+
both verify that the certificate chains to a known CA and that
272+
the <literal>cn</> attribute of the certificate matches the
273+
hostname the connection is being made to (default).
274+
</para>
275+
276+
<para>
277+
It is always recommended to use the <literal>cn</> value for
278+
this parameter, since this is the only option that prevents
279+
man-in-the-middle attacks. Note that this requires the server
280+
name on the certificate to match exactly with the host name
281+
used for the connection, and therefore does not support connections
282+
to aliased names. It can be used with pure IP address connections
283+
only if the certificate also has just the IP address in the
284+
<literal>cn</> field.
285+
</para>
286+
</listitem>
287+
</varlistentry>
288+
262289
<varlistentry>
263290
<term><literal>requiressl</literal></term>
264291
<listitem>
@@ -5679,6 +5706,22 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough)
56795706
</para>
56805707
</listitem>
56815708

5709+
<listitem>
5710+
<para>
5711+
<indexterm>
5712+
<primary><envar>PGSSLVERIFY</envar></primary>
5713+
</indexterm>
5714+
<envar>PGSSLVERIFY</envar> controls how libpq verifies the certificate on the
5715+
server when performing an <acronym>SSL</> connection. There are
5716+
three options: <literal>none</> disables verification completely
5717+
(not recommended!); <literal>cert</> enables verification that
5718+
the certificate chains to a known CA only; <literal>cn</> will
5719+
both verify that the certificate chains to a known CA and that
5720+
the <literal>cn</> attribute of the certificate matches the
5721+
hostname the connection is being made to (default).
5722+
</para>
5723+
</listitem>
5724+
56825725
<listitem>
56835726
<para>
56845727
<indexterm>
@@ -6026,9 +6069,11 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough)
60266069
</para>
60276070

60286071
<para>
6029-
To verify the server certificate is trustworthy, place certificates of
6030-
the certificate authorities (<acronym>CA</acronym>) you trust in the
6031-
file <filename>~/.postgresql/root.crt</> in the user's home directory.
6072+
When the <literal>sslverify</> parameter is set to <literal>cn</> or
6073+
<literal>cert</>, libpq will verify that the server certificate is
6074+
trustworthy by checking the certificate chain up to a <acronym>CA</>.
6075+
For this to work, place the certificate of a trusted <acronym>CA</>
6076+
in the file <filename>~/.postgresql/root.crt</> in the user's home directory.
60326077
(On Microsoft Windows the file is named
60336078
<filename>%APPDATA%\postgresql\root.crt</filename>.)
60346079
<application>libpq</application> will then verify that the server's

doc/src/sgml/runtime.sgml

+9-7
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<!-- $PostgreSQL: pgsql/doc/src/sgml/runtime.sgml,v 1.419 2008/11/04 04:18:50 momjian Exp $ -->
1+
<!-- $PostgreSQL: pgsql/doc/src/sgml/runtime.sgml,v 1.420 2008/11/13 09:45:24 mha Exp $ -->
22

33
<chapter Id="runtime">
44
<title>Operating System Environment</title>
@@ -1418,9 +1418,9 @@ $ <userinput>kill -INT `head -1 /usr/local/pgsql/data/postmaster.pid`</userinput
14181418
<filename>server.key</filename> (key) and
14191419
<filename>server.crt</filename> (certificate) files (<xref
14201420
linkend="ssl-tcp">). The TCP client must connect using
1421-
<literal>sslmode='require'</> (<xref linkend="libpq-connect">) and have
1422-
a <filename>~/.postgresql/root.crt</> SSL certificate (<xref
1423-
linkend="libpq-ssl">).
1421+
<literal>sslmode='require'</>, specify <literal>sslverify='cn'</>
1422+
or <literal>sslverify='cert'</> and have the required certificate
1423+
files present (<xref linkend="libpq-connect">).
14241424
</para>
14251425
</sect1>
14261426

@@ -1544,8 +1544,8 @@ $ <userinput>kill -INT `head -1 /usr/local/pgsql/data/postmaster.pid`</userinput
15441544

15451545
<listitem>
15461546
<para>
1547-
It is possible for both the client and server to provide SSL keys
1548-
or certificates to each other. It takes some extra configuration
1547+
It is possible for both the client and server to provide SSL
1548+
certificates to each other. It takes some extra configuration
15491549
on each side, but this provides stronger verification of identity
15501550
than the mere use of passwords. It prevents a computer from
15511551
pretending to be the server just long enough to read the password
@@ -1757,7 +1757,9 @@ chmod og-rwx server.key
17571757
A self-signed certificate can be used for testing, but a certificate
17581758
signed by a certificate authority (<acronym>CA</>) (either one of the
17591759
global <acronym>CAs</> or a local one) should be used in production
1760-
so the client can verify the server's identity.
1760+
so the client can verify the server's identity. If all the clients
1761+
are local to the organization, using a local <acronym>CA</> is
1762+
recommended.
17611763
</para>
17621764

17631765
</sect2>

src/interfaces/libpq/fe-connect.c

+28-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/interfaces/libpq/fe-connect.c,v 1.367 2008/11/09 00:28:35 tgl Exp $
11+
* $PostgreSQL: pgsql/src/interfaces/libpq/fe-connect.c,v 1.368 2008/11/13 09:45:24 mha Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -92,8 +92,10 @@ static int ldapServiceLookup(const char *purl, PQconninfoOption *options,
9292
#define DefaultPassword ""
9393
#ifdef USE_SSL
9494
#define DefaultSSLMode "prefer"
95+
#define DefaultSSLVerify "cn"
9596
#else
9697
#define DefaultSSLMode "disable"
98+
#define DefaultSSLVerify "none"
9799
#endif
98100

99101
/* ----------
@@ -181,6 +183,9 @@ static const PQconninfoOption PQconninfoOptions[] = {
181183
{"sslmode", "PGSSLMODE", DefaultSSLMode, NULL,
182184
"SSL-Mode", "", 8}, /* sizeof("disable") == 8 */
183185

186+
{"sslverify", "PGSSLVERIFY", DefaultSSLVerify, NULL,
187+
"SSL-Verify", "", 5}, /* sizeof("chain") == 5 */
188+
184189
#if defined(KRB5) || defined(ENABLE_GSS) || defined(ENABLE_SSPI)
185190
/* Kerberos and GSSAPI authentication support specifying the service name */
186191
{"krbsrvname", "PGKRBSRVNAME", PG_KRB_SRVNAM, NULL,
@@ -415,6 +420,8 @@ connectOptions1(PGconn *conn, const char *conninfo)
415420
conn->connect_timeout = tmp ? strdup(tmp) : NULL;
416421
tmp = conninfo_getval(connOptions, "sslmode");
417422
conn->sslmode = tmp ? strdup(tmp) : NULL;
423+
tmp = conninfo_getval(connOptions, "sslverify");
424+
conn->sslverify = tmp ? strdup(tmp) : NULL;
418425
#ifdef USE_SSL
419426
tmp = conninfo_getval(connOptions, "requiressl");
420427
if (tmp && tmp[0] == '1')
@@ -529,6 +536,24 @@ connectOptions2(PGconn *conn)
529536
else
530537
conn->sslmode = strdup(DefaultSSLMode);
531538

539+
/*
540+
* Validate sslverify option
541+
*/
542+
if (conn->sslverify)
543+
{
544+
if (strcmp(conn->sslverify, "none") != 0
545+
&& strcmp(conn->sslverify, "cert") != 0
546+
&& strcmp(conn->sslverify, "cn") != 0)
547+
{
548+
conn->status = CONNECTION_BAD;
549+
printfPQExpBuffer(&conn->errorMessage,
550+
libpq_gettext("invalid sslverify value: \"%s\"\n"),
551+
conn->sslverify);
552+
return false;
553+
}
554+
}
555+
556+
532557
/*
533558
* Only if we get this far is it appropriate to try to connect. (We need a
534559
* state flag, rather than just the boolean result of this function, in
@@ -2008,6 +2033,8 @@ freePGconn(PGconn *conn)
20082033
free(conn->pgpass);
20092034
if (conn->sslmode)
20102035
free(conn->sslmode);
2036+
if (conn->sslverify)
2037+
free(conn->sslverify);
20112038
#if defined(KRB5) || defined(ENABLE_GSS) || defined(ENABLE_SSPI)
20122039
if (conn->krbsrvname)
20132040
free(conn->krbsrvname);

src/interfaces/libpq/fe-secure.c

+51-64
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
*
1212
*
1313
* IDENTIFICATION
14-
* $PostgreSQL: pgsql/src/interfaces/libpq/fe-secure.c,v 1.106 2008/10/24 12:29:11 mha Exp $
14+
* $PostgreSQL: pgsql/src/interfaces/libpq/fe-secure.c,v 1.107 2008/11/13 09:45:25 mha Exp $
1515
*
1616
* NOTES
1717
*
@@ -87,9 +87,7 @@
8787
#define ERR_pop_to_mark() ((void) 0)
8888
#endif
8989

90-
#ifdef NOT_USED
91-
static int verify_peer_name_matches_certificate(PGconn *);
92-
#endif
90+
static bool verify_peer_name_matches_certificate(PGconn *);
9391
static int verify_cb(int ok, X509_STORE_CTX *ctx);
9492
static int client_cert_cb(SSL *, X509 **, EVP_PKEY **);
9593
static int init_ssl_system(PGconn *conn);
@@ -438,77 +436,44 @@ verify_cb(int ok, X509_STORE_CTX *ctx)
438436
return ok;
439437
}
440438

441-
#ifdef NOT_USED
442439
/*
443440
* Verify that common name resolves to peer.
444441
*/
445-
static int
442+
static bool
446443
verify_peer_name_matches_certificate(PGconn *conn)
447444
{
448-
struct hostent *cn_hostentry = NULL;
449-
struct sockaddr server_addr;
450-
struct sockaddr_in *sin (struct sockaddr_in *) &server_addr;
451-
ACCEPT_TYPE_ARG3 len;
452-
char **s;
453-
unsigned long l;
454-
455-
/* Get the address on the other side of the socket. */
456-
len = sizeof(server_addr);
457-
if (getpeername(conn->sock, &server_addr, &len) == -1)
458-
{
459-
char sebuf[256];
460-
461-
printfPQExpBuffer(&conn->errorMessage,
462-
libpq_gettext("error querying socket: %s\n"),
463-
SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
464-
return -1;
465-
}
445+
/*
446+
* If told not to verify the peer name, don't do it. Return
447+
* 0 indicating that the verification was successful.
448+
*/
449+
if(strcmp(conn->sslverify, "cn") != 0)
450+
return true;
466451

467-
if (server_addr.sa_family != AF_INET)
452+
if (conn->pghostaddr)
468453
{
469454
printfPQExpBuffer(&conn->errorMessage,
470-
libpq_gettext("unsupported protocol\n"));
471-
return -1;
455+
libpq_gettext("verified SSL connections are only supported when connecting to a hostname"));
456+
return false;
472457
}
473-
474-
/* Get the IP addresses of the certificate's common name (CN) */
458+
else
475459
{
476-
struct hostent hpstr;
477-
char buf[BUFSIZ];
478-
int herrno = 0;
479-
480460
/*
481-
* Currently, pqGethostbyname() is used only on platforms that don't
482-
* have getaddrinfo(). If you enable this function, you should
483-
* convert the pqGethostbyname() function call to use getaddrinfo().
461+
* Connect by hostname.
462+
*
463+
* XXX: Should support alternate names here
464+
* XXX: Should support wildcard certificates here
484465
*/
485-
pqGethostbyname(conn->peer_cn, &hpstr, buf, sizeof(buf),
486-
&cn_hostentry, &herrno);
487-
}
488-
489-
/* Did we get an IP address? */
490-
if (cn_hostentry == NULL)
491-
{
492-
printfPQExpBuffer(&conn->errorMessage,
493-
libpq_gettext("could not get information about host \"%s\": %s\n"),
494-
conn->peer_cn, hstrerror(h_errno));
495-
return -1;
466+
if (pg_strcasecmp(conn->peer_cn, conn->pghost) != 0)
467+
{
468+
printfPQExpBuffer(&conn->errorMessage,
469+
libpq_gettext("server common name '%s' does not match hostname '%s'"),
470+
conn->peer_cn, conn->pghost);
471+
return false;
472+
}
473+
else
474+
return true;
496475
}
497-
498-
/* Does one of the CN's IP addresses match the server's IP address? */
499-
for (s = cn_hostentry->h_addr_list; *s != NULL; s++)
500-
if (!memcmp(&sin->sin_addr.s_addr, *s, cn_hostentry->h_length))
501-
return 0;
502-
503-
l = ntohl(sin->sin_addr.s_addr);
504-
printfPQExpBuffer(&conn->errorMessage,
505-
libpq_gettext(
506-
"server common name \"%s\" does not resolve to %ld.%ld.%ld.%ld\n"),
507-
conn->peer_cn, (l >> 24) % 0x100, (l >> 16) % 0x100,
508-
(l >> 8) % 0x100, l % 0x100);
509-
return -1;
510476
}
511-
#endif /* NOT_USED */
512477

513478
/*
514479
* Callback used by SSL to load client cert and key.
@@ -846,6 +811,12 @@ initialize_SSL(PGconn *conn)
846811
if (init_ssl_system(conn))
847812
return -1;
848813

814+
/*
815+
* If sslverify is set to anything other than "none", perform certificate
816+
* verification. If set to "cn" we will also do further verifications after
817+
* the connection has been completed.
818+
*/
819+
849820
/* Set up to verify server cert, if root.crt is present */
850821
if (pqGetHomeDirectory(homedir, sizeof(homedir)))
851822
{
@@ -889,6 +860,24 @@ initialize_SSL(PGconn *conn)
889860

890861
SSL_CTX_set_verify(SSL_context, SSL_VERIFY_PEER, verify_cb);
891862
}
863+
else
864+
{
865+
if (strcmp(conn->sslverify, "none") != 0)
866+
{
867+
printfPQExpBuffer(&conn->errorMessage,
868+
libpq_gettext("root certificate file (%s) not found"), fnbuf);
869+
return -1;
870+
}
871+
}
872+
}
873+
else
874+
{
875+
if (strcmp(conn->sslverify, "none") != 0)
876+
{
877+
printfPQExpBuffer(&conn->errorMessage,
878+
libpq_gettext("cannot find home directory to locate root certificate file"));
879+
return -1;
880+
}
892881
}
893882

894883
/* set up mechanism to provide client certificate, if available */
@@ -1004,13 +993,11 @@ open_client_SSL(PGconn *conn)
1004993
NID_commonName, conn->peer_cn, SM_USER);
1005994
conn->peer_cn[SM_USER] = '\0';
1006995

1007-
#ifdef NOT_USED
1008-
if (verify_peer_name_matches_certificate(conn) == -1)
996+
if (!verify_peer_name_matches_certificate(conn))
1009997
{
1010998
close_SSL(conn);
1011999
return PGRES_POLLING_FAILED;
10121000
}
1013-
#endif
10141001

10151002
/* SSL handshake is complete */
10161003
return PGRES_POLLING_OK;

src/interfaces/libpq/libpq-int.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
1313
* Portions Copyright (c) 1994, Regents of the University of California
1414
*
15-
* $PostgreSQL: pgsql/src/interfaces/libpq/libpq-int.h,v 1.136 2008/10/28 12:10:44 mha Exp $
15+
* $PostgreSQL: pgsql/src/interfaces/libpq/libpq-int.h,v 1.137 2008/11/13 09:45:25 mha Exp $
1616
*
1717
*-------------------------------------------------------------------------
1818
*/
@@ -291,6 +291,7 @@ struct pg_conn
291291
char *pguser; /* Postgres username and password, if any */
292292
char *pgpass;
293293
char *sslmode; /* SSL mode (require,prefer,allow,disable) */
294+
char *sslverify; /* Verify server SSL certificate (none,chain,cn) */
294295
#if defined(KRB5) || defined(ENABLE_GSS) || defined(ENABLE_SSPI)
295296
char *krbsrvname; /* Kerberos service name */
296297
#endif

0 commit comments

Comments
 (0)