Commit 397831e1 authored by Bruce Momjian's avatar Bruce Momjian

At long last I put together a patch to support 4 client SSL negotiation

modes (and replace the requiressl boolean). The four options were first
spelled out by Magnus Hagander <mha@sollentuna.net> on 2000-08-23 in email
to pgsql-hackers, archived here:

http://archives.postgresql.org/pgsql-hackers/2000-08/msg00639.php

My original less-flexible patch and the ensuing thread are archived at:

http://dbforums.com/t623845.html

Attached is a new patch, including documentation.

To sum up, there's a new client parameter "sslmode" and environment
variable "PGSSLMODE", with these options:

sslmode   description
-------   -----------
disable   Unencrypted non-SSL only
allow     Negotiate, prefer non-SSL
prefer    Negotiate, prefer SSL (default)
require   Require SSL

The only change to the server is a new pg_hba.conf line type,
"hostnossl", for specifying connections that are not allowed to use SSL
(for example, to prevent servers on a local network from accidentally
using SSL and wasting cycles). Thus the 3 pg_hba.conf line types are:

pg_hba.conf line types
----------------------
host       applies to either SSL or regular connections
hostssl    applies only to SSL connections
hostnossl  applies only to regular connections

These client and server options, the postgresql.conf ssl = false option,
and finally the possibility of compiling with no SSL support at all,
make quite a range of combinations to test. I threw together a test
script to try many of them out. It's in a separate tarball with its
config files, a patch to psql so it'll announce SSL connections even in
absence of a tty, and the test output. The test is especially informative
when run on the same tty the postmaster was started on, so the FATAL:
errors during negotiation are interleaved with the psql client output.

I saw Tom write that new submissions for 7.4 have to be in before midnight
local time, and since I'm on the east coast in the US, this just makes it
in before the bell. :)

