diff --git a/doc/src/sgml/client-auth.sgml b/doc/src/sgml/client-auth.sgml index cad51caca9c7ff634fb240cc85e93a17eaa0a7d9..e6180c762eec9e5f8fd082cf93087d846195e5b7 100644 --- a/doc/src/sgml/client-auth.sgml +++ b/doc/src/sgml/client-auth.sgml @@ -1,5 +1,5 @@ <!-- -$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> diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml index fa2db652b267156c16b43077c8ca6043200588be..1ebbd314439b7e6998bca3217d75e91a4e6c1985 100644 --- a/doc/src/sgml/libpq.sgml +++ b/doc/src/sgml/libpq.sgml @@ -1,5 +1,5 @@ <!-- -$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> diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c index a24f0978466a126510b41053399fac4d62c58184..fd8d54a012db06e5c64cea309b9ac95e9e5f57e6 100644 --- a/src/backend/libpq/auth.c +++ b/src/backend/libpq/auth.c @@ -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; } diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c index 0d98e729a40eb4e60cacd03a6636ef8e8b2a79fe..b233ee235d84139ef24cb905d2f429373def18b0 100644 --- a/src/backend/libpq/hba.c +++ b/src/backend/libpq/hba.c @@ -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); diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c index 0518cd21b4c75c070731a54e766f0ce72fb7afeb..6688e570381e1efd699101a73d94d28cc05fcfbb 100644 --- a/src/interfaces/libpq/fe-connect.c +++ b/src/interfaces/libpq/fe-connect.c @@ -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; } diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h index d97db7c88b1189220fbba05b0020cb8b055f8709..d05681d37f03887ef2cd54d83d16a0f7fe5d9d90 100644 --- a/src/interfaces/libpq/libpq-int.h +++ b/src/interfaces/libpq/libpq-int.h @@ -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 */