Jon Jensen
parent 5f2499d5
<!--
$Header: /cvsroot/pgsql/doc/src/sgml/client-auth.sgml,v 1.52 2003/06/25 01:20:50 momjian Exp $
$Header: /cvsroot/pgsql/doc/src/sgml/client-auth.sgml,v 1.53 2003/07/26 13:50:01 momjian Exp $
-->
<chapter id="client-authentication">
......@@ -83,13 +83,15 @@ $Header: /cvsroot/pgsql/doc/src/sgml/client-auth.sgml,v 1.52 2003/06/25 01:20:50
</para>
<para>
A record may have one of the five formats
A record may have one of the seven formats
<synopsis>
local <replaceable>database</replaceable> <replaceable>user</replaceable> <replaceable>authentication-method</replaceable> <optional><replaceable>authentication-option</replaceable></optional>
host <replaceable>database</replaceable> <replaceable>user</replaceable> <replaceable>IP-address</replaceable> <replaceable>IP-mask</replaceable> <replaceable>authentication-method</replaceable> <optional><replaceable>authentication-option</replaceable></optional>
hostssl <replaceable>database</replaceable> <replaceable>user</replaceable> <replaceable>IP-address</replaceable> <replaceable>IP-mask</replaceable> <replaceable>authentication-method</replaceable> <optional><replaceable>authentication-option</replaceable></optional>
hostnossl <replaceable>database</replaceable> <replaceable>user</replaceable> <replaceable>IP-address</replaceable> <replaceable>IP-mask</replaceable> <replaceable>authentication-method</replaceable> <optional><replaceable>authentication-option</replaceable></optional>
host <replaceable>database</replaceable> <replaceable>user</replaceable> <replaceable>IP-address</replaceable>/<replaceable>CIDR-mask</replaceable> <replaceable>authentication-method</replaceable> <optional><replaceable>authentication-option</replaceable></optional>
hostssl <replaceable>database</replaceable> <replaceable>user</replaceable> <replaceable>IP-address</replaceable>/<replaceable>CIDR-mask</replaceable> <replaceable>authentication-method</replaceable> <optional><replaceable>authentication-option</replaceable></optional>
hostnossl <replaceable>database</replaceable> <replaceable>user</replaceable> <replaceable>IP-address</replaceable>/<replaceable>CIDR-mask</replaceable> <replaceable>authentication-method</replaceable> <optional><replaceable>authentication-option</replaceable></optional>
</synopsis>
The meaning of the fields is as follows:
......@@ -136,6 +138,17 @@ hostssl <replaceable>database</replaceable> <replaceable>user</replaceable> <
</listitem>
</varlistentry>
<varlistentry>
<term><literal>hostnossl</literal></term>
<listitem>
<para>
This record is similar to <literal>hostssl</> but with the
opposite logic: it matches only regular connection attempts not
using SSL.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><replaceable>database</replaceable></term>
<listitem>
......@@ -196,8 +209,8 @@ hostssl <replaceable>database</replaceable> <replaceable>user</replaceable> <
</para>
<para>
These fields only apply to <literal>host</literal> and
<literal>hostssl</literal> records.
These fields only apply to <literal>host</literal>,
<literal>hostssl</literal>, and <literal>hostnossl</> records.
</para>
</listitem>
</varlistentry>
......@@ -224,8 +237,8 @@ hostssl <replaceable>database</replaceable> <replaceable>user</replaceable> <
</para>
<para>
This field only applies to <literal>host</literal> and
<literal>hostssl</literal> records.
This field only applies to <literal>host</literal>,
<literal>hostssl</literal>, and <literal>hostnossl</> records.
</para>
</listitem>
</varlistentry>
......
<!--
$Header: /cvsroot/pgsql/doc/src/sgml/libpq.sgml,v 1.128 2003/07/23 17:27:28 momjian Exp $
$Header: /cvsroot/pgsql/doc/src/sgml/libpq.sgml,v 1.129 2003/07/26 13:50:01 momjian Exp $
-->
<chapter id="libpq">
......@@ -206,14 +206,44 @@ PGconn *PQconnectdb(const char *conninfo);
</listitem>
</varlistentry>
<varlistentry>
<term><literal>sslmode</literal></term>
<listitem>
<para>
This option determines whether or with what priority an <acronym>SSL</>
connection will be negotiated with the server. There are four
modes: <literal>disable</> will attempt only an unencrypted
<acronym>SSL</> connection; <literal>allow</> will negotiate,
trying first a non-<acronym>SSL</> connection, then if that fails,
trying an <acronym>SSL</> connection; <literal>prefer</>
(the default) will negotiate, trying first an <acronym>SSL</> connection,
then if that fails, trying a regular non-<acronym>SSL</> connection;
<literal>require</> will try only an <acronym>SSL</> connection.
</para>
<para>
If <productname>PostgreSQL</> is compiled without SSL support,
using option <literal>require</> will cause an error, and options
<literal>allow</> and <literal>prefer</> will be tolerated but
<application>libpq</> will be unable to negotiate an <acronym>SSL</>
connection.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>requiressl</literal></term>
<listitem>
<para>
If set to 1, an <acronym>SSL</acronym> connection to the server is required.
This option is deprecated in favor of the <literal>sslmode</>
setting.
</para>
<para>
If set to 1, an <acronym>SSL</acronym> connection to the server is required
(this is equivalent to <literal>sslmode</> <literal>require</>).
<application>libpq</> will then refuse to connect if the server does not
accept an <acronym>SSL</acronym> connection.
If set to 0 (default), <application>libpq</> will negotiate the connection type with server.
If set to 0 (default), <application>libpq</> will negotiate the connection
type with the server (equivalent to <literal>sslmode</> <literal>prefer</>).
This option is only available if
<productname>PostgreSQL</> is compiled with SSL support.
</para>
......@@ -3140,6 +3170,27 @@ the <productname>PostgreSQL</productname> server.
</listitem>
<listitem>
<para>
<indexterm>
<primary><envar>PGSSLMODE</envar></primary>
</indexterm>
<envar>PGSSLMODE</envar> determines whether and with what priority an
<acronym>SSL</> connection will be negotiated with the server. There are
four modes: <literal>disable</> will attempt only an unencrypted
<acronym>SSL</> connection; <literal>allow</> will negotiate,
trying first a non-<acronym>SSL</> connection, then if that fails,
trying an <acronym>SSL</> connection; <literal>prefer</>
(the default) will negotiate, trying first an <acronym>SSL</>
connection, then if that fails, trying a regular non-<acronym>SSL</>
connection; <literal>require</> will try only an <acronym>SSL</>
connection. If <productname>PostgreSQL</> is compiled without SSL support,
using option <literal>require</> will cause an error, and options
<literal>allow</> and <literal>prefer</> will be tolerated but
<application>libpq</> will be unable to negotiate an <acronym>SSL</>
connection.
</para>
</listitem>
<listitem>
<para>
<indexterm>
<primary><envar>PGREQUIRESSL</envar></primary>
</indexterm>
......@@ -3147,8 +3198,10 @@ the <productname>PostgreSQL</productname> server.
made over <acronym>SSL</acronym>. If set to
<quote>1</quote>, <application>libpq</>
will refuse to connect if the server does not accept
an <acronym>SSL</acronym> connection.
This option is only available if
an <acronym>SSL</acronym> connection (equivalent to <literal>sslmode</>
<literal>prefer</>).
This option is deprecated in favor of the <literal>sslmode</>
setting, and is only available if
<productname>PostgreSQL</> is compiled with SSL support.
</para>
</listitem>
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/libpq/auth.c,v 1.105 2003/07/23 23:30:40 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/libpq/auth.c,v 1.106 2003/07/26 13:50:02 momjian Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -439,10 +439,16 @@ ClientAuthentication(Port *port)
NULL, 0,
NI_NUMERICHOST);
#ifdef USE_SSL
#define EREPORT_SSL_STATUS (port->ssl ? "on" : "off")
#else
#define EREPORT_SSL_STATUS "off"
#endif
ereport(FATAL,
(errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
errmsg("no pg_hba.conf entry for host \"%s\", user \"%s\", database \"%s\"",
hostinfo, port->user_name, port->database_name)));
errmsg("no pg_hba.conf entry for host \"%s\", user \"%s\", database \"%s\", SSL \"%s\"",
hostinfo, port->user_name, port->database_name, EREPORT_SSL_STATUS)));
break;
}
......
......@@ -10,7 +10,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/libpq/hba.c,v 1.107 2003/07/23 23:30:40 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/libpq/hba.c,v 1.108 2003/07/26 13:50:02 momjian Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -595,10 +595,12 @@ parse_hba(List *line, hbaPort *port, bool *found_p, bool *error_p)
if (port->raddr.addr.ss_family != AF_UNIX)
return;
}
else if (strcmp(token, "host") == 0 || strcmp(token, "hostssl") == 0)
else if (strcmp(token, "host") == 0
|| strcmp(token, "hostssl") == 0
|| strcmp(token, "hostnossl") == 0)
{
if (strcmp(token, "hostssl") == 0)
if (token[4] == 's') /* "hostssl" */
{
#ifdef USE_SSL
/* Record does not match if we are not on an SSL connection */
......@@ -614,6 +616,14 @@ parse_hba(List *line, hbaPort *port, bool *found_p, bool *error_p)
goto hba_syntax;
#endif
}
#ifdef USE_SSL
else if (token[4] == 'n') /* "hostnossl" */
{
/* Record does not match if we are on an SSL connection */
if (port->ssl)
return;
}
#endif
/* Get the database. */
line = lnext(line);
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v 1.253 2003/07/23 23:30:41 tgl Exp $
* $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v 1.254 2003/07/26 13:50:02 momjian Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -60,6 +60,11 @@ long ioctlsocket_ret;
#define DefaultOption ""
#define DefaultAuthtype ""
#define DefaultPassword ""
#ifdef USE_SSL
#define DefaultSSLMode "prefer"
#else
#define DefaultSSLMode "disable"
#endif
/* ----------
......@@ -131,10 +136,22 @@ static const PQconninfoOption PQconninfoOptions[] = {
"Backend-Debug-Options", "D", 40},
#ifdef USE_SSL
/*
* "requiressl" is deprecated, its purpose having been taken over
* by "sslmode". It remains for backwards compatibility.
*/
{"requiressl", "PGREQUIRESSL", "0", NULL,
"Require-SSL", "", 1},
"Require-SSL", "D", 1},
#endif
/*
* "sslmode" option is allowed even without client SSL support
* because the client can still handle SSL modes "disable" and
* "allow".
*/
{"sslmode", "PGSSLMODE", DefaultSSLMode, NULL,
"SSL-Mode", "", 8}, /* sizeof("disable") == 8 */
/* Terminating entry --- MUST BE LAST */
{NULL, NULL, NULL, NULL,
NULL, NULL, 0}
......@@ -340,10 +357,17 @@ connectOptions1(PGconn *conn, const char *conninfo)
conn->pgpass = tmp ? strdup(tmp) : NULL;
tmp = conninfo_getval(connOptions, "connect_timeout");
conn->connect_timeout = tmp ? strdup(tmp) : NULL;
tmp = conninfo_getval(connOptions, "sslmode");
conn->sslmode = tmp ? strdup(tmp) : NULL;
#ifdef USE_SSL
tmp = conninfo_getval(connOptions, "requiressl");
if (tmp && tmp[0] == '1')
conn->require_ssl = true;
{
/* here warn that the requiressl option is deprecated? */
if (conn->sslmode)
free(conn->sslmode);
conn->sslmode = "require";
}
#endif
/*
......@@ -412,6 +436,46 @@ connectOptions2(PGconn *conn)
}
#endif
/*
* validate sslmode option
*/
if (conn->sslmode)
{
if (strcmp(conn->sslmode, "disable") != 0
&& strcmp(conn->sslmode, "allow") != 0
&& strcmp(conn->sslmode, "prefer") != 0
&& strcmp(conn->sslmode, "require") != 0)
{
conn->status = CONNECTION_BAD;
printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("unknown sslmode \"%s\" requested\n"),
conn->sslmode);
return false;
}
#ifndef USE_SSL
switch (conn->sslmode[0]) {
case 'a': /* "allow" */
case 'p': /* "prefer" */
/*
* warn user that an SSL connection will never be
* negotiated since SSL was not compiled in?
*/
break;
case 'r': /* "require" */
conn->status = CONNECTION_BAD;
printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("sslmode \"%s\" invalid when SSL "
"support is not compiled in\n"),
conn->sslmode);
return false;
}
#endif
}
else
conn->sslmode = DefaultSSLMode;
return true;
}
......@@ -878,6 +942,14 @@ connectDBStart(PGconn *conn)
goto connect_errReturn;
}
#ifdef USE_SSL
/* setup values based on SSL mode */
if (conn->sslmode[0] == 'd') /* "disable" */
conn->allow_ssl_try = false;
else if (conn->sslmode[0] == 'a') /* "allow" */
conn->wait_ssl_try = true;
#endif
/*
* Set up to try to connect, with protocol 3.0 as the first attempt.
*/
......@@ -1278,9 +1350,8 @@ retry_connect:
{
/* Don't bother requesting SSL over a Unix socket */
conn->allow_ssl_try = false;
conn->require_ssl = false;
}
if (conn->allow_ssl_try && conn->ssl == NULL)
if (conn->allow_ssl_try && !conn->wait_ssl_try && conn->ssl == NULL)
{
ProtocolVersion pv;
......@@ -1384,13 +1455,22 @@ retry_ssl_read:
}
else if (SSLok == 'N')
{
if (conn->require_ssl)
{
/* Require SSL, but server does not want it */
printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("server does not support SSL, but SSL was required\n"));
goto error_return;
switch (conn->sslmode[0]) {
case 'r': /* "require" */
/* Require SSL, but server does not want it */
printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("server does not support SSL, but SSL was required\n"));
goto error_return;
case 'a': /* "allow" */
/*
* normal startup already failed,
* so SSL failure means the end
*/
printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("server does not support SSL, and previous non-SSL attempt failed\n"));
goto error_return;
}
/* Otherwise, proceed with normal startup */
conn->allow_ssl_try = false;
conn->status = CONNECTION_MADE;
......@@ -1401,13 +1481,22 @@ retry_ssl_read:
/* Received error - probably protocol mismatch */
if (conn->Pfdebug)
fprintf(conn->Pfdebug, "Postmaster reports error, attempting fallback to pre-7.0.\n");
if (conn->require_ssl)
{
/* Require SSL, but server is too old */
printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("server does not support SSL, but SSL was required\n"));
goto error_return;
switch (conn->sslmode[0]) {
case 'r': /* "require" */
/* Require SSL, but server is too old */
printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("server does not support SSL, but SSL was required\n"));
goto error_return;
case 'a': /* "allow" */
/*
* normal startup already failed,
* so SSL failure means the end
*/
printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("server does not support SSL, and previous non-SSL attempt failed\n"));
goto error_return;
}
/* Otherwise, try again without SSL */
conn->allow_ssl_try = false;
/* Assume it ain't gonna handle protocol 3, either */
......@@ -1594,6 +1683,45 @@ retry_ssl_read:
}
/* OK, we read the message; mark data consumed */
conn->inStart = conn->inCursor;
#ifdef USE_SSL
/*
* if sslmode is "allow" and we haven't tried an
* SSL connection already, then retry with an SSL connection
*/
if (conn->wait_ssl_try
&& conn->ssl == NULL
&& conn->allow_ssl_try)
{
conn->wait_ssl_try = false;
/* Must drop the old connection */
closesocket(conn->sock);
conn->sock = -1;
conn->status = CONNECTION_NEEDED;
goto keep_going;
}
/*
* if sslmode is "prefer" and we're in an SSL
* connection and we haven't already tried a non-SSL
* for "allow", then do a non-SSL retry
*/
if (!conn->wait_ssl_try
&& conn->ssl
&& conn->allow_ssl_try
&& conn->sslmode[0] == 'p') /* "prefer" */
{
conn->allow_ssl_try = false;
/* Must drop the old connection */
pqsecure_close(conn);
closesocket(conn->sock);
conn->sock = -1;
free(conn->ssl);
conn->status = CONNECTION_NEEDED;
goto keep_going;
}
#endif
goto error_return;
}
......@@ -1645,6 +1773,44 @@ retry_ssl_read:
if (fe_sendauth(areq, conn, conn->pghost, conn->pgpass,
conn->errorMessage.data) != STATUS_OK)
{
#ifdef USE_SSL
/*
* if sslmode is "allow" and we haven't tried an
* SSL connection already, then retry with an SSL connection
*/
if (conn->wait_ssl_try
&& conn->ssl == NULL
&& conn->allow_ssl_try)
{
conn->wait_ssl_try = false;
/* Must drop the old connection */
closesocket(conn->sock);
conn->sock = -1;
conn->status = CONNECTION_NEEDED;
goto keep_going;
}
/*
* if sslmode is "prefer" and we're in an SSL
* connection and we haven't already tried a non-SSL
* for "allow", then do a non-SSL retry
*/
if (!conn->wait_ssl_try
&& conn->ssl
&& conn->allow_ssl_try
&& conn->sslmode[0] == 'p') /* "prefer" */
{
conn->allow_ssl_try = false;
/* Must drop the old connection */
pqsecure_close(conn);
closesocket(conn->sock);
conn->sock = -1;
free(conn->ssl);
conn->status = CONNECTION_NEEDED;
goto keep_going;
}
#endif
conn->errorMessage.len = strlen(conn->errorMessage.data);
goto error_return;
}
......
......@@ -12,7 +12,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: libpq-int.h,v 1.76 2003/06/23 19:20:25 tgl Exp $
* $Id: libpq-int.h,v 1.77 2003/07/26 13:50:02 momjian Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -316,9 +316,11 @@ struct pg_conn
PGresult *result; /* result being constructed */
PGresAttValue *curTuple; /* tuple currently being read */
char *sslmode; /* SSL mode option string */
#ifdef USE_SSL
bool allow_ssl_try; /* Allowed to try SSL negotiation */
bool require_ssl; /* Require SSL to make connection */
bool wait_ssl_try; /* Delay SSL negotiation until after
attempting normal connection */
SSL *ssl; /* SSL status, if have SSL connection */
X509 *peer; /* X509 cert of server */
char peer_dn[256 + 1]; /* peer distinguished name */
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment